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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ encoding_rs = "0.8.35"
url = "2"

# HTTP client
reqwest = { version = "0.13.4", default-features = false, features = ["http2", "json", "stream", "multipart", "query", "form"] }
reqwest = { version = "0.13.4", default-features = false }
semver = "1.0"

# Debug Log HTTP Server
Expand Down
13 changes: 9 additions & 4 deletions docs/architecture/rust-build-dependency-boundaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ Core 的 `agent-runtime` 只承载 Agent 生命周期基线和明确的基线工

Owner feature 不等于“无前置依赖”。当实现确实调用较低层基线时,依赖必须按 `owner → baseline` 显式组合,禁止反向把 owner 藏回基线:例如 Core MCP 工具桥和 Remote Connect 依赖 Agent 生命周期,Workspace Search 依赖本地 Workspace Runtime。每个新增或调整后的 owner 闭包都必须单独 `cargo check`,避免被 Desktop/CLI 的 feature union 偶然补齐。

只为已经启用的 optional dependency 增加子能力时,使用 Cargo 的弱依赖转发
`dependency?/feature`,并把 modifier 与 runtime owner 分开命名和看护。modifier 单独启用不得激活
runtime dependency;真实产品入口必须同时显式选择 owner 与 modifier。不要为了复用一个子 feature
把完整 adapter、service 或 tool runtime 拉回窄闭包。

Function Agent 的 Git/AI 适配由 `function-agents` 选择,MiniApp 的 domain/runtime/market
闭包由 `tools-miniapp` 选择;不得再通过一个通用 `product-domains` Core feature 把两者、
Plugin Source 和完整 domain feature 集合一起带回 Agent Runtime。产品装配计划若声明了当前
Expand All @@ -77,11 +82,11 @@ Plugin Source 和完整 domain feature 集合一起带回 Agent Runtime。产品
- target-specific dependency 放在最接近平台实现的 owner,不因单一平台需求污染跨平台 crate;
- 修改共享 dependency feature 视为构建影响变更,必须检查真实产品组合的 feature graph。

### 3.4 Reqwest TLS 后端由客户端 owner 选择
### 3.4 Reqwest 能力由客户端 owner 选择

- workspace 级 `reqwest` 只统一版本以及跨产品共享的 HTTP、序列化和流能力,不启用 TLS 后端
- 真正创建 HTTPS client 的 app、service 或 adapter 必须在自身依赖声明中显式选择 `reqwest/rustls`只使用 `reqwest::Url` 的 contract/assembly 路径不加载 TLS
- capability crate 的每个 Reqwest owner feature 必须独立带齐 `reqwest/rustls`,不能依赖 `product-full` 或其他 feature 的 Cargo feature-union 偶然补齐;
- workspace 级 `reqwest` 只统一版本并关闭默认 feature,不替任何客户端选择 HTTP/2、序列化、表单、流、代理或 TLS 能力
- 真正创建 client 的 app、service 或 adapter 必须在自身依赖声明中显式选择实际使用的 Reqwest feature 和 `reqwest/rustls`只使用 `reqwest::Url` 的 contract/assembly 路径不加载传输能力
- capability crate 的每个 Reqwest owner feature 必须独立带齐自己的数据/传输 feature 与 `reqwest/rustls`,不能依赖 `product-full` 或其他 feature 的 Cargo feature-union 偶然补齐;
- 边界检查以 Cargo metadata 的解码结果看护全部直接 consumer,并检查 resolved Reqwest feature union,防止传递依赖重新激活 Native TLS;
- 不并列启用 native-tls 兼容栈。只有真实产品场景无法由 Rustls 平台证书验证承载时,才以明确行为证据评审替换方案,而不是重新叠加第二后端。

