feat: add direct model programming agents
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s

Direct model conversations can now execute bounded platform-native processes and delegate one level of programming work without duplicating tools in Agent Runtimes. Process and Subagent activity follows existing workspace, mode, approval, cancellation, and lifecycle boundaries, with chat attribution and complete feature documentation.

Remove the static Renderer bundle baseline gate and its build-only manifests; production builds continue to compile all processes and verify the control-plane installer. Also include the pending documentation link normalization.

Release note: 直连模型现在可在 Execute 模式运行本机命令,并可委派编程 Subagent 完成修改、测试和复核;构建流程不再因静态 Renderer 包体基线阻塞。
This commit is contained in:
mesalogo
2026-09-02 00:50:54 +08:00
parent d36889d9f6
commit b36d688900
49 changed files with 4791 additions and 1191 deletions
-103
View File
@@ -1,10 +1,8 @@
// @vitest-environment node
import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import {
stableMainEntryFileName,
sanitizeRendererModuleId,
serializeDeepSeekHarnessBundleManifest
} from '../electron.vite.config'
@@ -35,105 +33,4 @@ describe('Electron Vite configuration', () => {
})
expect(source.endsWith('\n')).toBe(true)
})
it('sanitizes host-native renderer paths without leaking the host root', () => {
const projectRoot = resolve('test-fixtures', 'renderer-project')
const appModule = join(
projectRoot,
'src',
'renderer',
'src',
'App.tsx'
)
const g6Module = join(
projectRoot,
'..',
'npm-cache',
'node_modules',
'@antv',
'g6',
'esm',
'index.js'
)
const eventEmitterModule = join(
projectRoot,
'node_modules',
'eventemitter3',
'index.js'
)
expect(
sanitizeRendererModuleId(appModule, projectRoot)
).toBe('src/renderer/src/App.tsx')
expect(
sanitizeRendererModuleId(g6Module, projectRoot)
).toBe('node_modules/@antv/g6/esm/index.js')
expect(
sanitizeRendererModuleId(
'\0vite/preload-helper.js',
projectRoot
)
).toBe('virtual:vite/preload-helper.js')
expect(
sanitizeRendererModuleId(
`\0${eventEmitterModule}?commonjs-module`,
projectRoot
)
).toBe(
'virtual:node_modules/eventemitter3/index.js?commonjs-module'
)
for (const sanitized of [
sanitizeRendererModuleId(appModule, projectRoot),
sanitizeRendererModuleId(g6Module, projectRoot),
sanitizeRendererModuleId(
`\0${eventEmitterModule}?commonjs-module`,
projectRoot
)
]) {
expect(sanitized).not.toContain(
projectRoot.replaceAll('\\', '/')
)
}
})
it('handles foreign absolute path forms without host path parsing', () => {
const projectRoot = resolve('test-fixtures', 'renderer-project')
expect(
sanitizeRendererModuleId(
'C:\\npm-cache\\node_modules\\@antv\\g6\\esm\\index.js',
projectRoot
)
).toBe('node_modules/@antv/g6/esm/index.js')
expect(() =>
sanitizeRendererModuleId(
'C:\\Users\\private\\outside.ts',
projectRoot
)
).toThrow(/outside the project/u)
expect(() =>
sanitizeRendererModuleId(
'file:///C:/Users/private/outside.ts',
projectRoot
)
).toThrow(/outside the project/u)
expect(() =>
sanitizeRendererModuleId(
'\\\\server\\private\\outside.ts',
projectRoot
)
).toThrow(/outside the project/u)
expect(() =>
sanitizeRendererModuleId(
'/home/private/outside.ts',
projectRoot
)
).toThrow(/outside the project/u)
expect(() =>
sanitizeRendererModuleId(
'\0file:///tmp/private/outside.ts',
projectRoot
)
).toThrow(/virtual module leaks a path/u)
})
})
-361
View File
@@ -1,361 +0,0 @@
// @vitest-environment node
import { createRequire } from 'node:module'
import {
existsSync,
mkdtempSync,
mkdirSync,
rmSync,
writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { describe, expect, it } from 'vitest'
type ManifestItem = {
file: string
src?: string
name?: string
isEntry?: boolean
imports?: string[]
dynamicImports?: string[]
css?: string[]
}
type Manifest = Record<string, ManifestItem>
type Analysis = {
keys: {
initial: string
knowledge: string
graph: string
activity: string
magicNotes: string
settings: string
g6: string[]
}
sections: Record<
string,
{
raw: number
gzip: number
rootRaw: number
rootGzip: number
closureKeys: Set<string>
incrementalKeys: Set<string>
files: string[]
}
>
}
type RendererBundleChecker = {
analyzeRendererManifest: (
manifest: Manifest,
readAsset: (file: string) => Buffer,
moduleManifest: Record<string, string[]>
) => Analysis
checkBudgets: (
analysis: Analysis,
budgets: Record<string, { raw: number; gzip: number }>
) => void
checkRendererBundle: (root: string) => Analysis
collectSynchronousClosure: (
manifest: Manifest,
root: string
) => Set<string>
assertSafeModuleManifest: (
moduleManifest: Record<string, string[]>
) => void
}
const require = createRequire(import.meta.url)
const checker = require(
'../build/check-renderer-bundle.cjs'
) as RendererBundleChecker
function createManifest(
overrides: Partial<Manifest> = {}
): Manifest {
return {
'src/main.tsx': {
file: 'assets/main.js',
src: 'src/main.tsx',
isEntry: true,
imports: ['_shared.js'],
dynamicImports: [
'src/KnowledgeWorkspace.tsx',
'src/ActivityPanel.tsx'
]
},
'_shared.js': {
file: 'assets/shared.js'
},
'src/KnowledgeWorkspace.tsx': {
file: 'assets/KnowledgeWorkspace.js',
src: 'src/KnowledgeWorkspace.tsx',
imports: ['_shared.js'],
dynamicImports: ['src/KnowledgeGraphChart.tsx']
},
'src/KnowledgeGraphChart.tsx': {
file: 'assets/KnowledgeGraphChart.js',
src: 'src/KnowledgeGraphChart.tsx',
imports: ['_shared.js', '_g6.js']
},
'_g6.js': {
file: 'assets/knowledge-graph-g6.js',
name: 'knowledge-graph-g6'
},
'src/ActivityPanel.tsx': {
file: 'assets/ActivityPanel.js',
src: 'src/ActivityPanel.tsx',
imports: ['_shared.js']
},
'src/MagicNotesWorkspace.tsx': {
file: 'assets/MagicNotesWorkspace.js',
src: 'src/MagicNotesWorkspace.tsx',
imports: ['_shared.js'],
css: ['assets/MagicNotesWorkspace.css']
},
'src/SettingsPanel.tsx': {
file: 'assets/SettingsPanel.js',
src: 'src/SettingsPanel.tsx',
imports: ['_shared.js']
},
...overrides
}
}
const readAsset = (file: string): Buffer =>
Buffer.from(file.repeat(4), 'utf8')
const moduleManifest = {
'assets/KnowledgeGraphChart.js': [
'src/KnowledgeGraphChart.tsx',
'node_modules/@antv/g6/esm/index.js'
]
}
function writeBundleFixture(
root: string,
manifest: Manifest
): string {
const rendererRoot = join(root, 'out', 'renderer')
const viteRoot = join(rendererRoot, '.vite')
mkdirSync(viteRoot, { recursive: true })
writeFileSync(
join(viteRoot, 'manifest.json'),
JSON.stringify(manifest)
)
const moduleManifestPath = join(viteRoot, 'module-manifest.json')
writeFileSync(
moduleManifestPath,
JSON.stringify(moduleManifest)
)
const files = new Set(
Object.values(manifest).flatMap((item) => [
item.file,
...(item.css ?? [])
])
)
for (const file of files) {
const assetPath = join(rendererRoot, file)
mkdirSync(dirname(assetPath), { recursive: true })
writeFileSync(assetPath, file)
}
return moduleManifestPath
}
describe('renderer bundle manifest checker', () => {
it('traverses only synchronous manifest imports', () => {
const closure = checker.collectSynchronousClosure(
createManifest(),
'src/main.tsx'
)
expect([...closure].sort()).toEqual([
'_shared.js',
'src/main.tsx'
])
})
it('reports dynamic roots without recounting the initial closure', () => {
const analysis = checker.analyzeRendererManifest(
createManifest(),
readAsset,
moduleManifest
)
expect(analysis.sections.knowledge!.incrementalKeys).toEqual(
new Set(['src/KnowledgeWorkspace.tsx'])
)
expect(analysis.sections.graph!.incrementalKeys).toEqual(
new Set(['src/KnowledgeGraphChart.tsx', '_g6.js'])
)
expect(analysis.sections.activity!.incrementalKeys).toEqual(
new Set(['src/ActivityPanel.tsx'])
)
expect(analysis.sections.magicNotes!.files).toContain(
'assets/MagicNotesWorkspace.css'
)
expect(analysis.sections.magicNotes!.raw).toBe(
Buffer.byteLength(
'assets/MagicNotesWorkspace.js'.repeat(4) +
'assets/MagicNotesWorkspace.css'.repeat(4)
)
)
expect(analysis.sections.initial!.raw).toBeGreaterThan(0)
expect(analysis.sections.initial!.gzip).toBeGreaterThan(0)
const g6Keys: string[] = analysis.keys.g6
expect(g6Keys).toEqual(['src/KnowledgeGraphChart.tsx'])
})
it('reads and gzips each shared asset once per analysis', () => {
const reads = new Map<string, number>()
const analysis = checker.analyzeRendererManifest(
createManifest(),
(file) => {
reads.set(file, (reads.get(file) ?? 0) + 1)
return readAsset(file)
},
moduleManifest
)
const reportedFiles = new Set(
Object.values(analysis.sections).flatMap(
(section) => section.files
)
)
expect([...reads.keys()].sort()).toEqual(
[...reportedFiles].sort()
)
expect([...reads.values()].every((count) => count === 1)).toBe(true)
})
it('rejects graph code in the Knowledge shell synchronous closure', () => {
const manifest = createManifest({
'src/KnowledgeWorkspace.tsx': {
file: 'assets/KnowledgeWorkspace.js',
src: 'src/KnowledgeWorkspace.tsx',
imports: ['_shared.js', 'src/KnowledgeGraphChart.tsx']
}
})
expect(() =>
checker.analyzeRendererManifest(manifest, readAsset, moduleManifest)
).toThrow(/Knowledge shell.*KnowledgeGraphChart/u)
})
it('rejects Activity in the synchronous initial closure', () => {
const manifest = createManifest({
'src/main.tsx': {
file: 'assets/main.js',
src: 'src/main.tsx',
isEntry: true,
imports: ['_shared.js', 'src/ActivityPanel.tsx']
}
})
expect(() =>
checker.analyzeRendererManifest(
manifest,
readAsset,
moduleManifest
)
).toThrow(/renderer entry.*ActivityPanel/u)
})
it('enforces raw and gzip budgets', () => {
const analysis = checker.analyzeRendererManifest(
createManifest(),
readAsset,
moduleManifest
)
expect(() =>
checker.checkBudgets(analysis, {
initial: { raw: 1, gzip: Number.MAX_SAFE_INTEGER }
})
).toThrow(/initial raw/u)
})
it('deduplicates aliased output files across manifest keys', () => {
const manifest = createManifest({
'src/main.tsx': {
file: 'assets/main.js',
src: 'src/main.tsx',
isEntry: true,
imports: ['_shared.js', '_shared-alias.js']
},
'_shared-alias.js': {
file: 'assets/shared.js'
}
})
const analysis = checker.analyzeRendererManifest(
manifest,
readAsset,
moduleManifest
)
expect(analysis.sections.initial!.files).toEqual([
'assets/main.js',
'assets/shared.js'
])
expect(analysis.sections.initial!.raw).toBe(
Buffer.byteLength(
'assets/main.js'.repeat(4) +
'assets/shared.js'.repeat(4)
)
)
})
it('rejects absolute paths in the build-only module manifest', () => {
expect(() =>
checker.assertSafeModuleManifest({
'assets/main.js': ['C:/Users/example/project/src/main.tsx']
})
).toThrow(/Unsafe renderer module manifest path/u)
expect(() =>
checker.assertSafeModuleManifest({
'assets/main.js': ['file:///home/example/project/src/main.tsx']
})
).toThrow(/Unsafe renderer module manifest path/u)
})
it('removes the build-only manifest only after a successful check', () => {
const root = mkdtempSync(join(tmpdir(), 'goodbuddy-bundle-'))
try {
const moduleManifestPath = writeBundleFixture(
root,
createManifest()
)
checker.checkRendererBundle(root)
expect(existsSync(moduleManifestPath)).toBe(false)
} finally {
rmSync(root, { recursive: true, force: true })
}
})
it('retains the build-only manifest after a failed check', () => {
const root = mkdtempSync(join(tmpdir(), 'goodbuddy-bundle-'))
try {
const manifest = createManifest({
'src/main.tsx': {
file: 'assets/main.js',
src: 'src/main.tsx',
isEntry: true,
imports: ['src/ActivityPanel.tsx']
}
})
const moduleManifestPath = writeBundleFixture(root, manifest)
expect(() => checker.checkRendererBundle(root)).toThrow(
/renderer entry.*ActivityPanel/u
)
expect(existsSync(moduleManifestPath)).toBe(true)
} finally {
rmSync(root, { recursive: true, force: true })
}
})
})