diff --git a/README.md b/README.md index f5952a2..826208a 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Only contracts that genuinely cross a repository boundary: | Schema | Version | Consumers | | --- | --- | --- | -| `aep-record` | `aep/v0.2` | wasmagent-js, wasmagent-proxy, trace-pipeline, wasmagent-train-replay, open-agent-audit | +| `aep-record` | `aep/v0.3` | wasmagent-js, wasmagent-proxy, trace-pipeline, wasmagent-train-replay, open-agent-audit | | `constraint-ir` | `compliance/v1` | wasmagent-js, trace-pipeline | | `constraint-violation` | `compliance/v1` | wasmagent-js, trace-pipeline | | `repair-trace` | `compliance/v1` | wasmagent-js, trace-pipeline | @@ -143,6 +143,7 @@ python3 -m wasmagent_protocol check --scan --root . Published to npm and PyPI from CI via OIDC trusted publishing on `v*` tags — no tokens stored. See [`docs/CONTRACT-CHANGE-PROCESS.md`](docs/CONTRACT-CHANGE-PROCESS.md). +- **0.1.7** — `aep-record` unified to `aep/v0.3`: reconciles the wasmagent-js and trace-pipeline forks into one canonical record. Additive optional fields `user_id`, `subject_id`, `side_effect_class` (per-record) + `run_side_effect_class_max` (per-run) sharing one enum, `recording_mode`, `argument_drift`. `aep/v0.1`/`aep/v0.2` stay accepted; `signature` stays optional. - **0.1.6** — cross-repo schema-drift gate: `wasmagent-protocol check` CLI (npm + PyPI) and the reusable `.github/workflows/schema-drift.yml` workflow. - **0.1.5** — first successful npm OIDC publish (trusted publisher now registered on npmjs). - **0.1.4** — npm OIDC groundwork; trusted publisher was not yet saved on npmjs. diff --git a/package.json b/package.json index 02437f6..b3d1f19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wasmagent/protocol", - "version": "0.1.6", + "version": "0.1.7", "description": "Canonical JSON Schemas for the WasmAgent Agent Evidence Protocol (AEP) and compliance contracts. Single source of truth across the WasmAgent org.", "keywords": ["wasmagent", "aep", "agent-evidence-protocol", "json-schema", "compliance", "provenance"], "homepage": "https://github.com/WasmAgent/wasmagent-protocol#readme", diff --git a/pyproject.toml b/pyproject.toml index 5e40cbd..e7aa892 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "wasmagent-protocol" -version = "0.1.6" +version = "0.1.7" description = "Canonical JSON Schemas for the WasmAgent Agent Evidence Protocol (AEP) and compliance contracts. Single source of truth across the WasmAgent org." readme = "README.md" requires-python = ">=3.9" diff --git a/schemas/aep/aep-record.schema.json b/schemas/aep/aep-record.schema.json index 9706647..99f458d 100644 --- a/schemas/aep/aep-record.schema.json +++ b/schemas/aep/aep-record.schema.json @@ -6,7 +6,7 @@ "type": "object", "required": ["schema_version", "run_id", "created_at_ms"], "properties": { - "schema_version": { "type": "string", "enum": ["aep/v0.1", "aep/v0.2"] }, + "schema_version": { "type": "string", "enum": ["aep/v0.1", "aep/v0.2", "aep/v0.3"] }, "run_id": { "type": "string" }, "trace_id": { "type": "string" }, "parent_trace_id": { "type": ["string", "null"] }, @@ -162,8 +162,44 @@ "dependency_lock_digest": { "type": "string" } } }, + "user_id": { + "type": "string", + "description": "Optional end-user / principal the run acted on behalf of (additive, aep/v0.3+)." + }, + "subject_id": { + "type": "string", + "description": "Optional agent subject identity for the run (additive, aep/v0.3+)." + }, + "side_effect_class": { + "type": "string", + "enum": ["read", "mutate-local", "mutate-external", "network-egress", "unknown"], + "description": "Per-record side-effect classification (additive, aep/v0.3+). Shares one vocabulary with run_side_effect_class_max; reconciles wasmagent-js and trace-pipeline forks." + }, + "run_side_effect_class_max": { + "type": "string", + "enum": ["read", "mutate-local", "mutate-external", "network-egress", "unknown"], + "description": "Per-run aggregate: the highest side-effect class observed across the whole run (additive, aep/v0.3+). Shares the side_effect_class vocabulary." + }, + "recording_mode": { + "type": "string", + "enum": ["full", "delta", "validation"], + "description": "How this run's evidence was captured (additive, aep/v0.3+)." + }, + "argument_drift": { + "type": "object", + "description": "Detected drift between an action's declared arguments and the arguments used at runtime (additive, aep/v0.3+).", + "properties": { + "tool_name": { "type": "string" }, + "declared_digest": { "type": "string" }, + "actual_digest": { "type": "string" }, + "diff_summary": { "type": "string" }, + "drifted_args": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": true + }, "signature": { "type": "object", + "description": "Optional tamper-evident signature. Stays OPTIONAL in aep/v0.3; tightening it to required is a breaking change (CONTRACT-CHANGE-PROCESS.md section 3) that needs an org RFC before it can land - do not silently add it to the 'required' array.", "required": ["alg", "key_id", "sig"], "properties": { "alg": { "type": "string" }, diff --git a/schemas/index.json b/schemas/index.json index 8afcecb..2bc43b1 100644 --- a/schemas/index.json +++ b/schemas/index.json @@ -9,7 +9,7 @@ "title": "AEPRecord", "path": "schemas/aep/aep-record.schema.json", "canonical_id": "https://wasmagent.dev/schemas/aep/aep-record.schema.json", - "version": "aep/v0.2", + "version": "aep/v0.3", "stability": "evolving", "owners": [ "wasmagent-protocol" diff --git a/tests/fixtures/invalid/aep-record/bad-argument-drift.json b/tests/fixtures/invalid/aep-record/bad-argument-drift.json new file mode 100644 index 0000000..af9c595 --- /dev/null +++ b/tests/fixtures/invalid/aep-record/bad-argument-drift.json @@ -0,0 +1,6 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-bad-argdrift", + "created_at_ms": 1737600000000, + "argument_drift": "drifted-args-should-be-an-object" +} diff --git a/tests/fixtures/invalid/aep-record/bad-recording-mode.json b/tests/fixtures/invalid/aep-record/bad-recording-mode.json new file mode 100644 index 0000000..8c27416 --- /dev/null +++ b/tests/fixtures/invalid/aep-record/bad-recording-mode.json @@ -0,0 +1,6 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-bad-mode", + "created_at_ms": 1737600000000, + "recording_mode": "partial" +} diff --git a/tests/fixtures/invalid/aep-record/bad-run-side-effect.json b/tests/fixtures/invalid/aep-record/bad-run-side-effect.json new file mode 100644 index 0000000..0fffd36 --- /dev/null +++ b/tests/fixtures/invalid/aep-record/bad-run-side-effect.json @@ -0,0 +1,6 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-bad-runsec", + "created_at_ms": 1737600000000, + "run_side_effect_class_max": "delete-everything" +} diff --git a/tests/fixtures/invalid/aep-record/bad-side-effect-class.json b/tests/fixtures/invalid/aep-record/bad-side-effect-class.json new file mode 100644 index 0000000..057a5eb --- /dev/null +++ b/tests/fixtures/invalid/aep-record/bad-side-effect-class.json @@ -0,0 +1,6 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-bad-sec", + "created_at_ms": 1737600000000, + "side_effect_class": "rm-rf" +} diff --git a/tests/fixtures/invalid/aep-record/bad-subject-id.json b/tests/fixtures/invalid/aep-record/bad-subject-id.json new file mode 100644 index 0000000..1508007 --- /dev/null +++ b/tests/fixtures/invalid/aep-record/bad-subject-id.json @@ -0,0 +1,6 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-bad-sid", + "created_at_ms": 1737600000000, + "subject_id": false +} diff --git a/tests/fixtures/invalid/aep-record/bad-user-id.json b/tests/fixtures/invalid/aep-record/bad-user-id.json new file mode 100644 index 0000000..13a685e --- /dev/null +++ b/tests/fixtures/invalid/aep-record/bad-user-id.json @@ -0,0 +1,6 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-bad-uid", + "created_at_ms": 1737600000000, + "user_id": 12345 +} diff --git a/tests/fixtures/valid/aep-record/v0.3-fields.json b/tests/fixtures/valid/aep-record/v0.3-fields.json new file mode 100644 index 0000000..d7a9f4b --- /dev/null +++ b/tests/fixtures/valid/aep-record/v0.3-fields.json @@ -0,0 +1,26 @@ +{ + "schema_version": "aep/v0.3", + "run_id": "run-v03", + "trace_id": "trace-v03", + "created_at_ms": 1737600000000, + "user_id": "user-42", + "subject_id": "agent-subject-1", + "side_effect_class": "mutate-external", + "run_side_effect_class_max": "network-egress", + "recording_mode": "full", + "argument_drift": { + "tool_name": "write_file", + "declared_digest": "sha256:aaa", + "actual_digest": "sha256:bbb", + "diff_summary": "path argument rewritten by policy", + "drifted_args": ["path"] + }, + "actions": [ + { + "action_id": "a1", + "tool_name": "write_file", + "state_changing": true, + "timestamp_ms": 1737600000100 + } + ] +}