opencode_ctl/tools/
messages.rs1use serde_json::{json, Value};
4
5use crate::clock::Clock;
6use crate::compact::{compact_message, ToolsMode};
7use crate::engine::Engine;
8use crate::error::{ErrorCode, ToolError, ToolResult};
9use crate::transport::Transport;
10
11use super::{SessionMessagesArgs, SessionPromptArgs};
12
13fn invalid(msg: String) -> ToolError {
14 ToolError::new(ErrorCode::InvalidArgument, msg)
15}
16
17impl<T: Transport, C: Clock> Engine<T, C> {
18 pub async fn session_prompt(&self, a: SessionPromptArgs) -> ToolResult {
19 let id = self.resolve_session(a.session_id.as_deref())?;
20 self.guard_created(&id)?;
21 let delivery = a.delivery.as_deref().unwrap_or("steer");
22 if delivery != "steer" && delivery != "queue" {
23 return Err(invalid(format!(
24 "delivery '{delivery}' is not steer or queue"
25 )));
26 }
27 let body = json!({"text": a.text, "delivery": delivery});
28 if body.to_string().len() > self.settings.body_max_bytes {
29 return Err(ToolError::new(
30 ErrorCode::BodyTooLarge,
31 format!(
32 "the prompt body exceeds body_max_bytes ({})",
33 self.settings.body_max_bytes
34 ),
35 )
36 .hint("send a shorter prompt, or put the detail in a file the worker can read"));
37 }
38 let (data, _, _) = self
39 .call_json(
40 "session.prompt",
41 vec![("sessionID".to_string(), id)],
42 Some(body),
43 )
44 .await?;
45 Ok(json!({
46 "message_id": data.get("id").cloned().unwrap_or(Value::Null),
47 "delivery": delivery,
48 "hint": "call session_wait with after=<message_id> on EVERY slice",
49 }))
50 }
51
52 pub async fn session_messages(&self, a: SessionMessagesArgs) -> ToolResult {
53 let id = self.resolve_session(a.session_id.as_deref())?;
54 let tools = match a.tools.as_deref().unwrap_or("summary") {
55 "none" => ToolsMode::None,
56 "summary" => ToolsMode::Summary,
57 "full" => ToolsMode::Full,
58 other => {
59 return Err(invalid(format!(
60 "tools '{other}' is not none, summary or full"
61 )))
62 }
63 };
64 let order = match a.order.as_deref().unwrap_or("desc") {
65 o @ ("asc" | "desc") => o.to_string(),
66 other => return Err(invalid(format!("order '{other}' is not asc or desc"))),
67 };
68 let mut params = vec![
69 ("sessionID".to_string(), id),
70 (
71 "limit".to_string(),
72 a.limit.unwrap_or(20).clamp(1, 200).to_string(),
73 ),
74 ];
75 match a.cursor {
76 Some(c) => params.push(("cursor".into(), c)),
78 None => params.push(("order".into(), order)),
79 }
80 if let Some(t) = a.r#type {
81 params.push(("type".into(), t));
82 }
83 let (data, cursor, _) = self.call_json("session.message.list", params, None).await?;
84 let reasoning = a.reasoning.unwrap_or(false);
85 let messages: Vec<Value> = data
86 .as_array()
87 .map(|rows| {
88 rows.iter()
89 .map(|r| {
90 serde_json::to_value(compact_message(r, reasoning, tools))
91 .unwrap_or(Value::Null)
92 })
93 .collect()
94 })
95 .unwrap_or_default();
96 let next = cursor
97 .as_ref()
98 .and_then(|c| c.get("next"))
99 .cloned()
100 .unwrap_or(Value::Null);
101 Ok(json!({"messages": messages, "next_cursor": next, "truncated": false}))
103 }
104}