@openwop/openwop-conformance 1.2.0 → 1.4.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/CHANGELOG.md +156 -1
- package/README.md +3 -2
- package/api/asyncapi.yaml +8 -0
- package/api/openapi.yaml +371 -1
- package/api/redocly.yaml +15 -0
- package/coverage.md +26 -5
- package/fixtures/conformance-agent-reasoning-streaming.json +37 -0
- package/fixtures/conformance-dispatch-cancellable-child.json +27 -0
- package/fixtures/conformance-dispatch-deterministic-fail-child.json +30 -0
- package/fixtures/conformance-dispatch-input-mapping-no-default.json +49 -0
- package/fixtures/conformance-dispatch-per-worker-override.json +59 -0
- package/fixtures/conformance-envelope-nl-to-format-engaged.json +41 -0
- package/fixtures/conformance-envelope-recovery-applied.json +39 -0
- package/fixtures/conformance-envelope-refusal.json +38 -0
- package/fixtures/conformance-envelope-retry-attempted.json +39 -0
- package/fixtures/conformance-envelope-retry-exhausted.json +38 -0
- package/fixtures/conformance-envelope-truncated.json +39 -0
- package/fixtures/conformance-envelope-truncation-cap-exhaustion.json +39 -0
- package/fixtures/conformance-model-capability-insufficient.json +25 -0
- package/fixtures/conformance-multi-agent-confidence-escalation.json +49 -0
- package/fixtures/conformance-multi-agent-handoff-child.json +27 -0
- package/fixtures/conformance-multi-agent-handoff.json +49 -0
- package/fixtures/conformance-prompt-all-four-kinds.json +39 -0
- package/fixtures/conformance-prompt-end-to-end.json +33 -0
- package/fixtures/conformance-subworkflow-input-mapping-no-default.json +33 -0
- package/fixtures/conformance-subworkflow-mid-run-mutation-child.json +31 -0
- package/fixtures/conformance-subworkflow-mid-run-mutation.json +33 -0
- package/fixtures/openwop-smoke-cost-emit.json +37 -0
- package/fixtures/prompt-templates/conformance-prompt-few-shot-2.json +14 -0
- package/fixtures/prompt-templates/conformance-prompt-few-shot.json +14 -0
- package/fixtures/prompt-templates/conformance-prompt-schema-hint.json +14 -0
- package/fixtures/prompt-templates/conformance-prompt-secret-redaction.json +23 -0
- package/fixtures/prompt-templates/conformance-prompt-trust-marker.json +23 -0
- package/fixtures/prompt-templates/conformance-prompt-writer-system.json +15 -0
- package/fixtures/prompt-templates/conformance-prompt-writer-user.json +15 -0
- package/fixtures.md +45 -0
- package/package.json +1 -1
- package/schemas/README.md +5 -0
- package/schemas/agent-manifest.schema.json +16 -0
- package/schemas/capabilities.schema.json +390 -0
- package/schemas/core-conformance-mock-agent-config.schema.json +5 -0
- package/schemas/envelopes/clarification.request.schema.json +9 -0
- package/schemas/envelopes/error.schema.json +4 -0
- package/schemas/envelopes/schema.request.schema.json +4 -0
- package/schemas/envelopes/schema.response.schema.json +1 -1
- package/schemas/node-pack-manifest.schema.json +28 -0
- package/schemas/orchestrator-decision.schema.json +12 -0
- package/schemas/prompt-kind.schema.json +8 -0
- package/schemas/prompt-pack-manifest.schema.json +80 -0
- package/schemas/prompt-ref.schema.json +40 -0
- package/schemas/prompt-template.schema.json +149 -0
- package/schemas/registry-version-manifest.schema.json +5 -0
- package/schemas/run-ancestry-response.schema.json +54 -0
- package/schemas/run-event-payloads.schema.json +513 -11
- package/schemas/run-event.schema.json +17 -1
- package/schemas/run-snapshot.schema.json +3 -2
- package/schemas/workflow-definition.schema.json +19 -1
- package/src/lib/driver.ts +15 -0
- package/src/lib/env.ts +51 -0
- package/src/lib/event-log-query.ts +62 -0
- package/src/lib/fixtures.ts +38 -1
- package/src/lib/host-toggle.ts +54 -0
- package/src/lib/llm-cache-key-recipe.ts +68 -0
- package/src/lib/multi-agent-capabilities.ts +10 -0
- package/src/lib/otel-scrape.ts +59 -0
- package/src/scenarios/agentReasoningStreaming.test.ts +193 -0
- package/src/scenarios/aiEnvelope.capBreached.test.ts +97 -9
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +224 -15
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +257 -25
- package/src/scenarios/aiEnvelope.redaction.test.ts +210 -29
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +163 -24
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +262 -12
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +107 -16
- package/src/scenarios/blob-presign-expiry.test.ts +42 -9
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/cache-ttl-expiry.test.ts +34 -8
- package/src/scenarios/cost-attribution.test.ts +124 -11
- package/src/scenarios/cross-engine-append-ordering.test.ts +99 -0
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +136 -0
- package/src/scenarios/cross-host-causation-shape.test.ts +117 -0
- package/src/scenarios/cross-host-traceparent-propagation.test.ts +60 -0
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +34 -3
- package/src/scenarios/dispatch-input-mapping.test.ts +75 -6
- package/src/scenarios/dispatch-output-mapping.test.ts +96 -6
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +223 -0
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +152 -0
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +343 -0
- package/src/scenarios/envelope-reasoning-shape.test.ts +190 -0
- package/src/scenarios/envelope-recovery-applied.test.ts +229 -0
- package/src/scenarios/envelope-refusal-shape.test.ts +289 -0
- package/src/scenarios/envelope-retry-attempted.test.ts +258 -0
- package/src/scenarios/envelope-retry-exhausted.test.ts +168 -0
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +229 -0
- package/src/scenarios/envelope-truncated.test.ts +136 -0
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +144 -0
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +152 -0
- package/src/scenarios/fixtures-gating.test.ts +139 -1
- package/src/scenarios/fixtures-valid.test.ts +123 -15
- package/src/scenarios/kv-ttl-expiry.test.ts +40 -9
- package/src/scenarios/model-capability-insufficient.test.ts +221 -0
- package/src/scenarios/model-capability-substituted.test.ts +203 -0
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +164 -0
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +167 -0
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +124 -0
- package/src/scenarios/multi-region-idempotency.test.ts +58 -0
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +185 -0
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +19 -0
- package/src/scenarios/pack-registry-publish.test.ts +231 -51
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +198 -0
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +178 -0
- package/src/scenarios/prompt-composed-trust-marker.test.ts +165 -0
- package/src/scenarios/prompt-end-to-end-events.test.ts +202 -0
- package/src/scenarios/prompt-list-and-fetch.test.ts +207 -0
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +216 -0
- package/src/scenarios/prompt-pack-install.test.ts +187 -0
- package/src/scenarios/prompt-render-deterministic.test.ts +240 -0
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +140 -0
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +172 -0
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +144 -0
- package/src/scenarios/prompt-template-shape.test.ts +359 -0
- package/src/scenarios/provider-usage.test.ts +185 -0
- package/src/scenarios/queue-ack-nack-dlq.test.ts +64 -10
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +50 -10
- package/src/scenarios/replay-divergence-at-refusal.test.ts +134 -0
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +197 -0
- package/src/scenarios/replay-llm-cache-key.test.ts +127 -25
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +80 -0
- package/src/scenarios/sandbox-capability-gate-respected.test.ts +31 -0
- package/src/scenarios/sandbox-memory-cap.test.ts +61 -0
- package/src/scenarios/sandbox-no-cross-pack-mutation.test.ts +35 -0
- package/src/scenarios/sandbox-no-host-env-leak.test.ts +38 -0
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +91 -0
- package/src/scenarios/sandbox-no-host-process-escape.test.ts +30 -0
- package/src/scenarios/sandbox-no-network-escape.test.ts +49 -0
- package/src/scenarios/sandbox-timeout-cap.test.ts +61 -0
- package/src/scenarios/search-bm25-roundtrip.test.ts +54 -9
- package/src/scenarios/spec-corpus-validity.test.ts +34 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +37 -8
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +46 -9
- package/src/scenarios/subworkflow-input-mapping.test.ts +146 -10
- package/src/scenarios/table-cursor-pagination.test.ts +47 -9
- package/src/scenarios/table-schema-enforcement.test.ts +46 -9
- package/src/scenarios/vector-knn-roundtrip.test.ts +50 -10
- package/src/scenarios/workflow-chain-host-expansion.test.ts +202 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://openwop.dev/spec/v1/run-event-payloads.schema.json",
|
|
4
4
|
"title": "RunEventPayloads",
|
|
5
|
-
"description": "Per-RunEventType payload schemas. The base RunEventDoc shape (run-event.schema.json) leaves `payload` permissive for forward-compat. This schema defines the canonical payload contract for each known RunEventType. Consumers MAY pin strict payload validation via `$defs.<typeId>` and `ajv.validate(schema.$defs[event.type], event.payload)`. Unknown event types MUST be tolerated (no $defs match → fold best-effort).\n\
|
|
5
|
+
"description": "Per-RunEventType payload schemas. The base RunEventDoc shape (run-event.schema.json) leaves `payload` permissive for forward-compat. This schema defines the canonical payload contract for each known RunEventType. Consumers MAY pin strict payload validation via `$defs.<typeId>` and `ajv.validate(schema.$defs[event.type], event.payload)`. Unknown event types MUST be tolerated (no $defs match → fold best-effort).\n\n64 variants from `run-event.schema.json#$defs.RunEventType` are covered, grouped into ~20 shape families with shared $defs. Naming convention: camelCase keys mirror dotted RunEventType names (e.g., `run.started` → `runStarted`).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"$defs": {
|
|
8
8
|
"_typeIndex": {
|
|
@@ -48,16 +48,128 @@
|
|
|
48
48
|
"lease.lost": { "$ref": "#/$defs/leaseLifecycle" },
|
|
49
49
|
"lease.handed-off": { "$ref": "#/$defs/leaseHandedOff" },
|
|
50
50
|
"replay.diverged": { "$ref": "#/$defs/replayDiverged" },
|
|
51
|
+
"replay.divergedAtRefusal": { "$ref": "#/$defs/replayDivergedAtRefusal" },
|
|
51
52
|
"agent.reasoned": { "$ref": "#/$defs/agentReasoned" },
|
|
53
|
+
"agent.reasoning.delta": { "$ref": "#/$defs/agentReasoningDelta" },
|
|
54
|
+
"provider.usage": { "$ref": "#/$defs/providerUsage" },
|
|
55
|
+
"prompt.composed": { "$ref": "#/$defs/promptComposed" },
|
|
56
|
+
"agent.promptResolved": { "$ref": "#/$defs/agentPromptResolved" },
|
|
57
|
+
"model.capability.substituted": { "$ref": "#/$defs/modelCapabilitySubstituted" },
|
|
58
|
+
"model.capability.insufficient": { "$ref": "#/$defs/modelCapabilityInsufficient" },
|
|
59
|
+
"envelope.retry.attempted": { "$ref": "#/$defs/envelopeRetryAttempted" },
|
|
60
|
+
"envelope.retry.exhausted": { "$ref": "#/$defs/envelopeRetryExhausted" },
|
|
61
|
+
"envelope.refusal": { "$ref": "#/$defs/envelopeRefusal" },
|
|
62
|
+
"envelope.truncated": { "$ref": "#/$defs/envelopeTruncated" },
|
|
63
|
+
"envelope.nlToFormat.engaged": { "$ref": "#/$defs/envelopeNlToFormatEngaged" },
|
|
64
|
+
"envelope.recovery.applied": { "$ref": "#/$defs/envelopeRecoveryApplied" },
|
|
52
65
|
"agent.toolCalled": { "$ref": "#/$defs/agentToolCalled" },
|
|
53
66
|
"agent.toolReturned": { "$ref": "#/$defs/agentToolReturned" },
|
|
54
67
|
"agent.handoff": { "$ref": "#/$defs/agentHandoff" },
|
|
55
68
|
"agent.decided": { "$ref": "#/$defs/agentDecided" },
|
|
56
69
|
"runOrchestrator.decided": { "$ref": "#/$defs/runOrchestratorDecided" },
|
|
70
|
+
"node.dispatched": { "$ref": "#/$defs/nodeDispatched" },
|
|
57
71
|
"conversation.opened": { "$ref": "#/$defs/conversationOpened" },
|
|
58
72
|
"conversation.exchanged": { "$ref": "#/$defs/conversationExchanged" },
|
|
59
73
|
"conversation.closed": { "$ref": "#/$defs/conversationClosed" },
|
|
60
|
-
"memory.compacted": { "$ref": "#/$defs/memoryCompacted" }
|
|
74
|
+
"memory.compacted": { "$ref": "#/$defs/memoryCompacted" },
|
|
75
|
+
"core.workflowChain.event": { "$ref": "#/$defs/coreWorkflowChainEvent" },
|
|
76
|
+
"core.workflowChain.confidence-escalated": { "$ref": "#/$defs/coreWorkflowChainConfidenceEscalated" }
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
"coreWorkflowChainEvent": {
|
|
81
|
+
"description": "RFC 0037 — emitted on every planner→worker handoff state-machine transition. Required when `capabilities.multiAgent.executionModel.supported: true`; MUST NOT be emitted otherwise. RFC 0040 §A extends with optional `causationHostId` (when `crossHostCausation.supported: true`): present + non-empty when the `causationId` points at an event on a different host; absent when the chained-event is on the same host (existing semantics).",
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"required": ["phase", "workerId", "parentRunId"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"phase": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"enum": [
|
|
89
|
+
"dispatch.began",
|
|
90
|
+
"dispatch.succeeded",
|
|
91
|
+
"dispatch.failed",
|
|
92
|
+
"child.completed",
|
|
93
|
+
"child.failed",
|
|
94
|
+
"child.cancelled",
|
|
95
|
+
"output.harvested"
|
|
96
|
+
],
|
|
97
|
+
"description": "Which handoff-state-machine transition this event records. See spec/v1/multi-agent-execution.md §'Handoff state machine'."
|
|
98
|
+
},
|
|
99
|
+
"workerId": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"minLength": 1,
|
|
102
|
+
"description": "The dispatched worker's workflowId — matches the entry in the supervisor's OrchestratorDecision.nextWorkerIds[]."
|
|
103
|
+
},
|
|
104
|
+
"parentRunId": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"minLength": 1,
|
|
107
|
+
"description": "The orchestrator-driven parent run's runId."
|
|
108
|
+
},
|
|
109
|
+
"childRunId": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"minLength": 1,
|
|
112
|
+
"description": "The dispatched child run's runId. REQUIRED on phases `dispatch.succeeded` and beyond; absent on `dispatch.began` and `dispatch.failed`."
|
|
113
|
+
},
|
|
114
|
+
"harvestedKeys": {
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": { "type": "string" },
|
|
117
|
+
"description": "On phase `output.harvested`: which parent-variable keys were populated by the dispatch config's outputMapping per RFC 0022 §A. SHOULD be present; conformance asserts presence when outputMapping is non-empty."
|
|
118
|
+
},
|
|
119
|
+
"error": {
|
|
120
|
+
"$ref": "#/$defs/_errorObject",
|
|
121
|
+
"description": "On phases `dispatch.failed` / `child.failed` / `child.cancelled`: the canonical error envelope."
|
|
122
|
+
},
|
|
123
|
+
"causationHostId": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"minLength": 1,
|
|
126
|
+
"description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` (top-level on RunEventDoc) points at an event on a DIFFERENT host; absent when the chained-event is on the SAME host (existing semantics). Value MUST equal the originating host's `capabilities.multiAgent.executionModel.crossHostCausation.hostId` advertisement. Hosts that don't advertise crossHostCausation MUST NOT emit this field."
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
"coreWorkflowChainConfidenceEscalated": {
|
|
132
|
+
"description": "RFC 0039 §A — emitted when a supervisor's OrchestratorDecision carries `confidence` below the active confidence floor (spec floor 0.5 OR operator-stricter `capabilities.multiAgent.executionModel.confidenceEscalationFloor`). Recorded BEFORE the host fires the matching clarify-or-escalate interrupt so the run event log carries the decision point even if the user later confirms the original decision. MUST NOT be emitted unless `capabilities.multiAgent.executionModel.version >= 2`.",
|
|
133
|
+
"type": "object",
|
|
134
|
+
"additionalProperties": false,
|
|
135
|
+
"required": ["confidence", "floor", "escalationKind", "parentRunId"],
|
|
136
|
+
"properties": {
|
|
137
|
+
"confidence": {
|
|
138
|
+
"type": "number",
|
|
139
|
+
"minimum": 0,
|
|
140
|
+
"maximum": 1,
|
|
141
|
+
"description": "The supervisor's stated confidence on the escalated decision, copied verbatim from `OrchestratorDecision.confidence`."
|
|
142
|
+
},
|
|
143
|
+
"floor": {
|
|
144
|
+
"type": "number",
|
|
145
|
+
"minimum": 0.5,
|
|
146
|
+
"maximum": 1,
|
|
147
|
+
"description": "The floor that triggered escalation — either the spec floor (0.5) when the host doesn't advertise a stricter `confidenceEscalationFloor`, or the host-advertised stricter value."
|
|
148
|
+
},
|
|
149
|
+
"escalationKind": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"enum": ["clarify", "escalate"],
|
|
152
|
+
"description": "Which interrupt-kind the host fired in response. `clarify` is preferred per RFC 0039 §A; `escalate` is permitted when the host doesn't expose a clarification UI."
|
|
153
|
+
},
|
|
154
|
+
"workerId": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"minLength": 1,
|
|
157
|
+
"description": "The decision's intended next-worker workflowId (when the escalated decision's kind is `next-worker`). OMITTED for `kind: 'terminate'` escalations — terminate decisions have no worker to name, so absent-field is the correct signal rather than empty-string."
|
|
158
|
+
},
|
|
159
|
+
"parentRunId": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"minLength": 1,
|
|
162
|
+
"description": "The orchestrator-driven parent run's runId."
|
|
163
|
+
},
|
|
164
|
+
"originalDecision": {
|
|
165
|
+
"$ref": "orchestrator-decision.schema.json",
|
|
166
|
+
"description": "The OrchestratorDecision that triggered escalation, captured verbatim. The host's existing event-payload redaction harness (the same one enforcing SECURITY invariant `secret-leakage-eventlog-payload` per `conformance/src/scenarios/redaction.test.ts`) applies uniformly across all RunEventType payloads including this one — no per-event-type opt-in is required. The protocol-tier MUST-NOT for raw key material in persisted payloads is the canonical contract; this field inherits that protection."
|
|
167
|
+
},
|
|
168
|
+
"causationHostId": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"minLength": 1,
|
|
171
|
+
"description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host (existing semantics). Value MUST equal the originating host's `crossHostCausation.hostId` advertisement."
|
|
172
|
+
}
|
|
61
173
|
}
|
|
62
174
|
},
|
|
63
175
|
|
|
@@ -124,7 +236,8 @@
|
|
|
124
236
|
"properties": {
|
|
125
237
|
"reason": { "type": "string" },
|
|
126
238
|
"cancelledBy": { "type": "string", "description": "Caller identity (uid / API key fingerprint / `system`)." },
|
|
127
|
-
"durationMs": { "type": "integer", "minimum": 0 }
|
|
239
|
+
"durationMs": { "type": "integer", "minimum": 0 },
|
|
240
|
+
"parentRunId": { "type": "string", "minLength": 1, "description": "When this cancellation was triggered by a parent-cancel cascade (`interrupt-profiles.md §openwop-interrupt-cascade-cancel`), the parent runId that initiated it. Pairs with `reason: 'parent-cancelled'`. Absent for direct cancellations." }
|
|
128
241
|
},
|
|
129
242
|
"additionalProperties": true
|
|
130
243
|
},
|
|
@@ -539,15 +652,61 @@
|
|
|
539
652
|
"additionalProperties": true
|
|
540
653
|
},
|
|
541
654
|
|
|
655
|
+
"replayDivergedAtRefusal": {
|
|
656
|
+
"type": "object",
|
|
657
|
+
"description": "RFC 0041 §B — emitted by `:fork` mode `replay` when the replay's LLM call returns a refusal but the original run got a valid envelope (or vice-versa: the original refused, the replay succeeded). Distinct from the generic `replay.diverged` event (which covers structural output/missing/extra/type-mismatch divergence) to let operators audit safety-policy shifts without filtering through the generic divergence stream. When this event fires, the host MUST also fail the replay with `error.code: \"replay_diverged_at_refusal\"` per `spec/v1/rest-endpoints.md` §\"Common error codes\". Capability-gated on `capabilities.multiAgent.executionModel.replayDeterminism.refusalDivergenceEmission: true`; non-Phase-4 hosts MUST NOT emit this event.",
|
|
658
|
+
"required": ["sourceRunId", "atSequence", "originalEnvelopeKind", "replayEnvelopeKind"],
|
|
659
|
+
"properties": {
|
|
660
|
+
"sourceRunId": {
|
|
661
|
+
"type": "string",
|
|
662
|
+
"minLength": 1,
|
|
663
|
+
"description": "The original run's runId (the source of the replay)."
|
|
664
|
+
},
|
|
665
|
+
"atSequence": {
|
|
666
|
+
"type": "integer",
|
|
667
|
+
"minimum": 0,
|
|
668
|
+
"description": "Event-log index at which the refusal-divergence was detected (the index of the LLM call whose envelope shifted)."
|
|
669
|
+
},
|
|
670
|
+
"originalEventId": {
|
|
671
|
+
"type": "string",
|
|
672
|
+
"description": "Optional eventId of the original event whose envelope is being compared. SHOULD be set when the host has a stable identifier for the event; MAY be omitted when only the sequence index is known."
|
|
673
|
+
},
|
|
674
|
+
"nodeId": {
|
|
675
|
+
"type": "string",
|
|
676
|
+
"minLength": 1,
|
|
677
|
+
"description": "ID of the node whose LLM call diverged. Operators use this to localize the safety-policy shift in the workflow definition."
|
|
678
|
+
},
|
|
679
|
+
"originalEnvelopeKind": {
|
|
680
|
+
"type": "string",
|
|
681
|
+
"enum": ["valid", "refusal"],
|
|
682
|
+
"description": "Whether the original run's LLM envelope was a valid response or a refusal."
|
|
683
|
+
},
|
|
684
|
+
"replayEnvelopeKind": {
|
|
685
|
+
"type": "string",
|
|
686
|
+
"enum": ["valid", "refusal"],
|
|
687
|
+
"description": "Whether the replay's LLM envelope was a valid response or a refusal. MUST differ from `originalEnvelopeKind` — otherwise there is no divergence to report."
|
|
688
|
+
},
|
|
689
|
+
"refusalReason": {
|
|
690
|
+
"type": "string",
|
|
691
|
+
"description": "Provider-supplied refusal reason when available (e.g., the model's `refusal` field on a chat-completion response). OPTIONAL; hosts MAY include for operator triage but MUST redact provider-specific identifiers per `SECURITY/threat-model-secret-leakage.md`."
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
"additionalProperties": false
|
|
695
|
+
},
|
|
696
|
+
|
|
542
697
|
"replayDiverged": {
|
|
543
698
|
"type": "object",
|
|
544
|
-
"description": "Emitted by `:fork` when a replay re-execution produces a different output than the original at a given sequence. See replay.md §divergence detection.",
|
|
699
|
+
"description": "Emitted by `:fork` when a replay re-execution produces a different output than the original at a given sequence. See replay.md §divergence detection. `divergencePoint` (RFC 0027 §F) is the canonical optional field for naming which event-emission diverged; the two fields are complementary, not mutually exclusive.",
|
|
545
700
|
"required": ["sourceRunId", "atSequence"],
|
|
546
701
|
"properties": {
|
|
547
702
|
"sourceRunId": { "type": "string", "minLength": 1 },
|
|
548
703
|
"atSequence": { "type": "integer", "minimum": 0 },
|
|
549
704
|
"originalEventId": { "type": "string" },
|
|
550
|
-
"divergenceKind": { "type": "string", "enum": ["output", "missing", "extra", "type-mismatch"] }
|
|
705
|
+
"divergenceKind": { "type": "string", "enum": ["output", "missing", "extra", "type-mismatch"] },
|
|
706
|
+
"divergencePoint": {
|
|
707
|
+
"type": "string",
|
|
708
|
+
"description": "RFC 0027 §F. Verbatim `RunEventType` enum string identifying which event-emission the replay diverged at (e.g., `\"prompt.composed\"` per RFC 0027, `\"agent.promptResolved\"` per RFC 0029, `\"envelope.retry.exhausted\"` / `\"envelope.recovery.applied\"` per RFC 0032). Set when divergence is detected on a specific cross-kind operational event's payload. When divergence is purely structural (output/missing/extra at the event-array level rather than within a typed payload), `divergenceKind` carries the shape and `divergencePoint` MAY be omitted. The two fields are complementary: a single `replay.diverged` event MAY carry both (e.g., `{ divergenceKind: \"output\", divergencePoint: \"prompt.composed\" }` reads as \"the `prompt.composed` event at sequence N had a different output on replay than the original\"). The field is defined by RFC 0027; values are contributed by consuming RFCs (0029 adds `agent.promptResolved`, 0032 adds the envelope-reliability event names)."
|
|
709
|
+
}
|
|
551
710
|
},
|
|
552
711
|
"additionalProperties": true
|
|
553
712
|
},
|
|
@@ -559,11 +718,337 @@
|
|
|
559
718
|
"properties": {
|
|
560
719
|
"agentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "AgentRef.agentId of the reasoning agent." },
|
|
561
720
|
"reasoning": { "type": "string", "description": "Reasoning text. Bounded by `capabilities.agents.reasoning.tokenLimit` when verbosity is `summary` (default 512 tokens)." },
|
|
562
|
-
"verbosity": { "type": "string", "enum": ["summary", "full", "off"], "description": "Verbosity mode this trace was produced under. Hosts MAY emit `'off'` to record the suppression decision without payload content." }
|
|
721
|
+
"verbosity": { "type": "string", "enum": ["summary", "full", "off"], "description": "Verbosity mode this trace was produced under. Hosts MAY emit `'off'` to record the suppression decision without payload content." },
|
|
722
|
+
"causationHostId": { "type": "string", "minLength": 1, "description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`." }
|
|
723
|
+
},
|
|
724
|
+
"additionalProperties": true
|
|
725
|
+
},
|
|
726
|
+
|
|
727
|
+
"agentReasoningDelta": {
|
|
728
|
+
"type": "object",
|
|
729
|
+
"description": "RFC 0024. Incremental reasoning chunk for live-streaming UX. Emitted while a reasoning block is still open, BEFORE the corresponding `agent.reasoned` finalization. Consumers concatenate `delta` strings in arrival order to reconstruct the in-progress trace; the closing `agent.reasoned` event carries the FULL authoritative `reasoning`. Gated on `capabilities.agents.reasoning.streaming: true`. NOTE: `additionalProperties: true` mirrors the Phase-1 multi-agent-shift carve-out applied to the sibling `agentReasoned` schema — a deliberate forward-compat exception per RFC 0024 §Compatibility, not a precedent generalizable to other event payloads.",
|
|
730
|
+
"required": ["agentId", "delta", "sequence"],
|
|
731
|
+
"properties": {
|
|
732
|
+
"agentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "AgentRef.agentId of the reasoning agent. MUST match the eventual closing `agent.reasoned`." },
|
|
733
|
+
"delta": { "type": "string", "description": "New reasoning content since the previous delta event in this block (or since block open, if `sequence` is 0)." },
|
|
734
|
+
"sequence": { "type": "integer", "minimum": 0, "description": "Monotonically-increasing index within the current reasoning block. Starts at 0 for the first delta in a block; resets at each new block open. Consumers MAY use this to detect dropped events." },
|
|
735
|
+
"verbosity": { "type": "string", "enum": ["summary", "full", "off"], "description": "Verbosity mode the host resolved for this block. SHOULD match the verbosity reported on the closing `agent.reasoned`." }
|
|
563
736
|
},
|
|
564
737
|
"additionalProperties": true
|
|
565
738
|
},
|
|
566
739
|
|
|
740
|
+
"providerUsage": {
|
|
741
|
+
"type": "object",
|
|
742
|
+
"description": "RFC 0026. Per-call usage record emitted after every LLM provider invocation. Durably persisted in the run event log; consumed by replay, webhook subscribers, billing reconciliation. The OTel `openwop.cost.*` attribute group (per `observability.md §\"Cost attribution attributes\"`) is the observability sibling — this event type is the durable record. Replay determinism: `inputTokens` + `outputTokens` MUST replay identically; `costEstimateUsd` MAY be omitted on replay. The payload MUST NOT carry credentialRefs, hashed credential identifiers, or prompt/response substrings per `SECURITY/threat-model-secret-leakage.md §SR-1` (enforced by SECURITY invariant `provider-usage-no-credential-leak`).",
|
|
743
|
+
"required": ["provider", "model", "inputTokens", "outputTokens"],
|
|
744
|
+
"properties": {
|
|
745
|
+
"provider": { "type": "string", "minLength": 1, "description": "Canonical provider id (lowercase ASCII, e.g. \"anthropic\", \"openai\", \"google\"). Same value as the `openwop.cost.provider` OTel attribute." },
|
|
746
|
+
"model": { "type": "string", "minLength": 1, "description": "Provider-stamped model id as the model expects it. Same value used in the LLM cache-key recipe per `replay.md §A`." },
|
|
747
|
+
"inputTokens": { "type": "integer", "minimum": 0, "description": "Input/prompt tokens billed for this call. Matches the provider response's input-token count verbatim." },
|
|
748
|
+
"outputTokens": { "type": "integer", "minimum": 0, "description": "Output/completion tokens billed for this call. Matches the provider response's output-token count verbatim." },
|
|
749
|
+
"totalTokens": { "type": "integer", "minimum": 0, "description": "Convenience sum (inputTokens + outputTokens). Consumers MAY compute themselves; emitters MAY include for readability." },
|
|
750
|
+
"costEstimateUsd": { "type": "number", "minimum": 0, "description": "ADVISORY estimate in USD computed by the host's static rate table. MUST NOT be used for billing — real billing is external. Hosts SHOULD omit when no rate is known rather than emit 0." },
|
|
751
|
+
"currency": { "type": "string", "pattern": "^[A-Z]{3}$", "description": "ISO 4217 code when `costEstimateUsd` is non-USD; the field name stays `costEstimateUsd` for back-compat but `currency` overrides the implied denomination." },
|
|
752
|
+
"cacheHit": { "type": "boolean", "description": "True iff this call was served from the LLM response cache per `replay.md §\"LLM cache-key recipe\"`. When true, inputTokens/outputTokens reflect the ORIGINAL call's billed values; the cached invocation incurred zero new provider cost." },
|
|
753
|
+
"nodeId": { "type": "string", "description": "The node id that initiated the provider call. Required for per-node cost attribution dashboards." },
|
|
754
|
+
"traceId": { "type": "string", "description": "OTel trace id linking this event to the matching `openwop.cost.*` span. Lets observability backends correlate event-log entries with traces." }
|
|
755
|
+
},
|
|
756
|
+
"additionalProperties": false
|
|
757
|
+
},
|
|
758
|
+
|
|
759
|
+
"promptComposed": {
|
|
760
|
+
"type": "object",
|
|
761
|
+
"description": "RFC 0027. Emitted once per (nodeId, kind) composition when a host resolves a PromptRef on a node and assembles its body for an LLM call. Gated on `capabilities.prompts.supported: true` AND `capabilities.prompts.observability !== 'off'`. Body fields (`systemPrompt`, `userPrompt`, `variableBindings`) are populated only under `observability: 'full'`; under `hashed` the event carries only `hash` + `variableHashes`. Replay-deterministic over `(refs, variableHashes, contentTrust)` per `spec/v1/prompts.md §\"Replay determinism\"`. Secret-source variable values MUST be replaced with `[REDACTED:<secretId>]` markers per SECURITY invariant `prompt-composed-secret-redaction`; untrusted-input segments MUST be wrapped with `<UNTRUSTED>...</UNTRUSTED>` markers per SECURITY invariant `prompt-composed-trust-marker`.",
|
|
762
|
+
"required": ["nodeId", "refs", "kind", "hash"],
|
|
763
|
+
"properties": {
|
|
764
|
+
"nodeId": {
|
|
765
|
+
"type": "string",
|
|
766
|
+
"description": "Lifted to top-level RunEventDoc.nodeId; mirrored here for self-contained payload validation."
|
|
767
|
+
},
|
|
768
|
+
"refs": {
|
|
769
|
+
"type": "array",
|
|
770
|
+
"items": { "type": "string", "pattern": "^prompt:[a-z0-9][a-z0-9._-]{0,127}(@\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)?$" },
|
|
771
|
+
"description": "Ordered list of PromptRef values (stringy form) that contributed to this composition: typically system, then user, then any few-shot or schema-hint additions. Object-form refs are projected to their stringy equivalent for stability."
|
|
772
|
+
},
|
|
773
|
+
"kind": {
|
|
774
|
+
"type": "string",
|
|
775
|
+
"enum": ["system+user", "system-only", "user-only", "agent-reasoning"],
|
|
776
|
+
"description": "Composition shape — what the host actually assembled. `system+user`: both system and user templates resolved. `system-only`: only a system template applied (e.g., agent intrinsic). `user-only`: only a user template applied. `agent-reasoning`: composition was emitted ahead of an agent reasoning trace per RFC 0024."
|
|
777
|
+
},
|
|
778
|
+
"hash": {
|
|
779
|
+
"type": "string",
|
|
780
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
781
|
+
"description": "SHA-256 of the composed body (post-substitution, post-redaction). Stable across replay. Always present, including under observability=hashed."
|
|
782
|
+
},
|
|
783
|
+
"composed": {
|
|
784
|
+
"type": "string",
|
|
785
|
+
"description": "Generic composed-body field. Present only when `capabilities.prompts.observability` is `full`. Populated for ALL four PromptKind values (system / user / few-shot / schema-hint) so consumers have a single body field to read — necessary for few-shot + schema-hint templates that don't fit the system/user split below. Same secret-redaction + trust-marker invariants apply as for the kind-specific fields. The kind-specific `systemPrompt` + `userPrompt` fields below remain populated for system/user kinds so existing consumers that key off them continue to work; both fields carry identical content for those kinds."
|
|
786
|
+
},
|
|
787
|
+
"systemPrompt": {
|
|
788
|
+
"type": "string",
|
|
789
|
+
"description": "Composed system prompt body. Present only when `capabilities.prompts.observability` is `full` AND template `kind: \"system\"`. Secret-sourced variable values MUST be replaced with `[REDACTED:<secretId>]` markers. Untrusted-content `<UNTRUSTED>...</UNTRUSTED>` markers MUST be preserved verbatim. Identical to `composed` when present."
|
|
790
|
+
},
|
|
791
|
+
"userPrompt": {
|
|
792
|
+
"type": "string",
|
|
793
|
+
"description": "Composed user prompt body. Same presence + redaction rules as `systemPrompt`."
|
|
794
|
+
},
|
|
795
|
+
"variableBindings": {
|
|
796
|
+
"type": "object",
|
|
797
|
+
"additionalProperties": true,
|
|
798
|
+
"description": "Variable-name → bound-value map. Secret-source bindings MUST appear as `[REDACTED:<secretId>]`. Only present under `observability: 'full'`."
|
|
799
|
+
},
|
|
800
|
+
"variableHashes": {
|
|
801
|
+
"type": "object",
|
|
802
|
+
"additionalProperties": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
803
|
+
"description": "Variable-name → sha256(value) map. Always present (under both `hashed` and `full`). Enables replay-determinism checks without exposing values."
|
|
804
|
+
},
|
|
805
|
+
"contentTrust": {
|
|
806
|
+
"type": "string",
|
|
807
|
+
"enum": ["trusted", "untrusted"],
|
|
808
|
+
"description": "Aggregate trust marker. `untrusted` iff ANY contributing input was tagged untrusted per RFC 0020 §D / AIEnvelope.meta.contentTrust propagation. When `untrusted`, the composed bodies MUST contain `<UNTRUSTED>...</UNTRUSTED>` markers around the untrusted segments."
|
|
809
|
+
},
|
|
810
|
+
"causationHostId": {
|
|
811
|
+
"type": "string",
|
|
812
|
+
"minLength": 1,
|
|
813
|
+
"description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`."
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
"additionalProperties": false
|
|
817
|
+
},
|
|
818
|
+
|
|
819
|
+
"agentPromptResolved": {
|
|
820
|
+
"type": "object",
|
|
821
|
+
"description": "RFC 0029 §B. Emitted once per (nodeId, kind) pair BEFORE the corresponding `prompt.composed` event (when emitted). Surfaces the four-layer resolution chain per `spec/v1/prompts.md` §\"Resolution chain (normative)\" so cross-host debuggers and multi-agent visualizers can render which PromptRef applied at this node and why. Gated on `capabilities.prompts.supported: true`; payload is durable in the event log and participates in replay. Carries refs (not bodies), so emission is safe regardless of `capabilities.prompts.observability` — the secret-redaction + trust-marker invariants on `prompt.composed` (RFC 0027 §G) don't apply here.",
|
|
822
|
+
"required": ["nodeId", "kind", "chain", "resolved"],
|
|
823
|
+
"properties": {
|
|
824
|
+
"nodeId": {
|
|
825
|
+
"type": "string",
|
|
826
|
+
"description": "Lifted to top-level RunEventDoc.nodeId; mirrored here for self-contained payload validation."
|
|
827
|
+
},
|
|
828
|
+
"kind": { "$ref": "./prompt-kind.schema.json" },
|
|
829
|
+
"agentId": {
|
|
830
|
+
"type": "string",
|
|
831
|
+
"description": "AgentManifest.agentId when the node has `config.agentId` set; omitted otherwise."
|
|
832
|
+
},
|
|
833
|
+
"chain": {
|
|
834
|
+
"type": "array",
|
|
835
|
+
"description": "Ordered traversal of the four normative resolution layers. Hosts MUST emit one entry per layer attempted; skipped layers produce an entry with `applied: false` and `reason: \"...\"` describing why (e.g., `\"agentId unresolved\"`, `\"no candidate at this layer\"`, `\"superseded by node-config layer\"`).",
|
|
836
|
+
"items": {
|
|
837
|
+
"type": "object",
|
|
838
|
+
"additionalProperties": false,
|
|
839
|
+
"required": ["layer", "applied"],
|
|
840
|
+
"properties": {
|
|
841
|
+
"layer": {
|
|
842
|
+
"type": "string",
|
|
843
|
+
"enum": ["run-configurable", "node", "agent-intrinsic", "agent-overrides", "agent-library-default", "workflow-defaults", "host-defaults"],
|
|
844
|
+
"description": "Identifies which precedence layer this chain entry represents. The four normative layers per RFC 0029 §A are `node`, `agent-*`, `workflow-defaults`, `host-defaults`. `run-configurable` is reserved for the optional `RunOptions.configurable.promptOverrides` extension described in RFC 0029 §\"Alternatives considered\" #5; hosts that honor that extension MUST emit a chain entry with this layer value above the `node` entry."
|
|
845
|
+
},
|
|
846
|
+
"source": {
|
|
847
|
+
"type": "string",
|
|
848
|
+
"description": "The PromptRef stringy form this layer would have applied (e.g., `prompt:writer@1.0.0`). Object-form refs are projected to their stringy equivalent for stability. Present only when this layer had a candidate."
|
|
849
|
+
},
|
|
850
|
+
"applied": {
|
|
851
|
+
"type": "boolean",
|
|
852
|
+
"description": "True for exactly one entry in the chain — the layer whose ref was selected. False for layers that yielded null OR were skipped after a higher-precedence layer already applied."
|
|
853
|
+
},
|
|
854
|
+
"reason": {
|
|
855
|
+
"type": "string",
|
|
856
|
+
"description": "Non-normative human-readable explanation. Hosts MAY omit; clients MUST tolerate absence."
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
"resolved": {
|
|
862
|
+
"type": ["string", "null"],
|
|
863
|
+
"description": "The winning PromptRef in stringy form (`prompt:templateId@version`), or null if no layer yielded a candidate. Mirrors the `chain[].source` whose `applied: true` when present."
|
|
864
|
+
},
|
|
865
|
+
"causationHostId": {
|
|
866
|
+
"type": "string",
|
|
867
|
+
"minLength": 1,
|
|
868
|
+
"description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`."
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
"additionalProperties": false
|
|
872
|
+
},
|
|
873
|
+
|
|
874
|
+
"modelCapabilitySubstituted": {
|
|
875
|
+
"type": "object",
|
|
876
|
+
"additionalProperties": false,
|
|
877
|
+
"description": "RFC 0031 §D. Emitted when a host substitutes the active model with a NodeModule's declared `fallbackModel` because the active model lacks one or more of the `requiredModelCapabilities`. MUST event per RFC 0031 §B step 3. The `fallbackProvider` + `fallbackModel` pair MAY be redacted as all-or-nothing `\"[REDACTED]\"` when workspace policy treats multi-vendor posture as confidential per SECURITY invariant `model-capability-substituted-no-credential-disclosure`. The other fields are not redactable — `originalProvider` is already public via `RunOptions.configurable.ai.provider`, and `nodeId` / `missingCapabilities` carry no provider-possession information.",
|
|
878
|
+
"required": ["nodeId", "originalProvider", "originalModel", "fallbackProvider", "fallbackModel", "missingCapabilities"],
|
|
879
|
+
"properties": {
|
|
880
|
+
"nodeId": { "type": "string", "description": "The node whose dispatch triggered the substitution." },
|
|
881
|
+
"originalProvider": { "type": "string", "description": "Provider id of the active model the host was about to use." },
|
|
882
|
+
"originalModel": { "type": "string", "description": "Model id of the active model." },
|
|
883
|
+
"fallbackProvider": { "type": "string", "description": "Provider id of the substitute model from `NodeModule.fallbackModel.provider`, or `\"[REDACTED]\"` when the host's workspace policy redacts multi-vendor posture." },
|
|
884
|
+
"fallbackModel": { "type": "string", "description": "Model id of the substitute model from `NodeModule.fallbackModel.model`, or `\"[REDACTED]\"` when redacted (always paired all-or-nothing with `fallbackProvider`)." },
|
|
885
|
+
"missingCapabilities": {
|
|
886
|
+
"type": "array",
|
|
887
|
+
"items": { "type": "string" },
|
|
888
|
+
"uniqueItems": true,
|
|
889
|
+
"description": "Subset of `NodeModule.requiredModelCapabilities` that the active model did not satisfy."
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
|
|
894
|
+
"modelCapabilityInsufficient": {
|
|
895
|
+
"type": "object",
|
|
896
|
+
"additionalProperties": false,
|
|
897
|
+
"description": "RFC 0031 §D. Emitted when a host refuses to dispatch a NodeModule because the active model lacks declared `requiredModelCapabilities` AND no viable fallback is available (no `fallbackModel` declared, OR the host cannot authenticate to the fallback provider). MUST event per RFC 0031 §B step 4. Pairs with `RunSnapshot.error.code = \"capability_not_provided\"` per `capabilities.md` §\"Unsupported capability — refusal contract\". Recursive fallback is NOT permitted (RFC 0031 §\"Unresolved questions\" #3): if a host attempts substitution and the fallback itself fails, the host MUST emit this event with `fallbackAttempted: true` and refuse, not chain to another fallback.",
|
|
898
|
+
"required": ["nodeId", "provider", "model", "missingCapabilities"],
|
|
899
|
+
"properties": {
|
|
900
|
+
"nodeId": { "type": "string", "description": "The node whose dispatch was refused." },
|
|
901
|
+
"provider": { "type": "string", "description": "Provider id of the active model the host attempted to use." },
|
|
902
|
+
"model": { "type": "string", "description": "Model id of the active model." },
|
|
903
|
+
"missingCapabilities": {
|
|
904
|
+
"type": "array",
|
|
905
|
+
"items": { "type": "string" },
|
|
906
|
+
"uniqueItems": true,
|
|
907
|
+
"description": "Subset of `NodeModule.requiredModelCapabilities` that the active model did not satisfy."
|
|
908
|
+
},
|
|
909
|
+
"fallbackAttempted": {
|
|
910
|
+
"type": "boolean",
|
|
911
|
+
"default": false,
|
|
912
|
+
"description": "True if the host attempted to authenticate to a declared `fallbackModel` and that attempt failed (e.g., no credential resolvable, or the fallback provider was outside `capabilities.aiProviders.supported`). False if no `fallbackModel` was declared on the NodeModule."
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
|
|
917
|
+
"envelopeRetryAttempted": {
|
|
918
|
+
"type": "object",
|
|
919
|
+
"additionalProperties": false,
|
|
920
|
+
"description": "RFC 0032 §B.1. Emitted when a host retries an envelope emission after a parse or validation failure on a prior attempt. The first attempt does NOT emit this event; the second attempt emits with `attempt: 2`, etc. SHOULD-tier — hosts that don't implement retry don't emit; hosts that DO retry on validation failure SHOULD emit per attempt past the first.",
|
|
921
|
+
"required": ["nodeId", "attempt", "reason"],
|
|
922
|
+
"properties": {
|
|
923
|
+
"nodeId": { "type": "string", "description": "The node whose envelope emission is being retried." },
|
|
924
|
+
"attempt": {
|
|
925
|
+
"type": "integer",
|
|
926
|
+
"minimum": 1,
|
|
927
|
+
"maximum": 16,
|
|
928
|
+
"description": "1-indexed attempt counter. The first attempt does NOT emit this event; the second attempt emits with `attempt: 2`, etc."
|
|
929
|
+
},
|
|
930
|
+
"reason": {
|
|
931
|
+
"type": "string",
|
|
932
|
+
"anyOf": [
|
|
933
|
+
{ "enum": ["schema-violation", "truncation", "type-drift", "type-mismatch", "refusal", "parse-error", "unknown"] },
|
|
934
|
+
{ "pattern": "^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$" }
|
|
935
|
+
],
|
|
936
|
+
"description": "Why the prior attempt failed. Spec-reserved values: `schema-violation` (model emitted wrong-shape JSON; corrective fragment + retry per RFC 0033 §C), `truncation` (stop_reason: max_tokens; budget-double + retry per RFC 0033 §B), `type-drift` (envelope `type` discriminator drifted from what was advertised mid-run), `type-mismatch` (a typed payload field was emitted with the wrong runtime type — e.g., string where number was declared), `refusal` (provider safety-stop; NO retry per RFC 0032 §B.3), `parse-error` (response was not extractable as JSON even after lenient parsing), `unknown` (host could not classify). Host-private extensions MUST prefix with `x-host-<host>-` per `host-extensions.md` §\"Canonical-prefix table\"; matches the RFC 0031 §B `requiredModelCapabilities` precedent."
|
|
937
|
+
},
|
|
938
|
+
"previousError": {
|
|
939
|
+
"type": ["string", "null"],
|
|
940
|
+
"description": "Diagnostic text from the failing attempt. MUST NOT contain prompt or response substring excerpts; hosts SHOULD limit to validator output (e.g., \"required field 'steps' missing\"). Subject to SR-1 redaction per `ai-envelope.md` §\"Redaction (SR-1 carry-forward)\"."
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
|
|
945
|
+
"envelopeRetryExhausted": {
|
|
946
|
+
"type": "object",
|
|
947
|
+
"additionalProperties": false,
|
|
948
|
+
"description": "RFC 0032 §B.2. Emitted when a host has exhausted its retry budget and is about to surface a terminal envelope failure to the node. MUST-tier — alternative is silent terminal failure with no event surface, leaving conformance suites unable to assert correct give-up behavior. Hosts that don't retry MUST still emit this event when an envelope attempt terminally fails (with `totalAttempts: 1`). Pairs with `cap.breached` when the failure mode is schema-violation or truncation per RFC 0033 §B + §C.",
|
|
949
|
+
"required": ["nodeId", "totalAttempts", "finalReason"],
|
|
950
|
+
"properties": {
|
|
951
|
+
"nodeId": { "type": "string" },
|
|
952
|
+
"totalAttempts": { "type": "integer", "minimum": 1 },
|
|
953
|
+
"finalReason": {
|
|
954
|
+
"type": "string",
|
|
955
|
+
"anyOf": [
|
|
956
|
+
{ "enum": ["schema-violation", "truncation", "type-drift", "type-mismatch", "refusal", "parse-error", "unknown"] },
|
|
957
|
+
{ "pattern": "^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$" }
|
|
958
|
+
],
|
|
959
|
+
"description": "Same value set as `envelope.retry.attempted.reason` (§B.1). Spec-reserved closed enum plus `x-host-<host>-` extensions."
|
|
960
|
+
},
|
|
961
|
+
"finalError": {
|
|
962
|
+
"type": ["string", "null"],
|
|
963
|
+
"description": "Diagnostic text from the final attempt. Same redaction discipline as `envelope.retry.attempted.previousError`."
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
},
|
|
967
|
+
|
|
968
|
+
"envelopeRefusal": {
|
|
969
|
+
"type": "object",
|
|
970
|
+
"additionalProperties": false,
|
|
971
|
+
"description": "RFC 0032 §B.3. Emitted when the underlying LLM provider returns an explicit refusal (e.g., OpenAI `message.refusal`, Anthropic safety-stop, Gemini safety-block). MUST-tier. Hosts MUST NOT retry on refusal — retrying refusal with prompt mutation creates a circumvention concern (the host automatically searches for a prompt the model will accept, evading the safety filter's intent) per RFC 0032 §B.3 + RFC 0033 §D. The node MUST fail with `error.code = \"envelope_refusal\"` per RFC 0033 §F (renamed from `envelope_refused_by_provider` per the 2026-05-21 RFC adoption-feedback amendment).",
|
|
972
|
+
"required": ["nodeId", "provider", "model"],
|
|
973
|
+
"properties": {
|
|
974
|
+
"nodeId": { "type": "string" },
|
|
975
|
+
"provider": { "type": "string" },
|
|
976
|
+
"model": { "type": "string" },
|
|
977
|
+
"refusalText": {
|
|
978
|
+
"type": ["string", "null"],
|
|
979
|
+
"description": "Provider-returned refusal message, if any. MUST be passed through the host's BYOK redaction harness AND prompt-content redaction pipeline before emission (per SECURITY invariant `envelope-refusal-no-prompt-leak`). Provider safety-refusal messages can echo offending prompt substrings; emitting them verbatim would create a side channel for prompt-injection-attack telemetry exfiltration AND for SR-1 secret-leak. Replay consumers MUST tolerate `null` even when the original was non-null (host redaction policies legitimately tighten over time)."
|
|
980
|
+
},
|
|
981
|
+
"safetyCategory": {
|
|
982
|
+
"type": ["string", "null"],
|
|
983
|
+
"description": "Provider-specific safety category if available (e.g., Anthropic `harmful-content`, Gemini `SAFETY_BLOCK_HARASSMENT`, OpenAI `policy_violation`). Verbatim from provider; no normalization."
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
|
|
988
|
+
"envelopeTruncated": {
|
|
989
|
+
"type": "object",
|
|
990
|
+
"additionalProperties": false,
|
|
991
|
+
"description": "RFC 0032 §B.4. Emitted when the LLM emission was cut off before the envelope was complete (typically `stop_reason: \"max_tokens\"`). SHOULD-tier — hosts that distinguish truncation from schema-violation per RFC 0033 §A MUST emit this event when truncation occurs. Hosts that conflate truncation with schema-violation (legacy behavior) MAY omit; they then fail RFC 0033 conformance.",
|
|
992
|
+
"required": ["nodeId", "provider", "model", "stopReason"],
|
|
993
|
+
"properties": {
|
|
994
|
+
"nodeId": { "type": "string" },
|
|
995
|
+
"provider": { "type": "string" },
|
|
996
|
+
"model": { "type": "string" },
|
|
997
|
+
"stopReason": {
|
|
998
|
+
"type": "string",
|
|
999
|
+
"enum": ["max_tokens", "length", "stop_sequence", "unknown"],
|
|
1000
|
+
"description": "Provider-normalized stop reason. `max_tokens` covers OpenAI `length` + Anthropic `max_tokens`; `length` preserved as a separate value for hosts that distinguish provider-side `length` (model self-determined length cap) from `max_tokens` (host-side budget cap)."
|
|
1001
|
+
},
|
|
1002
|
+
"partialPayloadAvailable": {
|
|
1003
|
+
"type": "boolean",
|
|
1004
|
+
"default": false,
|
|
1005
|
+
"description": "True if the host recovered a partial envelope before truncation. Hosts MAY use this signal to route to a recovery path (e.g., budget-doubled retry per RFC 0033 §B)."
|
|
1006
|
+
},
|
|
1007
|
+
"outputTokenCount": {
|
|
1008
|
+
"type": ["integer", "null"],
|
|
1009
|
+
"minimum": 0,
|
|
1010
|
+
"description": "Tokens emitted before truncation. Sourced from the provider response's usage block. MUST replay identically."
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
|
|
1015
|
+
"envelopeNlToFormatEngaged": {
|
|
1016
|
+
"type": "object",
|
|
1017
|
+
"additionalProperties": false,
|
|
1018
|
+
"description": "RFC 0032 §B.5. Emitted when the host has escalated to a two-call NL-to-Format fallback after retry exhaustion (per Tam et al., arXiv 2408.02442 mitigation strategy: free-form reasoning in the first call → schema coercion in the second call). MAY-tier — NL-to-Format is one of many possible recovery strategies; hosts that don't implement it don't advertise it.",
|
|
1019
|
+
"required": ["nodeId", "originalEnvelopeType"],
|
|
1020
|
+
"properties": {
|
|
1021
|
+
"nodeId": { "type": "string" },
|
|
1022
|
+
"originalEnvelopeType": { "type": "string", "description": "The envelope kind the original attempt was trying to emit." },
|
|
1023
|
+
"fallbackCalls": {
|
|
1024
|
+
"type": "integer",
|
|
1025
|
+
"minimum": 1,
|
|
1026
|
+
"default": 1,
|
|
1027
|
+
"description": "Number of secondary LLM calls used to reformat free-form output into the envelope's schema."
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
|
|
1032
|
+
"envelopeRecoveryApplied": {
|
|
1033
|
+
"type": "object",
|
|
1034
|
+
"additionalProperties": false,
|
|
1035
|
+
"description": "RFC 0032 §B.6. Emitted when lenient parsing recovered a malformed envelope (e.g., JSON repair via `jsonrepair`, markdown fence stripping, last-balanced-object extraction). Recovery is internal to the parsing step, BEFORE validation; recovery does NOT consume a retry attempt per RFC 0033 §D. MAY-tier — lenient parsing is a host-discretion recovery path. The event payload MUST NOT carry the recovered envelope content or any substring from the model's pre-recovery output per SECURITY invariant `envelope-recovery-no-content-leak`; only the recovery path identifier and the optional `byteOffset` are emitted. The recovered content rides on the subsequent envelope acceptance + downstream `RunEventDoc`, NOT on this event.",
|
|
1036
|
+
"required": ["nodeId", "path"],
|
|
1037
|
+
"properties": {
|
|
1038
|
+
"nodeId": { "type": "string" },
|
|
1039
|
+
"path": {
|
|
1040
|
+
"type": "string",
|
|
1041
|
+
"enum": ["direct", "jsonrepair", "markdown-fence", "brace-walker", "custom"],
|
|
1042
|
+
"description": "Which recovery path succeeded. `direct` is reserved for the no-recovery-needed path and is informational; hosts MAY omit emission when `path: \"direct\"`. MUST replay identically (deterministic parsing → deterministic recovery outcome)."
|
|
1043
|
+
},
|
|
1044
|
+
"byteOffset": {
|
|
1045
|
+
"type": ["integer", "null"],
|
|
1046
|
+
"minimum": 0,
|
|
1047
|
+
"description": "Byte position where recovery succeeded. Useful for debugging which fraction of the model's output was salvageable."
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
|
|
567
1052
|
"agentToolCalled": {
|
|
568
1053
|
"type": "object",
|
|
569
1054
|
"description": "Multi-Agent Shift Phase 1. Emitted when an agent invokes a tool. Pairs with `agent.toolReturned` via shared `callId`.",
|
|
@@ -572,7 +1057,8 @@
|
|
|
572
1057
|
"agentId": { "type": "string", "minLength": 3, "maxLength": 256 },
|
|
573
1058
|
"toolName": { "type": "string", "minLength": 1, "description": "Tool identifier (typically a node-pack typeId or function name)." },
|
|
574
1059
|
"callId": { "type": "string", "minLength": 1, "description": "Unique correlation id linking this call to the subsequent `agent.toolReturned`. Hosts SHOULD use UUIDs or content-addressable hashes." },
|
|
575
|
-
"inputs": { "description": "Tool inputs. Shape is tool-specific; consumers MUST NOT assume an object." }
|
|
1060
|
+
"inputs": { "description": "Tool inputs. Shape is tool-specific; consumers MUST NOT assume an object." },
|
|
1061
|
+
"causationHostId": { "type": "string", "minLength": 1, "description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`." }
|
|
576
1062
|
},
|
|
577
1063
|
"additionalProperties": true
|
|
578
1064
|
},
|
|
@@ -586,7 +1072,8 @@
|
|
|
586
1072
|
"toolName": { "type": "string", "minLength": 1 },
|
|
587
1073
|
"callId": { "type": "string", "minLength": 1 },
|
|
588
1074
|
"outcome": { "description": "Tool result. Discriminated by host on success vs error; payload shape is tool-specific." },
|
|
589
|
-
"error": { "$ref": "#/$defs/_errorObject", "description": "Set on tool-execution failure. Mutually exclusive with `outcome`." }
|
|
1075
|
+
"error": { "$ref": "#/$defs/_errorObject", "description": "Set on tool-execution failure. Mutually exclusive with `outcome`." },
|
|
1076
|
+
"causationHostId": { "type": "string", "minLength": 1, "description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`." }
|
|
590
1077
|
},
|
|
591
1078
|
"additionalProperties": true
|
|
592
1079
|
},
|
|
@@ -598,7 +1085,8 @@
|
|
|
598
1085
|
"properties": {
|
|
599
1086
|
"fromAgentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "Agent surrendering control." },
|
|
600
1087
|
"toAgentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "Agent receiving control." },
|
|
601
|
-
"reason": { "type": "string", "description": "Optional handoff rationale (e.g., 'specialist routing', 'escalation', 'dispatch-loop-iteration')." }
|
|
1088
|
+
"reason": { "type": "string", "description": "Optional handoff rationale (e.g., 'specialist routing', 'escalation', 'dispatch-loop-iteration')." },
|
|
1089
|
+
"causationHostId": { "type": "string", "minLength": 1, "description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`." }
|
|
602
1090
|
},
|
|
603
1091
|
"additionalProperties": true
|
|
604
1092
|
},
|
|
@@ -610,7 +1098,8 @@
|
|
|
610
1098
|
"properties": {
|
|
611
1099
|
"agentId": { "type": "string", "minLength": 3, "maxLength": 256 },
|
|
612
1100
|
"decision": { "description": "Typed decision payload. Shape is decision-specific; consumers MUST NOT assume an object." },
|
|
613
|
-
"confidence": { "type": "number", "minimum": 0, "maximum": 1, "description": "Optional confidence in `[0, 1]`. Below the threshold → escalate via `node.suspended { reason: 'low-confidence' }`. Absent values are treated as 'no signal' — no escalation." }
|
|
1101
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1, "description": "Optional confidence in `[0, 1]`. Below the threshold → escalate via `node.suspended { reason: 'low-confidence' }`. Absent values are treated as 'no signal' — no escalation." },
|
|
1102
|
+
"causationHostId": { "type": "string", "minLength": 1, "description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`." }
|
|
614
1103
|
},
|
|
615
1104
|
"additionalProperties": true
|
|
616
1105
|
},
|
|
@@ -621,7 +1110,20 @@
|
|
|
621
1110
|
"required": ["agentId", "decision"],
|
|
622
1111
|
"properties": {
|
|
623
1112
|
"agentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "AgentRef.agentId of the orchestrator. MUST equal `RunSnapshot.runOrchestrator.agentId` for the run's lifetime — orchestrator identity is set at first decision and does not change." },
|
|
624
|
-
"decision": { "$ref": "orchestrator-decision.schema.json" }
|
|
1113
|
+
"decision": { "$ref": "orchestrator-decision.schema.json" },
|
|
1114
|
+
"causationHostId": { "type": "string", "minLength": 1, "description": "RFC 0040 §A — optional cross-host causation pointer. Present + non-empty when this event's `causationId` points at an event on a DIFFERENT host; absent when same-host. MUST equal the originating host's `crossHostCausation.hostId`." }
|
|
1115
|
+
},
|
|
1116
|
+
"additionalProperties": false
|
|
1117
|
+
},
|
|
1118
|
+
|
|
1119
|
+
"nodeDispatched": {
|
|
1120
|
+
"type": "object",
|
|
1121
|
+
"description": "Emitted by `core.dispatch` (RFC 0007 §D + RFC 0022 §A) for each child workflow spawned by a `next-worker` OrchestratorDecision. The envelope's top-level `nodeId` carries the dispatching `core.dispatch` node's id; the payload names the spawned child run + its terminal status at the moment of emission. Lets observers reconstruct the parent → child linkage without scanning the runs table.",
|
|
1122
|
+
"required": ["childRunId", "childWorkflowId"],
|
|
1123
|
+
"properties": {
|
|
1124
|
+
"childRunId": { "type": "string", "minLength": 1, "description": "RunId of the spawned child run." },
|
|
1125
|
+
"childWorkflowId": { "type": "string", "minLength": 1, "description": "WorkflowId of the spawned child." },
|
|
1126
|
+
"childStatus": { "type": "string", "description": "Status of the child run at the moment the dispatch step returned (typically `completed`, `failed`, `cancelled`, or one of the `waiting-*` states when the child suspended)." }
|
|
625
1127
|
},
|
|
626
1128
|
"additionalProperties": false
|
|
627
1129
|
},
|