Expand Down
25 changes: 21 additions & 4 deletions docs/performance/01-compile-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> 最近核实:2026-08-10
>
> 实现复核基线:`gcwing/main@e63084bc5`
> 实现复核基线:`gcwing/main@734e5b05f`
>
> 性能 A/B 基线:`gcwing/main@1f538b96d`
>
Expand All @@ -17,8 +17,9 @@
| 结论 | 说明 |
|---|---|
| 服务测试链接拓扑已收敛 | Services 两个 crate 的集成 target 总数从 33 降到 25;选中的 `local-storage`、MCP、基础 SSH 闭包从 16 个集成 executable 降到 8 个 |
| Agent Runtime 基线不再隐藏全量 capability union | `bitfun-core/agent-runtime` 只保留生命周期和基础工具 owner;MCP、Remote Connect、Browser/Web、Git、模型目录等由产品入口显式组合,三平台 normal/build 闭包各减少 105–110 个版本化 package instance |
| 完整产品行为和闭包保持 | `product-full` 显式组合全部 owner,Windows normal/build 闭包保持 570;CLI 保持 649。ACP 只退出未选择的隐含能力,Windows/macOS/Linux 分别减少 10/13/22 |
| Agent Runtime 基线不再隐藏重型 capability | `bitfun-core/agent-runtime` 只保留生命周期和基础工具 owner;文档转换与订阅认证也改为产品显式 modifier。在最新主线 A/B 中,三平台 normal/build 闭包进一步减少 69/64/110 个版本化 package instance |
| App Server 不继承未消费能力 | App Server 保持现有 Agent/Git/外部来源 handler 边界,不再因 Core 基线携带文档转换和本地订阅凭据,三平台闭包减少 61/56/78 |
| 完整产品行为和闭包保持 | `product-full` 显式组合全部 owner,Windows normal/build 闭包保持 570;CLI 保持 649。ACP 只退出未选择或未使用的隐含能力,累计在 Windows/macOS/Linux 分别减少 12/15/24 |
| Installer 删除未使用的直接能力 | 独立 manifest 的直接 dependency 从 18 降到 10,Windows normal/build 闭包减少 6;不把 Installer 并入根 workspace,本 PR 按要求不提交其生成 lockfile |
| focused test 仍保持精确 | 同 owner、feature、平台和进程语义的源文件进入分组 target;使用 `--test <target> <module>::<filter>` 运行单模块 |

Expand Down Expand Up @@ -94,6 +95,21 @@ package/version,不等同于实际秒数。路径 package 因 A/B worktree 路
| Desktop | 792 → 792 | 807 → 807 | 892 → 892 | 完整产品继续使用既有跨平台截图行为,本轮不以扩大根 lock 依赖宇宙换取单平台闭包下降 |
| Installer | 333 → 327 | — | — | Windows 独立 workspace;直接 dependency 18 → 10 |

在最新实现复核基线 `gcwing/main@734e5b05f` 上,本轮继续把两个重型能力从 Core 基线改为弱
modifier。计数先移除 Cargo tree 的重复展示标记 `(*)`,再按 package/version 去重:

| 本轮闭包 | Windows | macOS | Linux | 行为边界 |
|---|---:|---:|---:|---|
| Core `agent-runtime` | 343 → 274 | 330 → 266 | 375 → 265 | 文档扩展识别保留;转换和本地订阅凭据明确不可用 |
| App Server | 490 → 429 | 477 → 421 | 508 → 430 | 现有 handler/DTO 保持,未消费的两个能力退出 |
| Core `product-full` | 570 → 570 | 557 → 557 | 601 → 601 | 显式恢复 `document-read` 与 `subscription-auth` |
| CLI | 649 → 649 | 649 → 649 | 672 → 672 | 显式保持原有能力 |
| ACP | 589 → 587 | 574 → 572 | 594 → 592 | 保持原有能力,同时退出 Reqwest 未使用的 `mime_guess`/`unicase` |

本轮没有新增 crate 或第三方 dependency。收益来自两类现有重闭包退出窄入口:`anydoc` 及其
文档解析/压缩依赖,以及订阅凭据的 keyring/加密/本地存储依赖。完整产品 package 集合不变,
因此这里只报告依赖图收敛,不宣称 `product-full` wall-clock 提速。

Package instance 会低估“同一个大 crate 少编译了多少 feature 代码”。在 Windows
`agent-runtime` 闭包中,`bitfun-services-integrations` 的 Cargo active feature 从 61 个降到 6 个,
只保留 `workspace-search` 及其 5 个直接依赖 feature;`bitfun-product-domains` 从 13 个降到 5 个,
Expand All @@ -106,7 +122,7 @@ Plugin Source 由各自 owner 选择,完整产品仍经 `product-full` 显式
| 状态 | 范围 | 处理结论 |
|---|---|---|
| 已稳定 | 根 `Cargo.lock`、Reqwest Rustls 单栈、workspace Tokio 最小基线 | 不重复治理 |
| 本轮完成 | Core Agent Runtime capability、Installer 未使用直接依赖 | 以真实入口 closure 收敛,不建立新的产品 umbrella,也不扩大根依赖宇宙 |
| 本轮完成 | Core Agent Runtime capability、文档转换与订阅认证 modifier、Installer 未使用直接依赖 | 以真实入口 closure 收敛,不建立新的产品 umbrella,也不扩大根依赖宇宙 |
| 当前不动 | App Server / Server | 只为保持现有 handler 编译显式声明其已消费的 Core owner;不在改造稳定前继续拆其生产路径 |
| 明确保留 | Desktop screenshots backend | 替换方案必须同时保持三平台坐标/权限/区域捕获语义且不增加根 lock package;当前候选不满足 |
| 明确保留 | `portable-pty 0.8/0.9` | 非 OHOS 与 OHOS 的平台兼容选择,不为去重破坏 |
Expand All @@ -131,6 +147,7 @@ Plugin Source 由各自 owner 选择,完整产品仍经 `product-full` 显式
| CI 拓扑 | Rust job 不再等待完整前端构建,自建 Tauri 检查所需资源目录 |
| 依赖收敛 | Desktop 直接 image 版本和 Reqwest TLS 双栈已治理 |
| Agent Runtime 闭包 | Core 基线不再暗带具体 capability;完整产品和 CLI 显式保持原能力,ACP 退出未选择闭包 |
| 重型可选能力 | 文档转换和本地订阅凭据由弱 modifier 细化已有 runtime owner;Core 基线和 App Server 退出未消费闭包 |
| Installer 闭包 | 删除 8 个未使用直接 dependency;独立 workspace 和发布生命周期不变,本 PR 不提交其生成 lockfile |
| Agent Runtime 测试 | 28 个 integration executable 已收敛为 5 个职责/平台 target |
| Services 测试 | 两个服务 crate 使用显式 target;选中闭包少 8 个 integration executable,进程/feature/external-system 边界保持独立 |
Expand Down
94 changes: 71 additions & 23 deletions scripts/check-core-boundaries.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,29 @@ test('Core Agent Runtime baseline excludes concrete capability unions', () => {
}
});

