goosedump 0.6.5

Coding agent context data browser
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) Jarkko Sakkinen 2026

use serde_json::Value as JsonValue;

#[derive(Debug, Clone)]
pub struct ConversationMessage {
    pub entry_id: String,
    pub kind: MessageKind,
}

impl ConversationMessage {
    #[must_use]
    pub fn new(entry_id: impl Into<String>, kind: MessageKind) -> Self {
        Self {
            entry_id: entry_id.into(),
            kind,
        }
    }

    /// The conversational role (`user`/`assistant`) this message carries.
    #[must_use]
    pub fn role(&self) -> &str {
        match &self.kind {
            MessageKind::TextContent(tc) => tc.role.as_str(),
            MessageKind::AssistantResponse(_) => "assistant",
            MessageKind::ToolResultData(_) | MessageKind::BashOutput(_) => "user",
        }
    }

    /// A human-readable role label for structured search/grep results
    /// (`tool/<name>` for tool results, `bash` for shell output).
    #[must_use]
    pub fn role_label(&self) -> String {
        match &self.kind {
            MessageKind::TextContent(tc) => {
                if tc.role.is_empty() {
                    "unknown".to_string()
                } else {
                    tc.role.clone()
                }
            }
            MessageKind::AssistantResponse(_) => "assistant".to_string(),
            MessageKind::ToolResultData(tr) => format!("tool/{}", tr.tool_name),
            MessageKind::BashOutput(_) => "bash".to_string(),
        }
    }
}

#[derive(Debug, Clone)]
pub enum MessageKind {
    TextContent(TextContent),
    AssistantResponse(AssistantResponse),
    ToolResultData(ToolResultData),
    BashOutput(BashOutput),
}

#[derive(Debug, Clone)]
pub struct TextContent {
    pub role: String,
    pub text: String,
}

#[derive(Debug, Clone)]
pub struct AssistantResponse {
    pub thinking: Vec<String>,
    pub tool_calls: Vec<ToolCall>,
    pub text: String,
}

#[derive(Debug, Clone)]
pub struct ToolResultData {
    /// Correlation id of the call this result answers (provider `tool_use_id`
    /// / `call_id`); empty when the source format does not address results by
    /// id. Lets renderers re-pair a result with its originating call.
    pub call_id: String,
    pub tool_name: String,
    pub content: String,
    pub is_error: bool,
}

#[derive(Debug, Clone)]
pub struct BashOutput {
    pub command: String,
    pub output: String,
}

#[derive(Debug, Clone)]
pub struct ToolCall {
    /// Provider call id (`tool_use.id` / `call_id`); empty when the source
    /// format does not assign one. Lets renderers correlate a later tool
    /// result back to this call.
    pub id: String,
    pub name: String,
    pub arguments: JsonValue,
}

#[derive(Debug, Clone)]
pub struct Entry {
    pub id: String,
    pub parent_id: String,
}

#[derive(Debug, Clone)]
pub struct Context {
    pub entries: Vec<Entry>,
    pub messages: Vec<ConversationMessage>,
    /// Working directory recorded in the session, when the source format
    /// carries one. Re-emitted by renderers whose native format has a `cwd`
    /// slot (e.g. the Pi/Codex session header).
    pub cwd: Option<String>,
}

#[derive(Debug, Clone)]
pub struct SearchHit {
    pub entry_id: String,
    pub score: f64,
    pub role: String,
    pub text: String,
    pub files: Vec<String>,
    pub terms: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct ContextListing {
    pub id: String,
    pub detail: String,
}

/// A listing paired with the provider it was discovered under, so that
/// aggregated `list` output can be tagged as `<provider>:<id>`.
#[derive(Debug, Clone)]
pub struct TaggedListing {
    pub provider: &'static str,
    pub listing: ContextListing,
}