feat: share built-in browser across runtimes

The built-in browser was limited to direct-model tools, and its Agent switch also blocked manual Browser workbar actions. It is now a request-scoped GoodBuddy capability assignable to direct models, managed OpenCode, and Continue, while manual browser controls remain available when Agent access is off.

Existing browser enablement migrates to capability settings version 6. DeepSeek Harness remains unsupported.

Release note: 内置浏览器现在可分配给直连模型、OpenCode 和 Continue;关闭 Agent 浏览器能力不会影响用户在浏览器工作栏中的手动操作。
This commit is contained in:
mesalogo
2026-09-03 19:36:06 +08:00
parent 70ec2bf792
commit feaebdf221
23 changed files with 958 additions and 289 deletions
+19
View File
@@ -8,6 +8,7 @@ import {
magicNoteScopedDataTools
} from './scoped-data-tools'
import { goodbuddyConfigTools } from './goodbuddy-config-tools'
import { builtinModelTools } from './builtin-model-tools'
export type BuiltinMcpServerSummary = {
id: BuiltinMcpServerId
@@ -22,6 +23,7 @@ export type BuiltinMcpServerSummary = {
access: 'read' | 'mixed'
authorization: 'conversation-scoped'
requiresFeature?: 'magic-notes'
computerCapabilityId?: 'host-browser-control'
}
export const builtinMcpServers = [
@@ -67,5 +69,22 @@ export const builtinMcpServers = [
supportedAssignments: ['model', 'opencode', 'continue'],
access: 'mixed',
authorization: 'conversation-scoped'
},
{
id: 'builtin-browser',
name: '内置浏览器',
description:
'允许已分配的 Runtime 使用 GoodBuddy 临时隔离浏览器,不会控制客户端已安装的浏览器;关闭此能力不影响你在浏览器工作栏中手动操作。',
tools: builtinModelTools
.filter((tool) => tool.group === 'browser')
.map(({ name, description, access }) => ({
name,
description,
access
})),
supportedAssignments: ['model', 'opencode', 'continue'],
access: 'mixed',
authorization: 'conversation-scoped',
computerCapabilityId: 'host-browser-control'
}
] as const satisfies readonly BuiltinMcpServerSummary[]
+9 -3
View File
@@ -11,13 +11,19 @@ describe('built-in model tool catalog', () => {
expect(new Set(names).size).toBe(names.length)
})
it('places every tool in exactly its declared group', () => {
it('places every direct-model group outside the shared browser capability', () => {
const groupedNames = builtinModelToolGroups.flatMap((group) =>
group.tools.map((tool) => tool.name)
)
const directModelTools = builtinModelTools.filter(
(tool) => tool.group !== 'browser'
)
expect(groupedNames).toHaveLength(builtinModelTools.length)
expect(new Set(groupedNames).size).toBe(builtinModelTools.length)
expect(groupedNames).toHaveLength(directModelTools.length)
expect(new Set(groupedNames).size).toBe(directModelTools.length)
expect(groupedNames).toEqual(
expect.arrayContaining(directModelTools.map((tool) => tool.name))
)
for (const group of builtinModelToolGroups) {
expect(group.tools.every((tool) => tool.group === group.id)).toBe(true)
-7
View File
@@ -120,13 +120,6 @@ export const builtinModelToolGroups = [
'在 Execute 模式下读取、列出或写入当前工作区范围内的文件。',
tools: builtinModelTools.filter((tool) => tool.group === 'filesystem')
},
{
id: 'browser',
name: '内置浏览器',
description:
'启用“内置浏览器”后,在 Execute 模式下操作 GoodBuddy 隔离浏览器。',
tools: builtinModelTools.filter((tool) => tool.group === 'browser')
},
{
id: 'web',
name: '联网搜索',
+3 -2
View File
@@ -38,7 +38,8 @@ export type CapabilityAssignments = z.infer<
export const builtinMcpServerIdSchema = z.enum([
'knowledge-base',
'magic-notes',
'goodbuddy-config'
'goodbuddy-config',
'builtin-browser'
])
export type BuiltinMcpServerId = z.infer<
typeof builtinMcpServerIdSchema
@@ -369,7 +370,7 @@ export const capabilitySnapshotSchema = z
skills: z.array(skillSummarySchema).max(256),
builtinMcpServers: z
.array(builtinMcpServerStateSummarySchema)
.max(3)
.max(4)
.optional(),
mcpServers: z.array(mcpServerSummarySchema).max(64),
webSearch: webSearchCapabilitySchema.optional(),