test('Core optional document and subscription capabilities have independent modifiers', () => {
const ruleByFeature = new Map(
coreClosedFeatureProfileRules.map((rule) => [rule.featureName, rule]),
);
assert.deepEqual(ruleByFeature.get('document-read')?.requiredFeatureRefs, [
'tool-runtime?/document-read',
]);
assert.deepEqual(ruleByFeature.get('subscription-auth')?.requiredFeatureRefs, [
'bitfun-ai-adapters?/subscription-auth',
]);
assert.deepEqual(ruleByFeature.get('ai-adapter-runtime')?.requiredFeatureRefs, [
'dep:bitfun-ai-adapters',
]);
assert.ok(
!ruleByFeature.get('tools-basic')?.requiredFeatureRefs.includes('tool-runtime/document-read'),
'baseline tools must not activate document conversion',
);
});

test('Core product-full explicitly assembles service and tool capability owners', () => {
for (const required of [
'document-read',
'subscription-auth',
'model-catalog',
'mcp-runtime',
'remote-connect',
Expand Down Expand Up @@ -487,6 +508,8 @@ test('explicit product entrypoint bitfun-core feature selections pass', () => {

const ACP_REVIEWED_CORE_FEATURES = [
'agent-runtime',
'document-read',
'subscription-auth',
'deep-research',
'lsp',
'external-sources',
Expand Down Expand Up @@ -1399,16 +1422,17 @@ test('services integrations Reqwest policy uses Cargo-decoded feature references
reqwest = ["dep:reqwest"]
announcement = ["reqwest", "reqwest/rustls"]
file-watch = ["reqwest?/__native-tls"]
mcp = ["reqwest"]
mcp = ["reqwest", "reqwest/rustls", "reqwest/json"]
models-dev = ["reqwest", "reqwest/rustls", "reqwest/system-proxy"]
speech = ["reqwest", "reqwest/rustls", "reqwest/http3"]
`);

const messages = findServicesIntegrationsReqwestFeatureViolations(pkg)
.map((violation) => violation.message)
.join('\n');
assert.match(messages, /announcement.*missing Reqwest feature reference reqwest\/json/);
assert.match(messages, /file-watch.*outside its reviewed owner features/);
assert.match(messages, /mcp.*missing reqwest\/rustls/);
assert.match(messages, /mcp.*missing Reqwest feature reference reqwest\/stream/);
assert.doesNotMatch(messages, /models-dev.*system-proxy/);
assert.match(messages, /speech.*unreviewed Reqwest feature reference reqwest\/http3/);
});
Expand All @@ -1422,11 +1446,7 @@ test('direct Reqwest clients reject extra decoded dependency and package feature
uses_default_features: false,
features: [
'http2',
'json',
'stream',
'multipart',
'query',
'form',
'rustls',
'__native-tls',
],
Expand All @@ -1449,15 +1469,19 @@ test('direct Reqwest clients reject extra decoded dependency and package feature
});

test('AI adapters Reqwest profile owns the supported SOCKS transport', () => {
const baseFeatures = ['http2', 'json', 'stream', 'multipart', 'query', 'form'];
const valid = packageAt('bitfun-ai-adapters', 'src/crates/adapters/ai-adapters/Cargo.toml', [{
name: 'reqwest',
kind: null,
optional: false,
uses_default_features: false,
features: [...baseFeatures, 'rustls', 'socks'],
}]);
const missingSocks = packageAt(
const baseFeatures = ['http2', 'json', 'stream'];
const valid = {
...packageAt('bitfun-ai-adapters', 'src/crates/adapters/ai-adapters/Cargo.toml', [{
name: 'reqwest',
kind: null,
optional: false,
uses_default_features: false,
features: [...baseFeatures, 'rustls', 'socks'],
}]),
features: { 'subscription-auth': ['reqwest/form'] },
};
const missingSocks = {
...packageAt(
'bitfun-ai-adapters',
'src/crates/adapters/ai-adapters/Cargo.toml',
[{
Expand All @@ -1467,7 +1491,9 @@ test('AI adapters Reqwest profile owns the supported SOCKS transport', () => {
uses_default_features: false,
features: [...baseFeatures, 'rustls'],
}],
);
),
features: { 'subscription-auth': ['reqwest/form'] },
};

assert.deepEqual(findReqwestDependencyFeatureViolations([valid]), []);
const messages = findReqwestDependencyFeatureViolations([missingSocks])
Expand All @@ -1477,14 +1503,14 @@ test('AI adapters Reqwest profile owns the supported SOCKS transport', () => {
});

test('Reqwest metadata policy covers URL-only and future dependency owners', () => {
const baseFeatures = ['http2', 'json', 'stream', 'multipart', 'query', 'form'];
const coreFeatures = [];
const core = {
...packageAt('bitfun-core', 'src/crates/assembly/core/Cargo.toml', [{
name: 'reqwest',
kind: null,
optional: true,
uses_default_features: false,
features: baseFeatures,
features: coreFeatures,
}]),
features: { product: ['dep:reqwest', 'reqwest/__native-tls'] },
};
Expand All @@ -1493,7 +1519,7 @@ test('Reqwest metadata policy covers URL-only and future dependency owners', ()
kind: null,
optional: false,
uses_default_features: false,
features: [...baseFeatures, 'rustls'],
features: ['http2', 'rustls', 'stream'],
}]);
const duplicate = packageAt(
'bitfun-services-integrations',
Expand All @@ -1504,7 +1530,7 @@ test('Reqwest metadata policy covers URL-only and future dependency owners', ()
kind: null,
optional: true,
uses_default_features: false,
features: baseFeatures,
features: ['http2'],
},
{
name: 'reqwest',
Expand All @@ -1513,7 +1539,7 @@ test('Reqwest metadata policy covers URL-only and future dependency owners', ()
optional: true,
target: 'cfg(windows)',
uses_default_features: false,
features: [...baseFeatures, '__native-tls'],
features: ['http2', '__native-tls'],
},
],
);
Expand All @@ -1526,6 +1552,22 @@ test('Reqwest metadata policy covers URL-only and future dependency owners', ()
assert.match(messages, /bitfun-services-integrations.*exactly one normal Reqwest dependency/);
});

test('Reqwest consumers inherit the workspace version without duplicating feature rules', async () => {
const { requiredContentRules } = await import(
'./core-boundaries/rules/source/required-rules.mjs'
);
const rules = requiredContentRules.filter((rule) =>
rule.reason.includes('Reqwest consumers must inherit the workspace-owned compatible version')
);

assert.equal(rules.length, 7);
for (const rule of rules) {
const pattern = rule.patterns[0].regex;
assert.match('reqwest = { workspace = true, features = ["rustls"] }', pattern);
assert.doesNotMatch('reqwest = { version = "99", features = ["rustls"] }', pattern);
}
});

test('resolved Reqwest feature union rejects every native TLS backend alias', () => {
const violations = findResolvedReqwestNativeTlsViolations(
[
Expand Down Expand Up @@ -1980,7 +2022,10 @@ test('split core boundary check keeps self-test and default execution behavior',
});

test('optional dependency ownership rejects undeclared direct feature owners', async () => {
const { unexpectedDependencyOwnerFeatures } = await import(
const {
featureReferencesOptionalDependencyOwner,
unexpectedDependencyOwnerFeatures,
} = await import(
'./core-boundaries/manifest-feature-helpers.mjs'
);
const features = new Map([
Expand All @@ -1996,8 +2041,11 @@ test('optional dependency ownership rejects undeclared direct feature owners', a
depName: 'example',
ownerFeatures: ['declared'],
}).map(([featureName]) => featureName),
['missing', 'feature-ref'],
['missing', 'feature-ref', 'weak-ref'],
);
assert.equal(featureReferencesOptionalDependencyOwner(features.get('declared'), 'example'), true);
assert.equal(featureReferencesOptionalDependencyOwner(features.get('weak-ref'), 'example'), true);
assert.equal(featureReferencesOptionalDependencyOwner(features.get('unrelated'), 'example'), false);
});

test('services-core capability profiles keep heavy owners out of the empty profile', async () => {
Expand Down
Loading