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
4 changes: 0 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions docs/architecture/app-server-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,13 @@ Embedded direct invocation 可以依赖同进程构造身份,但仍必须传
- server wiring 可以依赖生产 handler 所需的明确 owner feature,但禁止选择 `bitfun-core/product-full`。
- 新 domain 只能增加真实 handler 所需的最窄 owner feature,并通过边界检查证明依赖方向。
- protocol DTO 不复制 Runtime 内部对象;只暴露 Rich Client 需要的稳定字段和 read model。
- `app-server-protocol` 是 TypeScript wire schema 的唯一导出 owner;`app-server/ts`
仅保留为兼容转发。owner 对象到 wire read model 的转换留在 server adapter,不能为了
生成类型把 Core、Agent Runtime 或 Service 实现依赖下沉到 protocol。
- `app-server::schema` 只保留对 protocol 类型的兼容 re-export;正式 typed client 由
`app-server-client` 单独拥有,server crate 不维护第二套隐藏 client。该 re-export 保证
已有平铺类型路径和 wire shape,不承诺保留 server-only 的 inherent/`From` 转换 helper;
这些内部调用应迁到 server adapter 或稳定 contract 方法。
- transport 实现留在 Host/adapter 边界,generic role/transport helper 保持 schema-free。
- App Server 不持有第二份 Session、Permission、Config 或 capability 权威状态。

Expand Down
36 changes: 31 additions & 5 deletions docs/performance/01-compile-performance.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# BitFun 编译与依赖治理计划

> 最近核实:2026-08-13
> 最近核实:2026-08-14
>
> 实现复核基线:`gcwing/main@a4d944e5b`
> 实现复核基线:`gcwing/main@76f8b89e2`
>
> 性能 A/B 基线:`gcwing/main@1f538b96d`
>
Expand Down Expand Up @@ -312,7 +312,7 @@ Clap、Tracing Subscriber、Notify 等默认即产品契约或缺少独立收益
|---|---|---|
| 已稳定 | 根 `Cargo.lock`、Reqwest Rustls 单栈、workspace Tokio 最小基线 | 不重复治理 |
| 本轮完成 | Core 空默认与 capability-local 工具依赖、ACP client/server 角色、Core Agent Runtime capability、文档转换与订阅认证 modifier、SDK Host 显式 owner closure、Installer/CLI/Desktop/Core/MiniApp Market/Page Function 未使用直接依赖 | 以真实入口 closure 收敛,不建立新的产品 umbrella;根 lock 不增加 package |
| 当前不动 | App Server / Server | 只为保持现有 handler 编译显式声明其已消费的 Core owner;不在改造稳定前继续拆其生产路径 |
| 本轮完成 | App Server schema owner | wire DTO 与 TypeScript 导出收敛到 protocol;server 只保留 handler 与 owner-to-wire 转换,正式 client 继续由独立 client crate 持有 |
| 明确保留 | Desktop screenshots backend | 替换方案必须同时保持三平台坐标/权限/区域捕获语义且不增加根 lock package;当前候选不满足 |
| 明确保留 | `portable-pty 0.8/0.9` | 非 OHOS 与 OHOS 的平台兼容选择,不为去重破坏 |

Expand Down Expand Up @@ -353,7 +353,33 @@ consumer 零影响。完整 Rust Runtime SDK 同时将兼容版本提升为 v6
这里仍只报告依赖图和 focused-test 输入,不宣称完整产品 wall-clock 提速。根 `Cargo.lock` package
集合与字节均不变,新增/升级/降级 package 为 0;`.github` 和 `ci.yml` 不变。

### 3.3 CI 与本地验证
### 3.3 App Server TypeScript schema owner

变更前,Web `gen:types` 先编译 protocol,再编译完整 `bitfun-app-server`,后者会把
Core、Agent Runtime 和具体 Service handler 闭包带入前端类型生成。最新同代码状态的
Frontend Build 样本中,`gen:types` 约 430 秒,占 `build:web` 约 484 秒的 88.8%;其中
protocol 导出约 42.58 秒,App Server 导出约 6 分 26 秒。单次 CI 样本只用于定位热点,
不能独立证明合入后的 wall-clock 收益。

