1#![forbid(unsafe_code)]
4
5use async_trait::async_trait;
6
7use serde_json::{Value, json};
8use std::sync::Arc;
9use wm_core::{Context, EffectRow, Gana, Resource, Tool, ToolStats};
10use wm_memory::{AssociationStore, MemoryStore};
11
12pub struct SpiralReportTool {
13 tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
14 stats: ToolStats,
15 effects: EffectRow,
16}
17
18impl SpiralReportTool {
19 pub fn new(tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>) -> Self {
20 Self {
21 tracker,
22 stats: ToolStats::default(),
23 effects: EffectRow::pure(),
24 }
25 }
26}
27
28#[async_trait]
29impl Tool for SpiralReportTool {
30 fn name(&self) -> &str {
31 "spiral.report"
32 }
33 fn gana(&self) -> Gana {
34 Gana::Encampment
35 }
36 fn effects(&self) -> &EffectRow {
37 &self.effects
38 }
39 fn description(&self) -> &str {
40 "Report on autonomy expansion or circling (spiral direction, novelty, suspensions)"
41 }
42 async fn call(&self, _ctx: &mut Context, _args: Value) -> wm_core::Result<Value> {
43 let report = {
44 let tracker = self
45 .tracker
46 .lock()
47 .map_err(|e| wm_core::CoreError::Internal(format!("spiral tracker lock: {e}")))?;
48 tracker.report()
49 };
50 Ok(report.to_json())
51 }
52 fn stats(&self) -> &ToolStats {
53 &self.stats
54 }
55}
56
57pub struct ConsolidationConnectTool {
63 store: Arc<MemoryStore>,
64 associations: Arc<AssociationStore>,
65 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
66 stats: ToolStats,
67 effects: EffectRow,
68}
69
70impl ConsolidationConnectTool {
71 pub fn new(
72 store: Arc<MemoryStore>,
73 associations: Arc<AssociationStore>,
74 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
75 ) -> Self {
76 Self {
77 store,
78 associations,
79 spiral_tracker,
80 stats: ToolStats::default(),
81 effects: EffectRow {
82 reads: super::common::memory_galaxy_reads(),
85 writes: vec![Resource::Galaxy("substrate".into())],
86 ..Default::default()
87 },
88 }
89 }
90}
91
92#[async_trait]
93impl Tool for ConsolidationConnectTool {
94 fn name(&self) -> &str {
95 "consolidation.connect"
96 }
97 fn gana(&self) -> Gana {
98 Gana::Encampment
99 }
100 fn effects(&self) -> &EffectRow {
101 &self.effects
102 }
103 fn description(&self) -> &str {
104 "Propose typed associations for disconnected memories (gated, human review)"
105 }
106 async fn call(&self, _ctx: &mut Context, args: Value) -> wm_core::Result<Value> {
107 let health_score = args
108 .get("health_score")
109 .and_then(Value::as_f64)
110 .unwrap_or(0.8) as f32;
111
112 let mut runner = wm_cognitive::AutonomousCycleRunner::default();
113 let cycle_ctx =
114 wm_cognitive::CycleContext::new(&self.store, &self.associations, health_score);
115 let result = runner.run_cycle(wm_cognitive::CycleType::Connect, &cycle_ctx);
116
117 if let Ok(mut tracker) = self.spiral_tracker.lock() {
119 tracker.record(&result);
120 }
121
122 Ok(json!({
123 "status": "success",
124 "cycle": result.cycle.name(),
125 "cycle_status": format!("{:?}", result.status),
126 "purpose": result.purpose,
127 "memories_scanned": result.memories_scanned,
128 "proposals_generated": result.proposals_generated,
129 "duration_ms": result.duration_ms,
130 "requires_human_review": true,
131 "notes": result.notes,
132 "connections": result.connections,
133 }))
134 }
135 fn stats(&self) -> &ToolStats {
136 &self.stats
137 }
138}
139
140pub struct ConsolidationCompressTool {
146 store: Arc<MemoryStore>,
147 associations: Arc<AssociationStore>,
148 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
149 stats: ToolStats,
150 effects: EffectRow,
151}
152
153impl ConsolidationCompressTool {
154 pub fn new(
155 store: Arc<MemoryStore>,
156 associations: Arc<AssociationStore>,
157 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
158 ) -> Self {
159 Self {
160 store,
161 associations,
162 spiral_tracker,
163 stats: ToolStats::default(),
164 effects: EffectRow {
165 reads: super::common::memory_galaxy_reads(),
168 writes: vec![Resource::Galaxy("substrate".into())],
169 ..Default::default()
170 },
171 }
172 }
173}
174
175#[async_trait]
176impl Tool for ConsolidationCompressTool {
177 fn name(&self) -> &str {
178 "consolidation.compress"
179 }
180 fn gana(&self) -> Gana {
181 Gana::Encampment
182 }
183 fn effects(&self) -> &EffectRow {
184 &self.effects
185 }
186 fn description(&self) -> &str {
187 "Propose merging semantically overlapping memories (gated, human review)"
188 }
189 async fn call(&self, _ctx: &mut Context, args: Value) -> wm_core::Result<Value> {
190 let health_score = args
191 .get("health_score")
192 .and_then(Value::as_f64)
193 .unwrap_or(0.8) as f32;
194
195 let mut runner = wm_cognitive::AutonomousCycleRunner::default();
196 let cycle_ctx =
197 wm_cognitive::CycleContext::new(&self.store, &self.associations, health_score);
198 let result = runner.run_cycle(wm_cognitive::CycleType::Compress, &cycle_ctx);
199
200 if let Ok(mut tracker) = self.spiral_tracker.lock() {
202 tracker.record(&result);
203 }
204
205 Ok(json!({
206 "status": "success",
207 "cycle": result.cycle.name(),
208 "cycle_status": format!("{:?}", result.status),
209 "purpose": result.purpose,
210 "memories_scanned": result.memories_scanned,
211 "proposals_generated": result.proposals_generated,
212 "duration_ms": result.duration_ms,
213 "requires_human_review": true,
214 "notes": result.notes,
215 "compressions": result.compressions,
216 }))
217 }
218 fn stats(&self) -> &ToolStats {
219 &self.stats
220 }
221}
222
223pub struct EmergenceScanTool {
229 store: Arc<MemoryStore>,
230 associations: Arc<AssociationStore>,
231 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
232 stats: ToolStats,
233 effects: EffectRow,
234}
235
236impl EmergenceScanTool {
237 pub fn new(
238 store: Arc<MemoryStore>,
239 associations: Arc<AssociationStore>,
240 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
241 ) -> Self {
242 Self {
243 store,
244 associations,
245 spiral_tracker,
246 stats: ToolStats::default(),
247 effects: EffectRow {
248 reads: super::common::memory_galaxy_reads(),
251 writes: vec![Resource::Galaxy("substrate".into())],
252 ..Default::default()
253 },
254 }
255 }
256}
257
258#[async_trait]
259impl Tool for EmergenceScanTool {
260 fn name(&self) -> &str {
261 "emergence.scan"
262 }
263 fn gana(&self) -> Gana {
264 Gana::Encampment
265 }
266 fn effects(&self) -> &EffectRow {
267 &self.effects
268 }
269 fn description(&self) -> &str {
270 "Detect tag/topic emergence patterns across memories (gated, logged)"
271 }
272 async fn call(&self, _ctx: &mut Context, args: Value) -> wm_core::Result<Value> {
273 let health_score = args
274 .get("health_score")
275 .and_then(Value::as_f64)
276 .unwrap_or(0.8) as f32;
277
278 let mut runner = wm_cognitive::AutonomousCycleRunner::default();
279 let cycle_ctx =
280 wm_cognitive::CycleContext::new(&self.store, &self.associations, health_score);
281 let result = runner.run_cycle(wm_cognitive::CycleType::Emergence, &cycle_ctx);
282
283 if let Ok(mut tracker) = self.spiral_tracker.lock() {
285 tracker.record(&result);
286 }
287
288 Ok(json!({
289 "status": "success",
290 "cycle": result.cycle.name(),
291 "cycle_status": format!("{:?}", result.status),
292 "purpose": result.purpose,
293 "memories_scanned": result.memories_scanned,
294 "proposals_generated": result.proposals_generated,
295 "duration_ms": result.duration_ms,
296 "requires_human_review": false,
297 "notes": result.notes,
298 "emergences": result.emergences,
299 }))
300 }
301 fn stats(&self) -> &ToolStats {
302 &self.stats
303 }
304}
305
306pub struct RetentionPruneTool {
312 store: Arc<MemoryStore>,
313 associations: Arc<AssociationStore>,
314 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
315 stats: ToolStats,
316 effects: EffectRow,
317}
318
319impl RetentionPruneTool {
320 pub fn new(
321 store: Arc<MemoryStore>,
322 associations: Arc<AssociationStore>,
323 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
324 ) -> Self {
325 Self {
326 store,
327 associations,
328 spiral_tracker,
329 stats: ToolStats::default(),
330 effects: EffectRow {
331 reads: super::common::memory_galaxy_reads(),
334 writes: vec![Resource::Galaxy("substrate".into())],
335 ..Default::default()
336 },
337 }
338 }
339}
340
341#[async_trait]
342impl Tool for RetentionPruneTool {
343 fn name(&self) -> &str {
344 "retention.prune"
345 }
346 fn gana(&self) -> Gana {
347 Gana::Encampment
348 }
349 fn effects(&self) -> &EffectRow {
350 &self.effects
351 }
352 fn description(&self) -> &str {
353 "Identify memories ready for forgetting based on decay + neuro_score (gated, human review)"
354 }
355 async fn call(&self, _ctx: &mut Context, args: Value) -> wm_core::Result<Value> {
356 let health_score = args
357 .get("health_score")
358 .and_then(Value::as_f64)
359 .unwrap_or(0.8) as f32;
360
361 let mut runner = wm_cognitive::AutonomousCycleRunner::default();
362 let cycle_ctx =
363 wm_cognitive::CycleContext::new(&self.store, &self.associations, health_score);
364 let result = runner.run_cycle(wm_cognitive::CycleType::Prune, &cycle_ctx);
365
366 if let Ok(mut tracker) = self.spiral_tracker.lock() {
368 tracker.record(&result);
369 }
370
371 Ok(json!({
372 "status": "success",
373 "cycle": result.cycle.name(),
374 "cycle_status": format!("{:?}", result.status),
375 "purpose": result.purpose,
376 "memories_scanned": result.memories_scanned,
377 "proposals_generated": result.proposals_generated,
378 "duration_ms": result.duration_ms,
379 "requires_human_review": true,
380 "notes": result.notes,
381 "prunes": result.prunes,
382 }))
383 }
384 fn stats(&self) -> &ToolStats {
385 &self.stats
386 }
387}
388
389pub struct SensorimotorScanTool {
396 store: Arc<MemoryStore>,
397 associations: Arc<AssociationStore>,
398 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
399 sensorimotor_bus: Arc<std::sync::Mutex<wm_substrate::sensorimotor::SensorimotorBus>>,
400 reflex_loop: Arc<std::sync::Mutex<wm_substrate::sensorimotor::ReflexLoop>>,
401 stats: ToolStats,
402 effects: EffectRow,
403}
404
405impl SensorimotorScanTool {
406 pub fn new(
407 store: Arc<MemoryStore>,
408 associations: Arc<AssociationStore>,
409 spiral_tracker: Arc<std::sync::Mutex<wm_cognitive::SpiralTracker>>,
410 sensorimotor_bus: Arc<std::sync::Mutex<wm_substrate::sensorimotor::SensorimotorBus>>,
411 reflex_loop: Arc<std::sync::Mutex<wm_substrate::sensorimotor::ReflexLoop>>,
412 ) -> Self {
413 Self {
414 store,
415 associations,
416 spiral_tracker,
417 sensorimotor_bus,
418 reflex_loop,
419 stats: ToolStats::default(),
420 effects: EffectRow {
421 reads: super::common::memory_galaxy_reads(),
424 writes: vec![Resource::Galaxy("substrate".into())],
425 ..Default::default()
426 },
427 }
428 }
429}
430
431#[async_trait]
432impl Tool for SensorimotorScanTool {
433 fn name(&self) -> &str {
434 "sensorimotor.scan"
435 }
436 fn gana(&self) -> Gana {
437 Gana::Encampment
438 }
439 fn effects(&self) -> &EffectRow {
440 &self.effects
441 }
442 fn description(&self) -> &str {
443 "Poll sensors, evaluate reflex rules, and execute triggered actuator commands (gated, logged)"
444 }
445 async fn call(&self, _ctx: &mut Context, args: Value) -> wm_core::Result<Value> {
446 let health_score = args
447 .get("health_score")
448 .and_then(Value::as_f64)
449 .unwrap_or(0.8) as f32;
450
451 let mut runner = wm_cognitive::AutonomousCycleRunner::default();
452 let cycle_ctx =
453 wm_cognitive::CycleContext::new(&self.store, &self.associations, health_score)
454 .with_sensorimotor(&self.sensorimotor_bus, &self.reflex_loop);
455
456 let result = runner.run_cycle(wm_cognitive::CycleType::Sensorimotor, &cycle_ctx);
457
458 if let Ok(mut tracker) = self.spiral_tracker.lock() {
459 tracker.record(&result);
460 }
461
462 Ok(json!({
463 "status": "success",
464 "cycle": result.cycle.name(),
465 "cycle_status": format!("{:?}", result.status),
466 "purpose": result.purpose,
467 "memories_scanned": result.memories_scanned,
468 "proposals_generated": result.proposals_generated,
469 "duration_ms": result.duration_ms,
470 "requires_human_review": false,
471 "notes": result.notes,
472 "sensorimotor": result.sensorimotor,
473 }))
474 }
475 fn stats(&self) -> &ToolStats {
476 &self.stats
477 }
478}