diff --git a/README.md b/README.md index 826208a..9a37554 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Only contracts that genuinely cross a repository boundary: | Schema | Version | Consumers | | --- | --- | --- | | `aep-record` | `aep/v0.3` | wasmagent-js, wasmagent-proxy, trace-pipeline, wasmagent-train-replay, open-agent-audit | +| `canonical-event` | `canonical-event/v0.1` | open-agent-audit, wasmagent-js | | `constraint-ir` | `compliance/v1` | wasmagent-js, trace-pipeline | | `constraint-violation` | `compliance/v1` | wasmagent-js, trace-pipeline | | `repair-trace` | `compliance/v1` | wasmagent-js, trace-pipeline | diff --git a/index.d.ts b/index.d.ts index 465117e..d598c0e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -31,6 +31,49 @@ export const schemas: Record; */ export function getSchema(id: string): unknown; +// --------------------------------------------------------------------------- +// Schema instance types (convenience views). +// +// The authoritative contract for every record shape is its JSON Schema +// (see schemas/index.json). Load and validate against it at runtime via +// getSchema(). The interfaces below capture only the required top-level +// fields so consumers can type their payloads ergonomically; the index +// signature passes through every optional schema property unchanged. +// --------------------------------------------------------------------------- + +/** + * Convenience structural view of an Agent Evidence Protocol (AEP) record — + * runtime action evidence and run provenance. Authoritative shape: + * getSchema('aep-record') → schemas/aep/aep-record.schema.json. + */ +export interface AEPRecord { + /** Schema version, one of `aep/v0.1` | `aep/v0.2` | `aep/v0.3`. */ + schema_version: string; + /** Identifier of the run this record captures. */ + run_id: string; + /** UTC epoch milliseconds when the record was created. */ + created_at_ms: number; + [key: string]: unknown; +} + +/** + * Convenience structural view of a normalized event — the foundational record + * heterogeneous agent evidence is normalized into before being mapped onto an + * AEPRecord. Authoritative shape: + * getSchema('canonical-event') → schemas/aep/canonical-event.schema.json. + */ +export interface CanonicalEvent { + /** Canonical-event schema version, currently `canonical-event/v0.1`. */ + schema_version: string; + /** Globally-unique identifier for this event within its trace. */ + event_id: string; + /** Kind of event: action | decision | observation | error | lifecycle. */ + event_type: string; + /** UTC epoch milliseconds when the event occurred. */ + timestamp_ms: number; + [key: string]: unknown; +} + // --------------------------------------------------------------------------- // Cross-repo schema drift detection. // --------------------------------------------------------------------------- diff --git a/schemas/aep/canonical-event.schema.json b/schemas/aep/canonical-event.schema.json new file mode 100644 index 0000000..49cc4bb --- /dev/null +++ b/schemas/aep/canonical-event.schema.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://wasmagent.dev/schemas/aep/canonical-event.schema.json", + "title": "CanonicalEvent", + "description": "Foundational, normalized event record for the WasmAgent evidence stream. Heterogeneous agent evidence (e.g. an open-agent-audit run) is normalized into one or more CanonicalEvents, which an adapter then maps onto an aep-record. Field names deliberately mirror aep-record (trace_id, run_id, tool_name, state_changing, refs, signature) so the mapping is mechanical rather than lossy.", + "type": "object", + "required": ["schema_version", "event_id", "event_type", "timestamp_ms"], + "properties": { + "schema_version": { + "type": "string", + "enum": ["canonical-event/v0.1"], + "description": "Version of the canonical-event schema this record conforms to." + }, + "event_id": { + "type": "string", + "description": "Globally-unique identifier for this event within its trace." + }, + "event_type": { + "type": "string", + "enum": ["action", "decision", "observation", "error", "lifecycle"], + "description": "Kind of event. 'action' maps to an aep-record action; 'decision' to a capability_decision; 'observation'/'error'/'lifecycle' to verifier_results or provenance." + }, + "timestamp_ms": { + "type": "number", + "description": "UTC epoch milliseconds when the event occurred." + }, + "trace_id": { + "type": "string", + "description": "Correlation id linking related events; maps to aep-record.trace_id." + }, + "run_id": { + "type": "string", + "description": "Identifier of the run this event belongs to; maps to aep-record.run_id." + }, + "parent_event_id": { + "type": ["string", "null"], + "description": "Event id of the causally-preceding event, enabling causal-chain reconstruction; maps to aep-record.actions[].parent_action_id." + }, + "actor": { + "type": "object", + "description": "The agent/tool/human/system that produced the event.", + "required": ["actor_id"], + "properties": { + "actor_id": { "type": "string", "description": "Stable identifier for the actor." }, + "actor_type": { + "type": "string", + "enum": ["agent", "tool", "human", "system"], + "description": "Category of actor." + }, + "agent_version": { "type": "string", "description": "Agent/runtime version, e.g. wasmagent-js@1.20.0; maps to aep-record.runtime_version / run_context.agent_version." } + }, + "additionalProperties": true + }, + "subject_id": { + "type": "string", + "description": "Optional principal the event was performed on behalf of; maps to aep-record.subject_id / user_id." + }, + "tool_name": { + "type": "string", + "description": "For action events, the tool invoked; maps to aep-record.actions[].tool_name." + }, + "state_changing": { + "type": "boolean", + "description": "Whether the event mutates external state; maps to aep-record.actions[].state_changing and side_effect_class." + }, + "data": { + "type": "object", + "description": "Type-specific payload for the event. Free-form; consumers narrow it by event_type. May carry tool arguments/results, decision rationale, verifier scores, etc.", + "additionalProperties": true + }, + "refs": { + "type": "array", + "description": "Input/output/evidence artifacts referenced by the event; maps to aep-record.input_refs / output_refs / evidence_refs.", + "items": { + "type": "object", + "required": ["uri"], + "properties": { + "uri": { "type": "string", "description": "Location of the referenced artifact." }, + "digest": { "type": "string", "description": "Content digest of the referenced artifact." }, + "relation": { + "type": "string", + "enum": ["input", "output", "evidence", "memory"], + "description": "How the artifact relates to the event." + } + }, + "additionalProperties": true + } + }, + "source": { + "type": "object", + "description": "Provenance of the event for adapter traceability.", + "properties": { + "system": { "type": "string", "description": "Originating system, e.g. open-agent-audit, wasmagent-js." }, + "adapter": { "type": "string", "description": "Name/version of the adapter that produced this canonical event." }, + "source_event_type": { "type": "string", "description": "The native event type this was normalized from." } + }, + "additionalProperties": true + }, + "signature": { + "type": "object", + "description": "Optional tamper-evident signature over the event content; maps to aep-record.signature.", + "required": ["alg", "key_id", "sig"], + "properties": { + "alg": { "type": "string", "description": "Signature algorithm, e.g. ed25519." }, + "key_id": { "type": "string", "description": "Identifier of the signing key." }, + "sig": { "type": "string", "description": "Base64-encoded signature bytes." } + }, + "additionalProperties": true + } + }, + "additionalProperties": true +} diff --git a/schemas/index.json b/schemas/index.json index 2bc43b1..f02ffbf 100644 --- a/schemas/index.json +++ b/schemas/index.json @@ -41,6 +41,22 @@ ], "summary": "Shared envelope base for all AEP evidence records \u2014 schema_version, trace_id, created_at_ms, and optional signature." }, + { + "id": "canonical-event", + "title": "CanonicalEvent", + "path": "schemas/aep/canonical-event.schema.json", + "canonical_id": "https://wasmagent.dev/schemas/aep/canonical-event.schema.json", + "version": "canonical-event/v0.1", + "stability": "evolving", + "owners": [ + "wasmagent-protocol" + ], + "consumers": [ + "open-agent-audit", + "wasmagent-js" + ], + "summary": "Foundational normalized event record. Heterogeneous agent evidence is normalized into CanonicalEvents that an adapter maps onto an aep-record." + }, { "id": "constraint-ir", "title": "ConstraintIR", diff --git a/tests/fixtures/invalid/canonical-event/example.json b/tests/fixtures/invalid/canonical-event/example.json new file mode 100644 index 0000000..011936a --- /dev/null +++ b/tests/fixtures/invalid/canonical-event/example.json @@ -0,0 +1,5 @@ +{ + "schema_version": "canonical-event/v9.9", + "event_type": "not-a-real-event-type", + "timestamp_ms": 1737600000000 +} diff --git a/tests/fixtures/valid/canonical-event/example.json b/tests/fixtures/valid/canonical-event/example.json new file mode 100644 index 0000000..356a783 --- /dev/null +++ b/tests/fixtures/valid/canonical-event/example.json @@ -0,0 +1,38 @@ +{ + "schema_version": "canonical-event/v0.1", + "event_id": "evt-0001", + "event_type": "action", + "timestamp_ms": 1737600000100, + "trace_id": "trace-001", + "run_id": "run-abc123", + "parent_event_id": "evt-0000", + "actor": { + "actor_id": "agent-1", + "actor_type": "agent", + "agent_version": "wasmagent-js@1.20.0" + }, + "subject_id": "user-42", + "tool_name": "write_file", + "state_changing": true, + "data": { + "path": "/tmp/out.txt", + "bytes_written": 128 + }, + "refs": [ + { + "uri": "file:///tmp/out.txt", + "digest": "sha256:deadbeef", + "relation": "output" + } + ], + "source": { + "system": "open-agent-audit", + "adapter": "oaa-aep-adapter@0.1", + "source_event_type": "tool_call" + }, + "signature": { + "alg": "ed25519", + "key_id": "k1", + "sig": "base64sig==" + } +} diff --git a/tests/fixtures/valid/canonical-event/minimal.json b/tests/fixtures/valid/canonical-event/minimal.json new file mode 100644 index 0000000..510ef18 --- /dev/null +++ b/tests/fixtures/valid/canonical-event/minimal.json @@ -0,0 +1,6 @@ +{ + "schema_version": "canonical-event/v0.1", + "event_id": "evt-minimal", + "event_type": "observation", + "timestamp_ms": 1737600000000 +}