feat: layer runtime and model selection across conversation, project and global

- Store runtime selection as { provider?, model? } layers and resolve them per
  field (conversation -> project -> global); drop the legacy auto provider.
- Migrate stored selections (database v49) and clear deleted profile ids.
- Re-resolve selections in Main per request and at queue dispatch.
- Add ProjectRuntimeSelector and a two-column RuntimeModelPicker; manage
  link opens model connections; restore per-runtime default model tabs.
- Stop duplicating trusted instructions and history across OpenCode,
  Continue and DeepSeek Harness runtimes.
- Document the layered design, global provider removal plan and planned
  purpose-specific models.
This commit is contained in:
mesalogo
2026-09-29 21:33:06 +08:00
parent d1b92deff3
commit 9c88ca20f3
50 changed files with 2879 additions and 1836 deletions
@@ -1,7 +1,10 @@
import { join } from 'node:path'
import { SqliteRuntimeSessionBindingStore } from '../agent/runtime-session-binding-store'
import type { ResolvedModelProfile } from '../runtime-settings-store'
import type { AgentRuntimeSelection } from '../../shared/runtime-selection-contracts'
import type {
AgentRuntimeSelection,
RuntimeSelectionLayer
} from '../../shared/runtime-selection-contracts'
import type { SshHostStore } from '../ssh/ssh-host-store'
import {
resolveBundledRemoteRuntimeResourcePaths,
@@ -44,7 +47,7 @@ export type ManagedRemoteExecutionServicesOptions = {
selection: AgentRuntimeSelection
): Promise<ResolvedModelProfile | undefined>
resolveRuntimeSelection(
selection: AgentRuntimeSelection
layer: RuntimeSelectionLayer | undefined
): Promise<AgentRuntimeSelection>
}
@@ -1,7 +1,10 @@
import { EventEmitter } from 'node:events'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { AssistantProject } from '../../shared/assistant-contracts'
import type { AgentRuntimeSelection } from '../../shared/runtime-selection-contracts'
import type {
AgentRuntimeSelection,
RuntimeSelectionLayer
} from '../../shared/runtime-selection-contracts'
import type { SshConnectionTarget } from '../ssh/ssh-host-store'
import { verifyAgentInstallationId } from '../ssh/ssh-agent-command'
import type { AgentInstallationIdentity } from './agent-installation-manager'
@@ -110,7 +113,7 @@ type HarnessOptions = {
create?: () => AssistantProject
update?: () => AssistantProject
resolveRuntimeSelection?: (
selection: AgentRuntimeSelection
layer: RuntimeSelectionLayer | undefined
) => Promise<AgentRuntimeSelection>
}
@@ -262,7 +265,7 @@ function harness(options: HarnessOptions = {}) {
},
resolveRuntimeSelection:
options.resolveRuntimeSelection ??
(async (selection) => selection),
(async (layer) => ({ provider: layer?.provider === 'continue' ? 'continue' : 'opencode' }) as AgentRuntimeSelection),
runtimeValidator: {
validate: vi.fn(async () => {
calls.push('runtime')
@@ -11,7 +11,10 @@ import {
remoteWorkspaceValidateResultSchema,
type RemoteWorkspaceHandle
} from '../../shared/remote-agent-contracts'
import type { AgentRuntimeSelection } from '../../shared/runtime-selection-contracts'
import type {
AgentRuntimeSelection,
RuntimeSelectionLayer
} from '../../shared/runtime-selection-contracts'
import type {
AssistantDatabase,
SshProjectWrite
@@ -99,11 +102,11 @@ type ActiveSave = {
type RemoteProjectPreparationDraft = Readonly<{
hostId: string
remoteRootPath: string
runtimeSelection: AgentRuntimeSelection
runtimeSelection?: RuntimeSelectionLayer
}>
type PreparedRemoteProject = Readonly<{
runtimeSelection: AgentRuntimeSelection
runtimeSelection?: RuntimeSelectionLayer
target: SshConnectionTarget
installation: AgentInstallationIdentity
connection: RemoteAgentConnection
@@ -125,7 +128,7 @@ export type RemoteProjectSaveServiceOptions = {
installationManager: Pick<AgentInstallationManager, 'activateInstalled'>
connectionManager: Pick<RemoteAgentConnectionManager, 'acquire'>
resolveRuntimeSelection(
selection: AgentRuntimeSelection
layer: RuntimeSelectionLayer | undefined
): Promise<AgentRuntimeSelection>
runtimeValidator?: RemoteProjectRuntimeValidator
notify?: (