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 uniquecall_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.