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.Eventand everyCliSubprocessCore.Payload.*module own aschema/0,parse/1,parse!/1, andto_map/1boundary.- 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'sextrafield. - Provider-native detail that does not belong in the normalized vocabulary
should stay in
event.rawor 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 idkind: normalized runtime kindprovider: normalized provider idsequence: per-session event orderingtimestamp: event creation timestamppayload: normalized payload struct for the given kindraw: optional provider-native data retained for debuggingprovider_session_id: provider-assigned session identifier when availablemetadata: runtime-owned metadataextra: 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:
| Kind | Payload |
|---|---|
:run_started | CliSubprocessCore.Payload.RunStarted |
:assistant_delta | CliSubprocessCore.Payload.AssistantDelta |
:assistant_message | CliSubprocessCore.Payload.AssistantMessage |
:user_message | CliSubprocessCore.Payload.UserMessage |
:thinking | CliSubprocessCore.Payload.Thinking |
:tool_use | CliSubprocessCore.Payload.ToolUse |
:tool_result | CliSubprocessCore.Payload.ToolResult |
:approval_requested | CliSubprocessCore.Payload.ApprovalRequested |
:approval_resolved | CliSubprocessCore.Payload.ApprovalResolved |
:cost_update | CliSubprocessCore.Payload.CostUpdate |
:result | CliSubprocessCore.Payload.Result |
:error | CliSubprocessCore.Payload.Error |
:stderr | CliSubprocessCore.Payload.Stderr |
:raw | CliSubprocessCore.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:
CliSubprocessCore.Payload.AssistantDeltaholds streamed assistant text.CliSubprocessCore.Payload.ToolUseandCliSubprocessCore.Payload.ToolResultrepresent tool invocation semantics.CliSubprocessCore.Payload.ApprovalRequestedandCliSubprocessCore.Payload.ApprovalResolvedrepresent human approval flow.CliSubprocessCore.Payload.CostUpdatecarries token and cost accounting.CliSubprocessCore.Payload.Erroruses the normalized shared severity lane::info,:warning,:error, or terminal:fatal.CliSubprocessCore.Payload.Rawretains unnormalized material when a provider event does not map cleanly to a richer normalized struct yet.
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