Cancellation
Ctrl+C interrupts a running task. The agent returns to Idle
promptly, the in-flight LLM HTTP request is dropped, and any files
already modified are listed.
How It Works
The CLI installs a tokio::signal::ctrl_c handler that fires a
tokio_util::sync::CancellationToken. The agent holds a clone of the
token and:
- Checks
token.is_cancelled()at every state transition and before every tool call. - Wraps the streaming LLM call in
tokio::select!racing againsttoken.cancelled(). When the token wins, the LLM future is dropped, which cancels the underlyingreqwestrequest.
When cancellation lands, run_task returns Ok(TaskResult { cancelled: true, verified: false, summary, files_changed }):
summary— text from the most recent LLM turn, if any.files_changed— every file the agent had time to mutate viafiles-writeorfiles-edit.
Wiring Your Own Token
If you’re embedding merlin-core programmatically, register your own
CancellationToken via the fluent setter:
use merlin_core::agent::Agent;
use tokio_util::sync::CancellationToken;
let token = CancellationToken::new();
let mut agent = Agent::new(provider, config, commands);
agent
.on_event(|event| println!("{event:?}"))
.with_cancellation(token.clone());
// Fire from anywhere -- UI button, parent supervisor, parent context.
token.cancel();
Tested
The integration test suite (crates/merlin-core/tests/integration_agent.rs)
includes cancellation_returns_promptly_with_partial_result, which
uses a HangingProvider that never produces a response. The test
asserts that firing the token returns a cancelled = true result well
within a 2-second timeout.