Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 8 additions & 8 deletions docs/interactive-capabilities/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!-- generated by scripts/generate-interactive-capabilities.mjs; do not edit -->
# OpenBitFun 功能与设置目录 / OpenBitFun Features & Settings

OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**319 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` Agent 工具。
OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**320 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` Agent 工具。

OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **319** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool.
OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **320** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool.

## 唯一事实源 / Single source of truth

Expand All @@ -27,20 +27,20 @@ OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, a
- Generated per-item interaction audit: `docs/interactive-capabilities/technical/product-control-open-audit.json`
- Generated low-level audit map: `docs/interactive-capabilities/technical/tauri-command-map.json`

说明书、网站、搜索和 Agent 只看“功能 + 设置 + 子能力”。每项子能力都必须引用已注册 Tauri Command 或可解析的源码标记;这些证据不会进入公开目录。当前 **645** 个 Tauri 命令只用于实现覆盖审计。产品 UI 交互源码会在生成和检查时扫描并校验,但不会保存成随普通 UI 改动频繁变化的版本化快照。
说明书、网站、搜索和 Agent 只看“功能 + 设置 + 子能力”。每项子能力都必须引用已注册 Tauri Command 或可解析的源码标记;这些证据不会进入公开目录。当前 **646** 个 Tauri 命令只用于实现覆盖审计。产品 UI 交互源码会在生成和检查时扫描并校验,但不会保存成随普通 UI 改动频繁变化的版本化快照。

Docs, website, search, and agents see only features, settings, and documented sub-capabilities. Every sub-capability must reference a registered Tauri command or a resolvable source marker; evidence is stripped from public projections. The **645** Tauri commands remain implementation-audit evidence only. Product UI interaction sources are scanned and validated during generation and checks, but are not stored as a versioned snapshot that churns with ordinary UI changes.
Docs, website, search, and agents see only features, settings, and documented sub-capabilities. Every sub-capability must reference a registered Tauri command or a resolvable source marker; evidence is stripped from public projections. The **646** Tauri commands remain implementation-audit evidence only. Product UI interaction sources are scanned and validated during generation and checks, but are not stored as a versioned snapshot that churns with ordinary UI changes.

## 控制边界 / Control boundary

- 每个子能力都明确标记为直接控制、委托给专用 Agent 工具、需交互打开或不支持;“打开页面”不会再被统计成“Agent 已控制”。当前覆盖:直接 **48**、委托 **61**、需交互 **210**、不支持 **0**。
- 每个子能力都明确标记为直接控制、委托给专用 Agent 工具、需交互打开或不支持;“打开页面”不会再被统计成“Agent 已控制”。当前覆盖:直接 **48**、委托 **61**、需交互 **211**、不支持 **0**。
- 稳定行为声明为带 JSON 输入契约的 `operations` 或 `options`,并绑定原生产品控制 Provider;Agent 不接触原始 Tauri Command。
- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 319 项子能力都不会写入 system prompt。
- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 320 项子能力都不会写入 system prompt。
- 目录发现与契约读取不依赖 React 或可见窗口。普通配置型 option 统一由 Product Assembly 的共享 ConfigService 执行器读、写并回读,因此 Desktop、CLI 与 Headless 表面走同一份实现;只有宿主原生 operation/provider option 和界面导航按表面注册适配器,缺失时必须明确返回不可用,禁止静默回退本机。只读 Agent 只能发现和读取目录。

- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **48 direct**, **61 delegated**, **210 interactive**, and **0 unsupported**.
- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **48 direct**, **61 delegated**, **211 interactive**, and **0 unsupported**.
- Stable behavior becomes a typed `operation` or `option` with a JSON input contract and a native product-control provider. Agents never receive raw Tauri commands.
- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 319 documented items enters the system prompt.
- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 320 documented items enters the system prompt.
- Discovery and contract lookup do not depend on React or a visible window. Ordinary config-backed options are read, written, and read back by one Product Assembly ConfigService executor shared by Desktop, CLI, and headless surfaces. Only host-native operations/provider options and presentation routes install surface adapters; missing adapters return explicit unavailability without local fallback. Read-only agents may only discover and inspect entries.