本轮将 behavior-light wire DTO 和 TS derive 统一到 `bitfun-app-server-protocol`,server 端
保留 Core/Runtime/Service owner 到 wire read model 的显式转换。既有 `app-server::schema`
继续兼容 re-export,`bitfun-app-server/ts` 继续兼容转发;隐藏且无生产 consumer 的第二套
client 删除,正式 client 仍由 `bitfun-app-server-client` 拥有。兼容面是已有平铺类型路径、
method 和 wire shape;旧 server-only inherent/`From` helper 不是版本化公共 SDK,仓内调用已
迁入 adapter 或稳定 contract 方法。三平台
`normal,build,dev` 版本化 package instance 去重结果如下:

| TS 导出闭包 | Windows | macOS | Linux |
|---|---:|---:|---:|
| 变更前:完整 App Server | 433 | 429 | 437 |
| 变更后:App Server Protocol | 153 | 159 | 159 |
| 收敛 | -280 | -270 | -278 |

Web 实际引用的 19 个直接类型及其递归导入闭包保持生成内容字节一致;不再把 95 个历史
导出文件全部视为兼容面。根 `Cargo.lock` package 集合和版本不变,只从 App Server package
记录删除不再直接消费的依赖边;`.github`、CI job 和矩阵均不变。

### 3.4 CI 与本地验证

- 现有 CI 已覆盖 workspace check、Core/Desktop lib、平台敏感 owner 测试和独立 runtime/CLI 验证;本轮不新增 job、矩阵或 changed-path 分类器。
- CI 不负责穷举所有测试;新增验证只有具备独立 owner、平台矩阵或失败归因价值时才进入既有流水线,否则由最近模块的 focused command 维护。
Expand Down Expand Up @@ -393,7 +419,7 @@ consumer 零影响。完整 Rust Runtime SDK 同时将兼容版本提升为 v6
|---|---|
| CI 收敛 | 先积累多次相同 owner 的 step wall-clock、cache hit/miss 和失败历史;只有能证明收益且不会静默缩小覆盖时再独立设计 |
| Desktop 截图后端 | 新候选同时满足三平台行为等价、区域捕获无性能回退、系统依赖可 feature-gate,且根 lock package 不增加 |
| App Server / Server | 当前改造合入并稳定后,重新锁定最新生产调用链和可信 owner 边界 |
| App Server / Server | 观察 protocol 单一 schema owner 合入后的 Frontend Build 样本;没有新的生产 owner 或稳定行为收益前,不继续拆 handler 路径 |
| 其他产品入口重型 capability | 证明入口不消费该能力,具备 typed unsupported/fallback 行为,并能让一个真实重依赖子图退出 |
| 重复 native/sys 库版本 | 同一 owner 能升级收敛且三平台打包/ABI 有证据;不因版本数字重复强行 patch |

