aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-ai/src/fsm/effects.rs
diff options
context:
space:
mode:
authorMichelle Tilley <michelle@michelletilley.net>2026-04-21 13:07:27 -0700
committerGitHub <noreply@github.com>2026-04-21 13:07:27 -0700
commit2f702ad446fcd6a261a3bea0ab2807d70eca43e2 (patch)
tree4cfa6276257cefbe73f7fa46a74026170aaf8435 /crates/atuin-ai/src/fsm/effects.rs
parentdocs: document show_numeric_shortcuts (#3433) (diff)
downloadatuin-2f702ad446fcd6a261a3bea0ab2807d70eca43e2.zip
refactor: Replace ad-hoc dispatch with FSM + driver architecture (#3434)
Replaces the tangled dispatch handler system (`tui/dispatch.rs`, `tui/state.rs`) with a pure finite state machine + driver architecture. The FSM handles all state transitions as explicit `(State, Event) → (NewState, Effects)` mappings. The driver executes IO effects and bridges the TUI to the FSM.
Diffstat (limited to 'crates/atuin-ai/src/fsm/effects.rs')
-rw-r--r--crates/atuin-ai/src/fsm/effects.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/crates/atuin-ai/src/fsm/effects.rs b/crates/atuin-ai/src/fsm/effects.rs
new file mode 100644
index 00000000..ede72a42
--- /dev/null
+++ b/crates/atuin-ai/src/fsm/effects.rs
@@ -0,0 +1,81 @@
+//! Effects (outputs) from the agent FSM.
+//!
+//! The FSM returns these as data; the driver is responsible for executing them.
+
+use std::path::PathBuf;
+use std::time::Duration;
+
+use serde_json::Value;
+
+use crate::permissions::rule::Rule;
+use crate::permissions::writer::RuleDisposition;
+use crate::tools::ClientToolCall;
+
+/// Where to write a permission rule.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum PermissionTarget {
+ /// Project-level: `<git_root_or_cwd>/.atuin/permissions.ai.toml`
+ Project,
+ /// Global: `~/.config/atuin/permissions.ai.toml`
+ Global,
+}
+
+/// Side effects the driver should execute after a state transition.
+#[derive(Debug, Clone)]
+pub(crate) enum Effect {
+ // ─── Network ────────────────────────────────────────────────
+ /// Start a new streaming request to the server.
+ StartStream {
+ messages: Vec<Value>,
+ session_id: Option<String>,
+ },
+ /// Abort the active stream connection.
+ AbortStream,
+
+ // ─── Tool orchestration ─────────────────────────────────────
+ /// Run the permission resolver for a tool call.
+ CheckPermission {
+ tool_id: String,
+ tool: ClientToolCall,
+ },
+ /// Execute a tool (file read, edit, write, shell, history search).
+ ExecuteTool {
+ tool_id: String,
+ tool: ClientToolCall,
+ },
+ /// Kill a running tool (send interrupt to shell command).
+ AbortTool { tool_id: String },
+
+ // ─── Persistence ────────────────────────────────────────────
+ /// Persist current conversation state to disk.
+ Persist,
+ /// Write a permanent permission rule to disk.
+ WritePermissionRule {
+ target: PermissionTarget,
+ rule: Rule,
+ disposition: RuleDisposition,
+ },
+ /// Cache a session-scoped file permission grant.
+ CacheSessionGrant { path: PathBuf },
+ /// Archive current session and start fresh (IO only — state already updated by FSM).
+ ArchiveSession,
+
+ // ─── Timers ─────────────────────────────────────────────────
+ /// Schedule a timer that will fire ConfirmationTimeout after delay.
+ ScheduleTimeout { timeout_id: u64, duration: Duration },
+
+ // ─── Exit ───────────────────────────────────────────────────
+ /// Exit the application with the given action.
+ ExitApp(ExitAction),
+}
+
+/// What to do when exiting the TUI.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum ExitAction {
+ /// Run the suggested command.
+ Execute(String),
+ /// Insert the command into the shell without running.
+ Insert(String),
+ /// Exit without action.
+ Cancel,
+}