Event-Driven Async
One contradiction runs through it — the LLM's training assumes synchrony (a tool call is followed by its result), while real deployment demands asynchrony (users interrupt, tasks run in parallel, events arrive before a tool returns). Event-driven architecture exists to hold that contradiction.
The perception, execution, and collaboration tools discussed in the previous sections are all actively invoked by the Agent. This section turns to another challenge raised at the beginning of this chapter: how does an Agent manage time-consuming tasks and respond to external events that may arrive at any time? This requires an event-driven asynchronous architecture, and two of the five tool categories—Event-Triggered Tools and User Communication Tools—leverage this architecture to function.
Why Asynchrony is Needed
Let’s start with an analogy to explain why asynchrony is needed. Synchronous means “do one thing before you can do the next,” while asynchronous means “multiple things can happen concurrently.” A traditional synchronous Agent architecture is like a single checkout counter at a store—it can only handle one customer at a time, and only calls the next number after finishing with the current one. A truly intelligent assistant is more like a flexible secretary—with multiple pending items on the desk (emails, phone calls, visitors), the secretary decides which to handle first based on urgency, and can pause and switch to a more urgent task mid-way. In synchronous mode, the Agent either has to wait for a background task to complete before talking to the user, or wait for the conversation to end before processing a newly arrived event. It cannot deliver the core capabilities a real assistant scenario requires:
- Asynchronous execution is the norm—Many tasks require long runtimes and should not block user interaction.
- Dynamic judgment of event priority—Not all events are equally important. The Agent needs to intelligently choose a handling strategy: cancel the current operation (urgent), add it to a queue (routine), or process in parallel (independent lightweight query).
- Fluency in interruption and resumption—An interrupted conversation or task should be able to resume naturally.
The asynchronous paradigm, however, collides with a fundamental fact about current LLMs: their training assumes synchrony—after a tool call, the next message must be the tool result—while real deployment demands asynchrony: users interrupt at will, tasks progress concurrently, and external events arrive before a tool returns. This “synchronous training / asynchronous deployment” contradiction runs through every engineering trade-off in the rest of this section.
To solve this, we need an event-driven asynchronous Agent architecture. Technically, this means the system no longer actively and repeatedly checks for “new messages” (this is polling, which is inefficient), but instead automatically triggers processing logic when a new message arrives. All inputs, outputs, thought processes, and external interactions are uniformly modeled as an event stream—a sequence of event records arranged on a timeline. Figure 4-2 shows the overall architecture of an event-driven asynchronous Agent, illustrating the relationship between event sources, the event queue, and the Agent processing flow.
OpenClaw and the Real-World Need for Event-Driven Architecture
The open-source framework OpenClaw (its architecture will be detailed in Chapter 5) receives multi-channel messages through a Gateway control plane and routes them to the Agent runtime. It provides three built-in automation mechanisms:
- Hooks: Respond to events in the Agent’s lifecycle, such as session creation and reset, similar to event triggers in GitHub Actions
- Cron (scheduled-task scheduler): Execute periodic tasks according to cron expressions (a widely used syntax for scheduled tasks in Unix systems, e.g.,
0 9 * * 5means 9 AM every Friday), such as generating a weekly report every Friday or summarizing data at the beginning of each month - Heartbeat (Heartbeat Daemon): Wakes up the Agent every N minutes to check whether anything requires attention, using judgment to avoid alert fatigue
These three mechanisms give OpenClaw Agents the appearance of autonomy—even with the user offline, the Agent can generate reports on schedule, check system status, and handle routine chores. Look closer, though, and a fundamental limitation appears. To be precise: the Gateway already handles messages from built-in channels (IM, the web interface) in push fashion—they are routed to the Agent the moment they arrive. And of the three automation mechanisms, only Cron and Heartbeat let the Agent act without a user message, and both are time-driven—Heartbeat checks at fixed intervals, Cron fires at preset times. Hooks merely react to the framework’s internal lifecycle events and cannot bring in new changes from the outside world. The real gap is this: for any third-party event source beyond the built-in channels—a new email, an external API callback pushing data, an urgent notification demanding immediate attention—OpenClaw has no immediate ingress path. The Agent cannot respond the moment the event occurs; at best it notices at the next Cron/Heartbeat tick.
This delay is unacceptable in many scenarios. Take PineClaw (Pine AI’s OpenClaw plugin) as an example: Pine AI is an AI assistant that makes real phone calls on behalf of the user, with typical scenarios including negotiating bills, canceling subscriptions, and handling insurance claims. When a user initiates a Pine phone task through an OpenClaw Agent, Pine’s voice AI will make the call on behalf of the user, but the user may need to intervene at any time during the call:
- Real-time Identity Verification: The customer service representative asks to verify the account holder’s identity, and Pine needs the user to immediately provide a security code or one-time password (OTP)
- Three-Way Call Confirmation: The customer service representative asks to speak directly with the account holder, and Pine needs the user to answer the phone within seconds
- Progress Sync and Decision Confirmation: At a critical point in the negotiation (e.g., the other party proposes a price reduction), Pine needs the user to confirm whether to accept
With Heartbeat’s periodic polling—say a 5-minute interval—the user might not get the notification while the representative is still waiting for the verification code; the representative hangs up and the call fails. Shortening the interval to a few seconds would simply flood the system with useless requests.
PineClaw’s solution is to introduce a Channel mechanism—establishing a real-time event channel between OpenClaw’s Gateway and the Pine API. When key events occur, such as when a call connects, when user input is required, or when the call ends, the message is instantly pushed to the OpenClaw Agent. The Agent processes it immediately and notifies the user, reducing response latency from minutes to seconds.
This case reveals the core value of an event-driven architecture for Agent frameworks: true “proactive service” requires not only that the Agent can periodically check the world, but also that the world can actively notify the Agent. Unifying all inputs—user messages, tool returns, external callbacks, scheduled triggers—into an event stream, and driving the Agent’s thinking and actions through an event loop, is the architectural foundation for achieving this goal. Under this architecture, we will first introduce the two tool categories directly related to events, as well as the virtual identity and isolated execution environment that support the Agent’s independent actions, before discussing the specific design of the event handling mechanism.
Event-Triggered Tools
Event-triggered tools are the entry points through which external events drive an Agent’s actions. Without them, an Agent can only operate in a continuous loop of thinking, calling tools, and finally outputting a result, then waiting for the user’s next input. To translate changes in the world into events an Agent can process, there are three common types of event-triggered tools.
Timers (set_timer) handle events tied to physical time. If an email goes unanswered, the Agent should follow up after a while to ask about progress; if a call is placed outside the recipient’s business hours, it should retry during the next business window. To support this, tools like OpenClaw and Claude Code include timer functionality, letting the Agent wake itself at a specified physical time. One-shot timers are used for tasks with a specific execution time: for instance, if a user asks to “call the DMV” on a Saturday, the Agent sets a timer for “next Monday at 10:00 AM to call the DMV,” which triggers the call automatically. Recurring timers are used for periodic tasks: such as checking server health every hour or sending a progress report every Friday. Additionally, some external services don’t support proactive progress updates, requiring the Agent to actively poll for status. In such cases, a recurring timer is needed for repeated queries—the Heartbeat mechanism in OpenClaw from the previous section is a systematized form of this, and it’s the root of OpenClaw’s “proactive service” capability.
Background Task Monitoring (monitor_shell) handles events from asynchronously executing tools or command-line tasks. Some command-line tasks run in the background for a long time, and the Agent needs to track their progress. If the Agent “stares at the command line,” repeatedly calling a tool to poll for progress, it burns tokens; if it waits until the task has fully finished before thinking again, it misses critical problems as they unfold—and if the command hangs, it cannot intervene at all, stalling the whole task. Claude Code solves this by introducing a monitor tool, allowing the Agent to monitor new command-line output, including output that contains specific keywords.
External Event Channels (connect_channel) push external events like new emails, API callbacks, or IM messages to the Agent in real time. The Channel mechanism in PineClaw from the previous section is a typical implementation.
From a design perspective, event-triggered tools should define clear trigger conditions and filtering rules to prevent irrelevant events from waking the Agent and wasting computational resources. The event payload should contain sufficient context information to minimize the number of additional queries the Agent needs to make after being woken up.
User Communication Tools
User communication tools arise from the increasing diversification of communication channels between the Agent and the user. Many Agents (like Claude Code, Manus, Genspark) use a native ReAct loop, where everything the Agent “says” (i.e., assistant messages) is sent directly to the user, who must open a specific session in the app to converse with the Agent. OpenClaw is one of the most influential general-purpose Agents that breaks this human-computer communication paradigm: its sessions are transparent to the user—the user doesn’t need to be aware of the session’s existence or care about the details of the Agent’s tool calls; both the user and the Agent can send messages to each other at any time, rather than a strict user-message/Agent-response pattern. Consequently, many users feel OpenClaw has a “human-like presence,” messaging them asynchronously the way a secretary would. These text messages are not the model’s assistant messages piped straight to the user; they are sent through dedicated tools, can carry image and file attachments, and can trigger push notifications according to urgency.
Beyond text-based communication, an increasing number of Agents possess multimodal communication capabilities, such as sending structured card messages or reminder emails. Some Agents have begun experimenting with generative UI, using HTML or other methods to create interactive interfaces for presenting information to users in a more user-friendly way. From a design perspective, user communication tools should support asynchronous messaging (the user may not be online), provide read/unread status tracking, and maintain message consistency across multiple channels.
Multi-channel User Communication and Re-engagement.
One category boundary is easy to blur: both tool categories “send notifications,” but if the recipient is an approver or collaborator (requesting admin approval, reporting progress to a collaborating Agent), the tool belongs to the collaboration category; only when the recipient is the end user does it count as a user communication tool. The distinction lies not in the channel but in who is being notified, and why.
An Agent’s response should not be limited to a single channel; the notification mechanism also serves as a user re-engagement mechanism. Message sending extends to instant messaging, SMS, email, phone calls, push notifications, and other channels. The Agent decides on the channel based on a combination of urgency, user status, content nature, and user preferences, ensuring important messages are not missed while avoiding redundant interruptions.
For long-running tasks, the Agent needs to proactively notify the user upon completion to bring the user’s attention back. For periodic tasks (like daily summaries or weekly reports), notifications can help users develop a regular interaction habit.
User communication tools solve the problem of “how to reach the user.” However, the identity the Agent assumes on these channels and the environment in which it performs actions on behalf of the user require a layer of identity and execution-environment infrastructure, which is the topic of the next section.
Virtual Identity and Isolated Execution Environment
A word on this section’s placement: virtual identity and isolated execution environments are fundamentally execution-environment infrastructure, of a piece with the sandboxes discussed under execution tools. They appear here, in the asynchronous architecture section, because the Agents that need them most urgently are the ones that run independently, stay resident, and act on the user’s behalf at any moment.
As mentioned at the beginning of this chapter, Samantha in Her has an independent identity and operating environment. Achieving such a general-purpose assistant forces a key architectural choice: should the Agent manage the user’s personal accounts directly, or hold a virtual identity of its own? Direct management looks convenient, but one Agent error or compromise exposes the user’s entire digital identity. The safer approach is to give the Agent an independent virtual identity—the way a secretary has their own office phone and mailbox—comprising dedicated communication accounts, storage, and computing environments, so the Agent can work on the user’s behalf under a transparent, clearly declared identity. This transparency does not weaken trust; it can make communication more authentic.
Virtual identities need to be grounded in isolated execution environments. Virtual computers (VMs/containers) and virtual phones (Android emulators) provide the Agent with operating system-level isolation and full desktop/mobile operation capabilities: the Agent has its own user account, home directory, and login credentials within them, making all operations traceable and auditable; even if erroneous operations are performed, the host system and the user’s real device remain unaffected. This is an extension of the sandbox concept discussed in the execution tools section into the “digital identity” dimension—sandboxes isolate code execution, while virtual computers and phones isolate the entire digital identity.
An independent identity also presents two practical challenges. First, there are anti-automation mechanisms: many websites use CAPTCHAs and IP reputation checks to block automated access. Virtual environments using data center IPs are easily identified; in practice, normal access often requires configuring a residential proxy network (which uses real household IPs). Second, access to the user’s real accounts: when a task must log in as the user, use Human-in-the-Loop authentication—a VNC/RDP remote desktop where the user logs in personally, sees the full interface the Agent is operating, and understands why authentication is needed. The session token is then reused within its validity period to avoid interrupting the user repeatedly, balancing autonomy and security.
Data exchange between the main Agent and the virtual environment is accomplished through a shared file system: using volume mounts (e.g., /workspace/shared) to connect the main Agent, virtual computer, and virtual phone. Data is passed as file-path references rather than content copying, avoiding context window consumption. For example, in a data analysis task: the user uploads a CSV file to the shared directory, the Agent in the virtual computer reads the file, performs analysis, generates charts, and saves them back to the shared directory. The main Agent only needs to return the file path of the chart to the user—what is passed between parties is always a lightweight path string.
Event-triggered tools allow the world to wake the Agent, user communication tools allow the Agent to reach the user, and virtual identities with isolated execution environments allow the Agent to act independently and auditably. The remaining question is: when multiple events converge on the same Agent instance simultaneously, how should they be handled?
Event Handling Mechanism
A single Agent instance may face multiple events concurrently: a new message from the user, a result from a tool, a timer expiring, a collaboration request from another Agent. How these events are handled efficiently and correctly directly impacts performance and user experience.
The skeleton of this mechanism is the event loop from concurrent programming. Think of an asynchronous Agent as a long-running loop: each round takes a batch of events off the input queue, appends them to the trajectory, invokes the LLM once, executes the tools it decides to call, then returns to the top of the loop to wait for the next batch of events—the same structure as a Go goroutine reading messages from a channel and processing them round by round inside a for { select {... } }. This model has one crucial property: events are consumed only at the boundaries of each loop iteration. While the LLM is reasoning or a tool is executing, a newly arrived event cannot inject itself out of nowhere and disrupt the current step; it waits in the queue until the round reaches a safe point (the end of a stretch of reasoning, a tool return) and is then handled as a batch. Cancellation follows the same discipline: rather than forcibly cutting off at an arbitrary moment, the Agent checks “have I been asked to stop?” at a safe point—which is exactly the role played by ctx.Done() in Go (Chapter 10 uses the same context idiom to discuss a parent Agent’s cascading cancellation of its sub-agents). Once this is understood, the three processing strategies below differ only in how they treat the safe point: let the event wait for the next naturally occurring safe point (queued), proactively force a safe point early (cancellation), or simply spin up a separate loop and not wait for the main loop’s safe point at all (parallel).
Structured Event Modeling.
Handling requires understanding. A general-purpose Agent’s input doesn’t come only from the user—a third-party message is not sent by the user to the Agent, yet the Agent must understand it, weigh its importance, and decide whether to step in. This requires modeling each input as a structured event rich with semantics:
- Source (who): The user themselves, a contact, a stranger, a system notification
- Channel (how): Phone call, SMS, instant message, email, social media, timer trigger, asynchronous tool call result, command-line monitoring status update
- Content (what): Message text, emotional tone, urgency, whether a reply is needed
- Context (background): Whether it’s a reply to a previous conversation or a new communication, its relevance to the current task
Taking a customer refund request email as an example, the structured event looks like this:
{
"source": {"type": "email", "sender": "[email protected]"},
"channel": "gmail_webhook",
"content": {"subject": "Refund Request", "body": "Order #12345, requesting a refund..."},
"context": {"priority": "high", "customer_tier": "vip", "related_orders": ["#12345"]}
}
Only when these dimensions are clearly modeled as structured events can the Agent maintain a clear understanding in multi-party communication, avoiding mistaking user input for a tool result, or mistaking a tool result containing hidden instructions for a user command (prompt injection). The complexity of multi-threaded context management also requires the Agent to understand the relationships between multiple conversation threads—how a message from a third party affects the user’s mood, the user’s role transitions across different conversations, and when to synthesize information from different threads to provide advice. The trigger ecosystem of workflow platforms like n8n—webhooks, timers, emails, database changes, file watchers—illustrates the same principle: each trigger is a “sense organ” through which the Agent perceives the world. Once these heterogeneous events are modeled into one structured format, the Agent can process stimuli from any source consistently. The urgency determination and processing strategies below are all built on this unified modeling.
Dynamic Processing Strategy Based on Urgency.
Humans juggling multiple tasks adapt their strategy to urgency: an emergency makes them drop what they’re doing; a routine to-do goes on the list for later. An Agent’s event handling should show the same intelligence.
Cancellation-Based Processing is used for urgent events; its essence is forcing a safe point early for the urgent event: proactively interrupting the current step to turn this instant into a boundary at which the new event can be consumed. When an urgent event arrives (e.g., the user clicks “stop” or a supervisory system sends a high-priority instruction): (1) Stop the current operation—if the LLM is reasoning, immediately cancel the streaming response; if a synchronous tool is executing, send a cancel signal; (2) Drain the pending queue by removing all pending events; (3) Append those events together with the urgent event to the end of the trajectory; (4) Immediately re-invoke the LLM with the updated complete trajectory as input to assess the situation. For example, if the user inputs “Stop! I said the wrong thing” while the Agent is about to perform a potentially erroneous operation, the Agent will immediately see this new input, re-understand the true intent, and thus avoid executing the wrong action.
Queued Processing is used for routine events. When a non-urgent event arrives (e.g., an asynchronous tool returns a result or the user sends supplementary information): (1) Add the event to the end of the queue without interrupting the current operation; (2) Wait for the current operation to complete—let the LLM finish reasoning, let the synchronous tool finish executing; (3) When any tool call completes and returns a tool.result, check the queue. If the queue is non-empty, append all events to the trajectory at once; (4) The LLM processes the updated trajectory comprehensively. This enables batch processing, improving efficiency—for example, while the Agent is waiting for a search tool result, the user adds “only show results from the last month.” This supplementary information enters the queue, and when the search results return, both events are presented to the LLM together, avoiding unnecessary round trips.
Parallel Processing is used for independent, lightweight queries. For example, while the Agent is analyzing a large amount of data, the user suddenly asks, “What’s the weather like today?” Such queries have three characteristics: they are unrelated to the main task, require a quick response, and have low execution cost. Neither cancellation-based (would interrupt the important main task) nor queued processing (would make the user wait too long) is suitable. The system first assesses the query’s independence and complexity, then executes it independently in a parallel reasoning session, calling necessary tools to generate a response and returning it immediately. The query and response are appended to the main task’s trajectory, clearly marked as “executed in parallel with the main task” to avoid confusing the LLM.
Urgency Determination.
Urgent events: User interrupt (user.interrupt), supervisor instruction (supervisor.instruction), inter-Agent interrupt (agent.interrupt), external triggers marked as urgent (e.g., system alerts, payment failures).
Non-urgent events: Regular user input (user.input), Agent input (agent.input), tool results (tool.result), timer triggers (timer.trigger), regular external triggers.
Hardcoded rules have limitations; the semantics of the event dictate the handling method—“Stop immediately!” uses cancellation-based processing, “What’s the weather like today?” uses parallel processing, “Send the report in Chinese” uses queued processing. It is recommended to use a lightweight classification LLM as an event router, quickly determining which strategy to adopt when an event arrives.
The following experiment, an event-driven email processing Agent, implements the event handling strategies discussed above into a runnable implementation.
Experiment 4-4 demonstrates the simplest event-driven pattern—events enter a queue, and the Agent processes them sequentially. However, when the Agent needs to respond to interruptions during long-running tool executions, or manage multiple concurrent tasks simultaneously, a simple event queue is insufficient. Next, we discuss deeper engineering challenges.
Engineering Implementation: How to Make Synchronous Models Support Asynchronous Interruptions
Experiment 4-4 only handles serial events—events enter the queue one by one, and the Agent processes them one after another. Now, let’s return to the “synchronous training / asynchronous deployment” contradiction raised at the beginning of this section: when the user interrupts while a tool has not yet returned, how can the synchronous format accommodate it? This section lays out the engineering workarounds the industry uses today.
Let’s first illustrate this contradiction with a specific scenario. Suppose the Agent is helping a user draft an email (tool call: search for contact information). Before the search returns results, the user suddenly says, “Wait, first check tomorrow’s weather for me.” In a synchronous ReAct loop, the Agent must wait for the search to return before processing the next message—because the API requires that “after issuing a tool call, the next message must be the tool result.” But in the asynchronous real world, events can interrupt ongoing tasks at any time. Expressing the semantics of “asynchronous interruption” under the constraints of a “synchronous format” is precisely the problem this engineering solution aims to solve.
Engineering Expedient: An Asynchronous Implementation Simulating Synchronous Behavior.
The core idea is: Under normal conditions without interruptions, let the LLM see a standard synchronous trajectory; only when an interruption occurs, insert placeholders to fix the format. Here are five key rules:
Rule 1: Immediately record the assistant message (including thinking, content, and tool call) when the LLM produces it.
Rule 2: Record the tool result only when the tool call is complete. The trajectory is in a “partially completed” state during execution.
Rule 3: Interruptions during tool execution require placeholders. Generate a placeholder response for the unfinished tool (e.g., “The tool is executing in the background, please prioritize the new event”), append the interruption event, and re-invoke the LLM. From the LLM’s perspective, the assistant message still has a paired tool result.
Rule 4: Interruptions during LLM thinking directly discard the current thinking. Do not write it to the trajectory; instead, append the new event and start a new round of thinking.
Rule 5: Non-interrupting events enter the queue for batch processing. They are appended all at once only after the current cycle is complete.
Using the example of the Agent drafting an email when the user interrupts to ask about the weather, the operation of these five rules is as follows:
- The Agent calls
search_contactsto search for contact information, and the assistant message is immediately written to the trajectory (Rule 1). - Before the search tool returns results, the user sends “First check tomorrow’s weather for me.” Since this is a user interruption, the system generates a placeholder tool result for the unfinished
search_contacts(“The tool is executing in the background, please prioritize the new event”, Rule 3), then appends the user’s weather query to the trajectory and re-invokes the LLM. At this point, the trajectory format seen by the LLM is completely valid—the assistant message and tool result are perfectly paired. - After the Agent answers the weather query, the original
search_contactsresult arrives and is appended to the trajectory as a new event (Rule 2). The Agent reads the contact information and continues drafting the email.
The core advantage of this scheme: under normal conditions, the LLM sees a perfect synchronous trajectory—assistant messages and tool results strictly paired, the timeline clear, no placeholders or anomalous states. This is the friendliest arrangement for LLMs trained under the synchronous paradigm, and it preserves thinking quality. The placeholder—a necessary compromise—appears only when an interruption genuinely occurs.
But there remains a risk of exacerbating hallucinations. Even though the placeholder states explicitly that the tool “has not yet completed,” the model may still fabricate a tool result in later thinking—convincing itself the tool returned valid data and basing decisions on fabricated data. This is because, in the vast majority of trajectories seen during training, a tool call is immediately followed by the real result; the model has never learned how to handle situations where “the result hasn’t come back yet.” Therefore, in practice, interruptions are only triggered in truly urgent situations (when the user explicitly requests a stop); non-urgent events are placed in a queue for batch processing.
Asynchronous Tool Interfaces Suitable for Existing Models.
Since the synchronous assumption of models is difficult to break, a more fundamental strategy is to embrace asynchronous semantics at the tool-interface design level.
Traditional tool design implies a “call equals completion” semantics. For example, the name phone_call suggests “calling will dial the phone and wait for the call to end, returning the call log.” Under the asynchronous paradigm, “initiation” and “completion” should be decoupled:
initiate_phone_call: Initiates a phone call, immediately returning a task identifier and initial status (e.g., “Call initiated, dialing…”)- Call progress is communicated via event notifications (
phone_call_connected,phone_call_ended)
The key is that the tool’s name and description themselves should convey asynchronous semantics. When the model sees initiate_phone_call, its language understanding capabilities will naturally infer this is “initiating” rather than “completing.” The tool description should further reinforce this: “This tool initiates a phone call task handled by a sub-agent. It returns the task ID immediately upon successful initiation, allowing you to continue with other matters. A separate notification event will be sent when the call ends.”
Attention Dispersion in Queue-Based Processing.
When processing batch events, the model often focuses only on the last event. The root cause is that the model is trained to react to the most recent input, and batch events break this assumption.
Intervention can be applied at two levels:
Prompt Level: Inform the model, “When you receive multiple consecutive events, please ensure you comprehensively consider all the information.”
Agent Status Bar Markers: Add explicit markers before each event:
[Unprocessed Event 1/4] Tool result from database_query: ...
[Unprocessed Event 2/4] User supplementary note: Only look at Beijing data
[Unprocessed Event 3/4] System reminder: Report deadline is in 30 minutes
[Unprocessed Event 4/4] User asks: What's the progress?
Add a summary at the end: “There are 4 unprocessed events above, including 1 tool result, 2 user messages, and 1 system reminder. Please ensure your response covers all the information.”
Deeper Contradictions and Future Directions
Ultimately, the placeholders, asynchronous tool interfaces, and status bar markers from the previous sections are all using prompt engineering to patch the same “synchronous training / asynchronous deployment” contradiction—the cause of this contradiction has been detailed at the beginning of this section, so we do not repeat it here; instead, we focus on the fundamental solution.
Anticipating Model Evolution: From Synchronous to Asynchronous.
The engineering techniques above are essentially using prompt engineering to compensate for the shortcomings of model training, a temporary expedient during a transitional period. The real solution requires a paradigm shift at the model training level.
VLA (Vision-Language-Action, see Chapter 9) models in the robotics field are already beginning to face similar challenges: there is an unavoidable delay between perception and action. The success of VLA points the way for the evolution of Agent models. The next generation of models needs to acquire three core capabilities through reinforcement learning in asynchronous environments:
- Understanding Asynchronous Interleaving of Events in Trajectories: This is the most critical capability deficiency. Current models expect a strictly synchronous sequence, but in a real asynchronous environment, a tool call might be followed not by a tool result but by a new user message; thinking might be interrupted halfway, but the intermediate state should be retained in the trajectory, and thinking should continue after the new message is processed, rather than starting over. The model needs to maintain a clear understanding in such “out-of-order” trajectories—which tool calls are still waiting for results, and which thoughts are unfinished fragments.
- Resuming Interrupted Tasks and Thoughts: When interrupted to handle an urgent event, the model must still remember the unfinished task. For example, if the user suddenly asks about the weather while the Agent is executing a data analysis tool, after answering, the Agent should naturally wait for the data analysis result, rather than forgetting that a tool is still running. It is particularly important to avoid hallucinations where the model mistakenly believes the interrupted tool call has completed.
- Comprehensive Processing of Batch Events: When multiple events are appended to the trajectory in a batch, the model must not only focus on the last one; it must comprehensively consider all unprocessed information.
Achieving this asynchronous RL training requires new infrastructure: an asynchronous environment simulator (generating scenarios like delayed tool returns, random user interruptions, etc.) and specialized rewards for asynchronous capabilities (correctly understanding out-of-order trajectories, successfully resuming interrupted thoughts, avoiding hallucinations, comprehensively processing batch events).
Continuous thinking, however, need not wait for the next generation of models. A thin layer of orchestration logic (about two hundred lines) can turn an off-the-shelf text-thinking model into a continuous-time Agent on the spot—neatly bridging the “engineering expedient” and “model evolution” halves above. The mechanism is Rule 4, upgraded: instead of discarding a half-finished thought on interruption, build the entire interaction as one uninterrupted stream of thought—at any moment, forcibly close the <think> block the model is writing, inject the newly arrived observation (a tool return, a user interruption, a fresh recognition result) as an ordinary message, and let the model keep decoding. This exploits a resource that usually goes to waste: a model can generate thousands of tokens per second, while a tool call or a user utterance takes several seconds—those waits are free computation, usable for thinking ahead. Two behaviors emerge: thinking while waiting—rather than waiting for the tool to return or the user to finish speaking, the model reasons over the partial information it already has, even firing off the next tool call early (this “anticipatory thinking” tendency was reproduced zero-shot across multiple model families; see the paper cited in the footnote for the data); and thinking while doing—continuing to think while producing output, able to correct itself mid-action.
But the more critical half of this research concerns training, and it answers the “anticipating model evolution” call above: orchestration alone makes continuous thinking possible; whether it becomes useful depends on the training signal. The research found that with an “LLM-as-judge” style reward, the model learns to hide its thoughts—trading silence for the judge’s approval—while objective metrics actually worsen; only verifiable objectives that safeguard information coverage make continuous thinking pay off. In a nutshell: orchestration makes the behavior possible; training makes the behavior good—which confirms this section’s judgment that asynchronous capability must ultimately be consolidated through the right training, not patched forever with prompt engineering.
Engineering Practice
Safe points and cooperative cancellation are the part Zapvol lands most completely. User Stop, timeout, and process shutdown all converge on one AbortSignal (session.abortController). It acts only at safe points: LLM generation is cancelled by the SDK at the next await, tools are cooperative (the signal only notifies; a tool that ignores it is force-killed at the stepMs timeout), and cancellation winds down gracefully rather than dying mid-flight — terminatePendingToolParts(parts, "Cancelled") settles orphaned tool parts so the message sequence stays protocol-legal. Async execution rides the background job queue (BullMQ); isolated execution rides the sandbox.
Event triggers are richer than book’s minimal set: draft_schedule (a recurring plan the user confirms before it persists) and wait_and_resume (one-shot delayed self-continuation) are experimental tools; NudgeScheduler is book’s Heartbeat (a per-session debounced timer); long-lived rules run on a cron Scheduler. Mid-flight input is accepted via appendMessage / steer.
Three directions book names that Zapvol has not reached:
Optimization · external event channel: self-wakeup today is time-driven (schedule / nudge) — the gap is a general channel for the world to push events (book’s
connect_channel,monitor_shell).
Optimization · three-strategy dispatch and event router: Zapvol built cancel-style to production grade, but queue-style batching, parallel lightweight queries, and an urgency-based event router are not explicit.
Optimization · full virtual identity: Zapvol has browser control (BUA) and code-sandbox isolation, but book’s full “virtual identity” — independent accounts, virtual phone, residential proxy, VNC-visualized HITL auth — is not there.
Related reading
- Perception, Execution, Collaboration — the five tool kinds in full