Enhance tool documentation and add new features
- Added module-level documentation for memory tools (`remember`, `recall`, `forget`) to clarify their purpose. - Improved documentation in `recall.rs` and `remember.rs` to describe the functionality and flow of memory entry operations. - Updated `mod.rs` to include descriptions for the tool trait and execution context. - Enhanced `plan.rs` with detailed comments on plan-mode signaling tools. - Documented text search tools in `search.rs` to explain their functionality. - Improved sequential-thinking tool documentation in `seqthink.rs`. - Added safety filter documentation in `shell_filter` for credential and git operations. - Enhanced utility tools documentation, including `cd`, `dir_cache_update`, and `todowrite`. - Improved rendering documentation in view modules (`chat`, `markdown`, `status`, `workflow`) to clarify rendering flows and purposes.
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
//! Shallow state diffing — records opaque "modified" markers so the TUI
|
||||
//! knows to re-render without computing fine-grained deltas.
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A collection of changes tracking which parts of app state have been
|
||||
/// modified since the last render sweep.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StateDiff {
|
||||
changes: Vec<Change>,
|
||||
}
|
||||
|
||||
/// A single named change — currently always carries a flat `"."` path
|
||||
/// and `"modified"` kind because the system does not track granular diffs.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Change {
|
||||
pub path: String,
|
||||
@@ -12,23 +18,36 @@ pub struct Change {
|
||||
}
|
||||
|
||||
impl StateDiff {
|
||||
/// Create an empty diff.
|
||||
pub fn new() -> Self {
|
||||
StateDiff { changes: Vec::new() }
|
||||
}
|
||||
|
||||
/// Record a change at `path` of the given `kind`.
|
||||
pub fn add_change(&mut self, path: String, kind: String) {
|
||||
self.changes.push(Change { path, kind });
|
||||
}
|
||||
|
||||
/// Return true if no changes have been recorded.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.changes.is_empty()
|
||||
}
|
||||
|
||||
/// Remove all recorded changes.
|
||||
pub fn clear(&mut self) {
|
||||
self.changes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute a shallow diff between two serialised state values.
|
||||
///
|
||||
/// Flow: compare with `==`, return an empty vec if equal, otherwise
|
||||
/// return a single `Change { ".", "modified" }`.
|
||||
///
|
||||
/// Why: a placeholder — the current rendering model re-validates the
|
||||
/// whole viewport every frame, so fine-grained diffs are unnecessary.
|
||||
///
|
||||
/// Return: the list of changes (always 0 or 1 entry).
|
||||
pub fn compute_diff(before: &serde_json::Value, after: &serde_json::Value) -> Vec<Change> {
|
||||
if before == after {
|
||||
return Vec::new();
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
//! Application-level "miscellaneous" state: scroll, input buffer,
|
||||
//! overlay stack, toasts, editor, and autocomplete.
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use super::types::Overlay;
|
||||
|
||||
/// A shared, async-writable cache of directory entries, used to avoid
|
||||
/// re-reading a directory every render frame.
|
||||
#[derive(Clone)]
|
||||
pub struct DirCache {
|
||||
entries: Arc<RwLock<Vec<PathBuf>>>,
|
||||
}
|
||||
|
||||
impl DirCache {
|
||||
/// Create an empty `DirCache`.
|
||||
pub fn new() -> Self {
|
||||
DirCache {
|
||||
entries: Arc::new(RwLock::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace the cached entries (async write).
|
||||
pub async fn set(&self, paths: Vec<PathBuf>) {
|
||||
let mut w = self.entries.write().await;
|
||||
*w = paths;
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages the viewport scroll offset.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScrollState {
|
||||
pub offset: usize,
|
||||
@@ -28,6 +35,7 @@ pub struct ScrollState {
|
||||
}
|
||||
|
||||
impl ScrollState {
|
||||
/// Create a `ScrollState` with zero offset and 30 rows visible.
|
||||
pub fn new() -> Self {
|
||||
ScrollState {
|
||||
offset: 0,
|
||||
@@ -35,19 +43,25 @@ impl ScrollState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Scroll the viewport up by `amount` lines (increasing the offset).
|
||||
/// Scroll the viewport up by `amount` lines (increasing the offset).
|
||||
pub fn scroll_up(&mut self, amount: usize) {
|
||||
self.offset = self.offset.saturating_add(amount);
|
||||
}
|
||||
|
||||
/// Scroll the viewport down by `amount` lines (decreasing the offset).
|
||||
pub fn scroll_down(&mut self, amount: usize) {
|
||||
self.offset = self.offset.saturating_sub(amount);
|
||||
}
|
||||
|
||||
/// Update the maximum number of visible lines.
|
||||
pub fn set_max_visible(&mut self, max: usize) {
|
||||
self.max_visible = max;
|
||||
}
|
||||
}
|
||||
|
||||
/// The user's input buffer, cursor position, history, and autocomplete
|
||||
/// state for the chat prompt.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InputState {
|
||||
pub buffer: String,
|
||||
@@ -81,6 +95,8 @@ const COMMANDS: &[&str] = &[
|
||||
];
|
||||
|
||||
impl InputState {
|
||||
/// Create an empty input state with no buffer, no history, and no
|
||||
/// autocomplete.
|
||||
pub fn new() -> Self {
|
||||
InputState {
|
||||
buffer: String::new(),
|
||||
@@ -94,6 +110,7 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Hide the autocomplete dropdown and clear its state.
|
||||
pub fn close_autocomplete(&mut self) {
|
||||
self.autocomplete_visible = false;
|
||||
self.autocomplete_candidates.clear();
|
||||
@@ -101,6 +118,12 @@ impl InputState {
|
||||
self.autocomplete_idx = 0;
|
||||
}
|
||||
|
||||
/// Open or refresh the autocomplete dropdown by filtering `COMMANDS`
|
||||
/// against the current buffer prefix.
|
||||
///
|
||||
/// Flow: if buffer is empty or doesn't start with `/`, close and return
|
||||
/// → filter `COMMANDS` by prefix match → store candidates → set
|
||||
/// `autocomplete_visible` if any candidates found.
|
||||
pub fn open_autocomplete(&mut self) {
|
||||
let trimmed = self.buffer.trim().to_string();
|
||||
if trimmed.is_empty() || !trimmed.starts_with('/') {
|
||||
@@ -119,6 +142,8 @@ impl InputState {
|
||||
self.autocomplete_visible = !self.autocomplete_candidates.is_empty();
|
||||
}
|
||||
|
||||
/// Move the autocomplete selection up (forward=false) or down (forward=true).
|
||||
/// Wraps around at the boundaries.
|
||||
pub fn cycle_autocomplete(&mut self, forward: bool) {
|
||||
let n = self.autocomplete_candidates.len();
|
||||
if n == 0 { return; }
|
||||
@@ -129,6 +154,10 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept the currently selected autocomplete candidate, placing it
|
||||
/// in the buffer and closing the dropdown.
|
||||
///
|
||||
/// Return: `true` if a candidate was selected, `false` if none existed.
|
||||
pub fn select_autocomplete(&mut self) -> bool {
|
||||
if let Some(candidate) = self.autocomplete_candidates.get(self.autocomplete_idx) {
|
||||
self.buffer = candidate.clone();
|
||||
@@ -140,6 +169,8 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Legacy inline tab-complete — opens the dropdown on first Tab press,
|
||||
/// then cycles forward on subsequent presses.
|
||||
pub fn tab_complete(&mut self) {
|
||||
// Legacy inline tab-complete — used as a fallback when the dropdown
|
||||
// isn't visible yet. Opens the dropdown on the first Tab press.
|
||||
@@ -150,23 +181,27 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the cursor left by one character (if not at the start).
|
||||
pub fn char_left(&mut self) {
|
||||
if self.cursor > 0 {
|
||||
self.cursor -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the cursor right by one character (if not at the end).
|
||||
pub fn char_right(&mut self) {
|
||||
if self.cursor < self.buffer.len() {
|
||||
self.cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a character at the cursor position.
|
||||
pub fn insert(&mut self, c: char) {
|
||||
self.buffer.insert(self.cursor, c);
|
||||
self.cursor += 1;
|
||||
}
|
||||
|
||||
/// Delete the character to the left of the cursor (backspace).
|
||||
pub fn delete_left(&mut self) {
|
||||
if self.cursor > 0 {
|
||||
self.cursor -= 1;
|
||||
@@ -174,12 +209,17 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete the character at the cursor position (forward delete).
|
||||
pub fn delete_right(&mut self) {
|
||||
if self.cursor < self.buffer.len() {
|
||||
self.buffer.remove(self.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Submit the current buffer: push it to history, clear the buffer,
|
||||
/// and return the submitted text.
|
||||
///
|
||||
/// Return: the text that was in the buffer before clearing.
|
||||
pub fn submit(&mut self) -> String {
|
||||
let result = self.buffer.clone();
|
||||
if !result.is_empty() {
|
||||
@@ -191,6 +231,7 @@ impl InputState {
|
||||
result
|
||||
}
|
||||
|
||||
/// Navigate backward through input history.
|
||||
pub fn history_up(&mut self) {
|
||||
if self.history.is_empty() {
|
||||
return;
|
||||
@@ -205,6 +246,7 @@ impl InputState {
|
||||
self.cursor = self.buffer.len();
|
||||
}
|
||||
|
||||
/// Navigate forward through input history (back toward the newest entry).
|
||||
pub fn history_down(&mut self) {
|
||||
match self.history_idx {
|
||||
Some(i) if i < self.history.len() - 1 => {
|
||||
@@ -223,6 +265,8 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
/// The "miscellaneous" slice of app state: which overlay is showing,
|
||||
/// toasts, thinking/connected flags, effort level, editor state, and tick.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MiscState {
|
||||
pub overlay: Overlay,
|
||||
@@ -237,6 +281,8 @@ pub struct MiscState {
|
||||
}
|
||||
|
||||
impl MiscState {
|
||||
/// Create a fresh `MiscState` with no overlay, no toasts, and default
|
||||
/// effort level 1.
|
||||
pub fn new() -> Self {
|
||||
MiscState {
|
||||
overlay: Overlay::None,
|
||||
@@ -255,6 +301,9 @@ impl MiscState {
|
||||
self.toasts.push(toast);
|
||||
}
|
||||
|
||||
/// Remove and return all toasts whose lifetime has expired at `now_ms`.
|
||||
///
|
||||
/// Return: the expired toasts (after removal).
|
||||
pub fn drain_expired_toasts(&mut self, now_ms: i64) -> Vec<super::types::Toast> {
|
||||
let expired: Vec<_> = self.toasts.iter().filter(|t| t.expired(now_ms)).cloned().collect();
|
||||
self.toasts.retain(|t| !t.expired(now_ms));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Application state: misc fields, the main `AppStateRest` struct,
|
||||
//! runtime-only state, and shared types (overlays, toasts, origins).
|
||||
pub mod misc;
|
||||
pub mod rest;
|
||||
pub mod runtime;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
//! Top-level mutable application state (`AppStateRest`) and the transcript
|
||||
//! display type it owns.
|
||||
//!
|
||||
//! `AppStateRest` is the single source-of-truth struct mutated in-place from
|
||||
//! `actions/mod.rs` and `controller/input.rs`; every other module reads it.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -12,6 +18,7 @@ use crate::model::app_config::AppConfig;
|
||||
use crate::model::editlog::EditLog;
|
||||
use crate::model::settings::Settings;
|
||||
|
||||
/// A single transcript entry rendered in the TUI chat pane.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ChatMessageDisplay {
|
||||
pub role: crate::dto::chat::message::Role,
|
||||
@@ -20,6 +27,7 @@ pub struct ChatMessageDisplay {
|
||||
}
|
||||
|
||||
impl ChatMessageDisplay {
|
||||
/// Build a display entry, stamping it with the current time.
|
||||
pub fn new(role: crate::dto::chat::message::Role, content: String) -> Self {
|
||||
ChatMessageDisplay {
|
||||
role,
|
||||
@@ -29,6 +37,11 @@ impl ChatMessageDisplay {
|
||||
}
|
||||
}
|
||||
|
||||
/// The single source-of-truth state struct for the entire application.
|
||||
///
|
||||
/// Mutated in-place from two locations: `actions/mod.rs` (`apply_action`)
|
||||
/// and `controller/input.rs` (key event handlers). Read-only from every
|
||||
/// other module.
|
||||
#[derive(Clone)]
|
||||
pub struct AppStateRest {
|
||||
|
||||
@@ -58,6 +71,15 @@ pub struct AppStateRest {
|
||||
}
|
||||
|
||||
impl AppStateRest {
|
||||
/// Construct the initial application state for a session.
|
||||
///
|
||||
/// Flow: load settings/config -> derive download/worktree dirs from
|
||||
/// `memory_dir`'s parent -> derive `session_id` from the session dir's
|
||||
/// file name -> build the sub-state structs.
|
||||
///
|
||||
/// Why: falls back to `memory_dir` itself (with a warning) when it has
|
||||
/// no parent, and to an empty session id when the dir name can't be
|
||||
/// read, so construction never fails.
|
||||
pub fn new(workspace_roots: Vec<PathBuf>, session_dir: PathBuf, memory_dir: PathBuf) -> Self {
|
||||
let settings = Settings::load();
|
||||
let app_config = AppConfig::load();
|
||||
@@ -105,6 +127,10 @@ impl AppStateRest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether an agent turn is currently running.
|
||||
///
|
||||
/// Return: `false` (and logs a warning) if the mutex is poisoned, rather
|
||||
/// than propagating a panic.
|
||||
pub fn turn_in_flight(&self) -> bool {
|
||||
self.turn_in_flight.lock().map(|g| *g).unwrap_or_else(|_| {
|
||||
tracing::warn!("[state] turn_in_flight mutex poisoned");
|
||||
@@ -114,6 +140,8 @@ impl AppStateRest {
|
||||
|
||||
|
||||
|
||||
/// Append a message to the transcript, evicting the oldest entry once
|
||||
/// `max_lines` is exceeded, and mark both the cache and the app dirty.
|
||||
pub fn push_transcript(&mut self, msg: ChatMessageDisplay) {
|
||||
self.transcript_cache.messages.push(msg);
|
||||
if self.transcript_cache.messages.len() > self.transcript_cache.max_lines {
|
||||
@@ -123,11 +151,19 @@ impl AppStateRest {
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Queue a toast notification for display and mark the app dirty.
|
||||
pub fn push_toast(&mut self, toast: Toast) {
|
||||
self.misc.push_toast(toast);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Resolve the base directory that stores this session (grandparent of
|
||||
/// `session_dir`, i.e. the sessions root, not the individual session
|
||||
/// folder).
|
||||
///
|
||||
/// Why: falls back progressively -- grandparent, then parent, then
|
||||
/// `session_dir` itself -- logging a warning at each step down, so this
|
||||
/// never fails even on a shallow path.
|
||||
pub fn store_base_dir(&self) -> std::path::PathBuf {
|
||||
self.session_dir.parent()
|
||||
.and_then(|p| p.parent())
|
||||
@@ -143,10 +179,13 @@ impl AppStateRest {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a `ToolCtx` for tool calls originating from the main agent.
|
||||
pub fn tool_ctx(&self) -> crate::tool::ToolCtx {
|
||||
self.tool_ctx_for(Origin::Main)
|
||||
}
|
||||
|
||||
/// Build a `ToolCtx` scoped to the given call origin (main, subagent,
|
||||
/// reviewer), copying workspace/session/memory paths from state.
|
||||
pub fn tool_ctx_for(&self, origin: Origin) -> crate::tool::ToolCtx {
|
||||
crate::tool::ToolCtx {
|
||||
workspaces: self.workspace_roots.clone(),
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
//! Per-session runtime state: message history, pending tool queue,
|
||||
//! background bash jobs, lesson/review counters, and the `TurnEvent`
|
||||
//! stream emitted while an agent turn is in flight.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Cumulative token/latency counters for a session, persisted alongside it.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
|
||||
pub struct UsageStats {
|
||||
pub tokens_in: u64,
|
||||
@@ -10,6 +15,9 @@ pub struct UsageStats {
|
||||
pub total_ms: u64,
|
||||
}
|
||||
|
||||
/// Mutable, serializable state for one session: chat history, tool
|
||||
/// results, pending tools, background jobs, and lesson/review counters
|
||||
/// shown in the TUI status bar.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SessionRuntime {
|
||||
pub messages: Vec<crate::dto::chat::message::ChatMessage>,
|
||||
@@ -36,6 +44,7 @@ pub struct SessionRuntime {
|
||||
pub usage: UsageStats,
|
||||
}
|
||||
|
||||
/// Record of one completed tool invocation, kept for transcript/history.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolCallResult {
|
||||
pub tool_call_id: String,
|
||||
@@ -45,6 +54,8 @@ pub struct ToolCallResult {
|
||||
pub duration_ms: u64,
|
||||
}
|
||||
|
||||
/// A tool call awaiting execution, along with which execution model
|
||||
/// (inline, deferred, async) it should run under.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PendingTool {
|
||||
pub tool_name: String,
|
||||
@@ -52,6 +63,8 @@ pub struct PendingTool {
|
||||
pub execution_model: crate::app::state::types::ExecutionModel,
|
||||
}
|
||||
|
||||
/// Reference to a background bash job tracked in session state (the actual
|
||||
/// process handle lives elsewhere; this is just the display/status record).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BashJobRef {
|
||||
pub id: String,
|
||||
@@ -60,6 +73,8 @@ pub struct BashJobRef {
|
||||
pub running: bool,
|
||||
}
|
||||
|
||||
/// Events emitted onto the turn-event queue while an agent turn runs,
|
||||
/// consumed by the event loop to update state and drive re-renders.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TurnEvent {
|
||||
AssistantMessage(crate::dto::chat::message::ChatMessage),
|
||||
@@ -86,6 +101,8 @@ pub enum TurnEvent {
|
||||
}
|
||||
|
||||
impl SessionRuntime {
|
||||
/// Create fresh runtime state for a session rooted at `session_dir`,
|
||||
/// with all counters zeroed and `session_start` set to now.
|
||||
pub fn new(session_dir: PathBuf) -> Self {
|
||||
SessionRuntime {
|
||||
messages: Vec::new(),
|
||||
@@ -113,6 +130,7 @@ impl SessionRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a message to the session's conversation history.
|
||||
pub fn push_message(&mut self, msg: crate::dto::chat::message::ChatMessage) {
|
||||
self.messages.push(msg);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
//! Opaque, serializable snapshot of application state used for
|
||||
//! attach/daemon IPC transfer.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A JSON-boxed snapshot of app state, opaque to the transport layer.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StateSnapshot {
|
||||
pub snapshot: serde_json::Value,
|
||||
}
|
||||
|
||||
impl StateSnapshot {
|
||||
/// Create an empty snapshot (`{}`).
|
||||
pub fn new() -> Self {
|
||||
StateSnapshot {
|
||||
snapshot: serde_json::json!({}),
|
||||
@@ -13,10 +18,16 @@ impl StateSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a snapshot to bytes for transport over the daemon socket.
|
||||
///
|
||||
/// Return: JSON-encoded bytes, or a serde error.
|
||||
pub fn serialize_snapshot(snapshot: &StateSnapshot) -> anyhow::Result<Vec<u8>> {
|
||||
Ok(serde_json::to_vec(snapshot)?)
|
||||
}
|
||||
|
||||
/// Parse a snapshot previously produced by `serialize_snapshot`.
|
||||
///
|
||||
/// Return: the decoded `StateSnapshot`, or a serde error.
|
||||
pub fn deserialize_snapshot(data: &[u8]) -> anyhow::Result<StateSnapshot> {
|
||||
Ok(serde_json::from_slice(data)?)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
//! Shared small state types: toasts, overlays, the transcript cache,
|
||||
//! tool execution model, and call origin tags.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
/// Severity/category of a toast notification, used to pick its color.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ToastKind {
|
||||
Info,
|
||||
@@ -10,6 +14,8 @@ pub enum ToastKind {
|
||||
Lesson,
|
||||
}
|
||||
|
||||
/// A transient status message shown in the TUI, auto-dismissed after
|
||||
/// `lifetime_ms`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Toast {
|
||||
pub kind: ToastKind,
|
||||
@@ -19,6 +25,7 @@ pub struct Toast {
|
||||
}
|
||||
|
||||
impl Toast {
|
||||
/// Create a toast with a default 5-second lifetime, stamped with now.
|
||||
pub fn new(kind: ToastKind, message: String) -> Self {
|
||||
Toast {
|
||||
kind,
|
||||
@@ -28,11 +35,13 @@ impl Toast {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this toast's lifetime has elapsed as of `now_ms`.
|
||||
pub fn expired(&self, now_ms: i64) -> bool {
|
||||
now_ms - self.created_at > self.lifetime_ms as i64
|
||||
}
|
||||
}
|
||||
|
||||
/// Which modal overlay, if any, is currently shown over the main TUI view.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Overlay {
|
||||
None,
|
||||
@@ -56,11 +65,13 @@ pub enum Overlay {
|
||||
}
|
||||
|
||||
impl Overlay {
|
||||
/// Whether any overlay (i.e. anything other than `None`) is active.
|
||||
pub fn is_active(self) -> bool {
|
||||
!matches!(self, Overlay::None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounded ring of recent chat messages used to render the transcript view.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TranscriptCache {
|
||||
pub messages: Vec<super::rest::ChatMessageDisplay>,
|
||||
@@ -69,6 +80,7 @@ pub struct TranscriptCache {
|
||||
}
|
||||
|
||||
impl TranscriptCache {
|
||||
/// Create an empty transcript cache holding at most `max_lines` messages.
|
||||
pub fn new(max_lines: usize) -> Self {
|
||||
TranscriptCache {
|
||||
messages: Vec::new(),
|
||||
@@ -78,6 +90,7 @@ impl TranscriptCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// How a pending tool call should be executed when the turn resumes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ExecutionModel {
|
||||
Inline,
|
||||
@@ -85,6 +98,8 @@ pub enum ExecutionModel {
|
||||
AsyncTokio,
|
||||
}
|
||||
|
||||
/// Which kind of caller (main agent vs. subagent vs. reviewer) is
|
||||
/// invoking a tool, used to scope permissions and tag log/output paths.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
pub enum Origin {
|
||||
Main,
|
||||
@@ -93,6 +108,7 @@ pub enum Origin {
|
||||
}
|
||||
|
||||
impl Origin {
|
||||
/// Short string tag for this origin, used in filenames and logs.
|
||||
pub fn tag(&self) -> String {
|
||||
match self {
|
||||
Origin::Main => "main".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user