Skip to main content

apollo/tools/
session.rs

1//! Session tools — model switching, status, config management.
2//! Gives the AI control over its own session (like OpenClaw's session_status).
3
4use async_trait::async_trait;
5use serde::Deserialize;
6use std::sync::Arc;
7
8use super::traits::*;
9use crate::agent::AgentRunner;
10
11/// session_status — view/change model, check status
12pub struct SessionStatusTool {
13    runner: Arc<AgentRunner>,
14}
15
16impl SessionStatusTool {
17    pub fn new(runner: Arc<AgentRunner>) -> Self {
18        Self { runner }
19    }
20}
21
22#[derive(Deserialize)]
23struct SessionStatusArgs {
24    /// Set model override (e.g. "claude-opus-4", "claude-haiku-3-5")
25    model: Option<String>,
26}
27
28#[async_trait]
29impl Tool for SessionStatusTool {
30    fn name(&self) -> &str {
31        "session_status"
32    }
33
34    fn spec(&self) -> ToolSpec {
35        ToolSpec {
36            name: "session_status".to_string(),
37            description: "Show session status (current model, tools, uptime). Optionally set model override with model parameter.".to_string(),
38            parameters: serde_json::json!({
39                "type": "object",
40                "properties": {
41                    "model": {
42                        "type": "string",
43                        "description": "Set model override (e.g. 'gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini'). Use 'default' to reset."
44                    }
45                }
46            }),
47        }
48    }
49
50    async fn execute(&self, arguments: &str) -> anyhow::Result<ToolResult> {
51        let args: SessionStatusArgs =
52            serde_json::from_str(arguments).unwrap_or(SessionStatusArgs { model: None });
53
54        if let Some(model) = &args.model {
55            if model == "default" || model == "reset" {
56                let default = self.runner.get_default_model().to_string();
57                self.runner.reset_model();
58                return Ok(ToolResult::success(format!(
59                    "Model reset to configured default: {default}"
60                )));
61            }
62            self.runner.set_model(model.as_str());
63            return Ok(ToolResult::success(format!("Model switched to: {model}")));
64        }
65
66        let tools = self.runner.list_tools().await;
67        let cfg = &self.runner.agent_config;
68        let status = format!(
69            "Session Status:\n\
70            Model (current):  {}\n\
71            Model (default):  {}\n\
72            Model (fast):     {}\n\
73            Model (heavy):    {}\n\
74            Tools: {} active\n\
75            PID: {}\n\
76            Runtime: apollo v{}\n\n\
77            Tool list: {}\n\n\
78            Tip: use session_status{{\"model\":\"...\"}}\n\
79            For swarms — use fast model as runner, heavy as orchestrator.\n\
80            Available aliases: default/reset (restore configured model)",
81            self.runner.get_model(),
82            self.runner.get_default_model(),
83            cfg.fast_model,
84            cfg.heavy_model,
85            tools.len(),
86            std::process::id(),
87            env!("CARGO_PKG_VERSION"),
88            tools.join(", "),
89        );
90
91        Ok(ToolResult::success(status))
92    }
93}