Expand Down
23 changes: 23 additions & 0 deletions scripts/check-core-boundaries.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,29 @@ const MODULES = [

const TEST_ROOT = join('C:', 'repo');

test('App Server TypeScript capability is owned by the protocol crate', () => {
const appServerTs = coreClosedFeatureProfileRules.find(
(rule) => rule.manifestPath === 'src/crates/interfaces/app-server/Cargo.toml'
&& rule.featureName === 'ts',
);
assert.deepEqual(appServerTs?.requiredFeatureRefs, [
'bitfun-app-server-protocol/ts',
]);
assert.equal(appServerTs?.exact, true);

const protocolTs = coreClosedFeatureProfileRules.find(
(rule) => rule.manifestPath === 'src/crates/interfaces/app-server-protocol/Cargo.toml'
&& rule.featureName === 'ts',
);
assert.deepEqual(protocolTs?.requiredFeatureRefs, [
'bitfun-core-types/ts',
'bitfun-product-domains/ts',
'bitfun-runtime-ports/ts',
'dep:ts-rs',
]);
assert.equal(protocolTs?.exact, true);
});

test('Agent Runtime leaf capabilities have one managed feature and source contract', async () => {
const rule = capabilityContractDependencyRules.find(
(candidate) => candidate.packageName === 'bitfun-agent-runtime',
Expand Down
27 changes: 22 additions & 5 deletions scripts/core-boundaries/rules/feature-rules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -385,13 +385,11 @@ export const capabilityContractDependencyRules = [
capabilityEdge(['agent-api', 'git-port']),
])],
['bitfun-agent-tools', capabilityConsumer([capabilityEdge()])],
['bitfun-app-server', capabilityConsumer(
[capabilityEdge(['agent-api'])],
['bitfun-app-server', capabilityConsumer([capabilityEdge(['agent-api'])])],
['bitfun-app-server-protocol', capabilityConsumer(
[capabilityEdge(['agent-api', 'git-port'])],
[capabilityForwarder('ts', 'ts')],
)],
['bitfun-app-server-protocol', capabilityConsumer([
capabilityEdge(['agent-api', 'git-port']),
])],
['bitfun-cli', capabilityConsumer([
capabilityEdge(['agent-api', 'git-port', 'permission', 'plugin-runtime', 'workspace-ports']),
])],
Expand Down Expand Up @@ -744,6 +742,25 @@ export const acpClosedFeatureProfileRules = [
];

export const coreClosedFeatureProfileRules = [
{
manifestPath: 'src/crates/interfaces/app-server/Cargo.toml',
featureName: 'ts',
requiredFeatureRefs: ['bitfun-app-server-protocol/ts'],
exact: true,
reason: 'App Server must delegate TypeScript wire export to the protocol owner',
},
{
manifestPath: 'src/crates/interfaces/app-server-protocol/Cargo.toml',
featureName: 'ts',
requiredFeatureRefs: [
'bitfun-core-types/ts',
'bitfun-product-domains/ts',
'bitfun-runtime-ports/ts',
'dep:ts-rs',
],
exact: true,
reason: 'App Server Protocol must own the complete behavior-light TypeScript wire surface',
},
{
manifestPath: 'src/crates/assembly/core/Cargo.toml',
featureName: 'default',
Expand Down
22 changes: 11 additions & 11 deletions src/apps/cli/src/agent/tui_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ use bitfun_runtime_ports::{
put_agent_workspace_references, AgentContextReloadRequest, AgentDialogSteerRequest,
AgentDialogTurnExecution, AgentDialogTurnRequest, AgentInputAttachment,
AgentMessageWorkspaceReferencesRequest, AgentSessionCompactionRequest,
AgentSessionCreateRequest, AgentSessionDeleteRequest,
AgentSessionLineageCancellationRequest, AgentSessionLineageInspection,
AgentSessionLineageRequest, AgentSessionLineageSnapshot, AgentSessionLineageTranscriptRequest,
AgentSessionListRequest, AgentSessionModeUpdateRequest, AgentSessionModelUpdateRequest,
AgentSessionRenameRequest, AgentSessionRevertRequest, AgentSessionRevertResult,
AgentSessionSummary, AgentSessionUsageRequest, AgentSessionWorkspaceBinding,
AgentSubmissionSource, AgentTurnCancellationRequest, AgentTurnCancellationResult,
AgentTurnSettlementRequest, AgentUserShellCommandRequest, AgentWorkspaceReference,
AgentWorkspaceReferenceSearchRequest, AgentWorkspaceReferenceSearchResult,
DialogSubmissionPolicy, SessionExecutionTarget, SessionTranscript, WorkspaceDiffSnapshot,
AgentSessionCreateRequest, AgentSessionDeleteRequest, AgentSessionLineageCancellationRequest,
AgentSessionLineageInspection, AgentSessionLineageRequest, AgentSessionLineageSnapshot,
AgentSessionLineageTranscriptRequest, AgentSessionListRequest, AgentSessionModeUpdateRequest,
AgentSessionModelUpdateRequest, AgentSessionRenameRequest, AgentSessionRevertRequest,
AgentSessionRevertResult, AgentSessionSummary, AgentSessionUsageRequest,
AgentSessionWorkspaceBinding, AgentSubmissionSource, AgentTurnCancellationRequest,
AgentTurnCancellationResult, AgentTurnSettlementRequest, AgentUserShellCommandRequest,
AgentWorkspaceReference, AgentWorkspaceReferenceSearchRequest,
AgentWorkspaceReferenceSearchResult, DialogSubmissionPolicy, SessionExecutionTarget,
SessionTranscript, WorkspaceDiffSnapshot,
};
use tokio::sync::{broadcast, Mutex};

Expand Down Expand Up @@ -1358,7 +1358,7 @@ impl TuiAgentClient {
.map_err(|error| anyhow::anyhow!(error.message))?;
let result = self
.backend
.submit_dialog_turn(SubmitDialogTurnRequest(AgentDialogTurnRequest {
.submit_dialog_turn(SubmitDialogTurnRequest::from(AgentDialogTurnRequest {
session_id: session_id.clone(),
message,
original_message,
Expand Down
8 changes: 6 additions & 2 deletions src/apps/cli/src/shared_tui_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use bitfun_app_server_protocol::worktree::*;
use bitfun_app_server_protocol::{MIN_PROTOCOL_VERSION, PROTOCOL_VERSION};
use bitfun_runtime_ports::{
AgentSessionCompactionResult, AgentSessionForkResult, AgentSessionWorkspaceBinding,
AgentUserShellCommandResult,
AgentSubmissionSource, AgentUserShellCommandResult, DialogSubmissionPolicy,
};
use tokio::sync::broadcast;

Expand Down Expand Up @@ -421,7 +421,11 @@ impl TuiBackend for SharedTuiBackend {
request: SubmitDialogTurnRequest,
) -> Result<SubmitDialogTurnResponse, TuiBackendError> {
match self
.request(RuntimeIpcOperation::SubmitTurn { request: request.0 })
.request(RuntimeIpcOperation::SubmitTurn {
request: request.0.to_request(DialogSubmissionPolicy::for_source(
AgentSubmissionSource::Cli,
)),
})
.await?
{
RuntimeIpcOperationResult::TurnAccepted {
Expand Down
3 changes: 3 additions & 0 deletions src/crates/interfaces/AGENTS-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
| Crate | 职责 | 本地文档 |
|---|---|---|
| `acp` | 基于已组装产品 runtime 的 Agent Client Protocol 接口 | [AGENTS.md](acp/AGENTS.md) |
| `app-server-protocol` | behavior-light App Server method、wire DTO、错误、事件和 protocol role | [AGENTS.md](app-server/AGENTS.md) |
| `app-server-client` | transport-agnostic 类型化 App Server client 和事件分发 | [AGENTS.md](app-server/AGENTS.md) |
| `app-server` | 生产 App Server handler 注册、生命周期和 owner-to-wire adapter | [AGENTS.md](app-server/AGENTS.md) |
| `sdk-host` | 版本化的本地 Agent SDK Host 协议与连接用例;进程启动和 stdio framing 仍由 `src/apps/sdk-host` 负责 | — |

## 放置规则
Expand Down
4 changes: 3 additions & 1 deletion src/crates/interfaces/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ product behavior. UI apps and delivery hosts remain under `src/apps`,
| Crate | Responsibility | Local doc |
|---|---|---|
| `acp` | Agent Client Protocol interface over the assembled product runtime | [AGENTS.md](acp/AGENTS.md) |
| `app-server` | Protocol-agnostic JSON-RPC app-server/client scaffold over `agent_client_protocol` custom roles | [AGENTS.md](app-server/AGENTS.md) |
| `app-server-protocol` | Behavior-light App Server methods, wire DTOs, errors, events, and protocol roles | [AGENTS.md](app-server/AGENTS.md) |
| `app-server-client` | Transport-agnostic typed App Server client and event distribution | [AGENTS.md](app-server/AGENTS.md) |
| `app-server` | Production App Server handler registration, lifecycle, and owner-to-wire adapters | [AGENTS.md](app-server/AGENTS.md) |
| `sdk-host` | Versioned local Agent SDK Host protocol and connection use cases; process bootstrap and stdio framing remain in `src/apps/sdk-host` | — |

## Placement Rules
Expand Down
7 changes: 6 additions & 1 deletion src/crates/interfaces/app-server-protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ ts-rs = { workspace = true, optional = true }

[features]
default = []
ts = ["dep:ts-rs"]
ts = [
"dep:ts-rs",
"bitfun-core-types/ts",
"bitfun-product-domains/ts",
"bitfun-runtime-ports/ts",
]

[lints]
workspace = true
110 changes: 110 additions & 0 deletions src/crates/interfaces/app-server-protocol/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,116 @@

use agent_client_protocol::{JsonRpcRequest, JsonRpcResponse};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};

/// API view of a mode configuration.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
#[serde(default)]
pub struct AgentProfileView {
pub profile_id: String,
pub enabled_tools: Vec<String>,
pub default_tools: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub disabled_user_skills: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub enabled_user_skills: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[request(method = "config/getAgentProfileConfigs", response = GetAgentProfileConfigsResponse)]
pub struct GetAgentProfileConfigsMessage {}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
pub struct GetAgentProfileConfigsResponse {
pub profiles: HashMap<String, AgentProfileView>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
#[request(method = "config/getAgentProfileConfig", response = GetAgentProfileConfigResponse)]
pub struct GetAgentProfileConfigMessage {
pub agent_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
pub struct GetAgentProfileConfigResponse(pub AgentProfileView);

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[request(method = "config/getModelConfigs", response = GetModelConfigsResponse)]
pub struct GetModelConfigsMessage {}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
pub struct GetModelConfigsResponse {
pub models: Vec<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[serde(rename_all = "camelCase")]
#[request(method = "config/getConfig", response = GetConfigResponse)]
pub struct GetConfigMessage {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "skip_if_false")]
pub skip_retry_on_not_found: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
pub struct GetConfigResponse(pub serde_json::Value);

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[serde(rename_all = "camelCase")]
#[request(method = "config/getConfigs", response = GetConfigsResponse)]
pub struct GetConfigsMessage {
pub paths: Vec<String>,
#[serde(default, skip_serializing_if = "skip_if_false")]
pub skip_retry_on_not_found: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
pub struct GetConfigsResponse {
pub configs: BTreeMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[serde(rename_all = "camelCase")]
#[request(method = "config/setConfig", response = SetConfigResponse)]
pub struct SetConfigMessage {
pub path: String,
pub value: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
pub struct SetConfigResponse {}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
#[serde(rename_all = "camelCase")]
#[request(method = "config/setAgentProfileConfig", response = SetAgentProfileConfigResponse)]
pub struct SetAgentProfileConfigMessage {
pub agent_id: String,
pub config: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct SetAgentProfileConfigResponse(pub AgentProfileView);

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
#[serde(rename_all = "camelCase")]
#[request(method = "config/resetAgentProfileConfig", response = ResetAgentProfileConfigResponse)]
pub struct ResetAgentProfileConfigMessage {
pub agent_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonRpcResponse)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct ResetAgentProfileConfigResponse(pub AgentProfileView);

fn skip_if_false(value: &bool) -> bool {
!*value
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
Expand Down
4 changes: 2 additions & 2 deletions src/crates/interfaces/app-server-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ pub use role::{AppClient, AppServer};
// Keep the established public domain paths while the schema sources live in a
// single physical directory.
pub use schemas::{
account, agent, app, error, event, external_source, hook, mcp, method, model, session, skill,
subagent, workspace, worktree,
account, agent, app, error, event, external_source, git, hook, i18n, mcp, method, model,
permission, session, skill, subagent, workspace, worktree,
};

/// Current App Server protocol version.
Expand Down
Loading