Session Types — Inter-Agent Protocol Conformance¶
Status: Experimental (developer preview). The projection engine and the runtime monitor exist and are enforced through the communication gate, but there is no DSL syntax for declaring protocols yet and
symbi updoes not open sessions on its own — you open a session through the Rust API (RuntimeBridge::open_session). The surface will change. Track it under thesymbi-sessioncrate.The integration is off by default and gated behind the
sessioncargo feature. Build with--features sessionto enable it (e.g.cargo build -p symbi-shell --features session); default builds do not compile thesymbi-sessioncrate or the runtime/repl-core session wiring.
Symbiont's per-message policy gate answers "may this sender talk to this
recipient via this primitive?" in isolation. Session types add the question
it cannot answer: "does this sequence of messages follow an agreed
choreography?" — ordering, branching, and causality across ask / delegate /
send_to / parallel / race.
This reproduces the ORGA reasoning loop's two-tier discipline one level up: a global protocol is projected to a per-role finite-state machine, and those FSMs run as runtime monitors that an LLM-driven message sequence cannot leave. The audit becomes an offline-verifiable record that a multi-agent workflow executed the approved choreography and nothing else.
How it works¶
- Declare a global protocol — the message sequence with branches and loops
(today via the
symbi_session::GlobalIR; a DSL/Scribble surface is deferred). - Project it to one local FSM per role. Unprojectable protocols are rejected at this step with an explanatory error.
- Open a session with
RuntimeBridge::open_session(&protocol, role_binding, ttl). This establishes the per-role FSMs, attaches the monitor to the communication gate, and binds agents to roles. - Send normally. Under an active session, the inter-agent primitives auto-derive the protocol label from the FSM for the sender→recipient edge — your agent names only the recipient; the runtime supplies the legal label. The gate then validates the transition and advances the FSM.
- Out-of-protocol messages are denied with a precise, recovery-oriented message (e.g. "expected label 'task' to 'Validator'; you sent 'validate'") that is fed back to the agent so it re-plans — never a hard crash.
- Stalls fail closed. A session past its deadline transitions to a defined
Abortedterminal state.
Why auto-derived labels¶
In practice the dominant failure when an LLM drives a protocol is label
exactness — the model writes a descriptive label ("fix rejected work") instead
of the exact protocol token ("fix"). Because the runtime derives the label from
the projected FSM for the chosen recipient, the agent cannot get the token wrong.
At a genuine choice point (multiple legal labels to the same recipient), pass an
explicit protocol_label named argument on ask / delegate.
Primitive mapping¶
| Primitive | Session-type construct |
|---|---|
send_to |
output |
ask |
output then input |
delegate |
sequential message exchange (true session delegation deferred) |
race |
external choice, resolved by the first legal response |
parallel |
parallel composition (deferred) |
Protocol transcript¶
Every transition (allowed and denied) is recorded to a tamper-evident, Ed25519
hash-chained transcript — the offline-verifiable proof that the workflow
executed the approved choreography and nothing else. Reach it via
RuntimeBridge::session_transcript() and verify it with verify(). (In-process
today; cross-node verification against a provisioned key comes with the
cross-instance work below.)
Current limits¶
- No protocol-authoring syntax; protocols are built via the Rust IR.
- Higher-order delegation,
parallelcomposition, and dynamic participants are out of scope for the current slice. - Cross-instance propagation is not wired yet — the gate isn't consulted on the inbound path and there's no remote session-establishment handshake.
- One active session per runtime bridge.
See the symbi-session
crate for the API.