Event And Payload Model

Copy Markdown View Source

The normalized runtime vocabulary in CliSubprocessCore is the source of truth for provider CLI execution events.

Schema Ownership And Forward Compatibility

Zoi is the canonical validation and normalization layer for new dynamic maps that enter the common runtime vocabulary.

  • CliSubprocessCore.Event and every CliSubprocessCore.Payload.* module own a schema/0, parse/1, parse!/1, and to_map/1 boundary.
  • The public contract remains the event or payload struct, not an anonymous parsed map.
  • Forward-compatible common-lane fields are preserved with Zoi.map(..., unrecognized_keys: :preserve) and projected into each struct's extra field.
  • Provider-native detail that does not belong in the normalized vocabulary should stay in event.raw or in the provider repo that owns the richer schema.

Event Envelope

CliSubprocessCore.Event is the common envelope emitted by the shared runtime.

%CliSubprocessCore.Event{
  id: 1,
  kind: :assistant_delta,
  provider: :codex,
  sequence: 42,
  timestamp: ~U[2026-03-19 00:00:00Z],
  payload: %CliSubprocessCore.Payload.AssistantDelta{},
  raw: nil,
  provider_session_id: "provider-session-1",
  metadata: %{},
  extra: %{}
}

Field meanings:

  • id: local unique event id
  • kind: normalized runtime kind
  • provider: normalized provider id
  • sequence: per-session event ordering
  • timestamp: event creation timestamp
  • payload: normalized payload struct for the given kind
  • raw: optional provider-native data retained for debugging
  • provider_session_id: provider-assigned session identifier when available
  • metadata: runtime-owned metadata
  • extra: preserved future-compatible event keys that are not part of the stable shared envelope yet

Normalized Kinds

The foundation currently defines these kinds:

  • :run_started
  • :assistant_delta
  • :assistant_message
  • :user_message
  • :thinking
  • :tool_use
  • :tool_result
  • :approval_requested
  • :approval_resolved
  • :cost_update
  • :result
  • :error
  • :stderr
  • :raw

Each kind maps to a payload module:

KindPayload
:run_startedCliSubprocessCore.Payload.RunStarted
:assistant_deltaCliSubprocessCore.Payload.AssistantDelta
:assistant_messageCliSubprocessCore.Payload.AssistantMessage
:user_messageCliSubprocessCore.Payload.UserMessage
:thinkingCliSubprocessCore.Payload.Thinking
:tool_useCliSubprocessCore.Payload.ToolUse
:tool_resultCliSubprocessCore.Payload.ToolResult
:approval_requestedCliSubprocessCore.Payload.ApprovalRequested
:approval_resolvedCliSubprocessCore.Payload.ApprovalResolved
:cost_updateCliSubprocessCore.Payload.CostUpdate
:resultCliSubprocessCore.Payload.Result
:errorCliSubprocessCore.Payload.Error
:stderrCliSubprocessCore.Payload.Stderr
:rawCliSubprocessCore.Payload.Raw

Payload Families

The payload structs intentionally separate shared runtime semantics from any provider-native output shape.

Every payload struct also preserves forward-compatible unknown keys in its own extra field when the boundary is intentionally map-backed and evolving.

Common examples:

Tool payloads are observation data only. They are serializable normalized facts about provider CLI output and do not carry BEAM callbacks, MFA tuples, PIDs, ports, registries, MCP routes, app-server routes, or any host-executable tool handler. Host tool execution belongs above this core payload layer and remains provider-native or explicitly unsupported until all-provider semantics are proven.

For neutral tool contract data outside an event envelope, use CliSubprocessCore.Tool.Descriptor, CliSubprocessCore.Tool.Request, and CliSubprocessCore.Tool.Response. These modules validate JSON-like values and reject executable BEAM terms such as functions, MFA tuples, PIDs, ports, references, and atoms in provider metadata.

Example

payload =
  CliSubprocessCore.Payload.ToolUse.new(
    tool_name: "shell",
    tool_call_id: "tool-1",
    input: %{"cmd" => "pwd"}
  )

event =
  CliSubprocessCore.Event.new(
    :tool_use,
    provider: :codex,
    sequence: 10,
    payload: payload,
    provider_session_id: "provider-session-1"
  )

The event and payload model is intended to be stable enough for:

  • the future core session engine
  • first-party SDK projections
  • ASM run envelopes that wrap, rather than redefine, the runtime vocabulary