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
28 changes: 28 additions & 0 deletions docs/architecture/extensions/external-ai-work-sources-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,34 @@ Command;明确缺失且未被标记失败的 Command 是稳定删除。产品
延后;本机 Node 进程不是 OS 沙箱,VM realm 与隐藏响应令牌只保护协议稳健性。脚本继承当前用户的文件、网络、
环境与进程权限;本阶段没有 Job Object/process group,脚本直接启动的后代进程和系统资源风险必须持续显示。

第三阶段以 OpenCode MCP 配置来源验证声明式外部连接的接纳与生命周期:

1. OpenCode adapter 按冻结版本解释用户全局、自定义文件/目录和项目 JSON/JSONC 的 MCP 合并顺序,只输出脱敏静态
候选;远程地址只展示 HTTPS origin,环境变量只展示变量名,值只在确认后的准备阶段解析。为避免一次确认在环境变化后
改写可执行文件、参数、工作目录或网络目标,本阶段只允许 `{env:NAME}` 出现在显式 environment 或 Header 值中;其他
位置明确标记不支持。展开后再次执行大小、HTTPS 与契约校验。
2. MCP coordinator 与 Command、Tool、Subagent 平行,负责 provider 隔离、last-success、稳定删除和 watcher roots;
审批、同名冲突和 BitFun 原生优先级由产品组装层决定,具体进程、连接、工具注册和回收仍由 MCP owner 负责。
3. 首次启用或命令、参数、工作目录、环境声明、URL origin、Header 名、认证方式等行为变化后重新确认;拒绝在行为变化前不重复
提示。同名候选未选择时保留 BitFun 当前实现,不把显示顺序当成用户决定,也不在外部失效时静默切换。
4. Desktop 在“外部 AI 应用”设置页显示安全摘要、状态、审批和冲突;交互式 TUI 复用 `/mcps`,在同一列表中用来源
标签区分 BitFun 与外部候选,不新增 `external-*` 命令。两端使用同一 generation、preference revision 和操作接口。
5. 已确认候选按规范化 workspace 建立独立运行实例;工具包装器同时校验当前 workspace route,Remote owner 未实现前直接
fail-closed。同名冲突选择外部候选时,只在当前 workspace 隐藏对应 BitFun 原生工具,不停止其他 workspace 的原生实例。
6. 更新、停用、空闲回收或稳定删除先撤下 workspace route 和新工具入口,再有界等待在途连接释放;慢启动在发布连接、
工具和目录前再次检查撤销标记,不能在撤销后重新发布。第三方启动、握手和回收在后台有界执行,不要求全量重启
MCP 或 BitFun,也不中断无关 session。
7. 产品快照区分“正在启动”“已启用”“暂时不可用”。后台启动失败会触发快照更新;暂时不可用的候选仍可由用户停用,
当前恢复入口是先停用该服务器,修复来源配置或认证后再启用;普通刷新不会自动重试失败进程,避免持续拉起故障或恶意
扩展。删除项的一次性用户通知/有界墓碑尚未实现,当前行会在稳定重扫后消失;该展示增强
留待后续 PR,不能改变“先撤 route、禁止新调用”的运行语义。
8. 外部本地进程不继承 BitFun 的完整父进程环境,只保留启动所需的系统基线和配置显式声明的变量;这仍不是 OS
沙箱,进程继续拥有当前用户的文件、网络和子进程权限。Remote 执行域、OpenCode OAuth client 配置、SSE、完整
`timeout`/Agent 范围和通用凭据 owner 明确延后。
9. 本阶段只把外部 MCP 的 Tool 目录接入 Agent Tool owner。通用 Resource/Prompt/MCP App Desktop 接口不接受无工作区
上下文的外部 runtime id;外部服务器发起的 roots、sampling 和 elicitation 请求也 fail-closed,防止跨工作区读取或借用
BitFun 宿主能力。后续若接入这些能力,必须先补独立契约、工作区路由与权限交互,不能复用全局连接绕过当前边界。

验收至少覆盖:

