fix: release runtime status probes after checks
Chat startup and Runtime switching previously cached status-only Runtime instances, leaving managed OpenCode servers running without active conversations. Status checks now resolve the selected or configured default Runtime through a disposable probe and release it immediately. Project execution Runtimes remain cached by model selection and execution space, preserving parallel work across different projects. Release note: 修复启动或切换 Runtime 后空闲 OpenCode 服务持续驻留的问题;状态检查完成后会立即回收,多个项目仍可并行执行。
This commit is contained in:
@@ -265,6 +265,29 @@ describe('SelectedRuntimeManager', () => {
|
||||
await manager.dispose()
|
||||
})
|
||||
|
||||
it('disposes a status-probe runtime without caching it', async () => {
|
||||
const probed = runtime()
|
||||
const cached = runtime()
|
||||
const create = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(probed.value)
|
||||
.mockResolvedValueOnce(cached.value)
|
||||
const manager = new SelectedRuntimeManager(create)
|
||||
const selection = { provider: 'opencode' as const }
|
||||
|
||||
await expect(manager.getStatus(selection)).resolves.toMatchObject({
|
||||
available: true
|
||||
})
|
||||
expect(probed.value.getStatus).toHaveBeenCalledOnce()
|
||||
expect(probed.testConnection).not.toHaveBeenCalled()
|
||||
expect(probed.dispose).toHaveBeenCalledOnce()
|
||||
|
||||
await manager.getRuntime(selection)
|
||||
expect(create).toHaveBeenCalledTimes(2)
|
||||
await manager.dispose()
|
||||
expect(cached.dispose).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('disposes a native-inventory runtime without caching it', async () => {
|
||||
const inspected = runtime()
|
||||
const cached = runtime()
|
||||
|
||||
@@ -154,7 +154,16 @@ export class SelectedRuntimeManager implements SelectedRuntimeResolver {
|
||||
async getStatus(
|
||||
selection: AgentRuntimeSelection
|
||||
): Promise<AgentRuntimeStatus> {
|
||||
return (await this.getRuntime(selection)).getStatus()
|
||||
if (this.disposed) {
|
||||
throw new Error('Agent Runtime 正在关闭')
|
||||
}
|
||||
const operation = this.runStatusProbe(selection)
|
||||
this.tests.add(operation)
|
||||
try {
|
||||
return await operation
|
||||
} finally {
|
||||
this.tests.delete(operation)
|
||||
}
|
||||
}
|
||||
|
||||
async testStatus(
|
||||
@@ -276,6 +285,20 @@ export class SelectedRuntimeManager implements SelectedRuntimeResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private async runStatusProbe(
|
||||
selection: AgentRuntimeSelection
|
||||
): Promise<AgentRuntimeStatus> {
|
||||
const runtime = await this.createRuntime(selection)
|
||||
try {
|
||||
if (this.disposed) {
|
||||
throw new Error('Agent Runtime 正在关闭')
|
||||
}
|
||||
return await runtime.getStatus()
|
||||
} finally {
|
||||
await runtime.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private async runNativeSnapshot(
|
||||
selection: AgentRuntimeSelection,
|
||||
executionSpace?: ExecutionSpaceDescriptor
|
||||
|
||||
@@ -6277,6 +6277,23 @@ describe('registerIpcHandlers agent terminal state', () => {
|
||||
expect(selectedRuntimes.getStatus).toHaveBeenCalledWith(
|
||||
secondSelection
|
||||
)
|
||||
harness.getResolvedSettings.mockResolvedValueOnce({
|
||||
provider: 'opencode',
|
||||
defaultModelProfileId: firstProfileId,
|
||||
opencodeBaseUrl: '',
|
||||
opencodeEmbedded: true,
|
||||
opencodeModelSource: { kind: 'platform' },
|
||||
continueModelSource: { kind: 'platform' }
|
||||
})
|
||||
await expect(
|
||||
harness.statusHandler?.(event)
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({ label: 'model-two' })
|
||||
)
|
||||
expect(selectedRuntimes.getStatus).toHaveBeenLastCalledWith({
|
||||
provider: 'opencode'
|
||||
})
|
||||
expect(fallbackRuntime.getStatus).not.toHaveBeenCalled()
|
||||
|
||||
await Promise.all([
|
||||
harness.handler?.(event, {
|
||||
|
||||
+10
-4
@@ -2837,12 +2837,18 @@ export function registerIpcHandlers(
|
||||
return tracked
|
||||
}, false)
|
||||
|
||||
registerHandler(ipcChannels.agentStatus, (event, input: unknown) => {
|
||||
registerHandler(ipcChannels.agentStatus, async (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const selection = agentRuntimeSelectionSchema.optional().parse(input)
|
||||
return selection && selectedRuntimes
|
||||
? selectedRuntimes.getStatus(selection)
|
||||
: runtime.getStatus()
|
||||
if (!selectedRuntimes) {
|
||||
return runtime.getStatus()
|
||||
}
|
||||
const effectiveSelection =
|
||||
selection ??
|
||||
getDefaultRuntimeSelection(
|
||||
await settingsStore.getResolvedSettings()
|
||||
)
|
||||
return selectedRuntimes.getStatus(effectiveSelection)
|
||||
})
|
||||
|
||||
registerHandler(ipcChannels.browserStop, async (event, input: unknown) => {
|
||||
|
||||
Reference in New Issue
Block a user