fix: reuse Runtime after native inventory load
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
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
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
Native capability loading previously cold-started a temporary Runtime and disposed it immediately, so Linux ARM users paid the OpenCode startup cost again on their first request. Native inventory loading now uses the cached execution Runtime, preserving normal idle eviction and lifecycle cleanup. Release note: 优化 Linux ARM 上切换 OpenCode 后的响应速度;原生能力清单与后续对话现在复用同一 Runtime,避免重复冷启动。
This commit is contained in:
@@ -338,7 +338,7 @@ GoodBuddy conversationId -> Harness sessionId + process generation
|
||||
|
||||
### 9.4 释放与退出
|
||||
|
||||
- 原生能力清单通过一次性 Runtime 探测,取得有界快照后立即 dispose,不得因浏览设置或切换项目把 Host 缓存在执行 Runtime 池中。
|
||||
- 原生能力清单由 `SelectedRuntimeManager` 缓存的执行 Runtime 提供,与随后的对话请求共用同一实例,不再为清单单独启动并销毁一次 Host;该实例遵循执行缓存的空闲淘汰、设置变更 reset 和退出 dispose 规则。
|
||||
- 删除或释放对话时调用 `goodbuddy/session/release`。
|
||||
- Runtime dispose 时先拒绝新请求,再取消所有 Session。
|
||||
- Harness Control Plane 完成 Agent、工具和会话清理,Host 完成 Cordis Fiber 与子进程的反向清理。
|
||||
|
||||
@@ -318,13 +318,9 @@ describe('SelectedRuntimeManager', () => {
|
||||
await manager.dispose()
|
||||
})
|
||||
|
||||
it('disposes a native-inventory runtime without caching it', async () => {
|
||||
const inspected = runtime()
|
||||
const cached = runtime()
|
||||
const create = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(inspected.value)
|
||||
.mockResolvedValueOnce(cached.value)
|
||||
it('serves the native inventory from the cached execution runtime', async () => {
|
||||
const shared = runtime()
|
||||
const create = vi.fn(async () => shared.value)
|
||||
const manager = new SelectedRuntimeManager(create)
|
||||
const selection = { provider: 'opencode' as const }
|
||||
|
||||
@@ -337,15 +333,42 @@ describe('SelectedRuntimeManager', () => {
|
||||
provider: 'opencode',
|
||||
inventoryStatus: 'available'
|
||||
})
|
||||
expect(inspected.getNativeSnapshot).toHaveBeenCalledOnce()
|
||||
expect(inspected.dispose).toHaveBeenCalledOnce()
|
||||
expect(shared.getNativeSnapshot).toHaveBeenCalledOnce()
|
||||
expect(shared.dispose).not.toHaveBeenCalled()
|
||||
|
||||
await manager.getRuntime(
|
||||
const controller = await manager.getRuntime(
|
||||
selection,
|
||||
executionSpace('local:project-one')
|
||||
)
|
||||
expect(create).toHaveBeenCalledOnce()
|
||||
await expect(controller.getStatus()).resolves.toMatchObject({
|
||||
available: true
|
||||
})
|
||||
|
||||
await manager.getNativeSnapshot(
|
||||
selection,
|
||||
executionSpace('local:project-two')
|
||||
)
|
||||
expect(create).toHaveBeenCalledTimes(2)
|
||||
await manager.dispose()
|
||||
expect(shared.dispose).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('rejects the native inventory when the runtime lacks it', async () => {
|
||||
const plain = runtime()
|
||||
const withoutInventory: AgentRuntime = { ...plain.value }
|
||||
delete withoutInventory.getNativeSnapshot
|
||||
const manager = new SelectedRuntimeManager(
|
||||
vi.fn(async () => withoutInventory)
|
||||
)
|
||||
|
||||
await expect(
|
||||
manager.getNativeSnapshot({
|
||||
provider: 'model',
|
||||
profileId: '00000000-0000-4000-8000-000000000001'
|
||||
})
|
||||
).rejects.toThrow('当前 Runtime 不支持原生能力清单')
|
||||
await manager.dispose()
|
||||
})
|
||||
|
||||
it('waits for a pending connection-test runtime during shutdown', async () => {
|
||||
|
||||
@@ -355,18 +355,11 @@ export class SelectedRuntimeManager implements SelectedRuntimeResolver {
|
||||
selection: AgentRuntimeSelection,
|
||||
executionSpace?: ExecutionSpaceDescriptor
|
||||
): Promise<RuntimeNativeSnapshot> {
|
||||
const runtime = await this.createRuntime(selection, executionSpace)
|
||||
try {
|
||||
if (this.disposed) {
|
||||
throw new Error('Agent Runtime 正在关闭')
|
||||
}
|
||||
if (!runtime.getNativeSnapshot) {
|
||||
throw new Error('当前 Runtime 不支持原生能力清单')
|
||||
}
|
||||
return await runtime.getNativeSnapshot()
|
||||
} finally {
|
||||
await runtime.dispose()
|
||||
}
|
||||
// The inventory runs on the same cached Runtime that later serves the
|
||||
// conversation, so a slow cold start (embedded OpenCode on Linux ARM)
|
||||
// is paid once instead of once per inventory and again per request.
|
||||
const controller = await this.getRuntime(selection, executionSpace)
|
||||
return controller.getNativeSnapshot()
|
||||
}
|
||||
|
||||
private async startRetiring(
|
||||
|
||||
Reference in New Issue
Block a user