WardrikProtocol

Copy Markdown

Wardrik's wire format: its events as CloudEvents, and its operations as one OpenAPI document. It also holds the typed Elixir functions, like Wardrik.Agent.ask/3, that packages built on Wardrik call.

Install

def deps do
  [
    {:wardrik_protocol, "~> 0.1.0-rc.1"}
  ]
end

The schemas

Each schema is a JSON file in priv/v1. Its $id is urn:wardrik:schema:v1:<file name>.

FileDescribes
<noun>.<verb>.jsonthe data of the event wardrik.<noun>.<verb>, like run.started.json
cloudevent.jsonthe envelope every event is sent in
defs.jsontypes events share, like message, principal and activity
openapi.jsonevery operation, and its HTTP route
any otheran operation's input or output, like ask.json and agent_snapshot.json

Live events are sent as they happen and never stored. Their schemas set "x-wardrik-live": true: activity.changed, message.delta, reasoning.delta, subagent.progress and tool_call.progress.

Check an event

WardrikProtocol.schema/1 returns a schema with every $ref inlined, so any JSON Schema validator can use it without a resolver. With JSV:

envelope = JSV.build!(WardrikProtocol.schema("cloudevent"))
data = JSV.build!(WardrikProtocol.schema("run.started"))

{:ok, _event} = JSV.validate(event, envelope)
{:ok, _data} = JSV.validate(event["data"], data)

WardrikProtocol.types/0 lists every event type. WardrikProtocol.dataschema/1 returns the dataschema for a type.

The operations

openapi.json defines each operation once, by its operationId, like agent.ask. The HTTP API, the CLI and agents' tools all use the same IDs. Any OpenAPI 3.1 tool can read the file, so SDKs are generated from it.

Each operation carries three extensions next to its HTTP route:

ExtensionMeaningExample
x-wardrik-actionwhat the caller must be allowed to doagent.ask
x-wardrik-effectread, change or destructiveread
x-wardrik-subjectwhat it acts onagent, agents, deployment

WardrikProtocol.operations/0 lists them as maps. WardrikProtocol.operation/1 finds one by ID. Each has a single input schema that merges its path parameters, query parameters and body, so surfaces other than HTTP see one set of named fields:

%{input: input, output: output} = WardrikProtocol.operation("agent.ask")

{:ok, _input} = JSV.validate(%{"agent" => "acme/triage", "content" => "Hi"}, JSV.build!(input))

parameters lists which fields go in the HTTP path or query. The rest go in the body.