## 防腐化门禁 / Anti-drift gates
Expand Down
66 changes: 63 additions & 3 deletions docs/interactive-capabilities/capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "OpenBitFun Playbook",
"origin": "https://playbook.openbitfun.com",
"source": "src/shared/interactive-capabilities/catalog.json",
"digest": "79584f59ffb49edb846b0beacc31f5393d81c3f117f3630445ab034c27546736",
"digest": "1230aa44c72c2cfb44fd28cb3da60fe04548cefd323b610b28e44bf48d574773",
"ownerDigest": "c0e5c187cf62bc6ed06196ce8520b3eb427bf268cf24659b72d2552fb1d99c54",
"searchAcceptance": [
{
Expand Down Expand Up @@ -138,11 +138,11 @@
"features": 22,
"settings": 21,
"userFacing": 43,
"documentedItems": 319,
"documentedItems": 320,
"controlCoverage": {
"direct": 48,
"delegated": 61,
"interactive": 210,
"interactive": 211,
"unsupported": 0
}
},
Expand Down Expand Up @@ -197,6 +197,7 @@
"permission-mailbox",
"history-search",
"agent-session-coordination",
"mcp-mentions",
"tool-activity",
"goals",
"goal-maintenance",
Expand Down Expand Up @@ -618,6 +619,52 @@
"sceneId": "session"
}
},
{
"id": "feature.ai-assistant:open:mcp-mentions",
"capabilityId": "feature.ai-assistant",
"itemIds": [
"mcp-mentions"
],
"kind": "open",
"risk": "ui",
"executionHost": "presentationSurface",
"availability": {
"desktop": {
"available": true
},
"cli": {
"available": false,
"reason": "This delivery profile has no live presentation surface"
},
"peer": {
"available": true,
"requiredCapabilities": [
"product_control_v1",
"product_control_presentation_v1"
]
},
"remoteControl": {
"available": true
},
"detachedDispatch": {
"available": false,
"reason": "This delivery profile has no live presentation surface"
}
},
"inputSchema": {
"type": "object",
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"additionalProperties": true
},
"openReason": "unstructuredInteraction",
"presentationTarget": {
"kind": "scene",
"sceneId": "session"
}
},
{
"id": "feature.ai-assistant:open:tool-activity",
"capabilityId": "feature.ai-assistant",
Expand Down Expand Up @@ -19214,6 +19261,17 @@
]
}
},
{
"id": "mcp-mentions",
"titleZh": "在对话中通过 @ 选择当前模式可用的 MCP 服务",
"titleEn": "Use @ in chat to select MCP servers available to the current mode",
"control": {
"kind": "open",
"reasonCode": "unstructuredInteraction",
"reasonZh": "“在对话中通过 @ 选择当前模式可用的 MCP 服务”需要用户在当前输入框选择服务并编辑草稿,没有可直接完成该交互的结构化 Command。也可通过工具名称或描述搜索所属服务;工具不会单独列出。目录按当前主机、工作区和模式过滤;Minimal 隐藏 MCP 入口。远程工作区暂不支持此选择器,Peer Host 必须协商 chat_mcp_catalog_v1 能力。",
"reasonEn": "“Use @ in chat to select MCP servers available to the current mode” requires the user to select a server and edit the current composer draft; no structured Command completes that interaction directly. Tool names and descriptions can find their owning server without separate tool rows. The catalog is scoped to the current host, workspace, and mode; Minimal hides MCP. Remote workspaces are unsupported; Peer Hosts must advertise chat_mcp_catalog_v1."
}
},
{
"id": "tool-activity",
"titleZh": "查看工具调用与后台命令,读取输出并继续输入或停止",
Expand Down Expand Up @@ -19396,6 +19454,8 @@
"Page through, search, and backfill historical turns and session content",
"让 Agent 创建、列出、取消、删除会话或向另一会话发送消息",
"Let an agent create, list, cancel, or delete sessions and send messages to another session",
"在对话中通过 @ 选择当前模式可用的 MCP 服务",
"Use @ in chat to select MCP servers available to the current mode",
"查看工具调用与后台命令,读取输出并继续输入或停止",
"Inspect tool calls and background commands, read output, send input, or stop them",
"查看、创建并完成或阻塞当前会话目标",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Work with AI in project context, create sessions, continue tasks, answer permiss
- Page through, search, and backfill historical turns and session content
- **由专用 Agent 工具控制 / Delegated Agent tool** · `SessionControl` / `SessionMessage` · 让 Agent 创建、列出、取消、删除会话或向另一会话发送消息
- Let an agent create, list, cancel, or delete sessions and send messages to another session
- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 在对话中通过 @ 选择当前模式可用的 MCP 服务
- Use @ in chat to select MCP servers available to the current mode
- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 查看工具调用与后台命令,读取输出并继续输入或停止
- Inspect tool calls and background commands, read output, send input, or stop them
- **由专用 Agent 工具控制 / Delegated Agent tool** · `get_goal` / `create_goal` / `update_goal` · 查看、创建并完成或阻塞当前会话目标
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"schemaVersion": 1,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "79584f59ffb49edb846b0beacc31f5393d81c3f117f3630445ab034c27546736",
"count": 210,
"catalogDigest": "1230aa44c72c2cfb44fd28cb3da60fe04548cefd323b610b28e44bf48d574773",
"count": 211,
"reasonCounts": {
"externalAuth": 4,
"secretEntry": 5,
"unstructuredInteraction": 183,
"unstructuredInteraction": 184,
"visualSelection": 18
},
"entries": [
Expand Down Expand Up @@ -96,6 +96,24 @@
"command:submit_user_answers"
]
},
{
"capabilityId": "feature.ai-assistant",
"itemId": "mcp-mentions",
"titleZh": "在对话中通过 @ 选择当前模式可用的 MCP 服务",
"titleEn": "Use @ in chat to select MCP servers available to the current mode",
"reasonCode": "unstructuredInteraction",
"reasonZh": "“在对话中通过 @ 选择当前模式可用的 MCP 服务”需要用户在当前输入框选择服务并编辑草稿,没有可直接完成该交互的结构化 Command。也可通过工具名称或描述搜索所属服务;工具不会单独列出。目录按当前主机、工作区和模式过滤;Minimal 隐藏 MCP 入口。远程工作区暂不支持此选择器,Peer Host 必须协商 chat_mcp_catalog_v1 能力。",
"reasonEn": "“Use @ in chat to select MCP servers available to the current mode” requires the user to select a server and edit the current composer draft; no structured Command completes that interaction directly. Tool names and descriptions can find their owning server without separate tool rows. The catalog is scoped to the current host, workspace, and mode; Minimal hides MCP. Remote workspaces are unsupported; Peer Hosts must advertise chat_mcp_catalog_v1.",
"presentationTarget": {
"kind": "scene",
"sceneId": "session"
},
"evidence": [
"command:get_chat_mcp_catalog",
"source:src/web-ui/src/flow_chat/components/ChatContextPicker.tsx#onSelectMcp",
"source:src/web-ui/src/flow_chat/components/ChatInput.tsx#selectContextMcp"
]
},
{
"capabilityId": "feature.ai-assistant",
"itemId": "tool-activity",
Expand Down
24 changes: 20 additions & 4 deletions docs/interactive-capabilities/technical/tauri-command-map.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"schemaVersion": 2,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "79584f59ffb49edb846b0beacc31f5393d81c3f117f3630445ab034c27546736",
"commandCount": 645,
"catalogDigest": "1230aa44c72c2cfb44fd28cb3da60fe04548cefd323b610b28e44bf48d574773",
"commandCount": 646,
"coverage": {
"commandCount": 645,
"documentedCommandCount": 608,
"commandCount": 646,
"documentedCommandCount": 609,
"implementationCommandCount": 37,
"implementationDigest": "3c5e64b6ff16d55d95c2a6b4f77046be009a554b395b4a52bf8ed7c162ab1093"
},
Expand Down Expand Up @@ -2690,6 +2690,22 @@
"signature": "fn get_baseline_snapshot_diff( request: GetBaselineSnapshotDiffRequest, ) -> Result<serde_json::Value, String>",
"remoteWorkspacePolicy": "LegacyUnaudited"
},
{
"id": "get_chat_mcp_catalog",
"moduleId": "tool",
"capabilityId": "feature.ai-assistant",
"capabilityIds": [
"feature.ai-assistant"
],
"documentedItemIds": [
"feature.ai-assistant:mcp-mentions"
],
"visibility": "documented",
"rustPath": "get_chat_mcp_catalog",
"sourceFile": "src/apps/desktop/src/api/tool_api.rs",
"signature": "fn get_chat_mcp_catalog( request: openbitfun_core::agentic::tools::product_runtime::ChatMcpCatalogRequest, ) -> Result<openbitfun_core::agentic::tools::product_runtime::ChatMcpCatalog, String>",
"remoteWorkspacePolicy": "RemoteUnsupported"
},
{
"id": "get_clipboard_files",
"moduleId": "clipboard_file",
Expand Down
35 changes: 32 additions & 3 deletions src/apps/cli/src/dispatch/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ mod tests {
#[cfg(unix)]
#[test]
fn cancellation_does_not_signal_an_unverified_group_after_leader_exit() {
use std::io::Write;

struct ProcessGroupGuard(i32);
impl Drop for ProcessGroupGuard {
fn drop(&mut self) {
Expand All @@ -440,16 +442,43 @@ mod tests {
}
}

let mut command = Command::new("/bin/sh");
let ready_dir = tempfile::tempdir().expect("ready directory");
let ready_path = ready_dir.path().join("hup-resistant-child-ready");
let mut command = openbitfun_services_core::process_manager::create_command("/bin/sh");
command
.args(["-c", "trap '' HUP; sleep 30 &"])
.stdin(Stdio::null())
.arg("-c")
// Keep the leader alive until the child has installed its HUP
// disposition; exiting immediately races child startup on macOS.
.arg(
"trap '' HUP; \
sh -c 'trap \"\" HUP; printf ready > \"$OPENBITFUN_DISPATCH_HUP_TEST_READY\"; \
exec sleep 30' & \
read release",
)
.env("OPENBITFUN_DISPATCH_HUP_TEST_READY", &ready_path)
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null());
configure_detached_process(&mut command);
let mut leader = command.spawn().expect("spawn process-group leader");
let process_group = i32::try_from(leader.id()).expect("safe pid");
let _guard = ProcessGroupGuard(process_group);
for _ in 0..100 {
if ready_path.is_file() {
break;
}
std::thread::sleep(Duration::from_millis(10));
}
assert!(
ready_path.is_file(),
"HUP-resistant child must be ready before the leader exits"
);
leader
.stdin
.take()
.expect("leader release pipe")
.write_all(b"release\n")
.expect("release process-group leader");
leader.wait().expect("reap process-group leader");
assert!(!process_alive(process_group as u32));
assert!(process_group_alive(process_group));
Expand Down
2 changes: 2 additions & 0 deletions src/apps/cli/src/peer_host/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub(crate) async fn dispatch(
// really has no tools". Without this the call fell into the unsupported
// dispatch branch and the UI silently rendered an empty tool list.
"get_all_tools_info" => tools::get_all_tools_info().await,
"get_chat_mcp_catalog" => tools::get_chat_mcp_catalog(args).await,

// Sessions
"list_persisted_sessions" => session::list_persisted_sessions(state, args).await,
Expand Down Expand Up @@ -187,6 +188,7 @@ pub(crate) const HANDLED_COMMANDS: &[&str] = &[
"get_agent_profile_config",
"get_agent_profile_configs",
"get_all_tools_info",
"get_chat_mcp_catalog",
"get_available_modes",
"get_config",
"get_configs",
Expand Down
9 changes: 9 additions & 0 deletions src/apps/cli/src/peer_host/commands/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ use serde_json::Value;

use openbitfun_core::agentic::tools::product_runtime::build_all_tools_info;

pub(crate) async fn get_chat_mcp_catalog(args: &Value) -> Result<Value, String> {
let request = serde_json::from_value(crate::peer_host::args::request_value(args).clone())
.map_err(|error| format!("Invalid MCP catalog request: {error}"))?;
let catalog =
openbitfun_core::agentic::tools::product_runtime::build_chat_mcp_catalog(request).await?;
serde_json::to_value(catalog)
.map_err(|error| format!("Failed to serialize MCP catalog: {error}"))
}

/// Read-only tool catalog for the Agents / Assistant Defaults UI.
///
/// CLI Host assembles the same Core tool registry as Desktop; this returns the
Expand Down
Loading
Loading