Skip to content

How the app handles a message

BubFramework connects the agent to application concerns: session identity, turn state, prompt preparation, and message delivery. Each stage exposes hooks so a plugin can change that part of the application.

This pipeline belongs to bub[app]. A direct core.Agent.run() starts the agent loop with the prompt, state, and tape you provide.

framework.process_inbound(message) runs these stages:

Stage What it does
resolve_session Select the session ID for the inbound message.
load_state Collect the current turn’s state from hook implementations.
build_prompt Turn inbound content into a prompt.
run_model / run_model_stream Produce the model output, through either a final result or a stream.
save_state Finish work associated with the model stage. Runs in its finally block.
render_outbound Turn the result into outbound envelopes.
dispatch_outbound Deliver each outbound envelope.

The built-in plugin supplies the application defaults. Plugins can contribute or replace behavior according to each hook’s contract; Hooks reference defines first-result hooks, combined results, and priority rules.

The built-in run_model_stream hook calls BubAgent.run_stream(). BubAgent uses the core model and tool loop, adding settings, skills, commands, steering, and application tool policies.

The framework adapts between streaming and non-streaming hook results. process_inbound(..., stream_output=True) lets the channel router consume incremental output and returns a completed TurnResult after consumption.

Replacing run_model with a plugin changes this behavior. For example, an echo plugin can return text without ever creating an agent run. Core events and tape recording occur when the chosen implementation actually uses the core agent.

Use async with framework.running(): when hooks provide stores or other application resources. It opens those resources and releases them on exit. The built-in per-message lifespan opens during state loading and closes in save_state.

save_state runs once execution reaches the model stage, including when that stage raises. A failure in session resolution, state loading, or prompt construction happens before this finally block and does not trigger it. Plugins that acquire resources in those earlier stages must account for that boundary.

The built-in save_state closes the per-message lifespan. The application records selected settings separately; saving other state requires an explicit persistence policy.

If the model stage raises an unhandled exception, the framework runs save_state, then skips normal rendering and dispatch. The outer error path calls on_error(stage="turn", ...) and re-raises the exception. The built-in error observer can send an error response through outbound dispatch. Failures in individual on_error observers are isolated so other observers can run.

Use plugin setup to register your implementation, hook recipes for specific changes, and the hooks reference for exact signatures and ordering.