Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Concurrency Locking

The harness WASM plugin runs a single agent session as an event loop. To keep LLM requests, tool execution, and summarization from interleaving, the session holds a SessionLock (crates/core/src/lock.rs) that treats those three activities as mutually exclusive states and queues user input that arrives while any of them is in flight.

SessionLock

SessionLock tracks:

  • llm_active: bool — an LLM request is in flight.
  • summarizing: bool — a tool-output summarization call is in flight.
  • active_tools: HashMap<String, ActiveToolState> — tool executions keyed by their unique call_id.
  • input_queue: VecDeque<String> — user inputs received while the session was busy.

The session is locked when any of those is active:

#![allow(unused)]
fn main() {
pub fn is_locked(&self) -> bool {
    self.llm_active || self.summarizing || !self.active_tools.is_empty()
}
}

A tool boundary is the inverse — no LLM request, no summarization, and no active tools. Queued input is only replayed at a boundary.

LLM lock

try_acquire_llm() sets llm_active and returns false if a request is already in flight, so concurrent LLM requests cannot be dispatched. force_release_llm() clears it when the response is handled.

Tool tracking

register_tool(call_id, tool_name, timeout) records an ActiveToolState with a start time and timeout. complete_tool(call_id) removes it. Results are matched by the unique call_id, not by tool name, so multiple concurrent calls to the same tool are tracked independently.

check_timeouts() returns and removes any tool whose elapsed time exceeds its timeout; the harness calls this on heartbeat ticks to clean up stuck executions.

Summarization gating

set_summarizing(true) is set while a large tool output is being summarized by the cheap summary model, and cleared when the response arrives. While set, the session is locked and user input is queued, exactly like an active LLM request.

Input queueing and coalescing

When is_locked() is true, incoming user input is pushed onto input_queue rather than processed immediately. At the next tool boundary, boundary_reached() drains the queue via drain_ready_inputs():

  • A single queued input is returned verbatim.
  • Multiple inputs are coalesced into one message under a [Multiple inputs received while processing:] header, joined by ---.

boundary_reached() is called after a tool result is recorded and when a batch of pending tool calls drains to empty. It returns None unless the session is actually at a boundary, so input is never injected mid-turn.

Status

Implemented in crates/core/src/lock.rs with unit tests covering LLM mutual exclusion, boundary detection, input coalescing, timeout removal, and the combined locked-state predicate.