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
@@ -838,6 +838,37 @@ describe('DeepSeekHarnessRuntime', () => {
await harness.runtime.dispose()
})
it('sends GoodBuddy history only when it creates the native session', async () => {
const harness = setup()
const history = [
{ role: 'user' as const, content: 'earlier question' },
{ role: 'assistant' as const, content: 'earlier answer' }
]
for (const [index, prompt] of ['first', 'second'].entries()) {
const running = collect(
harness.runtime.run(
{ ...request('reused'), requestId: `request-${prompt}`, prompt, history },
new AbortController().signal
)
)
await vi.waitFor(() =>
expect(harness.promptGates).toHaveLength(index + 1)
)
harness.promptGates[index]!.resolve({ stopReason: 'end_turn' })
await running
}
const texts = harness.requests
.filter(({ method }) => method === 'session/prompt')
.map(({ params }) =>
(params.prompt as Array<{ text?: string }>)[0]?.text
)
expect(texts[0]).toContain('<conversation-history>')
expect(texts[0]).toContain('earlier answer')
expect(texts[1]).toBe('second')
await harness.runtime.dispose()
})
it('fails Ask closed and never calls the authorizer', async () => {
const harness = setup()
const authorize = vi.fn().mockResolvedValue('once')