Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,25 @@ npm install @wasmagent/protocol

```ts
import { schemas, getSchema } from "@wasmagent/protocol";
import type { AEPRecord, AEPAction, AEPSideEffectClass } from "@wasmagent/protocol";

const aep = getSchema("aep-record"); // parsed JSON Schema object

// Compile-time type checking for AEP records:
const record: AEPRecord = {
schema_version: "aep/v0.3",
run_id: "run-abc",
created_at_ms: Date.now(),
};
```

The package exports TypeScript interfaces for all `aep-record` sub-structures:
`AEPRecord`, `AEPAction`, `AEPInputRef`, `AEPOutputRef`,
`AEPCapabilityDecision`, `AEPVerifierResult`, `AEPBudgetLedger`,
`AEPBudgetEntry`, `AEPRunContext`, `AEPArgumentDrift`, `AEPSignature`, and
the union types `AEPRecordSchemaVersion`, `AEPCapabilityDecisionValue`,
`AEPSideEffectClass`, and `AEPRecordingMode`.

### Python

```bash
Expand All @@ -73,11 +88,27 @@ pip install wasmagent-protocol

```python
from wasmagent_protocol import get_schema, schema_path
from wasmagent_protocol.types import AEPRecord, AEPAction

aep = get_schema("aep-record") # parsed dict
path = schema_path("aep-record") # pathlib.Path to the .json file

