1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use shuvarie_config::ProviderConfig;
use shuvarie_db::StoredScroll;
#[derive(Debug, Clone)]
pub enum Command {
Ping,
ListModels {
provider_name: String,
},
AddProvider {
id: String,
config: ProviderConfig,
},
/// Sign in to an OAuth-backed provider (`chatgpt`, `copilot`) through the
/// device flow: the verification URL + user code surface as
/// [`crate::Event::AuthPrompt`], and completion or failure arrives as
/// [`crate::Event::AuthSuccess`] / [`crate::Event::AuthFailed`]. A cached
/// or pasted credential is verified in place; providers without OAuth
/// sign-in report a failure explaining that.
AuthProviderLogin {
name: String,
},
RemoveProvider {
name: String,
},
SetActiveProvider {
name: String,
},
SetActiveModel {
model: String,
},
/// Cycle the active model's reasoning-effort variant (Ctrl+T): forward
/// through the Selune catalog's variants, wrapping past the last back to
/// the unset default. A no-op when the active model is not in the catalog.
CycleVariant,
/// Set the active model's reasoning-effort variant directly
/// (`/variant [name]`). The TUI validates the value against the catalog
/// before sending; `None` selects the unset default.
SelectVariant {
variant: Option<String>,
},
SaveConfig,
NewSession,
/// Start a user turn. `model` optionally overrides the streaming target
/// for this turn only: a `<provider_type>/<model>` spec resolved to a
/// provider connection through the `default-providers` config (see
/// `resolve_model_override`); `None` streams on the active provider.
/// An unresolvable spec reports [`crate::Event::StreamError`]. A prompt
/// queued behind a busy agent (steered) keeps its override.
SendMessage {
content: String,
model: Option<String>,
},
/// Run a bash-mode (`!`-prefixed) command through the resolved shell.
/// Display-only: the output never persists or reaches the model.
RunBash {
command: String,
},
CancelStream,
ListSessions,
LoadSession {
id: uuid::Uuid,
},
/// Persist the chat pane's last scroll position for a session, sent by
/// the TUI when it is about to leave the session (switch or quit).
SaveScroll {
id: uuid::Uuid,
scroll: StoredScroll,
},
DeleteSession {
id: uuid::Uuid,
},
/// Rename the active session. The core trims the title; an empty (or
/// unchanged) title is a no-op.
SetTitle {
title: String,
},
/// Draft the active session's title with the LLM configured under
/// `ui.title` `llm` (defaults: the active provider's catalog small model
/// and the built-in title prompt), from the active path's first user
/// prompt. Reports [`crate::Event::SessionTitleChanged`] on success and
/// [`crate::Event::SessionError`] when there is no session, no user
/// prompt, or no resolvable model. A manual rename that lands while the
/// call runs wins over the generated title.
GenTitle,
SearchHistory {
query: String,
},
AnswerQuestion {
id: u64,
/// `None` when the user dismissed the question.
answers: Option<Vec<Vec<String>>>,
},
/// The user's answer to a pending `ask` permission prompt (`id` from
/// [`crate::Event::PermissionRequested`]). `Allow` grants one call,
/// `AllowSession` also remembers the ask's scope for the rest of the
/// run, and `Deny` cuts the turn. A missing id is a no-op.
PermissionDecide {
id: u64,
decision: crate::permissions::PermissionAnswer,
},
/// Fork the session: with `node: None`, walk to the active path's last
/// user prompt (`/undo`); otherwise the fork targets `node`. Turn nodes
/// fork *before* themselves — their parent becomes the tip and their
/// content is recalled into the input — while summary/system nodes walk
/// to themselves. With `after`, the targeted turn node is walked to
/// instead: it becomes the tip and nothing is recalled (the tree's tool
/// rows walk to their reply and reply rows walk to themselves this
/// way). When `summarize`, an LLM summary of the prefix before the fork
/// point is created first and the forked-away node is reparented under
/// it.
ForkSession {
node: Option<u64>,
summarize: bool,
/// Fork *after* the node's turn instead of before it: the node
/// itself becomes the tip and nothing is recalled into the input.
after: bool,
},
/// Delete a branch of the session tree: the node and all of its
/// descendants (must not contain the active leaf).
DeleteBranch {
node: u64,
},
/// Load the active session's tree for the `/tree` popup; reports
/// [`crate::Event::SessionTree`].
OpenTree,
/// Write the active session to a JSON file (`/export [path]`); `None`
/// picks `<session_id>-<timestamp>.json` in the working directory.
/// Reports [`crate::Event::SessionExported`] or
/// [`crate::Event::SessionError`].
ExportSession {
path: Option<std::path::PathBuf>,
},
/// Switch the active session's scene (`None` = the built-in default
/// scene). Refused with [`crate::Event::SceneError`] while a stream is
/// busy or the name does not resolve.
SwitchScene {
name: Option<String>,
},
Replay,
Reload,
/// Recall the most recently steered prompt into the input area. `stacked`
/// (Alt+Shift+Up) prepends the content to the existing text (separated by
/// two line feeds) instead of overwriting it.
RecallSteered {
stacked: bool,
},
LspStart {
name: String,
},
LspStop {
name: String,
},
LspRestart {
name: String,
},
LspList {
all: bool,
filter: Option<String>,
},
/// Snapshot the configured MCP servers' states and report
/// [`crate::Event::McpStatus`] (`/mcp`).
McpList,
/// Force a fresh connection to an MCP server, dropping any existing one.
/// Reports [`crate::Event::McpStatus`] on success; on failure
/// [`crate::Event::McpError`] plus a status snapshot in which the server
/// carries its failure detail.
McpReconnect {
name: String,
},
/// Fetch the hosted (Selune) provider registry on demand. Reports
/// [`Event::RegistryLoaded`] or [`Event::RegistryError`].
FetchRegistry,
}