Skip to content

Commit 697dadc

Browse files
committed
feat: add Phase 9 swarm + Phase 11 subsystems + TUI integration
Phase 9 — Swarm/Team (6 new files): - swarm/backend.rs: SwarmBackend trait + InProcess/Tmux implementations - swarm/pane_manager.rs: tmux pane lifecycle management - swarm/teammate.rs: sub-agent state machine (Idle/Running/Stopped) - swarm/permission_sync.rs: cross-agent permission broadcast - swarm/init_script.rs: agent-mode shell script generation Phase 11 — Missing subsystems (7 new files): - computer_use/: screenshot/input/window stubs (CCB has no impl) - buddy/: sprite/personality/notification (seed-based ASCII art) - auto_dream.rs: background memory consolidation config - completions.rs: shell completion generation - deep_link.rs: crab-cli:// URL scheme parsing - installer.rs: package manager detection + install TUI integration: - session_sidebar wired into app layout with toggle - context_collapse integrated for message folding - output_styles used in tool output rendering
1 parent e9cb01f commit 697dadc

File tree

24 files changed

+3096
-16
lines changed

24 files changed

+3096
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/agent/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ crab-session.workspace = true
1515
crab-tools.workspace = true
1616
crab-api.workspace = true
1717
crab-plugin.workspace = true
18+
crab-process.workspace = true
1819
serde.workspace = true
1920
serde_json.workspace = true
2021
tokio.workspace = true

