ahp_types/actions.rs
1// Generated from types/*.ts — do not edit.
2//
3// Regenerate with: npm run generate:rust
4
5#![allow(missing_docs)]
6
7#[allow(unused_imports)]
8use crate::common::{AnyValue, JsonObject, StringOrMarkdown, Uri};
9#[allow(unused_imports)]
10use serde::{Deserialize, Serialize};
11#[allow(unused_imports)]
12use serde_repr::{Deserialize_repr, Serialize_repr};
13
14use crate::state::{
15 AgentInfo, AgentSelection, Annotation, AnnotationEntry, Changeset, ChangesetFile,
16 ChangesetOperation, ChangesetOperationStatus, ChangesetStatus, ChatInputAnswer,
17 ChatInputRequest, ChatInputResponseKind, ChatInteractivity, ChatOrigin, ChatSummary,
18 ConfirmationOption, Customization, ErrorInfo, McpServerState, Message, ModelSelection,
19 PendingMessageKind, ResponsePart, SessionActiveClient, TerminalClaim, TerminalInfo, TextRange,
20 ToolCallCancellationReason, ToolCallConfirmationReason, ToolCallContributor, ToolCallResult,
21 ToolDefinition, ToolResultContent, UsageInfo,
22};
23
24// ─── ActionType ──────────────────────────────────────────────────────
25
26/// Discriminant values for all state actions.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub enum ActionType {
29 #[serde(rename = "root/agentsChanged")]
30 RootAgentsChanged,
31 #[serde(rename = "root/activeSessionsChanged")]
32 RootActiveSessionsChanged,
33 #[serde(rename = "session/ready")]
34 SessionReady,
35 #[serde(rename = "session/creationFailed")]
36 SessionCreationFailed,
37 #[serde(rename = "session/chatAdded")]
38 SessionChatAdded,
39 #[serde(rename = "session/chatRemoved")]
40 SessionChatRemoved,
41 #[serde(rename = "session/chatUpdated")]
42 SessionChatUpdated,
43 #[serde(rename = "session/defaultChatChanged")]
44 SessionDefaultChatChanged,
45 #[serde(rename = "chat/turnStarted")]
46 ChatTurnStarted,
47 #[serde(rename = "chat/delta")]
48 ChatDelta,
49 #[serde(rename = "chat/responsePart")]
50 ChatResponsePart,
51 #[serde(rename = "chat/toolCallStart")]
52 ChatToolCallStart,
53 #[serde(rename = "chat/toolCallDelta")]
54 ChatToolCallDelta,
55 #[serde(rename = "chat/toolCallReady")]
56 ChatToolCallReady,
57 #[serde(rename = "chat/toolCallConfirmed")]
58 ChatToolCallConfirmed,
59 #[serde(rename = "chat/toolCallComplete")]
60 ChatToolCallComplete,
61 #[serde(rename = "chat/toolCallResultConfirmed")]
62 ChatToolCallResultConfirmed,
63 #[serde(rename = "chat/toolCallContentChanged")]
64 ChatToolCallContentChanged,
65 #[serde(rename = "chat/turnComplete")]
66 ChatTurnComplete,
67 #[serde(rename = "chat/turnCancelled")]
68 ChatTurnCancelled,
69 #[serde(rename = "chat/error")]
70 ChatError,
71 #[serde(rename = "session/titleChanged")]
72 SessionTitleChanged,
73 #[serde(rename = "chat/usage")]
74 ChatUsage,
75 #[serde(rename = "chat/reasoning")]
76 ChatReasoning,
77 #[serde(rename = "session/modelChanged")]
78 SessionModelChanged,
79 #[serde(rename = "session/agentChanged")]
80 SessionAgentChanged,
81 #[serde(rename = "session/serverToolsChanged")]
82 SessionServerToolsChanged,
83 #[serde(rename = "session/activeClientChanged")]
84 SessionActiveClientChanged,
85 #[serde(rename = "session/activeClientToolsChanged")]
86 SessionActiveClientToolsChanged,
87 #[serde(rename = "chat/pendingMessageSet")]
88 ChatPendingMessageSet,
89 #[serde(rename = "chat/pendingMessageRemoved")]
90 ChatPendingMessageRemoved,
91 #[serde(rename = "chat/queuedMessagesReordered")]
92 ChatQueuedMessagesReordered,
93 #[serde(rename = "chat/inputRequested")]
94 ChatInputRequested,
95 #[serde(rename = "chat/inputAnswerChanged")]
96 ChatInputAnswerChanged,
97 #[serde(rename = "chat/inputCompleted")]
98 ChatInputCompleted,
99 #[serde(rename = "session/customizationsChanged")]
100 SessionCustomizationsChanged,
101 #[serde(rename = "session/customizationToggled")]
102 SessionCustomizationToggled,
103 #[serde(rename = "session/customizationUpdated")]
104 SessionCustomizationUpdated,
105 #[serde(rename = "session/customizationRemoved")]
106 SessionCustomizationRemoved,
107 #[serde(rename = "session/mcpServerStateChanged")]
108 SessionMcpServerStateChanged,
109 #[serde(rename = "chat/truncated")]
110 ChatTruncated,
111 #[serde(rename = "session/isReadChanged")]
112 SessionIsReadChanged,
113 #[serde(rename = "session/isArchivedChanged")]
114 SessionIsArchivedChanged,
115 #[serde(rename = "session/activityChanged")]
116 SessionActivityChanged,
117 #[serde(rename = "session/changesetsChanged")]
118 SessionChangesetsChanged,
119 #[serde(rename = "session/configChanged")]
120 SessionConfigChanged,
121 #[serde(rename = "session/metaChanged")]
122 SessionMetaChanged,
123 #[serde(rename = "changeset/statusChanged")]
124 ChangesetStatusChanged,
125 #[serde(rename = "changeset/fileSet")]
126 ChangesetFileSet,
127 #[serde(rename = "changeset/fileRemoved")]
128 ChangesetFileRemoved,
129 #[serde(rename = "changeset/contentChanged")]
130 ChangesetContentChanged,
131 #[serde(rename = "changeset/operationsChanged")]
132 ChangesetOperationsChanged,
133 #[serde(rename = "changeset/operationStatusChanged")]
134 ChangesetOperationStatusChanged,
135 #[serde(rename = "changeset/cleared")]
136 ChangesetCleared,
137 #[serde(rename = "annotations/set")]
138 AnnotationsSet,
139 #[serde(rename = "annotations/updated")]
140 AnnotationsUpdated,
141 #[serde(rename = "annotations/removed")]
142 AnnotationsRemoved,
143 #[serde(rename = "annotations/entrySet")]
144 AnnotationsEntrySet,
145 #[serde(rename = "annotations/entryRemoved")]
146 AnnotationsEntryRemoved,
147 #[serde(rename = "root/terminalsChanged")]
148 RootTerminalsChanged,
149 #[serde(rename = "root/configChanged")]
150 RootConfigChanged,
151 #[serde(rename = "terminal/data")]
152 TerminalData,
153 #[serde(rename = "terminal/input")]
154 TerminalInput,
155 #[serde(rename = "terminal/resized")]
156 TerminalResized,
157 #[serde(rename = "terminal/claimed")]
158 TerminalClaimed,
159 #[serde(rename = "terminal/titleChanged")]
160 TerminalTitleChanged,
161 #[serde(rename = "terminal/cwdChanged")]
162 TerminalCwdChanged,
163 #[serde(rename = "terminal/exited")]
164 TerminalExited,
165 #[serde(rename = "terminal/cleared")]
166 TerminalCleared,
167 #[serde(rename = "terminal/commandDetectionAvailable")]
168 TerminalCommandDetectionAvailable,
169 #[serde(rename = "terminal/commandExecuted")]
170 TerminalCommandExecuted,
171 #[serde(rename = "terminal/commandFinished")]
172 TerminalCommandFinished,
173 #[serde(rename = "resourceWatch/changed")]
174 ResourceWatchChanged,
175}
176
177// ─── Action Envelope ─────────────────────────────────────────────────
178
179/// Identifies the client that originally dispatched an action.
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct ActionOrigin {
183 pub client_id: String,
184 pub client_seq: i64,
185}
186
187/// Every action is wrapped in an `ActionEnvelope`.
188///
189/// The envelope identifies the channel the action belongs to (e.g.
190/// `ahp-root://` for root actions, the session URI for session actions, the
191/// terminal URI for terminal actions). Individual action payloads carry only
192/// fields that are intrinsic to the action; the channel comes from the
193/// envelope so that any subscribable resource can route its actions uniformly.
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct ActionEnvelope {
197 /// Channel URI this action belongs to.
198 pub channel: Uri,
199 pub action: StateAction,
200 pub server_seq: u64,
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub origin: Option<ActionOrigin>,
203 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub rejection_reason: Option<String>,
205}
206
207// ─── Action Payloads ─────────────────────────────────────────────────
208
209/// Fired when available agent backends or their models change.
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct RootAgentsChangedAction {
213 /// Updated agent list
214 pub agents: Vec<AgentInfo>,
215}
216
217/// Fired when the number of active sessions changes.
218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
219#[serde(rename_all = "camelCase")]
220pub struct RootActiveSessionsChangedAction {
221 /// Current count of active sessions
222 pub active_sessions: i64,
223}
224
225/// Fired when agent-host configuration values change.
226///
227/// By default, the reducer merges the new values into `state.config.values`.
228/// Set `replace` to `true` to replace all values instead of merging.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct RootConfigChangedAction {
232 /// Updated config values
233 pub config: JsonObject,
234 /// When `true`, replaces all config values instead of merging
235 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub replace: Option<bool>,
237}
238
239/// Session backend initialized successfully.
240#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
241#[serde(rename_all = "camelCase")]
242pub struct SessionReadyAction {}
243
244/// Session backend failed to initialize.
245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
246#[serde(rename_all = "camelCase")]
247pub struct SessionCreationFailedAction {
248 /// Error details
249 pub error: ErrorInfo,
250}
251
252/// A chat was added to this session's catalog. Upsert semantics: if a chat
253/// with the same `summary.resource` already exists, the existing entry is
254/// replaced.
255///
256/// Mirrors the root-channel `root/sessionAdded` notification.
257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
258#[serde(rename_all = "camelCase")]
259pub struct SessionChatAddedAction {
260 /// The full summary of the newly added (or upserted) chat.
261 pub summary: ChatSummary,
262}
263
264/// A chat was removed from this session's catalog. No-op when no entry matches.
265///
266/// Mirrors the root-channel `root/sessionRemoved` notification.
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
268#[serde(rename_all = "camelCase")]
269pub struct SessionChatRemovedAction {
270 /// The URI of the chat to remove.
271 pub chat: Uri,
272}
273
274/// One existing chat's summary fields changed.
275///
276/// Partial-update semantics: only fields present in `changes` are written;
277/// omitted fields are preserved. Identity fields (`resource`) MUST NOT be
278/// carried in `changes`. No-op when no entry with `chat` exists — clients
279/// SHOULD then wait for a {@link SessionChatAddedAction | `session/chatAdded`}.
280///
281/// Mirrors the root-channel `root/sessionSummaryChanged` notification.
282#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
283#[serde(rename_all = "camelCase")]
284pub struct SessionChatUpdatedAction {
285 /// The URI of the chat whose summary changed.
286 pub chat: Uri,
287 /// Mutable summary fields that changed; omitted fields are unchanged.
288 ///
289 /// Identity fields (`resource`) never change and MUST be omitted by
290 /// senders; receivers SHOULD ignore them if present.
291 pub changes: PartialChatSummary,
292}
293
294/// The default chat input-routing hint for this session changed.
295#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
296#[serde(rename_all = "camelCase")]
297pub struct SessionDefaultChatChangedAction {
298 /// New default chat URI, or `undefined` to clear the hint.
299 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub default_chat: Option<Uri>,
301}
302
303/// A new message has been sent to the agent, and a new turn starts.
304///
305/// A client is only allowed to send {@link MessageKind.User} messages.
306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
307#[serde(rename_all = "camelCase")]
308pub struct ChatTurnStartedAction {
309 /// Turn identifier
310 pub turn_id: String,
311 /// The new message
312 pub message: Message,
313 /// If this turn was auto-started from a queued message, the ID of that message
314 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub queued_message_id: Option<String>,
316 /// Additional provider-specific metadata for this action.
317 ///
318 /// Clients MAY look for well-known keys here to provide enhanced UI, and
319 /// agent hosts MAY use it to carry per-event context that does not fit any
320 /// other field — for example, attributing the event to a specific agent
321 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
322 /// convention.
323 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
324 pub meta: Option<JsonObject>,
325}
326
327/// Streaming text chunk from the assistant, appended to a specific response part.
328///
329/// The server MUST first emit a `chat/responsePart` to create the target
330/// part (markdown or reasoning), then use this action to append text to it.
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332#[serde(rename_all = "camelCase")]
333pub struct ChatDeltaAction {
334 /// Turn identifier
335 pub turn_id: String,
336 /// Identifier of the response part to append to
337 pub part_id: String,
338 /// Text chunk
339 pub content: String,
340 /// Additional provider-specific metadata for this action.
341 ///
342 /// Clients MAY look for well-known keys here to provide enhanced UI, and
343 /// agent hosts MAY use it to carry per-event context that does not fit any
344 /// other field — for example, attributing the event to a specific agent
345 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
346 /// convention.
347 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
348 pub meta: Option<JsonObject>,
349}
350
351/// Structured content appended to the response.
352#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
353#[serde(rename_all = "camelCase")]
354pub struct ChatResponsePartAction {
355 /// Turn identifier
356 pub turn_id: String,
357 /// Response part (markdown or content ref)
358 pub part: ResponsePart,
359 /// Additional provider-specific metadata for this action.
360 ///
361 /// Clients MAY look for well-known keys here to provide enhanced UI, and
362 /// agent hosts MAY use it to carry per-event context that does not fit any
363 /// other field — for example, attributing the event to a specific agent
364 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
365 /// convention.
366 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
367 pub meta: Option<JsonObject>,
368}
369
370/// A tool call begins — parameters are streaming from the LM.
371///
372/// The server sets {@link ToolCallContributor | `contributor`} to identify
373/// the origin of the tool. For client-provided tools, the named client is
374/// responsible for executing the tool once it reaches the `running` state
375/// and dispatching `chat/toolCallComplete`. For MCP-served tools, the
376/// server executes the call against the named `McpServerCustomization`.
377#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
378#[serde(rename_all = "camelCase")]
379pub struct ChatToolCallStartAction {
380 /// Turn identifier
381 pub turn_id: String,
382 /// Tool call identifier
383 pub tool_call_id: String,
384 /// Additional provider-specific metadata for this tool call.
385 ///
386 /// Clients MAY look for well-known keys here to provide enhanced UI.
387 /// For example, a `ptyTerminal` key with `{ input: string; output: string }`
388 /// indicates the tool operated on a terminal (both `input` and `output` may
389 /// contain escape sequences).
390 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
391 pub meta: Option<JsonObject>,
392 /// Internal tool name (for debugging/logging)
393 pub tool_name: String,
394 /// Human-readable tool name
395 pub display_name: String,
396 /// Reference to the contributor of the tool being called. Absent for
397 /// server-side tools that are not contributed by a client or MCP server.
398 #[serde(default, skip_serializing_if = "Option::is_none")]
399 pub contributor: Option<ToolCallContributor>,
400}
401
402/// Streaming partial parameters for a tool call.
403#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
404#[serde(rename_all = "camelCase")]
405pub struct ChatToolCallDeltaAction {
406 /// Turn identifier
407 pub turn_id: String,
408 /// Tool call identifier
409 pub tool_call_id: String,
410 /// Additional provider-specific metadata for this tool call.
411 ///
412 /// Clients MAY look for well-known keys here to provide enhanced UI.
413 /// For example, a `ptyTerminal` key with `{ input: string; output: string }`
414 /// indicates the tool operated on a terminal (both `input` and `output` may
415 /// contain escape sequences).
416 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
417 pub meta: Option<JsonObject>,
418 /// Partial parameter content to append
419 pub content: String,
420 /// Updated progress message
421 #[serde(default, skip_serializing_if = "Option::is_none")]
422 pub invocation_message: Option<StringOrMarkdown>,
423}
424
425/// Tool call parameters are complete, or a running tool requires re-confirmation.
426///
427/// When dispatched for a `streaming` tool call, transitions to `pending-confirmation`
428/// or directly to `running` if `confirmed` is set.
429///
430/// When dispatched for a `running` tool call (e.g. mid-execution permission needed),
431/// transitions back to `pending-confirmation`. The `invocationMessage` and `_meta`
432/// SHOULD be updated to describe the specific confirmation needed. Clients use the
433/// standard `chat/toolCallConfirmed` flow to approve or deny.
434///
435/// For client-provided tools, the server typically sets `confirmed` to
436/// `'not-needed'` so the tool transitions directly to `running`, where the
437/// owning client can begin execution immediately.
438#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
439#[serde(rename_all = "camelCase")]
440pub struct ChatToolCallReadyAction {
441 /// Turn identifier
442 pub turn_id: String,
443 /// Tool call identifier
444 pub tool_call_id: String,
445 /// Additional provider-specific metadata for this tool call.
446 ///
447 /// Clients MAY look for well-known keys here to provide enhanced UI.
448 /// For example, a `ptyTerminal` key with `{ input: string; output: string }`
449 /// indicates the tool operated on a terminal (both `input` and `output` may
450 /// contain escape sequences).
451 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
452 pub meta: Option<JsonObject>,
453 /// Message describing what the tool will do or what confirmation is needed
454 pub invocation_message: StringOrMarkdown,
455 /// Raw tool input
456 #[serde(default, skip_serializing_if = "Option::is_none")]
457 pub tool_input: Option<String>,
458 /// Short title for the confirmation prompt (e.g. `"Run in terminal"`, `"Write file"`)
459 #[serde(default, skip_serializing_if = "Option::is_none")]
460 pub confirmation_title: Option<StringOrMarkdown>,
461 /// File edits that this tool call will perform, for preview before confirmation
462 #[serde(default, skip_serializing_if = "Option::is_none")]
463 pub edits: Option<AnyValue>,
464 /// Whether the agent host allows the client to edit the tool's input parameters before confirming
465 #[serde(default, skip_serializing_if = "Option::is_none")]
466 pub editable: Option<bool>,
467 /// If set, the tool was auto-confirmed and transitions directly to `running`
468 #[serde(default, skip_serializing_if = "Option::is_none")]
469 pub confirmed: Option<ToolCallConfirmationReason>,
470 /// Options the server offers for this confirmation. When present, the client
471 /// SHOULD render these instead of a plain approve/deny UI. Each option
472 /// belongs to a {@link ConfirmationOptionGroup} so the client can still
473 /// categorise the choices.
474 #[serde(default, skip_serializing_if = "Option::is_none")]
475 pub options: Option<Vec<ConfirmationOption>>,
476}
477
478/// Client approves or denies a pending tool call (merged approved + denied variants).
479#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
480#[serde(rename_all = "camelCase")]
481pub struct ChatToolCallConfirmedAction {
482 pub turn_id: String,
483 pub tool_call_id: String,
484 /// Additional provider-specific metadata for this tool call.
485 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
486 pub meta: Option<JsonObject>,
487 /// Whether the tool call was approved.
488 pub approved: bool,
489 /// How the tool was confirmed (present when approved).
490 #[serde(default, skip_serializing_if = "Option::is_none")]
491 pub confirmed: Option<ToolCallConfirmationReason>,
492 /// Why the tool was cancelled (present when denied).
493 #[serde(default, skip_serializing_if = "Option::is_none")]
494 pub reason: Option<ToolCallCancellationReason>,
495 /// Edited tool input parameters, if the client modified them before confirming.
496 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub edited_tool_input: Option<String>,
498 /// What the user suggested doing instead (present when denied).
499 #[serde(default, skip_serializing_if = "Option::is_none")]
500 pub user_suggestion: Option<Message>,
501 /// Explanation for the denial.
502 #[serde(default, skip_serializing_if = "Option::is_none")]
503 pub reason_message: Option<StringOrMarkdown>,
504 /// ID of the selected confirmation option, if the server provided options.
505 #[serde(default, skip_serializing_if = "Option::is_none")]
506 pub selected_option_id: Option<String>,
507}
508
509/// Tool execution finished. Transitions to `completed` or `pending-result-confirmation`
510/// if `requiresResultConfirmation` is `true`.
511///
512/// For client-provided tools (where `toolClientId` is set on the tool call state),
513/// the owning client dispatches this action with the execution result. The server
514/// SHOULD reject this action if the dispatching client does not match `toolClientId`.
515///
516/// Servers waiting on a client tool call MAY time out after a reasonable duration
517/// if the implementing client disconnects or becomes unresponsive, and dispatch
518/// this action with `result.success = false` and an appropriate error.
519#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
520#[serde(rename_all = "camelCase")]
521pub struct ChatToolCallCompleteAction {
522 /// Turn identifier
523 pub turn_id: String,
524 /// Tool call identifier
525 pub tool_call_id: String,
526 /// Additional provider-specific metadata for this tool call.
527 ///
528 /// Clients MAY look for well-known keys here to provide enhanced UI.
529 /// For example, a `ptyTerminal` key with `{ input: string; output: string }`
530 /// indicates the tool operated on a terminal (both `input` and `output` may
531 /// contain escape sequences).
532 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
533 pub meta: Option<JsonObject>,
534 /// Execution result
535 pub result: ToolCallResult,
536 /// If true, the result requires client approval before finalizing
537 #[serde(default, skip_serializing_if = "Option::is_none")]
538 pub requires_result_confirmation: Option<bool>,
539}
540
541/// Client approves or denies a tool's result.
542///
543/// If `approved` is `false`, the tool transitions to `cancelled` with reason `result-denied`.
544#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
545#[serde(rename_all = "camelCase")]
546pub struct ChatToolCallResultConfirmedAction {
547 /// Turn identifier
548 pub turn_id: String,
549 /// Tool call identifier
550 pub tool_call_id: String,
551 /// Additional provider-specific metadata for this tool call.
552 ///
553 /// Clients MAY look for well-known keys here to provide enhanced UI.
554 /// For example, a `ptyTerminal` key with `{ input: string; output: string }`
555 /// indicates the tool operated on a terminal (both `input` and `output` may
556 /// contain escape sequences).
557 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
558 pub meta: Option<JsonObject>,
559 /// Whether the result was approved
560 pub approved: bool,
561}
562
563/// Partial content produced while a tool is still executing.
564///
565/// Replaces the `content` array on the running tool call state. Clients can
566/// use this to display live feedback (e.g. a terminal reference) before the
567/// tool completes.
568///
569/// For client-provided tools (where `toolClientId` is set on the tool call state),
570/// the owning client dispatches this action to stream intermediate content while
571/// executing. The server SHOULD reject this action if the dispatching client does
572/// not match `toolClientId`.
573#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
574#[serde(rename_all = "camelCase")]
575pub struct ChatToolCallContentChangedAction {
576 /// Turn identifier
577 pub turn_id: String,
578 /// Tool call identifier
579 pub tool_call_id: String,
580 /// Additional provider-specific metadata for this tool call.
581 ///
582 /// Clients MAY look for well-known keys here to provide enhanced UI.
583 /// For example, a `ptyTerminal` key with `{ input: string; output: string }`
584 /// indicates the tool operated on a terminal (both `input` and `output` may
585 /// contain escape sequences).
586 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
587 pub meta: Option<JsonObject>,
588 /// The current partial content for the running tool call
589 pub content: Vec<ToolResultContent>,
590}
591
592/// Turn finished — the assistant is idle.
593#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
594#[serde(rename_all = "camelCase")]
595pub struct ChatTurnCompleteAction {
596 /// Turn identifier
597 pub turn_id: String,
598 /// Additional provider-specific metadata for this action.
599 ///
600 /// Clients MAY look for well-known keys here to provide enhanced UI, and
601 /// agent hosts MAY use it to carry per-event context that does not fit any
602 /// other field — for example, attributing the event to a specific agent
603 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
604 /// convention.
605 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
606 pub meta: Option<JsonObject>,
607}
608
609/// Turn was aborted; server stops processing.
610#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
611#[serde(rename_all = "camelCase")]
612pub struct ChatTurnCancelledAction {
613 /// Turn identifier
614 pub turn_id: String,
615 /// Additional provider-specific metadata for this action.
616 ///
617 /// Clients MAY look for well-known keys here to provide enhanced UI, and
618 /// agent hosts MAY use it to carry per-event context that does not fit any
619 /// other field — for example, attributing the event to a specific agent
620 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
621 /// convention.
622 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
623 pub meta: Option<JsonObject>,
624}
625
626/// Error during turn processing.
627#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
628#[serde(rename_all = "camelCase")]
629pub struct ChatErrorAction {
630 /// Turn identifier
631 pub turn_id: String,
632 /// Error details
633 pub error: ErrorInfo,
634 /// Additional provider-specific metadata for this action.
635 ///
636 /// Clients MAY look for well-known keys here to provide enhanced UI, and
637 /// agent hosts MAY use it to carry per-event context that does not fit any
638 /// other field — for example, attributing the event to a specific agent
639 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
640 /// convention.
641 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
642 pub meta: Option<JsonObject>,
643}
644
645/// Session title updated. Fired by the server when the title is auto-generated
646/// from conversation, or dispatched by a client to rename a session.
647#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
648#[serde(rename_all = "camelCase")]
649pub struct SessionTitleChangedAction {
650 /// New title
651 pub title: String,
652}
653
654/// Token usage report for a turn.
655#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
656#[serde(rename_all = "camelCase")]
657pub struct ChatUsageAction {
658 /// Turn identifier
659 pub turn_id: String,
660 /// Token usage data
661 pub usage: UsageInfo,
662 /// Additional provider-specific metadata for this action.
663 ///
664 /// Clients MAY look for well-known keys here to provide enhanced UI, and
665 /// agent hosts MAY use it to carry per-event context that does not fit any
666 /// other field — for example, attributing the event to a specific agent
667 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
668 /// convention.
669 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
670 pub meta: Option<JsonObject>,
671}
672
673/// Reasoning/thinking text from the model, appended to a specific reasoning response part.
674///
675/// The server MUST first emit a `chat/responsePart` to create the target
676/// reasoning part, then use this action to append text to it.
677#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
678#[serde(rename_all = "camelCase")]
679pub struct ChatReasoningAction {
680 /// Turn identifier
681 pub turn_id: String,
682 /// Identifier of the reasoning response part to append to
683 pub part_id: String,
684 /// Reasoning text chunk
685 pub content: String,
686 /// Additional provider-specific metadata for this action.
687 ///
688 /// Clients MAY look for well-known keys here to provide enhanced UI, and
689 /// agent hosts MAY use it to carry per-event context that does not fit any
690 /// other field — for example, attributing the event to a specific agent
691 /// (such as a sub-agent acting within the turn). Mirrors the MCP `_meta`
692 /// convention.
693 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
694 pub meta: Option<JsonObject>,
695}
696
697/// Model changed for this session.
698#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
699#[serde(rename_all = "camelCase")]
700pub struct SessionModelChangedAction {
701 /// New model selection
702 pub model: ModelSelection,
703}
704
705/// Custom agent selection changed for this session.
706///
707/// Omitting `agent` (or setting it to `undefined`) clears the selection and
708/// resets the session to no selected custom agent (provider default behavior).
709///
710/// When a turn is currently active, the server MUST defer the change until
711/// the active turn completes, then apply it for the next turn (same rule as
712/// {@link SessionModelChangedAction | `session/modelChanged`}).
713#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
714#[serde(rename_all = "camelCase")]
715pub struct SessionAgentChangedAction {
716 /// New agent selection, or `undefined` to clear the selection and reset the
717 /// session to no selected custom agent.
718 #[serde(default, skip_serializing_if = "Option::is_none")]
719 pub agent: Option<AgentSelection>,
720}
721
722/// The read state of the session changed.
723///
724/// Dispatched by a client to mark a session as read (e.g. after viewing it)
725/// or unread (e.g. after new activity since the client last looked at it).
726#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
727#[serde(rename_all = "camelCase")]
728pub struct SessionIsReadChangedAction {
729 /// Whether the session has been read
730 pub is_read: bool,
731}
732
733/// The archived state of the session changed.
734///
735/// Dispatched by a client to archive a session (e.g. the task is
736/// complete) or to unarchive it.
737#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
738#[serde(rename_all = "camelCase")]
739pub struct SessionIsArchivedChangedAction {
740 /// Whether the session is archived
741 pub is_archived: bool,
742}
743
744/// The activity description of the session changed.
745///
746/// Dispatched by the server to indicate what the session is currently doing
747/// (e.g. running a tool, thinking). Clear activity by setting it to `undefined`.
748#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
749#[serde(rename_all = "camelCase")]
750pub struct SessionActivityChangedAction {
751 /// Human-readable description of current activity, or `undefined` to clear
752 #[serde(default, skip_serializing_if = "Option::is_none")]
753 pub activity: Option<String>,
754}
755
756/// The {@link Changeset | catalogue of changesets} the agent host
757/// advertises for this session changed. Replaces
758/// {@link SessionState.changesets | `state.changesets`} entirely
759/// (full-replacement semantics) — set to `undefined` to clear the
760/// catalogue.
761///
762/// Producers dispatch this whenever entries are added or removed. The
763/// fan-out happens through this action so observers see catalogue
764/// mutations in the same {@link ChangesetAction | per-changeset} action
765/// stream they already follow for file-level updates.
766#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
767#[serde(rename_all = "camelCase")]
768pub struct SessionChangesetsChangedAction {
769 /// New catalogue, or `undefined` to clear it
770 #[serde(default, skip_serializing_if = "Option::is_none")]
771 pub changesets: Option<Vec<Changeset>>,
772}
773
774/// Server tools for this session have changed.
775///
776/// Full-replacement semantics: the `tools` array replaces the previous `serverTools` entirely.
777#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
778#[serde(rename_all = "camelCase")]
779pub struct SessionServerToolsChangedAction {
780 /// Updated server tools list (full replacement)
781 pub tools: Vec<ToolDefinition>,
782}
783
784/// The active client for this session has changed.
785///
786/// A client dispatches this action with its own `SessionActiveClient` to claim
787/// the active role, or with `null` to release it. The server SHOULD reject if
788/// another client is already active. The server SHOULD automatically dispatch
789/// this action with `activeClient: null` when the active client disconnects.
790#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
791#[serde(rename_all = "camelCase")]
792pub struct SessionActiveClientChangedAction {
793 /// The new active client, or `null` to unset
794 #[serde(default, skip_serializing_if = "Option::is_none")]
795 pub active_client: Option<SessionActiveClient>,
796}
797
798/// The active client's tool list has changed.
799///
800/// Full-replacement semantics: the `tools` array replaces the active client's
801/// previous tools entirely. The server SHOULD reject if the dispatching client
802/// is not the current active client.
803#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
804#[serde(rename_all = "camelCase")]
805pub struct SessionActiveClientToolsChangedAction {
806 /// Updated client tools list (full replacement)
807 pub tools: Vec<ToolDefinition>,
808}
809
810/// A pending message was set (upsert semantics: creates or replaces).
811///
812/// For steering messages, this always replaces the single steering message.
813/// For queued messages, if a message with the given `id` already exists it is
814/// updated in place; otherwise it is appended to the queue. If the chat is
815/// idle when a queued message is set, the server SHOULD immediately consume it
816/// and start a new turn.
817///
818/// A client is only allowed to send {@link MessageKind.User} messages.
819#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
820#[serde(rename_all = "camelCase")]
821pub struct ChatPendingMessageSetAction {
822 /// Whether this is a steering or queued message
823 pub kind: PendingMessageKind,
824 /// Unique identifier for this pending message
825 pub id: String,
826 /// The message content
827 pub message: Message,
828}
829
830/// A pending message was removed (steering or queued).
831///
832/// Dispatched by clients to cancel a pending message, or by the server when
833/// it consumes a message (e.g. starting a turn from a queued message or
834/// injecting a steering message into the current turn).
835#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
836#[serde(rename_all = "camelCase")]
837pub struct ChatPendingMessageRemovedAction {
838 /// Whether this is a steering or queued message
839 pub kind: PendingMessageKind,
840 /// Identifier of the pending message to remove
841 pub id: String,
842}
843
844/// Reorder the queued messages.
845///
846/// The `order` array contains the IDs of queued messages in their new
847/// desired order. IDs not present in the current queue are ignored.
848/// Queued messages whose IDs are absent from `order` are appended at
849/// the end in their original relative order (so a client with a stale
850/// view of the queue never silently drops messages).
851#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
852#[serde(rename_all = "camelCase")]
853pub struct ChatQueuedMessagesReorderedAction {
854 /// Queued message IDs in the desired order
855 pub order: Vec<String>,
856}
857
858/// A session requested input from the user.
859///
860/// Full-request upsert semantics: the `request` replaces any existing request
861/// with the same `id`, or is appended if it is new. Answer drafts are preserved
862/// unless `request.answers` is provided.
863#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
864#[serde(rename_all = "camelCase")]
865pub struct ChatInputRequestedAction {
866 /// Input request to create or replace
867 pub request: ChatInputRequest,
868}
869
870/// A client updated, submitted, skipped, or removed a single in-progress answer.
871///
872/// Dispatching with `answer: undefined` removes that question's answer draft.
873#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
874#[serde(rename_all = "camelCase")]
875pub struct ChatInputAnswerChangedAction {
876 /// Input request identifier
877 pub request_id: String,
878 /// Question identifier within the input request
879 pub question_id: String,
880 /// Updated answer, or `undefined` to clear an answer draft
881 #[serde(default, skip_serializing_if = "Option::is_none")]
882 pub answer: Option<ChatInputAnswer>,
883}
884
885/// A client accepted, declined, or cancelled a session input request.
886///
887/// If accepted, the server uses `answers` (when provided) plus the request's
888/// synced answer state to resume the blocked operation.
889#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
890#[serde(rename_all = "camelCase")]
891pub struct ChatInputCompletedAction {
892 /// Input request identifier
893 pub request_id: String,
894 /// Completion outcome
895 pub response: ChatInputResponseKind,
896 /// Optional final answer replacement, keyed by question ID
897 #[serde(default, skip_serializing_if = "Option::is_none")]
898 pub answers: Option<std::collections::HashMap<String, ChatInputAnswer>>,
899}
900
901/// The session's customizations have changed.
902///
903/// Full-replacement semantics: the `customizations` array replaces the
904/// previous `customizations` entirely.
905#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
906#[serde(rename_all = "camelCase")]
907pub struct SessionCustomizationsChangedAction {
908 /// Updated customization list (full replacement).
909 pub customizations: Vec<Customization>,
910}
911
912/// A client toggled a container customization on or off.
913///
914/// Targets a top-level container (plugin or directory) by `id`. Only
915/// containers have an `enabled` flag; children are always active when
916/// their container is enabled. Is a no-op when no matching container is
917/// found.
918#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
919#[serde(rename_all = "camelCase")]
920pub struct SessionCustomizationToggledAction {
921 /// The id of the container to toggle.
922 pub id: String,
923 /// Whether to enable or disable the container.
924 pub enabled: bool,
925}
926
927/// Upserts a top-level customization (plugin or directory).
928///
929/// The reducer locates the existing entry by `customization.id`:
930///
931/// - If found, the entry is replaced entirely with `customization`,
932/// including its `children` array. To preserve existing children, the
933/// host must include them on the payload.
934/// - If not found, the entry is appended.
935#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
936#[serde(rename_all = "camelCase")]
937pub struct SessionCustomizationUpdatedAction {
938 /// The customization to upsert (matched by `customization.id`).
939 pub customization: Customization,
940}
941
942/// Removes a customization by id.
943///
944/// Searches every container and its children for the entry. If the entry
945/// is a container, its children are removed with it. Is a no-op when no
946/// matching id is found.
947#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
948#[serde(rename_all = "camelCase")]
949pub struct SessionCustomizationRemovedAction {
950 /// The id of the customization to remove.
951 pub id: String,
952}
953
954/// Updates the runtime fields of an existing
955/// {@link McpServerCustomization} — narrow alternative to
956/// {@link SessionCustomizationUpdatedAction} for the high-frequency
957/// `starting` ↔ `ready` ↔ `authRequired` transitions.
958///
959/// Locates the target entry by `id`, searching both the top-level
960/// customization list and the `children` array of every container.
961/// Replaces the entry's {@link McpServerCustomization.state | `state`}
962/// and {@link McpServerCustomization.channel | `channel`}
963/// (full-replacement semantics: omit `channel` to clear an existing
964/// channel URI). Other fields of the customization are preserved.
965///
966/// Is a no-op when no matching `McpServerCustomization` is found. To
967/// update any other field (name, icons, `mcpApp` capabilities, etc.) use
968/// {@link SessionCustomizationUpdatedAction} instead.
969///
970/// When the transition is to {@link McpServerStatus.AuthRequired}
971/// because of a request issued mid-turn, the host SHOULD also raise
972/// {@link SessionStatus.InputNeeded} on the session — see
973/// {@link McpServerAuthRequiredState} for the rationale.
974#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
975#[serde(rename_all = "camelCase")]
976pub struct SessionMcpServerStateChangedAction {
977 /// The id of the {@link McpServerCustomization} to update.
978 pub id: String,
979 /// The new lifecycle state.
980 pub state: McpServerState,
981 /// Updated `mcp://` side-channel URI. Full-replacement: omit to clear
982 /// an existing channel (typical when leaving
983 /// {@link McpServerStatus.Ready | `Ready`}).
984 #[serde(default, skip_serializing_if = "Option::is_none")]
985 pub channel: Option<Uri>,
986}
987
988/// Truncates a session's history. If `turnId` is provided, all turns after that
989/// turn are removed and the specified turn is kept. If `turnId` is omitted, all
990/// turns are removed.
991///
992/// If there is an active turn it is silently dropped and the chat status
993/// returns to `idle`.
994///
995/// Common use-case: truncate old data then dispatch a new
996/// `chat/turnStarted` with an edited message.
997#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
998#[serde(rename_all = "camelCase")]
999pub struct ChatTruncatedAction {
1000 /// Keep turns up to and including this turn. Omit to clear all turns.
1001 #[serde(default, skip_serializing_if = "Option::is_none")]
1002 pub turn_id: Option<String>,
1003}
1004
1005/// Client changed a mutable config value mid-session.
1006///
1007/// Only properties with `sessionMutable: true` in the config schema may be
1008/// changed. The server validates and broadcasts the action; the reducer merges
1009/// the new values into `state.config.values`.
1010#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1011#[serde(rename_all = "camelCase")]
1012pub struct SessionConfigChangedAction {
1013 /// Updated config values
1014 pub config: JsonObject,
1015 /// When `true`, replaces all config values instead of merging
1016 #[serde(default, skip_serializing_if = "Option::is_none")]
1017 pub replace: Option<bool>,
1018}
1019
1020/// The session's `_meta` side-channel changed. Replaces `state._meta`
1021/// entirely (full-replacement semantics). Producers SHOULD merge any
1022/// keys they wish to preserve into the new value before dispatching.
1023#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1024#[serde(rename_all = "camelCase")]
1025pub struct SessionMetaChangedAction {
1026 /// New `_meta` payload, or `undefined` to clear it
1027 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1028 pub meta: Option<JsonObject>,
1029}
1030
1031/// The {@link ChangesetState.status} for this changeset transitioned (e.g.
1032/// `computing → ready`). The error payload is set together with `status`
1033/// whenever it transitions to {@link ChangesetStatus.Error | Error}.
1034#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1035#[serde(rename_all = "camelCase")]
1036pub struct ChangesetStatusChangedAction {
1037 /// New computation lifecycle status.
1038 pub status: ChangesetStatus,
1039 /// Cause when `status === ChangesetStatus.Error`; otherwise omitted.
1040 #[serde(default, skip_serializing_if = "Option::is_none")]
1041 pub error: Option<ErrorInfo>,
1042}
1043
1044/// Upsert a {@link ChangesetFile} in the changeset — adds a new entry, or
1045/// replaces an existing one identified by {@link ChangesetFile.id}.
1046#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1047#[serde(rename_all = "camelCase")]
1048pub struct ChangesetFileSetAction {
1049 /// The new or replacement file entry.
1050 pub file: ChangesetFile,
1051}
1052
1053/// Remove a {@link ChangesetFile} from the changeset by its id.
1054///
1055/// Typically dispatched when a file is reverted, staged out, or otherwise
1056/// no longer in scope (e.g. a renamed file is replaced by a new entry).
1057#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1058#[serde(rename_all = "camelCase")]
1059pub struct ChangesetFileRemovedAction {
1060 /// The {@link ChangesetFile.id} of the file to remove.
1061 pub file_id: String,
1062}
1063
1064/// The changeset's full content changed. Full replacement semantics: `files`
1065/// replaces the previous file list, and `operations`, when present, replaces
1066/// the previous operation list.
1067///
1068/// Producers SHOULD use this action for initial snapshots and bulk refreshes;
1069/// use {@link ChangesetFileSetAction}, {@link ChangesetFileRemovedAction}, and
1070/// {@link ChangesetOperationsChangedAction} for incremental updates.
1071#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1072#[serde(rename_all = "camelCase")]
1073pub struct ChangesetContentChangedAction {
1074 /// Full replacement file list.
1075 pub files: Vec<ChangesetFile>,
1076 /// Full replacement operation list. Omit when operations are unchanged.
1077 #[serde(default, skip_serializing_if = "Option::is_none")]
1078 pub operations: Option<Vec<ChangesetOperation>>,
1079 /// Error information, if the changeset content change failed.
1080 #[serde(default, skip_serializing_if = "Option::is_none")]
1081 pub error: Option<ErrorInfo>,
1082}
1083
1084/// The set of operations available on this changeset changed. Full
1085/// replacement semantics: `operations` replaces the previous list (or
1086/// removes it entirely when `operations` is `undefined`).
1087#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1088#[serde(rename_all = "camelCase")]
1089pub struct ChangesetOperationsChangedAction {
1090 /// Updated operation list. Pass `undefined` to clear all operations.
1091 #[serde(default, skip_serializing_if = "Option::is_none")]
1092 pub operations: Option<Vec<ChangesetOperation>>,
1093}
1094
1095/// The {@link ChangesetOperation.status} for a single operation transitioned
1096/// (e.g. `idle → running → idle`, or `running → error`). The error payload
1097/// is set together with `status` whenever it transitions to
1098/// {@link ChangesetOperationStatus.Error | Error}, and cleared on any other
1099/// transition.
1100///
1101/// Targets one operation by its {@link ChangesetOperation.id}. If no
1102/// operation with that id is currently present in the changeset, the action
1103/// is a no-op. Use {@link ChangesetOperationsChangedAction} to add, remove,
1104/// or otherwise replace the operation list itself.
1105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1106#[serde(rename_all = "camelCase")]
1107pub struct ChangesetOperationStatusChangedAction {
1108 /// The {@link ChangesetOperation.id} whose status changed.
1109 pub operation_id: String,
1110 /// New execution status.
1111 pub status: ChangesetOperationStatus,
1112 /// Cause when `status === ChangesetOperationStatus.Error`; otherwise omitted.
1113 #[serde(default, skip_serializing_if = "Option::is_none")]
1114 pub error: Option<ErrorInfo>,
1115}
1116
1117/// Drop every file from the changeset.
1118///
1119/// Two cases use this:
1120/// 1. The underlying source moved (branch switched, fork point invalidated,
1121/// …) and the server is recomputing from scratch — subsequent
1122/// {@link ChangesetFileSetAction} entries will repopulate it.
1123/// 2. The owning session has ended and the URI is becoming
1124/// un-subscribable — the server will unsubscribe all clients shortly
1125/// after dispatching this action.
1126///
1127/// Clients SHOULD release any references on receipt and SHOULD NOT
1128/// distinguish the two cases from the action alone — instead, react to
1129/// the corresponding session-level lifecycle signal (e.g.
1130/// `root/sessionRemoved`) for the "going away" case.
1131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1132#[serde(rename_all = "camelCase")]
1133pub struct ChangesetClearedAction {}
1134
1135/// Upsert an {@link Annotation} in the annotations channel — adds a new
1136/// annotation, or replaces an existing one identified by
1137/// {@link Annotation.id}.
1138///
1139/// Dispatched by a client to create an annotation (together with its
1140/// mandatory first entry) or to re-anchor / resolve an existing one; the
1141/// dispatching client assigns the {@link Annotation.id} and the id of any
1142/// new entry. When replacing, the full annotation payload (including its
1143/// {@link Annotation.entries | entries} list) is substituted; producers
1144/// SHOULD prefer {@link AnnotationsEntrySetAction} for per-entry edits, and
1145/// {@link AnnotationsUpdatedAction} to resolve / re-anchor an existing
1146/// annotation, to keep wire updates small.
1147#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1148#[serde(rename_all = "camelCase")]
1149pub struct AnnotationsSetAction {
1150 /// The new or replacement annotation. MUST contain at least one entry.
1151 pub annotation: Annotation,
1152}
1153
1154/// Partially update an existing {@link Annotation}'s own properties — a narrow
1155/// alternative to {@link AnnotationsSetAction} for the common case of resolving
1156/// / re-opening or re-anchoring an annotation without resending its
1157/// {@link Annotation.entries | entries}.
1158///
1159/// Targets one annotation by its {@link annotationId}. Only the fields present
1160/// on the action are written; omitted fields leave the corresponding
1161/// {@link Annotation} property unchanged. The annotation's
1162/// {@link Annotation.entries | entries}, {@link Annotation.id | id}, and
1163/// {@link Annotation._meta | _meta} are never touched — dispatch
1164/// {@link AnnotationsSetAction} to replace those, to clear {@link range}
1165/// (re-anchor to the whole file), or {@link AnnotationsEntrySetAction} /
1166/// {@link AnnotationsEntryRemovedAction} to edit individual entries.
1167///
1168/// If {@link annotationId} does not match any current annotation the action is
1169/// a no-op.
1170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1171#[serde(rename_all = "camelCase")]
1172pub struct AnnotationsUpdatedAction {
1173 /// The {@link Annotation.id} of the annotation to update.
1174 pub annotation_id: String,
1175 /// Re-anchors the annotation to the file versions this turn produced.
1176 /// Matches a {@link Turn.id} on the owning session. Omit to leave the
1177 /// current {@link Annotation.turnId} unchanged.
1178 #[serde(default, skip_serializing_if = "Option::is_none")]
1179 pub turn_id: Option<String>,
1180 /// Re-anchors the annotation to this file. Omit to leave the current
1181 /// {@link Annotation.resource} unchanged.
1182 #[serde(default, skip_serializing_if = "Option::is_none")]
1183 pub resource: Option<Uri>,
1184 /// Narrows the annotation to this range within {@link resource}. Omit to
1185 /// leave the current {@link Annotation.range} unchanged; this action cannot
1186 /// clear an existing range — dispatch {@link AnnotationsSetAction} to
1187 /// re-anchor to the whole file.
1188 #[serde(default, skip_serializing_if = "Option::is_none")]
1189 pub range: Option<TextRange>,
1190 /// Marks the annotation resolved (`true`) or re-opens it (`false`). Omit to
1191 /// leave the current {@link Annotation.resolved} state unchanged.
1192 #[serde(default, skip_serializing_if = "Option::is_none")]
1193 pub resolved: Option<bool>,
1194}
1195
1196/// Remove an {@link Annotation} from the channel by its id.
1197///
1198/// Dispatched to delete an entire annotation and every entry it contains.
1199/// Because the protocol forbids empty annotations, a client that wants to
1200/// remove the last remaining entry dispatches this action — collapsing the
1201/// annotation — rather than {@link AnnotationsEntryRemovedAction}.
1202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1203#[serde(rename_all = "camelCase")]
1204pub struct AnnotationsRemovedAction {
1205 /// The {@link Annotation.id} of the annotation to remove.
1206 pub annotation_id: String,
1207}
1208
1209/// Upsert an {@link AnnotationEntry} within an existing annotation — adds a
1210/// new entry, or replaces one identified by {@link AnnotationEntry.id}. The
1211/// dispatching client assigns the {@link AnnotationEntry.id} of a new entry.
1212/// If {@link annotationId} does not match any current annotation the action
1213/// is a no-op.
1214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1215#[serde(rename_all = "camelCase")]
1216pub struct AnnotationsEntrySetAction {
1217 /// The {@link Annotation.id} the entry belongs to.
1218 pub annotation_id: String,
1219 /// The new or replacement entry.
1220 pub entry: AnnotationEntry,
1221}
1222
1223/// Remove a single {@link AnnotationEntry} from an annotation without
1224/// collapsing the annotation itself. Used when more than one entry remains —
1225/// to remove the last entry a client dispatches {@link AnnotationsRemovedAction}
1226/// instead, since the protocol forbids empty annotations.
1227///
1228/// If either {@link annotationId} or {@link entryId} does not match the
1229/// current state the action is a no-op.
1230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1231#[serde(rename_all = "camelCase")]
1232pub struct AnnotationsEntryRemovedAction {
1233 /// The {@link Annotation.id} the entry belongs to.
1234 pub annotation_id: String,
1235 /// The {@link AnnotationEntry.id} to remove.
1236 pub entry_id: String,
1237}
1238
1239/// Fired when the list of known terminals changes.
1240///
1241/// Full-replacement semantics: the `terminals` array replaces the previous
1242/// `terminals` entirely.
1243#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1244#[serde(rename_all = "camelCase")]
1245pub struct RootTerminalsChangedAction {
1246 /// Updated terminal list (full replacement)
1247 pub terminals: Vec<TerminalInfo>,
1248}
1249
1250/// Terminal output data (pty → client direction).
1251///
1252/// Appends `data` to the terminal's `content` in the reducer.
1253///
1254/// `terminal/data` and `terminal/input` are intentionally separate actions
1255/// because standard write-ahead reconciliation is not safe for terminal I/O.
1256/// A pty is a stateful, mutable process — optimistically applying input or
1257/// predicting output would produce incorrect state. Instead, `terminal/input`
1258/// is a side-effect-only action (client → server → pty), and `terminal/data`
1259/// is server-authoritative output (pty → server → client).
1260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1261#[serde(rename_all = "camelCase")]
1262pub struct TerminalDataAction {
1263 /// Output data (may contain ANSI escape sequences)
1264 pub data: String,
1265}
1266
1267/// Keyboard input sent to the terminal process (client → pty direction).
1268///
1269/// This is a side-effect-only action: the server forwards the data to the
1270/// terminal's pty. The reducer treats this as a no-op since `terminal/data`
1271/// actions will reflect any resulting output.
1272///
1273/// See `terminal/data` for why these two actions are kept separate.
1274#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1275#[serde(rename_all = "camelCase")]
1276pub struct TerminalInputAction {
1277 /// Input data to send to the pty
1278 pub data: String,
1279}
1280
1281/// Terminal dimensions changed.
1282///
1283/// Dispatchable by clients to request a resize, or by the server to inform
1284/// clients of the actual terminal dimensions.
1285#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1286#[serde(rename_all = "camelCase")]
1287pub struct TerminalResizedAction {
1288 /// Terminal width in columns
1289 pub cols: i64,
1290 /// Terminal height in rows
1291 pub rows: i64,
1292}
1293
1294/// Terminal claim changed. A client or session transfers ownership of the terminal.
1295///
1296/// The server SHOULD reject if the dispatching client does not currently hold
1297/// the claim.
1298#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1299#[serde(rename_all = "camelCase")]
1300pub struct TerminalClaimedAction {
1301 /// The new claim
1302 pub claim: TerminalClaim,
1303}
1304
1305/// Terminal title changed.
1306///
1307/// Fired by the server when the terminal process updates its title (e.g. via
1308/// escape sequences), or dispatched by a client to rename a terminal.
1309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1310#[serde(rename_all = "camelCase")]
1311pub struct TerminalTitleChangedAction {
1312 /// New terminal title
1313 pub title: String,
1314}
1315
1316/// Terminal working directory changed.
1317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1318#[serde(rename_all = "camelCase")]
1319pub struct TerminalCwdChangedAction {
1320 /// New working directory
1321 pub cwd: Uri,
1322}
1323
1324/// Terminal process exited.
1325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1326#[serde(rename_all = "camelCase")]
1327pub struct TerminalExitedAction {
1328 /// Process exit code. `undefined` if the process was killed without an exit code.
1329 #[serde(default, skip_serializing_if = "Option::is_none")]
1330 pub exit_code: Option<i64>,
1331}
1332
1333/// Terminal scrollback buffer cleared.
1334#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1335#[serde(rename_all = "camelCase")]
1336pub struct TerminalClearedAction {}
1337
1338/// Shell integration has loaded and the terminal now supports command
1339/// detection. The server dispatches this when shell integration becomes
1340/// available (which may happen asynchronously after the terminal is created).
1341///
1342/// Clients MUST NOT assume command detection is available until this action
1343/// (or `terminal/commandExecuted`) has been received.
1344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1345#[serde(rename_all = "camelCase")]
1346pub struct TerminalCommandDetectionAvailableAction {}
1347
1348/// A command has been submitted to the shell and is now executing.
1349/// All subsequent `terminal/data` actions (until the matching
1350/// `terminal/commandFinished`) constitute this command's output.
1351#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1352#[serde(rename_all = "camelCase")]
1353pub struct TerminalCommandExecutedAction {
1354 /// Stable identifier for this command, scoped to the terminal URI.
1355 /// Allows correlating `commandExecuted` → `commandFinished` pairs.
1356 pub command_id: String,
1357 /// The command line text that was submitted
1358 pub command_line: String,
1359 /// Unix timestamp (ms) of when the command started executing, as measured
1360 /// on the server.
1361 pub timestamp: i64,
1362}
1363
1364/// A command has finished executing.
1365///
1366/// The sequence of `terminal/data` actions between the preceding
1367/// `terminal/commandExecuted` (same `commandId`) and this action constitutes
1368/// the complete output of the command.
1369#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1370#[serde(rename_all = "camelCase")]
1371pub struct TerminalCommandFinishedAction {
1372 /// Matches the `commandId` from the corresponding `commandExecuted`
1373 pub command_id: String,
1374 /// Shell exit code. `undefined` if the shell did not report one.
1375 #[serde(default, skip_serializing_if = "Option::is_none")]
1376 pub exit_code: Option<i64>,
1377 /// Wall-clock duration of the command in milliseconds, as measured by the
1378 /// shell integration script on the server side.
1379 #[serde(default, skip_serializing_if = "Option::is_none")]
1380 pub duration_ms: Option<i64>,
1381}
1382
1383/// A batch of resource changes observed by the watcher.
1384///
1385/// Watch events are coalesced into batches by the server to keep the
1386/// action stream tractable; an empty `changes.items` list MUST NOT be
1387/// dispatched. The reducer does not retain change history — these
1388/// actions exist purely to deliver events to subscribers, who consume
1389/// them directly off the action stream and apply their own logic.
1390#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1391#[serde(rename_all = "camelCase")]
1392pub struct ResourceWatchChangedAction {
1393 /// The set of changes in this batch, wrapped for forward compatibility.
1394 pub changes: AnyValue,
1395}
1396
1397// ─── Partial Summaries ────────────────────────────────────────────────
1398
1399/// Partial equivalent of ChatSummary — every field is optional for delta updates.
1400#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1401#[serde(rename_all = "camelCase")]
1402pub struct PartialChatSummary {
1403 /// Chat URI
1404 #[serde(default, skip_serializing_if = "Option::is_none")]
1405 pub resource: Option<Uri>,
1406 /// Chat title
1407 #[serde(default, skip_serializing_if = "Option::is_none")]
1408 pub title: Option<String>,
1409 /// Current chat status (reuses SessionStatus shape)
1410 #[serde(default, skip_serializing_if = "Option::is_none")]
1411 pub status: Option<u32>,
1412 /// Human-readable description of what the chat is currently doing
1413 #[serde(default, skip_serializing_if = "Option::is_none")]
1414 pub activity: Option<String>,
1415 /// Last modification timestamp (ISO 8601, e.g. `"2025-03-10T18:42:03.123Z"`)
1416 #[serde(default, skip_serializing_if = "Option::is_none")]
1417 pub modified_at: Option<String>,
1418 /// Optional per-chat model override (defaults to the session's model)
1419 #[serde(default, skip_serializing_if = "Option::is_none")]
1420 pub model: Option<ModelSelection>,
1421 /// Optional per-chat agent override (defaults to the session's agent)
1422 #[serde(default, skip_serializing_if = "Option::is_none")]
1423 pub agent: Option<AgentSelection>,
1424 /// How this chat came into existence
1425 #[serde(default, skip_serializing_if = "Option::is_none")]
1426 pub origin: Option<ChatOrigin>,
1427 /// How the user can interact with this chat. See {@link ChatInteractivity}.
1428 ///
1429 /// Supports agent-team patterns where worker chats are read-only or hidden.
1430 /// Absence defaults to {@link ChatInteractivity.Full} for backward
1431 /// compatibility.
1432 #[serde(default, skip_serializing_if = "Option::is_none")]
1433 pub interactivity: Option<ChatInteractivity>,
1434 /// Optional per-chat working directory.
1435 ///
1436 /// If absent, the chat inherits
1437 /// {@link SessionSummary.workingDirectory | the session's working directory}.
1438 /// See {@link ChatState.workingDirectory} for usage notes.
1439 #[serde(default, skip_serializing_if = "Option::is_none")]
1440 pub working_directory: Option<Uri>,
1441}
1442
1443// ─── StateAction Union ───────────────────────────────────────────────
1444
1445/// Discriminated union of every state action.
1446#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1447#[serde(tag = "type")]
1448pub enum StateAction {
1449 #[serde(rename = "root/agentsChanged")]
1450 RootAgentsChanged(RootAgentsChangedAction),
1451 #[serde(rename = "root/activeSessionsChanged")]
1452 RootActiveSessionsChanged(RootActiveSessionsChangedAction),
1453 #[serde(rename = "root/configChanged")]
1454 RootConfigChanged(RootConfigChangedAction),
1455 #[serde(rename = "session/ready")]
1456 SessionReady(SessionReadyAction),
1457 #[serde(rename = "session/creationFailed")]
1458 SessionCreationFailed(SessionCreationFailedAction),
1459 #[serde(rename = "session/chatAdded")]
1460 SessionChatAdded(SessionChatAddedAction),
1461 #[serde(rename = "session/chatRemoved")]
1462 SessionChatRemoved(SessionChatRemovedAction),
1463 #[serde(rename = "session/chatUpdated")]
1464 SessionChatUpdated(SessionChatUpdatedAction),
1465 #[serde(rename = "session/defaultChatChanged")]
1466 SessionDefaultChatChanged(SessionDefaultChatChangedAction),
1467 #[serde(rename = "chat/turnStarted")]
1468 ChatTurnStarted(ChatTurnStartedAction),
1469 #[serde(rename = "chat/delta")]
1470 ChatDelta(ChatDeltaAction),
1471 #[serde(rename = "chat/responsePart")]
1472 ChatResponsePart(ChatResponsePartAction),
1473 #[serde(rename = "chat/toolCallStart")]
1474 ChatToolCallStart(ChatToolCallStartAction),
1475 #[serde(rename = "chat/toolCallDelta")]
1476 ChatToolCallDelta(ChatToolCallDeltaAction),
1477 #[serde(rename = "chat/toolCallReady")]
1478 ChatToolCallReady(ChatToolCallReadyAction),
1479 #[serde(rename = "chat/toolCallConfirmed")]
1480 ChatToolCallConfirmed(ChatToolCallConfirmedAction),
1481 #[serde(rename = "chat/toolCallComplete")]
1482 ChatToolCallComplete(ChatToolCallCompleteAction),
1483 #[serde(rename = "chat/toolCallResultConfirmed")]
1484 ChatToolCallResultConfirmed(ChatToolCallResultConfirmedAction),
1485 #[serde(rename = "chat/toolCallContentChanged")]
1486 ChatToolCallContentChanged(ChatToolCallContentChangedAction),
1487 #[serde(rename = "chat/turnComplete")]
1488 ChatTurnComplete(ChatTurnCompleteAction),
1489 #[serde(rename = "chat/turnCancelled")]
1490 ChatTurnCancelled(ChatTurnCancelledAction),
1491 #[serde(rename = "chat/error")]
1492 ChatError(ChatErrorAction),
1493 #[serde(rename = "session/titleChanged")]
1494 SessionTitleChanged(SessionTitleChangedAction),
1495 #[serde(rename = "chat/usage")]
1496 ChatUsage(ChatUsageAction),
1497 #[serde(rename = "chat/reasoning")]
1498 ChatReasoning(ChatReasoningAction),
1499 #[serde(rename = "session/modelChanged")]
1500 SessionModelChanged(SessionModelChangedAction),
1501 #[serde(rename = "session/agentChanged")]
1502 SessionAgentChanged(SessionAgentChangedAction),
1503 #[serde(rename = "session/isReadChanged")]
1504 SessionIsReadChanged(SessionIsReadChangedAction),
1505 #[serde(rename = "session/isArchivedChanged")]
1506 SessionIsArchivedChanged(SessionIsArchivedChangedAction),
1507 #[serde(rename = "session/activityChanged")]
1508 SessionActivityChanged(SessionActivityChangedAction),
1509 #[serde(rename = "session/changesetsChanged")]
1510 SessionChangesetsChanged(SessionChangesetsChangedAction),
1511 #[serde(rename = "session/serverToolsChanged")]
1512 SessionServerToolsChanged(SessionServerToolsChangedAction),
1513 #[serde(rename = "session/activeClientChanged")]
1514 SessionActiveClientChanged(SessionActiveClientChangedAction),
1515 #[serde(rename = "session/activeClientToolsChanged")]
1516 SessionActiveClientToolsChanged(SessionActiveClientToolsChangedAction),
1517 #[serde(rename = "chat/pendingMessageSet")]
1518 ChatPendingMessageSet(ChatPendingMessageSetAction),
1519 #[serde(rename = "chat/pendingMessageRemoved")]
1520 ChatPendingMessageRemoved(ChatPendingMessageRemovedAction),
1521 #[serde(rename = "chat/queuedMessagesReordered")]
1522 ChatQueuedMessagesReordered(ChatQueuedMessagesReorderedAction),
1523 #[serde(rename = "chat/inputRequested")]
1524 ChatInputRequested(ChatInputRequestedAction),
1525 #[serde(rename = "chat/inputAnswerChanged")]
1526 ChatInputAnswerChanged(ChatInputAnswerChangedAction),
1527 #[serde(rename = "chat/inputCompleted")]
1528 ChatInputCompleted(ChatInputCompletedAction),
1529 #[serde(rename = "session/customizationsChanged")]
1530 SessionCustomizationsChanged(SessionCustomizationsChangedAction),
1531 #[serde(rename = "session/customizationToggled")]
1532 SessionCustomizationToggled(SessionCustomizationToggledAction),
1533 #[serde(rename = "session/customizationUpdated")]
1534 SessionCustomizationUpdated(Box<SessionCustomizationUpdatedAction>),
1535 #[serde(rename = "session/customizationRemoved")]
1536 SessionCustomizationRemoved(SessionCustomizationRemovedAction),
1537 #[serde(rename = "session/mcpServerStateChanged")]
1538 SessionMcpServerStateChanged(Box<SessionMcpServerStateChangedAction>),
1539 #[serde(rename = "chat/truncated")]
1540 ChatTruncated(ChatTruncatedAction),
1541 #[serde(rename = "session/configChanged")]
1542 SessionConfigChanged(SessionConfigChangedAction),
1543 #[serde(rename = "session/metaChanged")]
1544 SessionMetaChanged(SessionMetaChangedAction),
1545 #[serde(rename = "changeset/statusChanged")]
1546 ChangesetStatusChanged(ChangesetStatusChangedAction),
1547 #[serde(rename = "changeset/fileSet")]
1548 ChangesetFileSet(ChangesetFileSetAction),
1549 #[serde(rename = "changeset/fileRemoved")]
1550 ChangesetFileRemoved(ChangesetFileRemovedAction),
1551 #[serde(rename = "changeset/contentChanged")]
1552 ChangesetContentChanged(Box<ChangesetContentChangedAction>),
1553 #[serde(rename = "changeset/operationsChanged")]
1554 ChangesetOperationsChanged(ChangesetOperationsChangedAction),
1555 #[serde(rename = "changeset/operationStatusChanged")]
1556 ChangesetOperationStatusChanged(ChangesetOperationStatusChangedAction),
1557 #[serde(rename = "changeset/cleared")]
1558 ChangesetCleared(ChangesetClearedAction),
1559 #[serde(rename = "annotations/set")]
1560 AnnotationsSet(AnnotationsSetAction),
1561 #[serde(rename = "annotations/updated")]
1562 AnnotationsUpdated(AnnotationsUpdatedAction),
1563 #[serde(rename = "annotations/removed")]
1564 AnnotationsRemoved(AnnotationsRemovedAction),
1565 #[serde(rename = "annotations/entrySet")]
1566 AnnotationsEntrySet(AnnotationsEntrySetAction),
1567 #[serde(rename = "annotations/entryRemoved")]
1568 AnnotationsEntryRemoved(AnnotationsEntryRemovedAction),
1569 #[serde(rename = "root/terminalsChanged")]
1570 RootTerminalsChanged(RootTerminalsChangedAction),
1571 #[serde(rename = "terminal/data")]
1572 TerminalData(TerminalDataAction),
1573 #[serde(rename = "terminal/input")]
1574 TerminalInput(TerminalInputAction),
1575 #[serde(rename = "terminal/resized")]
1576 TerminalResized(TerminalResizedAction),
1577 #[serde(rename = "terminal/claimed")]
1578 TerminalClaimed(TerminalClaimedAction),
1579 #[serde(rename = "terminal/titleChanged")]
1580 TerminalTitleChanged(TerminalTitleChangedAction),
1581 #[serde(rename = "terminal/cwdChanged")]
1582 TerminalCwdChanged(TerminalCwdChangedAction),
1583 #[serde(rename = "terminal/exited")]
1584 TerminalExited(TerminalExitedAction),
1585 #[serde(rename = "terminal/cleared")]
1586 TerminalCleared(TerminalClearedAction),
1587 #[serde(rename = "terminal/commandDetectionAvailable")]
1588 TerminalCommandDetectionAvailable(TerminalCommandDetectionAvailableAction),
1589 #[serde(rename = "terminal/commandExecuted")]
1590 TerminalCommandExecuted(TerminalCommandExecutedAction),
1591 #[serde(rename = "terminal/commandFinished")]
1592 TerminalCommandFinished(TerminalCommandFinishedAction),
1593 #[serde(rename = "resourceWatch/changed")]
1594 ResourceWatchChanged(ResourceWatchChangedAction),
1595 /// Unknown or future variant — preserved as raw JSON for round-trip fidelity.
1596 /// Reducers treat this as a no-op.
1597 #[serde(untagged)]
1598 Unknown(serde_json::Value),
1599}