Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
7d9397b
feat: expand SessionControl/SessionMessage/SessionHistory to Expanded…
Jul 13, 2026
210054e
fix: resolve pre-existing deprecation warnings
Jul 13, 2026
fa3848a
feat: add session tools to Team mode and shared coding modes, dynamic…
Jul 13, 2026
10e29c5
feat: legion preset storage + ACP agents in core zone
Jul 13, 2026
acb23f3
feat: legion commander prompt + HookResult::Abort guard framework
Jul 13, 2026
3210f45
feat: legion UI components + 18 orchestration patterns
Jul 13, 2026
a009d65
fix-unsafe-warnings
Jul 13, 2026
432777c
fix-legion-list
Jul 13, 2026
08e0701
fix-legion-topo-skip-conditional-edges
Jul 13, 2026
3ac6d32
acp-mode-and-legion-topo-fixes
Jul 13, 2026
44b5c16
fix-acp-duplicate-tool-name
Jul 13, 2026
d805b7d
a
Jul 13, 2026
46d8539
feat: LegionControl auto-send + Gate Loop Protocol + behavior guard s…
Jul 13, 2026
23af21c
fix: LegionControl send_initial_message scope and metadata type
Jul 13, 2026
d70d189
fix: replace deprecated theme color tokens in LegionCard.scss
Jul 15, 2026
e47222c
fix: add default impl for behavior_guard() in trait for backward comp…
Jul 15, 2026
b0db461
chore: commit pending fixes - path security, rollback, i18n integrati…
Jul 15, 2026
42a8bd9
feat: expand SessionControl/SessionMessage/SessionHistory to Expanded…
Jul 13, 2026
7977e4f
fix: resolve pre-existing deprecation warnings
Jul 13, 2026
24503c3
feat: add session tools to Team mode and shared coding modes, dynamic…
Jul 13, 2026
13def22
feat: legion preset storage + ACP agents in core zone
Jul 13, 2026
a90dff1
feat: legion commander prompt + HookResult::Abort guard framework
Jul 13, 2026
56e6980
feat: legion UI components + 18 orchestration patterns
Jul 13, 2026
185eb8c
fix-unsafe-warnings
Jul 13, 2026
47c93f0
fix-legion-list
Jul 13, 2026
227f074
fix-legion-topo-skip-conditional-edges
Jul 13, 2026
7ca13e6
acp-mode-and-legion-topo-fixes
Jul 13, 2026
9e63ba9
fix-acp-duplicate-tool-name
Jul 13, 2026
d04ed00
a
Jul 13, 2026
d146bb5
feat: LegionControl auto-send + Gate Loop Protocol + behavior guard s…
Jul 13, 2026
f56e043
fix: LegionControl send_initial_message scope and metadata type
Jul 13, 2026
0ffd889
fix: replace deprecated theme color tokens in LegionCard.scss
Jul 15, 2026
78cf860
fix: add default impl for behavior_guard() in trait for backward comp…
Jul 15, 2026
89c73a1
chore: commit pending fixes - path security, rollback, i18n integrati…
Jul 15, 2026
d549781
fix: resolve GetToolSpec duplicate + update registry migration guard …
Jul 16, 2026
09285db
fix: ToolExposure::Expanded -> Direct after rebase to main
Jul 17, 2026
54c4ea0
fix: remove redundant unsafe block in register_message_handler
Jul 17, 2026
6222c57
fix: remove redundant unsafe blocks in windows_capture and windows_wg…
Jul 17, 2026
e73da53
fix: remove unnecessary mut in browser_api release build
Jul 17, 2026
fdf1375
feat(hooks): add Stop hook with B01/C01 dual-bee round-level monitoring
Jul 17, 2026
efaf64b
fix: add remote workspace policy for legion preset commands
Jul 17, 2026
d335a52
ci: trigger workflow run
Jul 17, 2026
75b80ac
feat: bee-colony review hook + DAG MiniApp + team mode verification
Jul 18, 2026
d769535
Merge branch 'feature/bee-colony-review-hook' into feature/agentic-dy…
Jul 18, 2026
8308ba3
fix: remove stray braces from unsafe-block cleanup in computer_use
Jul 18, 2026
f264a52
merge: resolve AgentsScene.tsx import conflict, keep both feature and…
Jul 18, 2026
63cc914
fix: prevent bee-review context accumulation and remove redundant uns…
Jul 18, 2026
45131ea
chore: remove temporary scripts, hook-dump, and planning docs
Jul 18, 2026
715e24a
fix: add cfg(target_os) guards to suppress dead_code warnings on Windows
Jul 18, 2026
01e68ad
PR #1612: Hook framework enhancement — bee-review reminder-only mode
Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/apps/cli/src/daemon/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,15 @@ fn render_launch_agent(executable: &Path) -> String {
)
}

