Skip to main content

plexus_substrate/activations/orcha/
context.rs

1//! Orcha Context - manages arbor tree for orchestration events
2//!
3//! This provides a clean abstraction over arbor tree operations,
4//! tracking all orchestration events in a structured way.
5
6use crate::activations::arbor::{ArborStorage, NodeId, TreeId};
7use std::sync::Arc;
8use tokio::sync::Mutex;
9
10/// Orcha context for tracking orchestration events in arbor
11pub struct OrchaContext {
12    arbor: Arc<ArborStorage>,
13    tree_id: TreeId,
14    session_id: String,
15    last_node_id: Mutex<Option<NodeId>>,
16}
17
18impl OrchaContext {
19    /// Create a new Orcha context with an arbor tree
20    pub async fn new(
21        arbor: Arc<ArborStorage>,
22        session_id: String,
23        task: String,
24        model: String,
25    ) -> Result<Self, String> {
26        // Create arbor tree for this orchestration session
27        let tree_id = arbor
28            .tree_create(
29                Some(serde_json::json!({
30                    "session_id": session_id.clone(),
31                    "task": task.clone(),
32                    "model": model.clone(),
33                })),
34                "orcha",
35            )
36            .await
37            .map_err(|e| format!("Failed to create arbor tree: {}", e))?;
38
39        let ctx = Self {
40            arbor,
41            tree_id,
42            session_id: session_id.clone(),
43            last_node_id: Mutex::new(None),
44        };
45
46        // Write initial session_started node
47        ctx.write_node(
48            format!(
49                "session_started: {}\ntask: {}\nmodel: {}",
50                session_id, task, model
51            ),
52            serde_json::json!({
53                "event": "session_started",
54                "session_id": session_id,
55            }),
56        )
57        .await;
58
59        Ok(ctx)
60    }
61
62    /// Get the tree_id for this context
63    pub fn tree_id(&self) -> String {
64        self.tree_id.to_string()
65    }
66
67    /// Write a node to the arbor tree, chained to the previous node
68    async fn write_node(&self, content: String, metadata: serde_json::Value) {
69        let parent = *self.last_node_id.lock().await;
70        match self
71            .arbor
72            .node_create_text(&self.tree_id, parent, content, Some(metadata))
73            .await
74        {
75            Ok(node_id) => {
76                *self.last_node_id.lock().await = Some(node_id);
77            }
78            Err(e) => {
79                tracing::warn!("Failed to write arbor node: {}", e);
80            }
81        }
82    }
83
84    /// Record that a Claude Code session was created
85    pub async fn claude_session_created(&self, claude_session_id: String, session_name: String) {
86        self.write_node(
87            format!(
88                "claude_session_created: {}\nsession_name: {}",
89                claude_session_id, session_name
90            ),
91            serde_json::json!({
92                "event": "claude_session_started",
93                "claude_session_id": claude_session_id,
94                "session_name": session_name,
95            }),
96        )
97        .await;
98    }
99
100    /// Record that a prompt was sent to Claude
101    pub async fn prompt_created(&self, prompt: String, retry_count: u32) {
102        self.write_node(
103            format!("prompt_created (retry {}):\n{}", retry_count, prompt),
104            serde_json::json!({
105                "event": "prompt_created",
106                "prompt": prompt,
107                "retry_count": retry_count,
108            }),
109        )
110        .await;
111    }
112
113    /// Record that Claude session completed
114    pub async fn claude_session_complete(&self, claude_session_id: String) {
115        self.write_node(
116            format!("claude_session_complete: {}", claude_session_id),
117            serde_json::json!({
118                "event": "claude_session_complete",
119                "claude_session_id": claude_session_id,
120            }),
121        )
122        .await;
123    }
124
125    /// Record that validation started
126    pub async fn validation_started(&self, test_command: String, cwd: String) {
127        self.write_node(
128            format!("validation_started:\n{}", test_command),
129            serde_json::json!({
130                "event": "validation_started",
131                "test_command": test_command,
132                "cwd": cwd,
133            }),
134        )
135        .await;
136    }
137
138    /// Record validation result
139    pub async fn validation_result(&self, success: bool, output: String) {
140        self.write_node(
141            format!(
142                "validation_result: {}\noutput:\n{}",
143                if success { "SUCCESS" } else { "FAILED" },
144                output
145            ),
146            serde_json::json!({
147                "event": "validation_result",
148                "success": success,
149                "output": output,
150            }),
151        )
152        .await;
153    }
154
155    /// Record session completion
156    pub async fn session_complete(&self, status: &str) {
157        self.write_node(
158            format!("session_complete: {} status={}", self.session_id, status),
159            serde_json::json!({
160                "event": "session_complete",
161                "status": status,
162            }),
163        )
164        .await;
165    }
166
167    /// Record a tool use event
168    pub async fn tool_use(&self, tool_name: String, tool_use_id: String, input: String) {
169        self.write_node(
170            format!(
171                "tool_use: {}\nid: {}\ninput:\n{}",
172                tool_name, tool_use_id, input
173            ),
174            serde_json::json!({
175                "event": "tool_use",
176                "tool_name": tool_name,
177                "tool_use_id": tool_use_id,
178                "input": input,
179            }),
180        )
181        .await;
182    }
183
184    /// Record a tool result
185    pub async fn tool_result(&self, tool_use_id: String, output: String, is_error: bool) {
186        self.write_node(
187            format!(
188                "tool_result for {}: {}\n{}",
189                tool_use_id,
190                if is_error { "ERROR" } else { "SUCCESS" },
191                output
192            ),
193            serde_json::json!({
194                "event": "tool_result",
195                "tool_use_id": tool_use_id,
196                "output": output,
197                "is_error": is_error,
198            }),
199        )
200        .await;
201    }
202
203    /// Record Claude's text output
204    pub async fn claude_output(&self, text: String) {
205        self.write_node(
206            format!("claude_output:\n{}", text),
207            serde_json::json!({
208                "event": "claude_output",
209                "text": text,
210            }),
211        )
212        .await;
213    }
214}