- 项目打开和无关会话不会因发现、解析、依赖准备或确认等待被阻塞。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ PR1 只实现上述本地 Command 子集:XDG 用户配置根、`OPENCODE_CONFI
扫描。`OPENCODE_CONFIG_CONTENT`、远程、组织、系统管理员与 MDM 来源仍保留在目标来源图中,不得在产品状态中误报
为已加载。路径按规范化来源身份去重,显式环境路径与默认路径相同时只保留 OpenCode 顺序中的最后一个阶段。

当前 MCP 子集复用同一来源解释,但只读取本地用户全局配置、`OPENCODE_CONFIG`、root-first 项目配置以及作为全局
晚覆盖的 `OPENCODE_CONFIG_DIR`;设置该目录后不再叠加默认全局目录。`OPENCODE_CONFIG_CONTENT`、远程、组织、
系统管理员和 MDM 来源尚未接入,不能因 MCP 候选可运行就把目标来源图标记为完整实现。

### 3.2 TUI 独立来源顺序

`tui.json/jsonc` 不是主配置来源图的附属字段。稳定版使用独立顺序:
Expand Down Expand Up @@ -185,7 +189,7 @@ OpenCode adapter 在来源发现、解析和审批前不 import module、不读
| Skills | `.opencode/.claude/.agents` 项目与用户根、`SKILL.md`、`skills.paths/urls` | Skill 归属模块复用按需加载并补齐规则顺序 | 说明和索引按需加载;URL、脚本或外部依赖按 L2 确认 | URL 或可执行资源失败只降级对应 Skill。 |
| References | `references` / 旧 `reference`,本地 path 或 Git repository/branch/description/hidden | **基础能力缺失**:先补 Workspace Reference 的异步准备与 `@alias` 消费接口 | 本地引用保留相对来源;Git 拉取按 L2 确认并保留缓存/隐藏语义 | 拉取失败不阻止项目,外部目录仍遵守工具权限。 |
| Commands | JSON/JSONC、Markdown、`$ARGUMENTS`、位置参数、`@file`、`!shell`、agent/model/variant/subtask | Prompt Command 专属契约;OpenCode adapter 保留发现、覆盖、解析和参数展开语义,交互式 TUI(ChatMode)只消费中立定义与展开结果 | PR1 支持 prompt-only 模板,用户显式选择或输入即确认本次发送;未接通的文件、shell、agent/model/variant/subtask 标为部分受限且不做部分执行 | 已知命令文件无效只回退该命令;稳定删除撤下新调用;目录枚举未知时回退对应目录来源,不能把未知当空目录。 |
| MCP | local 的 command/environment/cwd/timeout,remote 的 URL/headers/oauth/timeout,Agent 选择 | MCP 归属模块创建兼容配置视图 | 首次按命令、网络、凭据和作用域确认,再按来源启用状态运行 | 凭据或网络不可用时单个 Server 不可用。 |
| MCP | local 的 command/environment/cwd/timeout,remote 的 URL/headers/oauth/timeout,Agent 选择 | MCP 归属模块创建兼容配置视图 | 当前支持 local stdio 和 HTTPS remote 的静态发现、首次/行为变化审批、冲突选择与 workspace 隔离的运行期接纳;外部本地进程只继承系统启动基线和显式环境 | `{env:NAME}` 当前只允许用于 environment/Header 值;SSE、OpenCode OAuth client 配置、完整 timeout/Agent 范围与 Remote 执行域保持明确不支持;凭据或网络失败只影响单个 Server。 |
| LSP | command、extensions、env、initialization | LSP 归属模块注册兼容实例 | 首次确认外部进程和作用域后按文件类型启动 | 自定义 Server 缺少 extensions 或启动失败时只禁用该项。 |
| Formatters | command、environment、extensions、`$FILE` | **基础能力缺失**:先补文件写入后的 Formatter 执行消费点,再做格式转换 | 首次确认命令后执行匹配 Formatter | 超时后标记未格式化,文件写入结果保留。 |
| Themes | builtin/user/project/cwd JSON | **部分已有**:GUI Theme 已有;TUI 主题消费边界在终端阶段补齐 | 保留覆盖顺序和语义角色 | 颜色能力不支持时做可见降级。 |
Expand Down Expand Up @@ -263,7 +267,12 @@ provider 候选提供 `/external:<provider>:<name>`。一次外部候选选择

这些能力使用 BitFun 原生归属模块,但“原生已有”不自动等于兼容:

- MCP 必须覆盖 local 的 `environment`,remote 的 Headers、OAuth(含 clientId/clientSecret/scope/callbackPort/redirectUri)、超时、启用状态和 Agent 范围。
- MCP 当前覆盖 local 的 `command/environment/cwd/enabled` 与 remote 的 HTTPS URL、Headers、动态 OAuth 开关和
`enabled`,并在批准后按 workspace 交给现有 MCP owner;工具在调用前复核 workspace route,Remote 不回退到本机实例。
远端静态摘要只展示 HTTPS origin,环境引用只展示变量名;为避免审批后通过环境变量替换执行包络,`{env:NAME}` 仅
支持 environment/Header 值,展开后重新校验大小和协议。未配置 `cwd` 时遵循 OpenCode,使用当前 workspace。
外部本地进程默认不继承 BitFun 的完整父进程环境。SSE、OpenCode
`clientId/clientSecret/scope/callbackPort/redirectUri`、完整超时和 Agent 范围仍需后续接入,不能静默忽略。
- LSP 必须覆盖 initialization、扩展名匹配、环境变量和工作区生命周期。
- Formatter 必须覆盖写入后时机、`$FILE` 替换、`environment`、多个 Formatter 顺序和失败行为。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ OpenCode,和 OpenCode 配置/插件进入 BitFun 是两个独立验收方向
| References | 补基础能力 + 转换参数 | 未实现 | 可主要适配 | OC-R2 | R1 解析;R2 支持本地目录和 Git repository/branch/description/hidden,异步准备并接入 `@alias` | [声明式资产](opencode-config-assets-adapter-design.md#5-声明式资产映射) |
| Commands | 补扩展接口 + 转换参数 | 部分实现:prompt-only 子集 | 可完整适配 | OC-R2 | 已支持全局/项目 JSON、JSONC、Markdown 命令、`$ARGUMENTS` 与位置参数,并接入动态命令目录、运行时刷新和显式冲突选择;`@file`、shell、Agent/model/variant/subtask 保持受限且不做部分执行 | [Commands](opencode-config-assets-adapter-design.md#53-commands) |
| Models / Providers 配置 | 融合现有能力 | 未实现 | 可主要适配 | OC-R1 | 静态字段进入模型归属模块;动态模型、鉴权和请求头交给插件运行时 | [声明式资产](opencode-config-assets-adapter-design.md#5-声明式资产映射) |
| MCP | 转换参数 | 未实现 | 可完整适配 | OC-R2 | R1 解析;R2 转换本地命令、远程 URL、Headers、OAuth、超时和 Agent 选择并由 MCP owner 启动 | [MCP、LSP 与 Formatter](opencode-config-assets-adapter-design.md#54-mcplsp-与-formatter) |
| MCP | 转换参数 | 部分实现:local stdio 与 HTTPS remote 子集 | 可完整适配 | OC-R2 | 已支持用户全局、自定义目录/文件和项目 JSON/JSONC 的静态发现、显式审批、原生/外部冲突、workspace 隔离、启动失败回推、更新撤下及现有 MCP owner 运行;环境引用仅用于 environment/Header 值,Remote 不回退本机实例;SSE、OpenCode OAuth client 配置、完整 timeout/Agent 范围和其他配置来源继续明确降级 | [MCP、LSP 与 Formatter](opencode-config-assets-adapter-design.md#54-mcplsp-与-formatter) |
| LSP | 转换参数 | 未实现 | 可完整适配 | OC-R2 | R1 解析;R2 转换 command、extensions、env 和 initialization 并由 LSP owner 启动 | [MCP、LSP 与 Formatter](opencode-config-assets-adapter-design.md#54-mcplsp-与-formatter) |
| Formatters | 补基础能力 + 转换参数 | 未实现 | 可主要适配 | OC-R2 | R1 解析;R2 补文件写入后的格式化执行能力,再映射 command/environment/extensions/`$FILE` | [MCP、LSP 与 Formatter](opencode-config-assets-adapter-design.md#54-mcplsp-与-formatter) |
| Themes | 转换参数 | 未实现 | 可主要适配 | OC-R1 | 保留 builtin/user/project/cwd 覆盖顺序,分别映射 GUI 和 TUI 色彩能力 | [声明式资产](opencode-config-assets-adapter-design.md#5-声明式资产映射) |
Expand Down
10 changes: 7 additions & 3 deletions docs/architecture/extensions/plugin-runtime-host-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,16 @@ BitFun 受管插件包的 P0-C.1/P0-C.2 链路当前只验证了原生来源和

因此,受管包链路只能表述为“来源可识别、静态候选可预览”,不能表述为“OpenCode package plugin 可运行”。
CLI 已允许在包文件缺失或损坏时清理残留启用状态,这是现有链路必须保留的恢复能力。与此独立的外部来源轨道
已经接入 prompt-only Command、受支持的单文件 `.js` standalone Tool 和 Subagent 安全子集;其中 standalone Tool
已经接入 prompt-only Command、受支持的单文件 `.js` standalone Tool、Subagent 安全子集和 OpenCode MCP 配置
安全子集;其中 standalone Tool
通过窄 `ScriptToolRuntime` 真实执行,但这不证明 package plugin、Hook、完整 Client 或 TUI target 已经可运行。
三个切片当前都只支持本地执行域;Remote 外部来源发现、审批与冲突决策明确返回不支持,不回退读取本机来源。
四个切片当前都只支持本地执行域;Remote 外部来源发现、审批与冲突决策明确返回不支持,不回退读取本机来源。MCP
运行实例和工具 route 额外按规范化 workspace 隔离;更新、停用、空闲回收和删除先撤 route,再异步回收连接与进程,
慢启动在发布工具前复核撤销状态。

`bitfun.plugin.json` 继续作为 BitFun 原生包格式。现有外部来源轨道已直接发现用户和项目的 Command、standalone
Tool 与 Subagent 安全子集,包括配置文件中的受支持声明;完整 OpenCode 配置来源图尚未接入。后续 package plugin
Tool、Subagent 与 MCP 安全子集,包括配置文件中的受支持声明;远程、组织、系统管理员、MDM 和内联内容等完整
OpenCode 配置来源图尚未接入。后续 package plugin
路径还需直接发现 `.opencode/plugins` 和软件包配置并记录当前执行版本,不得要求作者先复制到 `.bitfun/plugins`
或维护另一份清单。

Expand Down
1 change: 1 addition & 0 deletions scripts/core-boundaries/rules/source/forbidden-rules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4118,6 +4118,7 @@ export const forbiddenContentUnderRules = [
'src/crates/adapters/opencode-adapter/tests/opencode_command_adapter.rs',
'src/crates/adapters/opencode-adapter/tests/tool_source_contracts.rs',
'src/crates/adapters/opencode-adapter/tests/opencode_subagent_adapter.rs',
'src/crates/adapters/opencode-adapter/tests/opencode_mcp_adapter.rs',
'src/crates/assembly/core/src/plugin_runtime.rs',
'src/crates/assembly/core/src/external_sources.rs',
],
Expand Down
82 changes: 82 additions & 0 deletions scripts/core-boundaries/rules/source/public-api-rules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const publicApiContractSlices = [
'external-source-command-contract',
'external-source-tool-contract',
'external-source-subagent-contract',
'external-source-mcp-contract',
];

const contractSlices = {
Expand All @@ -18,6 +19,7 @@ const contractSlices = {
externalSourceCommandContract: 'external-source-command-contract',
externalSourceToolContract: 'external-source-tool-contract',
externalSourceSubagentContract: 'external-source-subagent-contract',
externalSourceMcpContract: 'external-source-mcp-contract',
};

function pluginRuntimeEntry(symbol, p0, consumer, verification, contractSlice, wireImpact = true) {
Expand Down Expand Up @@ -202,6 +204,14 @@ export const opencodeAdapterPublicApiEntries = [
'OpenCodeSubagentProviderOptions',
'OpenCode subagent adapter fixture tests and explicit environment injection',
),
opencodeAdapterEntry(
'OpenCodeMcpProvider',
'bitfun-core external source composition root and OpenCode MCP adapter tests',
),
opencodeAdapterEntry(
'OpenCodeMcpProviderOptions',
'OpenCode MCP adapter fixture tests and explicit environment injection',
),
];

function externalSourceEntry(symbol, owner, consumer, wireImpact = false) {
Expand Down Expand Up @@ -253,6 +263,23 @@ function externalSubagentEntry(symbol, owner, consumer, wireImpact = false) {
};
}

function externalMcpEntry(symbol, owner, consumer, wireImpact = false) {
return {
symbol,
owner,
consumer,
verification:
'external MCP contract, coordinator, OpenCode adapter, MCP owner lifecycle, TUI, Desktop, and Web tests',
p0: 'PR6 ecosystem-neutral MCP source activation and OpenCode MCP configuration vertical slice',
contractSlice: contractSlices.externalSourceMcpContract,
wireImpact,
rationale:
'PR6 needs typed static discovery, versioned approval, conflict, preparation, and runtime status contracts without OpenCode or MCP-owner payload leakage',
exit:
'remove only through a reviewed MCP source contract migration with equivalent fail-closed activation and lifecycle tests',
};
}

export const externalSourceContractPublicApiEntries = [
'ExternalSourceContractError',
'SourceKey',
Expand Down Expand Up @@ -316,6 +343,33 @@ export const externalSourceContractPublicApiEntries = [
true,
),
),
[
'SourceQualifiedMcpServerId',
'ExternalMcpTransportKind',
'ExternalMcpStaticStatus',
'ExternalMcpServerDefinition',
'ExternalMcpActivationState',
'ExternalMcpCatalogEntry',
'ExternalMcpApprovalRequest',
'ExternalMcpConflictCandidate',
'ExternalMcpConflict',
'SecretValue',
'PreparedExternalMcpTransport',
'PreparedExternalMcpServer',
'ExternalMcpProviderIdentity',
'ExternalMcpProviderSnapshot',
'ExternalMcpSourceProvider',
'external_mcp_approval_key',
'external_mcp_conflict_key',
'ExternalMcpDiscoveryInput',
].map((symbol) =>
externalMcpEntry(
symbol,
'product-domains external MCP contract owner',
'ecosystem MCP providers, external-MCP coordinator, product reconciliation, and MCP runtime owner',
true,
),
),
);

export const externalSubagentContractPublicApiEntries = [
Expand Down Expand Up @@ -390,6 +444,18 @@ export const externalSourceCoordinatorPublicApiEntries = [
'bitfun-core bounded concurrent external-subagent provider scheduler',
),
),
...[
'ExternalMcpCoordinator',
'ExternalMcpCoordinatorSnapshot',
'ExternalMcpDiscoveryRequest',
'ExternalMcpDiscoveryResult',
].map((symbol) =>
externalMcpEntry(
symbol,
'external-sources assembly owner',
'bitfun-core bounded concurrent external-MCP provider scheduler',
),
),
];

export const externalSourceCorePublicApiEntries = [
Expand Down Expand Up @@ -452,6 +518,22 @@ export const externalSourceCorePublicApiEntries = [
'bitfun-cli and desktop host APIs',
),
),
...[
'ExternalMcpActivationState',
'ExternalMcpApprovalRequest',
'ExternalMcpCatalogEntry',
'ExternalMcpConflict',
'ExternalMcpTransportKind',
'native_mcp_candidate_id',
'set_external_mcp_server_decision',
'choose_external_mcp_conflict',
].map((symbol) =>
externalMcpEntry(
symbol,
'bitfun-core external MCP composition facade',
'bitfun-cli and desktop host APIs',
),
),
];

function pluginSourceEntry(symbol, owner, consumer, verification, wireImpact) {
Expand Down
Loading
Loading