# Static type checking with TypedDict:
record: AEPRecord = {
"schema_version": "aep/v0.3",
"run_id": "run-abc",
"created_at_ms": 1737600000000,
}
```

The ``wasmagent_protocol.types`` module exports TypedDict classes for all
`aep-record` sub-structures: `AEPRecord`, `AEPAction`, `AEPInputRef`,
`AEPOutputRef`, `AEPCapabilityDecision`, `AEPVerifierResult`,
`AEPBudgetEntry`, `AEPBudgetLedger`, `AEPRunContext`, `AEPArgumentDrift`,
`AEPSignature`, and the literal types `AEPRecordSchemaVersion`,
`SideEffectClass`, and `RecordingMode`. All are also re-exported from the
top-level package for convenience.

## Preventing cross-repo drift

Downstream repos must not keep local copies of these schemas — but "must not"
Expand Down
155 changes: 155 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,160 @@
// Type declarations for @wasmagent/protocol.

// ---------------------------------------------------------------------------
// AEP Record types — mirrors schemas/aep/aep-record.schema.json.
// ---------------------------------------------------------------------------

/** Schema version strings accepted by the canonical aep-record. */
export type AEPRecordSchemaVersion = 'aep/v0.1' | 'aep/v0.2' | 'aep/v0.3';

export interface AEPInputRef {
uri: string;
digest?: string;
taint_labels?: string[];
}

export interface AEPOutputRef {
uri: string;
digest?: string;
redaction_profile?: string;
}

/** Capability decision values. */
export type AEPCapabilityDecisionValue = 'allow' | 'deny' | 'ask_user' | 'dry_run';

export interface AEPCapabilityDecision {
capability: string;
subject: string;
resource: string;
decision: AEPCapabilityDecisionValue;
reason_code?: string;
}

export interface AEPAction {
action_id: string;
tool_name: string;
state_changing: boolean;
timestamp_ms: number;
precondition_digest?: string;
result_digest?: string;
evidence_refs?: string[];
parent_action_id?: string;
causal_chain_id?: string;
tool_descriptor_digest?: string;
server_card_digest?: string;
scope_lease_id?: string;
approval_context_hash?: string;
input_taint_labels?: string[];
output_taint_labels?: string[];
memory_read_refs?: string[];
memory_write_refs?: string[];
pre_state_digest?: string;
post_state_digest?: string;
}

export interface AEPVerifierResult {
verifier_id: string;
passed: boolean;
score?: number;
claim_ids?: string[];
}

export interface AEPBudgetEntry {
limit?: number;
spent: number;
}

export interface AEPBudgetLedger {
token_budget?: AEPBudgetEntry & { spent: number };
latency_budget?: { limit_ms?: number; actual_ms: number };
tool_budget?: AEPBudgetEntry & { spent: number };
risk_budget?: AEPBudgetEntry & { spent: number };
retry_budget?: AEPBudgetEntry & { spent: number };
human_approval_budget?: AEPBudgetEntry & { spent: number };
}

export interface AEPRunContext {
agent_id?: string;
agent_version?: string;
subagent_id?: string;
delegation_chain?: string[];
environment_digest?: string;
dependency_lock_digest?: string;
}

/** Per-record and per-run side-effect classification. */
export type AEPSideEffectClass =
| 'read'
| 'mutate-local'
| 'mutate-external'
| 'network-egress'
| 'unknown';

/** How evidence for a run was captured. */
export type AEPRecordingMode = 'full' | 'delta' | 'validation';

/**
* Detected drift between an action's declared arguments and the arguments
* used at runtime. The JSON Schema allows additional properties.
*/
export interface AEPArgumentDrift {
tool_name?: string;
declared_digest?: string;
actual_digest?: string;
diff_summary?: string;
drifted_args?: string[];
/** Additional properties beyond the known fields. */
[key: string]: string | string[] | undefined;
}

export interface AEPSignature {
alg: string;
key_id: string;
sig: string;
bundle?: Record<string, unknown>;
transparency_log_ref?: string;
}

/**
* Agent Evidence Protocol record — runtime action evidence and run provenance.
*
* Mirrors `schemas/aep/aep-record.schema.json`. Use `getSchema("aep-record")`
* for runtime JSON Schema validation; use this interface for compile-time type
* checking in TypeScript.
*/
export interface AEPRecord {
schema_version: AEPRecordSchemaVersion;
run_id: string;
created_at_ms: number;
trace_id?: string;
parent_trace_id?: string | null;
repo_commit?: string;
runtime_version?: string;
model_provider?: string;
model_id?: string;
policy_bundle_digest?: string;
tool_manifest_digest?: string;
mcp_server_card_digest?: string | null;
input_refs?: AEPInputRef[];
output_refs?: AEPOutputRef[];
capability_decisions?: AEPCapabilityDecision[];
actions?: AEPAction[];
verifier_results?: AEPVerifierResult[];
budget_ledger?: AEPBudgetLedger;
run_context?: AEPRunContext;
user_id?: string;
subject_id?: string;
side_effect_class?: AEPSideEffectClass;
run_side_effect_class_max?: AEPSideEffectClass;
recording_mode?: AEPRecordingMode;
argument_drift?: AEPArgumentDrift;
signature?: AEPSignature;
}

// ---------------------------------------------------------------------------
// Schema index and loader types.
// ---------------------------------------------------------------------------

export interface SchemaIndexEntry {
id: string;
title: string;
Expand Down
43 changes: 42 additions & 1 deletion src/wasmagent_protocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,27 @@
from pathlib import Path
from typing import Any

__all__ = ["INDEX", "get_schema", "schema_path", "schema_ids"]
__all__ = [
"INDEX",
"get_schema",
"schema_path",
"schema_ids",
# Re-export TypedDict types from types.py for convenience.
"AEPRecord",
"AEPRecordSchemaVersion",
"AEPInputRef",
"AEPOutputRef",
"AEPCapabilityDecision",
"AEPAction",
"AEPVerifierResult",
"AEPBudgetEntry",
"AEPBudgetLedger",
"AEPRunContext",
"SideEffectClass",
"RecordingMode",
"AEPArgumentDrift",
"AEPSignature",
]

_SCHEMAS_PKG = "wasmagent_protocol.schemas"
# Editable / source-checkout fallback: the canonical schemas live at the repo
Expand Down Expand Up @@ -105,3 +125,24 @@ def schema_path(schema_id: str) -> Path:
return node
with resources.as_file(node) as p:
return Path(p)


# ---------------------------------------------------------------------------
# Re-export TypedDict types for convenience.
# ---------------------------------------------------------------------------
from wasmagent_protocol.types import ( # noqa: E402, F811 — lazy re-export
AEPAction,
AEPArgumentDrift,
AEPBudgetEntry,
AEPBudgetLedger,
AEPCapabilityDecision,
AEPInputRef,
AEPRecord,
AEPRecordSchemaVersion,
AEPRunContext,
AEPSignature,
AEPOutputRef,
AEPVerifierResult,
RecordingMode,
SideEffectClass,
)
Loading
Loading