pub enum Msg {
Show 62 variants
Key(Key),
Paste(Paste),
ClipboardRead(ClipboardRead),
SubmitPrompt {
text: String,
attachment_ids: Vec<u64>,
},
Slash(SlashCmd),
CancelTurn,
ConfirmAccepted,
ConfirmDeclined,
Quit,
RuntimeSignal(RuntimeSignal),
StreamText {
turn: TurnId,
chunk: String,
},
StreamReasoning {
turn: TurnId,
chunk: ReasoningChunk,
},
StreamToolCall {
turn: TurnId,
call: ToolCall,
},
ContextUsageEstimated {
turn: TurnId,
snapshot: ContextUsageSnapshot,
},
ProviderContextResolved {
model_id: String,
model_max: Option<usize>,
effective: Option<usize>,
source: Option<NumCtxSource>,
max_output: Option<usize>,
},
OllamaPlacementResolved {
model_id: String,
size_vram_bytes: u64,
total_bytes: u64,
suggested_num_ctx: Option<u32>,
},
ProviderVisionResolved {
model_id: String,
supports_vision: Option<bool>,
warn: bool,
},
BuiltinToolSchemaTokens(usize),
CompactionFinished {
turn: TurnId,
result: CompactionResult,
},
CompactionFailed {
turn: TurnId,
trigger: CompactionTrigger,
message: String,
kind: StatusKind,
},
StreamDone {
turn: TurnId,
usage: Option<TokenUsage>,
provider_continuation: Option<ProviderContinuation>,
stop_reason: Option<FinishReason>,
},
UpstreamError {
turn: TurnId,
error: UserFacingError,
},
TurnCancelled(TurnId),
ToolStarted {
turn: TurnId,
call_id: ToolCallId,
},
ToolProgress {
turn: TurnId,
call_id: ToolCallId,
event: ProgressEvent,
},
ToolFinished {
turn: TurnId,
call_id: ToolCallId,
outcome: ToolOutcome,
},
ApprovalRequested {
turn: TurnId,
call_id: ToolCallId,
tool: String,
risk: String,
kind: ApprovalKind,
prompt: String,
allowlist_scope: String,
},
QuestionAsked {
turn: TurnId,
call_id: ToolCallId,
questions: Vec<Question>,
},
TasksUpdated {
store: TaskStore,
},
TaskNotice {
text: String,
},
McpServerReady {
name: String,
tools: Vec<McpToolSpec>,
},
McpServerErrored {
name: String,
reason: String,
},
McpServerStopped {
name: String,
},
HookContext {
turn: TurnId,
texts: Vec<String>,
},
InstructionsChanged(Option<LoadedInstructions>),
MemoryChanged(Option<LoadedMemory>),
SessionSaved,
ConversationLoaded(ConversationHistory),
ConversationsListed(Vec<ConversationSummary>),
ProjectFilesListed(Vec<String>),
ScratchpadReady {
session_id: String,
path: PathBuf,
},
RuntimeTasksListed(Vec<TaskRecord>),
RuntimeTaskLoaded {
task: Option<TaskRecord>,
events: Vec<TaskTimelineEvent>,
},
RuntimeProcessesListed(Vec<ProcessRecord>),
RuntimeText(String),
RuntimeApprovalsListed(Vec<ApprovalRecord>),
RuntimeCheckpointsListed(Vec<CheckpointRecord>),
ForkCheckpointsFound(Vec<CheckpointRecord>),
RuntimePluginsListed(Vec<PluginInstallRecord>),
ModelPullFinished {
model: String,
},
ModelPullProgress(String),
Tick,
Resize {
width: u16,
height: u16,
},
TransientStatus {
text: String,
},
EditorReturned {
text: Option<String>,
},
BackgroundAgentStarted {
agent_id: String,
description: String,
},
BackgroundAgentProgress {
agent_id: String,
activity: String,
tokens: usize,
},
BackgroundAgentFinished {
agent_id: String,
description: String,
report: String,
success: bool,
cancelled: bool,
usage: Option<TokenUsage>,
tokens: usize,
duration_secs: u64,
},
MouseScroll {
delta: i16,
},
FocusChanged(bool),
OpenImageAt {
message_index: usize,
image_index: usize,
image_number: Option<u64>,
},
CopySelection(String),
}Expand description
Single reducer input. Non-exhaustive is intentional: adding a new
variant is a deliberate act that forces every reducer arm to
consider it at compile time (the reducer’s match is NOT
_ => — see reducer.rs).
Serde derives exist for --record / --replay: every Msg the driver
feeds the reducer is serialized to one JSONL line, and replay folds the
deserialized stream back through update(). New variants round-trip
automatically via the externally-tagged representation — no per-variant
recording code to keep in sync.
Variants§
Key(Key)
Raw key event from crossterm, after the event source has stripped mouse/resize/paste.
Paste(Paste)
A terminal bracketed paste (always text; see Paste).
ClipboardRead(ClipboardRead)
Async result of a Cmd::ReadClipboard (Ctrl+V) — image, text, empty, or
error. Kept separate from Msg::Paste so the paste-race guard can
track exactly these reads (see ClipboardRead).
SubmitPrompt
User hit Enter on a non-empty input. The event source has already stripped the slash-command routing.
Slash(SlashCmd)
User ran a slash command (post-routing from app::event_source).
CancelTurn
Esc or another explicit cancellation source during an active turn.
ConfirmAccepted
Confirmation modal answer.
ConfirmDeclined
Quit
User wants to exit cleanly (Ctrl+D with empty input, or /quit).
RuntimeSignal(RuntimeSignal)
External process lifecycle signal. In raw-mode TUI sessions a
typed Ctrl+C still arrives as Msg::Key; this variant covers
OS-level SIGINT/SIGTERM/SIGHUP delivered from outside.
StreamText
Chunk of assistant text. Append to partial_text.
StreamReasoning
Chunk of reasoning / thinking content.
StreamToolCall
Model emitted a tool call. Append to the outgoing call list;
actual execution dispatches on StreamDone.
ContextUsageEstimated
Effect runner estimated the fully-enriched request context after built-in and MCP tool schemas were attached.
ProviderContextResolved
Effect runner resolved the provider’s context window. For Ollama,
model_max is the probed architectural window and effective is the
auto-fitted/overridden num_ctx. Model-level metadata (not turn-scoped);
model_id is carried so a probe that lands after a /model switch can
be dropped instead of overwriting the new model’s window. Drives the
/context display + quick-fix.
Fields
OllamaPlacementResolved
Effect runner verified the loaded model’s memory placement after a turn
(Ollama /api/ps). size_vram_bytes < total_bytes ⇒ the model spilled
to CPU/RAM (slow). Model-level metadata (not turn-scoped); model_id is
carried so a probe that lands after a /model switch can be dropped.
Drives the offload warning + /context placement line.
Fields
ProviderVisionResolved
Effect runner probed whether the active model can see images (Ollama
/api/show capabilities). Model-level metadata (not turn-scoped);
model_id is carried so a probe that lands after a /model switch is
dropped. warn (set at the trigger site — an image paste, a /model
switch with an image staged, or a send carrying images) gates the
one-shot no-vision-model notice; the capability snapshot refreshes
regardless. supports_vision is None when unknown (non-Ollama, or the
probe failed) → never warn.
BuiltinToolSchemaTokens(usize)
The effect runner’s estimate of the built-in tool-schema token cost
it appends to every request during dispatch. Not turn-scoped — the
reducer stores it on runtime so /context can fold it into its
MCP-only estimate and agree with what dispatch actually decides.
CompactionFinished
Context compaction completed and produced a replacement model-visible history.
CompactionFailed
Context compaction failed or no-oped. Manual failures end the compaction turn; auto failures may leave generation running.
StreamDone
Stream complete. Carries final token count and opaque provider state that must round-trip on the next request.
Fields
usage: Option<TokenUsage>provider_continuation: Option<ProviderContinuation>stop_reason: Option<FinishReason>Why the model stopped (truncation / content block / normal), when the provider reported it. Drives the truncation status note.
UpstreamError
Upstream returned a recoverable or terminal error. Reducer
commits an error line and returns to Idle (or surfaces a
retry affordance, if recoverable).
TurnCancelled(TurnId)
Terminal event for a cancelled turn. Emitted by the effect
runner’s drop_scope once every child task in the turn’s
TurnScope has unwound. Reducer transitions
Cancelling(id) → Idle when it arrives.
Without this, the reducer relies on the (wrong) side-channel of
UpstreamError arriving from a cancelled provider call to exit
Cancelling. If the provider task is aborted before it can
emit an error, the state would stick in Cancelling forever.
ToolStarted
Tool was picked up by the executor — useful for “spinner started” UI transitions.
ToolProgress
Mid-flight progress (streaming subprocess output, byte-count
updates, multimodal artifacts, nested subagent activity).
Reducer pattern-matches the variant and routes accordingly:
text variants update the status line; Artifact with an
image/* mime attaches to the in-flight assistant message;
Subagent* variants render as indented status.
ToolFinished
Tool finished (one of Finished / Error / Cancelled).
ApprovalRequested
A gated tool is awaiting the user’s inline approval (interactive
ask mode / Auto-mode escalation). The reducer enqueues a modal; the
answer flows back as Cmd::ResolveApproval. The tool task is parked
until then, so the turn naturally pauses (its outcome slot stays
None).
QuestionAsked
The ask_user_question tool is asking the user a batch of questions. The
reducer stores a PendingQuestionSet and renders a selectable modal; the
answer flows back as Cmd::ResolveQuestion. The tool task is parked until
then, so the turn naturally pauses (its outcome slot stays None).
TasksUpdated
The TaskBroker published a full checklist snapshot after a task tool
(or /tasks edit) mutated the store. Fire-and-forget — the broker
already holds the new truth; the reducer replaces conversation.tasks
and diffs old vs new for task_completed hook dispatch. Snapshot (not
a diff) so this arm is a plain replace and can never desync. Not
turn-scoped: /tasks edits arrive outside any turn, and gating would
only let the render copy drift from the broker’s truth.
TaskNotice
A one-line checklist notice for the model’s next request (user
/todos edit, vetoed completion). Buffered on
state.pending_task_notices; the reducer never turn-gates it.
McpServerReady
initialize succeeded; server is ready to dispatch tools.
McpServerErrored
Server startup failed OR the child exited with non-zero.
McpServerStopped
HookContext
Context strings returned by before_tool_use plugin hooks
(additionalContext). The reducer buffers them (capped) and the next
dispatched model request carries them in the instructions channel,
consumed exactly once. Turn-scoped so a stale hook’s context can’t
leak into a later run.
InstructionsChanged(Option<LoadedInstructions>)
MERMAID.md loaded / changed / removed since last check.
MemoryChanged(Option<LoadedMemory>)
Memory files loaded / changed / removed since last check.
SessionSaved
save_conversation finished.
ConversationLoaded(ConversationHistory)
/load <id> — a saved conversation has been read off disk.
ConversationsListed(Vec<ConversationSummary>)
Response to Cmd::ListConversations. Populates the /load
picker’s candidate list.
ProjectFilesListed(Vec<String>)
Response to Cmd::ListProjectFiles: relative project paths for the
@-mention picker (gitignore-aware walk, capped, sorted; directories
carry a trailing /).
ScratchpadReady
Response to Cmd::EnsureScratchpad: the per-session scratch directory
exists on disk at path. Carries the conversation id it was minted
for so the reducer can drop a stale ready that raced a /clear or
/load — it stamps Session::scratchpad only when the id still
matches the live conversation.
RuntimeTasksListed(Vec<TaskRecord>)
Response to /tasks.
RuntimeTaskLoaded
Response to /task <id>.
RuntimeProcessesListed(Vec<ProcessRecord>)
Response to /processes.
RuntimeText(String)
Generic daemon/runtime text response.
RuntimeApprovalsListed(Vec<ApprovalRecord>)
RuntimeCheckpointsListed(Vec<CheckpointRecord>)
ForkCheckpointsFound(Vec<CheckpointRecord>)
Reply to Cmd::ListForkCheckpoints: file checkpoints anchored past a
rewind’s fork point (oldest first). The reducer emits a system notice
naming the oldest so the user can /restore files the discarded
timeline changed; empty means no notice.
RuntimePluginsListed(Vec<PluginInstallRecord>)
ModelPullFinished
/model <name> finished pulling (Ollama only).
ModelPullProgress(String)
Streaming stdout line from an ollama pull subprocess.
Reducer forwards to the status line for the user to watch.
Tick
1/60s timer tick. Used for spinner animation + elapsed-time display. Reducer only advances derived fields.
Resize
Terminal was resized. Reducer normally no-ops; render consumes.
TransientStatus
Generic user-visible feedback from an async effect handler, routed into the chat transcript. Lets effects surface a result (clipboard read, config saved, plugin install, …) without a bespoke Msg per effect.
EditorReturned
The $EDITOR compose round-trip finished (Ctrl+O / /editor). The
run loop suspends the TUI, launches the editor, and pushes this with
the edited draft — so the recording captures the RESULT and --replay
never launches an editor. Some(text) replaces the input buffer
(empty = deliberate clear); None = no-op (defensive).
BackgroundAgentStarted
A subagent was detached from its turn via Ctrl+B and keeps running in its own task. Adds a row to the live agent panel registry.
BackgroundAgentProgress
Throttled live activity from a detached subagent (same discipline as
ProgressEvent::Subagent* — never per stream chunk).
BackgroundAgentFinished
A detached subagent finished. Removes its panel row, folds the child’s spend into the session totals, and delivers the report to the model via the queued-message path.
Fields
cancelled: boolThe drive ended because its cancel token fired (/agents kill,
the agent tool’s kill action). A cancelled child gets a system
note but NO queued report — the killer already knows.
usage: Option<TokenUsage>Provider-reported usage for the child’s whole drive (None when the
provider reported nothing — the display falls back to tokens).
MouseScroll
Mouse-wheel scroll in the chat pane. Positive delta = scroll
toward older messages (up), negative = toward newer (down). The
reducer accumulates into ui.mouse_scroll_accum; the render layer
diffs it and applies the delta to the ChatWidget’s scroll offset.
FocusChanged(bool)
The terminal window gained (true) or lost (false) focus, from
terminal focus reporting. The reducer records it in
ui.terminal_unfocused to gate the attention bell.
OpenImageAt
Ctrl+Click on an image thumbnail in the chat pane. The
coordinates are absolute screen row/col; the render cache’s
ChatState::find_image_at_screen_pos maps them to a
(message_index, image_index) pair. The main loop handles the
lookup before forwarding this message to the reducer, so by
the time the reducer sees it, the target has already been
resolved into a base64 payload and this Msg carries the
already-decoded image. The reducer just emits
Cmd::WriteImageToTemp + Cmd::OpenInSystem.
Fields
CopySelection(String)
Copy the current chat text selection to the system clipboard. The main
loop reads the selected text from the render layer (rstate.chat) and
emits this, so the side effect flows through update() — and is recorded
for replay — instead of being dispatched out-of-band (#18).
Implementations§
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Msg
impl<'de> Deserialize<'de> for Msg
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for Msg
impl RefUnwindSafe for Msg
impl Send for Msg
impl Sync for Msg
impl Unpin for Msg
impl UnsafeUnpin for Msg
impl UnwindSafe for Msg
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more