crates/agent/src/auto_dream.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
//! AutoDream — periodic background "dream" cycles for the agent.
2+
//!
3+
//! The dream system is designed to let the agent periodically consolidate
4+
//! context, prune stale information, or run background analysis.
5+
//! The current implementation logs that the cycle was skipped because the
6+
//! feature is not yet fully integrated.
7+
8+
use std::time::{Duration, Instant};
9+
10+
/// Default interval between dream cycles (10 minutes).
11+
const DEFAULT_INTERVAL: Duration = Duration::from_secs(600);
12+
13+
/// Configuration for the auto-dream subsystem.
14+
#[allow(dead_code)]
15+
#[derive(Debug, Clone)]
16+
pub struct AutoDreamConfig {
17+
/// How often a dream cycle should trigger.
18+
pub interval: Duration,
19+
/// Whether auto-dreaming is enabled.
20+
pub enabled: bool,
21+
}
22+
23+
impl AutoDreamConfig {
24+
/// Create a new configuration with the given interval.
25+
#[allow(dead_code)]
26+
pub fn new(interval: Duration, enabled: bool) -> Self {
27+
Self { interval, enabled }
28+
}
29+
}
30+
31+
impl Default for AutoDreamConfig {
32+
fn default() -> Self {
33+
Self {
34+
interval: DEFAULT_INTERVAL,
35+
enabled: false,
36+
}
37+
}
38+
}
39+
40+
/// Runtime state for the auto-dream subsystem.
41+
#[allow(dead_code)]
42+
#[derive(Debug)]
43+
pub struct AutoDream {
44+
/// Configuration.
45+
config: AutoDreamConfig,
46+
/// Timestamp of the last completed dream cycle.
47+
last_dream: Option<Instant>,
48+
/// Total number of dream cycles executed.
49+
cycle_count: u64,
50+
}
51+
52+
#[allow(dead_code)]
53+
impl AutoDream {
54+
/// Create a new `AutoDream` instance with the given configuration.
55+
pub fn new(config: AutoDreamConfig) -> Self {
56+
Self {
57+
config,
58+
last_dream: None,
59+
cycle_count: 0,
60+
}
61+
}
62+
63+
/// Check whether enough time has passed since the last dream cycle.
64+
///
65+
/// Returns `true` if auto-dream is enabled and the interval has elapsed
66+
/// (or no dream has ever been run).
67+
pub fn should_dream(&self) -> bool {
68+
if !self.config.enabled {
69+
return false;
70+
}
71+
match self.last_dream {
72+
Some(last) => last.elapsed() >= self.config.interval,
73+
None => true,
74+
}
75+
}
76+
77+
/// Run a dream cycle.
78+
///
79+
/// Currently logs that the cycle was skipped because the feature is
80+
/// not yet integrated. Updates internal bookkeeping regardless.
81+
pub fn run_dream_cycle(&mut self) {
82+
tracing::info!(
83+
cycle = self.cycle_count + 1,
84+
"dream cycle skipped (not yet integrated)"
85+
);
86+
self.last_dream = Some(Instant::now());
87+
self.cycle_count += 1;
88+
}
89+
90+
/// The number of dream cycles that have been executed.
91+
pub fn cycle_count(&self) -> u64 {
92+
self.cycle_count
93+
}
94+
95+
/// Whether auto-dream is enabled.
96+
pub fn is_enabled(&self) -> bool {
97+
self.config.enabled
98+
}
99+
100+
/// Access the current configuration.
101+
pub fn config(&self) -> &AutoDreamConfig {
102+
&self.config
103+
}
104+
105+
/// Update the configuration at runtime.
106+
pub fn set_config(&mut self, config: AutoDreamConfig) {
107+
self.config = config;
108+
}
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[test]
116+
fn default_config_is_disabled() {
117+
let config = AutoDreamConfig::default();
118+
assert!(!config.enabled);
119+
assert_eq!(config.interval, DEFAULT_INTERVAL);
120+
}
121+
122+
#[test]
123+
fn should_dream_when_enabled_and_never_run() {
124+
let config = AutoDreamConfig::new(Duration::from_secs(1), true);
125+
let dream = AutoDream::new(config);
126+
assert!(dream.should_dream());
127+
}
128+
129+
#[test]
130+
fn should_not_dream_when_disabled() {
131+
let config = AutoDreamConfig::new(Duration::from_secs(0), false);
132+
let dream = AutoDream::new(config);
133+
assert!(!dream.should_dream());
134+
}
135+
136+
#[test]
137+
fn should_not_dream_immediately_after_cycle() {
138+
let config = AutoDreamConfig::new(Duration::from_secs(3600), true);
139+
let mut dream = AutoDream::new(config);
140+
dream.run_dream_cycle();
141+
assert!(!dream.should_dream());
142+
}
143+
144+
#[test]
145+
fn should_dream_after_zero_interval_cycle() {
146+
let config = AutoDreamConfig::new(Duration::ZERO, true);
147+
let mut dream = AutoDream::new(config);
148+
dream.run_dream_cycle();
149+
// With zero interval, should_dream should be true immediately.
150+
assert!(dream.should_dream());
151+
}
152+
153+
#[test]
154+
fn cycle_count_increments() {
155+
let config = AutoDreamConfig::new(Duration::from_secs(1), true);
156+
let mut dream = AutoDream::new(config);
157+
assert_eq!(dream.cycle_count(), 0);
158+
dream.run_dream_cycle();
159+
assert_eq!(dream.cycle_count(), 1);
160+
dream.run_dream_cycle();
161+
assert_eq!(dream.cycle_count(), 2);
162+
}
163+
164+
#[test]
165+
fn set_config_updates_state() {
166+
let mut dream = AutoDream::new(AutoDreamConfig::default());
167+
assert!(!dream.is_enabled());
168+
169+
dream.set_config(AutoDreamConfig::new(Duration::from_secs(30), true));
170+
assert!(dream.is_enabled());
171+
assert_eq!(dream.config().interval, Duration::from_secs(30));
172+
}
173+
}

crates/agent/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod rollback;
1313
pub mod slash_commands;
1414
pub mod stop_hooks;
1515
pub mod summarizer;
16+
pub mod swarm;
1617
pub mod system_prompt;
1718
pub mod task;
1819
pub mod team;
@@ -39,6 +40,10 @@ pub use stop_hooks::{StopConditions, StopReason};
3940
pub use summarizer::{
4041
ConversationSummary, SummarizerConfig, SummaryItem, SummaryItemKind, summarize_conversation,
4142
};
43+
pub use swarm::{
44+
InProcessBackend, PaneInfo, PaneManager, PermissionDecisionEvent, PermissionSyncManager,
45+
SwarmBackend, Teammate, TeammateConfig, TeammateState, TmuxBackend, generate_init_script,
46+
};
4247
pub use system_prompt::{build_system_prompt, build_system_prompt_with_memories};
4348
pub use task::{SharedTaskList, Task, TaskList, TaskStatus, shared_task_list};
4449
pub use team::{Capability, Team, TeamMember, TeamMode};

0 commit comments

Comments
 (0)