Skip to main content

acp_utils/conversation/
activity.rs

1use agent_client_protocol::schema::{MaybeUndefined, v2 as acp};
2use schemars::JsonSchema;
3use serde::Serialize;
4
5/// What the agent is doing during the current turn.
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, JsonSchema)]
7#[serde(rename_all = "snake_case")]
8pub enum Activity {
9    #[default]
10    Idle,
11    Thinking,
12    Responding,
13    RequiresAction,
14    Working,
15}
16
17impl Activity {
18    pub(super) fn after(update: &acp::SessionUpdate) -> Option<Self> {
19        match update {
20            acp::SessionUpdate::AgentMessageChunk(_)
21            | acp::SessionUpdate::StateUpdate(acp::StateUpdate::Running(_)) => Some(Self::Responding),
22            acp::SessionUpdate::StateUpdate(acp::StateUpdate::RequiresAction(_)) => Some(Self::RequiresAction),
23            acp::SessionUpdate::ToolCallUpdate(_) => Some(Self::Working),
24            acp::SessionUpdate::AgentThoughtChunk(chunk) => match &chunk.content {
25                acp::ContentBlock::Text(text) if !text.text.is_empty() => Some(Self::Thinking),
26                _ => None,
27            },
28            acp::SessionUpdate::AgentThought(message) => match &message.content {
29                MaybeUndefined::Value(blocks) if blocks.iter().any(has_content) => Some(Self::Thinking),
30                _ => None,
31            },
32            _ => None,
33        }
34    }
35}
36
37fn has_content(block: &acp::ContentBlock) -> bool {
38    !matches!(block, acp::ContentBlock::Text(text) if text.text.is_empty())
39}