#[cfg(target_os = "linux")]
fn run_command(program: &str, args: &[&str]) -> Result<std::process::Output> {
std::process::Command::new(program)
.args(args)
.output()
.with_context(|| format!("run `{program} {}`", args.join(" ")))
}

#[cfg(target_os = "linux")]
fn ensure_success(program: &str, args: &[&str]) -> Result<()> {
let output = run_command(program, args)?;
if output.status.success() {
Expand Down
27 changes: 27 additions & 0 deletions src/apps/desktop/src/api/agentic_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2497,6 +2497,33 @@ fn system_time_to_unix_secs(time: std::time::SystemTime) -> u64 {
}
}

// ── Legion preset commands ──────────────────────────────────────────

#[tauri::command]
pub async fn list_legion_presets() -> Result<Vec<bitfun_core::agentic::agents::team_presets::LegionPreset>, String> {
bitfun_core::agentic::agents::team_presets::list_presets()
}

#[tauri::command]
pub async fn get_legion_preset(id: String) -> Result<bitfun_core::agentic::agents::team_presets::LegionPreset, String> {
bitfun_core::agentic::agents::team_presets::get_preset(&id)
}

#[tauri::command]
pub async fn create_legion_preset(preset: bitfun_core::agentic::agents::team_presets::LegionPreset) -> Result<(), String> {
bitfun_core::agentic::agents::team_presets::create_preset(&preset)
}

#[tauri::command]
pub async fn update_legion_preset(preset: bitfun_core::agentic::agents::team_presets::LegionPreset) -> Result<(), String> {
bitfun_core::agentic::agents::team_presets::update_preset(&preset)
}

#[tauri::command]
pub async fn delete_legion_preset(id: String) -> Result<(), String> {
bitfun_core::agentic::agents::team_presets::delete_preset(&id)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
6 changes: 2 additions & 4 deletions src/apps/desktop/src/api/browser_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,14 @@ pub async fn browser_webview_create(
let window = app
.get_window("main")
.ok_or_else(|| "main window not found".to_string())?;
let mut builder =
let builder =
tauri::webview::WebviewBuilder::new(request.label, tauri::WebviewUrl::External(url))
.initialization_script(video_decoder_compatibility_script())
.transparent(false)
.background_color(tauri::window::Color(0, 0, 0, 255));

#[cfg(any(debug_assertions, feature = "devtools"))]
{
builder = builder.devtools(true);
}
let builder = builder.devtools(true);

window
.add_child(
Expand Down
20 changes: 20 additions & 0 deletions src/apps/desktop/src/api/remote_workspace_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ pub const REMOTE_WORKSPACE_COMMAND_POLICIES: &[(&str, RemoteWorkspacePolicy)] =
),
("create_directory", RemoteWorkspacePolicy::LegacyUnaudited),
("create_file", RemoteWorkspacePolicy::LegacyUnaudited),
(
"create_legion_preset",
RemoteWorkspacePolicy::WorkspaceAgnostic,
),
("create_miniapp", RemoteWorkspacePolicy::LegacyUnaudited),
("create_session", RemoteWorkspacePolicy::LegacyUnaudited),
("create_subagent", RemoteWorkspacePolicy::LegacyUnaudited),
Expand Down Expand Up @@ -305,6 +309,10 @@ pub const REMOTE_WORKSPACE_COMMAND_POLICIES: &[(&str, RemoteWorkspacePolicy)] =
),
("delete_directory", RemoteWorkspacePolicy::LegacyUnaudited),
("delete_file", RemoteWorkspacePolicy::LegacyUnaudited),
(
"delete_legion_preset",
RemoteWorkspacePolicy::WorkspaceAgnostic,
),
("delete_mcp_server", RemoteWorkspacePolicy::LegacyUnaudited),
("delete_miniapp", RemoteWorkspacePolicy::LegacyUnaudited),
(
Expand Down Expand Up @@ -477,6 +485,10 @@ pub const REMOTE_WORKSPACE_COMMAND_POLICIES: &[(&str, RemoteWorkspacePolicy)] =
"get_latest_insights",
RemoteWorkspacePolicy::LegacyUnaudited,
),
(
"get_legion_preset",
RemoteWorkspacePolicy::WorkspaceAgnostic,
),
("get_mcp_prompt", RemoteWorkspacePolicy::LegacyUnaudited),
(
"get_mcp_remote_oauth_session",
Expand Down Expand Up @@ -727,6 +739,10 @@ pub const REMOTE_WORKSPACE_COMMAND_POLICIES: &[(&str, RemoteWorkspacePolicy)] =
"list_directory_files",
RemoteWorkspacePolicy::LegacyUnaudited,
),
(
"list_legion_presets",
RemoteWorkspacePolicy::WorkspaceAgnostic,
),
(
"list_manageable_subagents",
RemoteWorkspacePolicy::RemoteRouted,
Expand Down Expand Up @@ -1527,6 +1543,10 @@ pub const REMOTE_WORKSPACE_COMMAND_POLICIES: &[(&str, RemoteWorkspacePolicy)] =
RemoteWorkspacePolicy::WorkspaceAgnostic,
),
("update_cron_job", RemoteWorkspacePolicy::LegacyUnaudited),
(
"update_legion_preset",
RemoteWorkspacePolicy::WorkspaceAgnostic,
),
(
"update_custom_agent",
RemoteWorkspacePolicy::LegacyUnaudited,
Expand Down
5 changes: 5 additions & 0 deletions src/apps/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,11 @@ pub async fn run() {
api::agentic_api::generate_session_title,
api::agentic_api::get_available_modes,
api::agentic_api::get_default_review_team_definition,
api::agentic_api::list_legion_presets,
api::agentic_api::get_legion_preset,
api::agentic_api::create_legion_preset,
api::agentic_api::update_legion_preset,
api::agentic_api::delete_legion_preset,
api::btw_api::btw_ask_stream,
api::btw_api::btw_cancel,
api::editor_ai_api::editor_ai_stream,
Expand Down
23 changes: 22 additions & 1 deletion src/crates/adapters/ai-adapters/src/providers/openai/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,28 @@ pub(crate) fn convert_tools_flat(
tools: Option<Vec<ToolDefinition>>,
) -> Option<Vec<serde_json::Value>> {
tools.map(|defs| {
defs.into_iter()
let mut seen = std::collections::HashSet::new();
let mut dupes = Vec::new();
let filtered: Vec<ToolDefinition> = defs
.into_iter()
.filter(|tool| {
if !seen.insert(tool.name.clone()) {
dupes.push(tool.name.clone());
false
} else {
true
}
})
.collect();
if !dupes.is_empty() {
log::error!(
target: "ai::openai_stream_request",
"DUPLICATE TOOL NAMES detected and deduplicated: {:?}",
dupes
);
}
filtered
.into_iter()
.map(|tool| {
serde_json::json!({
"type": "function",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,27 @@ impl OpenAIMessageConverter {

pub fn convert_tools(tools: Option<Vec<ToolDefinition>>) -> Option<Vec<Value>> {
tools.map(|tool_defs| {
tool_defs
let mut seen = std::collections::HashSet::new();
let mut dupes = Vec::new();
let filtered: Vec<ToolDefinition> = tool_defs
.into_iter()
.filter(|tool| {
if !seen.insert(tool.name.clone()) {
dupes.push(tool.name.clone());
false
} else {
true
}
})
.collect();
if !dupes.is_empty() {
log::error!(
target: "ai::openai_stream_request",
"DUPLICATE TOOL NAMES detected and deduplicated: {:?}",
dupes
);
}
filtered
.into_iter()
.map(|tool| {
json!({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ impl TeamMode {
"Git".to_string(),
"ControlHub".to_string(),
"GetFileDiff".to_string(),
"SessionControl".to_string(),
"SessionMessage".to_string(),
"SessionHistory".to_string(),
"get_goal".to_string(),
"create_goal".to_string(),
"update_goal".to_string(),
"LegionControl".to_string(),
],
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! ACP bridge agent — an AgentRegistry entry for every configured ACP client.
//!
//! Each ACP client (OpenCode, Claude Code, CodeBuddy, etc.) is represented as a
//! `SubAgent` so it appears in the agent selector and can be targeted by
//! `SessionControl` / `SessionMessage` for legion orchestration.

use crate::agentic::agents::{Agent, UserContextPolicy};
use async_trait::async_trait;

/// A thin Agent wrapper around a single ACP client config.
#[allow(dead_code)]
pub struct AcpAgent {
agent_id: String,
display_name: String,
default_tools: Vec<String>,
}

impl AcpAgent {
pub fn new(client_id: String, display_name: String) -> Self {
let agent_id = Self::agent_id_for(&client_id);
Self {
// ACP prompt tool is registered in the global tool registry by
// register_configured_tools() — do NOT add it to default_tools
// here, or the tool name will appear twice in the model manifest.
default_tools: vec![
"Read".to_string(),
"Grep".to_string(),
"Glob".to_string(),
"LS".to_string(),
],
agent_id,
display_name,
}
}

/// The agent registry id: `acp__<client_id>`
pub fn agent_id_for(client_id: &str) -> String {
format!("acp__{client_id}")
}
}

#[async_trait]
impl Agent for AcpAgent {
fn as_any(&self) -> &dyn std::any::Any {
self
}

fn id(&self) -> &str {
&self.agent_id
}

fn name(&self) -> &str {
&self.display_name
}

fn description(&self) -> &str {
"ACP agent"
}

fn prompt_template_name(&self, _model_name: Option<&str>) -> &str {
"acp_agent"
}

fn default_tools(&self) -> Vec<String> {
self.default_tools.clone()
}

fn user_context_policy(&self) -> UserContextPolicy {
UserContextPolicy::empty()
.with_workspace_context()
.with_workspace_instructions()
}

fn is_readonly(&self) -> bool {
false
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod acp_agent;
mod computer_use;
mod explore;
mod file_finder;
mod general_purpose;
mod research_specialist;

pub use acp_agent::AcpAgent;
pub use computer_use::ComputerUseMode;
pub use explore::ExploreAgent;
pub use file_finder::FileFinderAgent;
Expand Down
6 changes: 5 additions & 1 deletion src/crates/assembly/core/src/agentic/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod definitions;
mod prompt_builder;
mod registry;
pub mod team_presets;

use crate::agentic::session::{SystemPromptCacheIdentity, UserContextCacheIdentity};
use crate::agentic::tools::framework::ToolExposure;
Expand Down Expand Up @@ -35,7 +36,8 @@ pub use definitions::review::{
};
pub use definitions::shared::ReadonlySubagent;
pub use definitions::subagents::{
ComputerUseMode, ExploreAgent, FileFinderAgent, GeneralPurposeAgent, ResearchSpecialistAgent,
AcpAgent, ComputerUseMode, ExploreAgent, FileFinderAgent, GeneralPurposeAgent,
ResearchSpecialistAgent,
};
use indexmap::IndexMap;
pub use prompt_builder::{
Expand Down Expand Up @@ -132,6 +134,8 @@ pub fn shared_coding_mode_tools() -> Vec<String> {
"ReviewPlatform".to_string(),
"ControlHub".to_string(),
"InitMiniApp".to_string(),
"SessionMessage".to_string(),
"SessionHistory".to_string(),
];
append_provider_group_tools(&mut tools, "core.canvas");
tools
Expand Down
17 changes: 17 additions & 0 deletions src/crates/assembly/core/src/agentic/agents/prompts/acp_agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
You are a bridge to an external ACP agent running inside BitFun. A commander has delegated a task to you. Your job is to forward the task through your ACP tool and return the result, nothing more.

## How You Work

1. Read the task that was sent to you via SessionMessage.
2. Call your ACP prompt tool with the task as the `prompt` parameter.
3. Return the ACP agent's response exactly as received — do not summarise, reinterpret, or embellish.
4. If the ACP tool returns an error, report the error with the original task context so the commander can decide how to proceed.

## Constraints

- You do NOT have file write or edit capabilities by default. Your only execution tool is the ACP bridge.
- Do NOT ask the user questions. The commander is your only audience.
- Be concise. The commander is managing many agents and needs clear, direct responses.
- Do NOT pretend to perform work that should be delegated through the ACP tool.

{LANGUAGE_PREFERENCE}
Loading