diff --git a/src/services/acp/__tests__/agent.test.ts b/src/services/acp/__tests__/agent.test.ts index 706010d5d..b3c58777b 100644 --- a/src/services/acp/__tests__/agent.test.ts +++ b/src/services/acp/__tests__/agent.test.ts @@ -325,13 +325,17 @@ describe('AcpAgent', () => { expect(res.sessionId.length).toBeGreaterThan(0) }) - test('returns modes and configOptions (models omitted for v1 compliance)', async () => { + test('returns modes, configOptions, and models (clients need models to populate selector)', async () => { const agent = new AcpAgent(makeConn()) const res = await agent.newSession({ cwd: '/tmp' } as any) expect(res.modes).toBeDefined() expect(res.configOptions).toBeDefined() - // Stable v1 NewSessionResponse does not define `models` - expect((res as any).models).toBeUndefined() + // SDK 0.19.2 marks NewSessionResponse.models as UNSTABLE but the schema allows it, and + // standard clients (Cursor/Zed/VS Code) read it to populate the model selector. Omitting + // it forces supportsModelSelection=false on the client. + expect(res.models).toBeDefined() + expect(Array.isArray(res.models!.availableModels)).toBe(true) + expect(typeof res.models!.currentModelId).toBe('string') }) test('each call returns a unique sessionId', async () => { @@ -865,8 +869,8 @@ describe('AcpAgent', () => { } as any) expect(agent.sessions.has(requestedId)).toBe(true) expect(res.modes).toBeDefined() - // models is omitted for v1 compliance - expect((res as any).models).toBeUndefined() + // resume also returns models so clients can render the selector after reconnect. + expect(res.models).toBeDefined() }) test('reuses existing session when sessionId matches and fingerprint unchanged', async () => { diff --git a/src/services/acp/agent/createSessionMethod.ts b/src/services/acp/agent/createSessionMethod.ts index 8e2cc04c2..5e1d0ff74 100644 --- a/src/services/acp/agent/createSessionMethod.ts +++ b/src/services/acp/agent/createSessionMethod.ts @@ -270,11 +270,16 @@ async function createSession( this.sessions.set(sessionId, session) - // Stable v1 NewSessionResponse only defines sessionId/modes/configOptions. - // `models` is a draft/unstable field — omit it for v1 compliance. + // Return models even though SDK 0.19.2 marks it UNSTABLE. The schema does allow the field + // (NewSessionResponse.models?: SessionModelState | null), and standard clients (Cursor/Zed/ + // VS Code ACP) rely on it to populate the model selector — omitting it forces + // supportsModelSelection=false on the client and the user can never switch models. + // The UNSTABLE marker only means "this field may change in a future schema version", not + // "agents MUST NOT return it". The previous "v1 compliance" omission was overzealous. return { sessionId, modes, + models, configOptions, } } finally { diff --git a/src/services/acp/agent/sessionLifecycle.ts b/src/services/acp/agent/sessionLifecycle.ts index 0b6eb7e03..50d23d332 100644 --- a/src/services/acp/agent/sessionLifecycle.ts +++ b/src/services/acp/agent/sessionLifecycle.ts @@ -79,6 +79,9 @@ async function getOrCreateSession( return { sessionId: params.sessionId, modes: existingSession.modes, + // Carry models over on reconnect so the client keeps its model selector + // populated (standard clients gate supportsModelSelection on this field). + models: existingSession.models, configOptions: existingSession.configOptions, } } @@ -150,6 +153,8 @@ async function getOrCreateSession( return { sessionId: response.sessionId, modes: response.modes, + // createSession already returns models; pass it through. Same reason as above. + models: response.models, configOptions: response.configOptions, } }