From 5691a039de92aea38fdbe1259e2db68b318fa2aa Mon Sep 17 00:00:00 2001 From: can1357 Date: Sat, 13 Jun 2026 00:20:05 +0200 Subject: [PATCH] refactor(packages/coding-agent): restructured acp 0.25 session state - Removed `userMessageId` from `AcpAgent` prompt state and response payloads. - Removed `models` from new/load/resume/fork session responses and deleted model-state building. - Removed `unstable_setSessionModel` and routed model changes through `setSessionConfigOption`. --- .../coding-agent/src/modes/acp/acp-agent.ts | 41 ------------------- packages/coding-agent/test/acp-agent.test.ts | 31 ++++++-------- 2 files changed, 13 insertions(+), 59 deletions(-) diff --git a/packages/coding-agent/src/modes/acp/acp-agent.ts b/packages/coding-agent/src/modes/acp/acp-agent.ts index ca0794376..86b658e67 100644 --- a/packages/coding-agent/src/modes/acp/acp-agent.ts +++ b/packages/coding-agent/src/modes/acp/acp-agent.ts @@ -31,14 +31,11 @@ import { type ResumeSessionResponse, type SessionConfigOption, type SessionInfo, - type SessionModelState, type SessionModeState, type SessionNotification, type SessionUpdate, type SetSessionConfigOptionRequest, type SetSessionConfigOptionResponse, - type SetSessionModelRequest, - type SetSessionModelResponse, type SetSessionModeRequest, type SetSessionModeResponse, type Usage, @@ -121,7 +118,6 @@ type PromptQueueState = { type PromptLifecycleError = Error & { readonly code: "ACP_SESSION_CLOSED" }; type PromptTurnState = { - userMessageId: string; cancelRequested: boolean; settled: boolean; /** @@ -465,7 +461,6 @@ export class AcpAgent implements Agent { const response: NewSessionResponse = { sessionId: record.session.sessionId, configOptions: this.#buildConfigOptions(record.session), - models: this.#buildModelState(record.session), modes: this.#buildModeState(record.session), }; this.#scheduleBootstrapUpdates(record.session.sessionId); @@ -478,7 +473,6 @@ export class AcpAgent implements Agent { await this.#replaySessionHistory(record); const response: LoadSessionResponse = { configOptions: this.#buildConfigOptions(record.session), - models: this.#buildModelState(record.session), modes: this.#buildModeState(record.session), }; this.#scheduleBootstrapUpdates(record.session.sessionId); @@ -507,7 +501,6 @@ export class AcpAgent implements Agent { const record = await this.#resumeManagedSession(params.sessionId, params.cwd, params.mcpServers ?? []); const response: ResumeSessionResponse = { configOptions: this.#buildConfigOptions(record.session), - models: this.#buildModelState(record.session), modes: this.#buildModeState(record.session), }; this.#scheduleBootstrapUpdates(record.session.sessionId); @@ -520,7 +513,6 @@ export class AcpAgent implements Agent { const response: ForkSessionResponse = { sessionId: record.session.sessionId, configOptions: this.#buildConfigOptions(record.session), - models: this.#buildModelState(record.session), modes: this.#buildModeState(record.session), }; this.#scheduleBootstrapUpdates(record.session.sessionId); @@ -588,13 +580,6 @@ export class AcpAgent implements Agent { return { configOptions: this.#buildConfigOptions(record.session) }; } - async unstable_setSessionModel(params: SetSessionModelRequest): Promise { - const record = this.#getSessionRecord(params.sessionId); - await this.#setModelById(record.session, params.modelId); - await this.#pushConfigOptionUpdate(record); - return {}; - } - async prompt(params: PromptRequest): Promise { const record = this.#getSessionRecord(params.sessionId); const activeTurn = record.promptTurn; @@ -633,7 +618,6 @@ export class AcpAgent implements Agent { const converted = this.#convertPromptBlocks(params.prompt); const pendingPrompt = Promise.withResolvers(); record.promptTurn = { - userMessageId: params.messageId ?? crypto.randomUUID(), cancelRequested: false, settled: false, cleanup: undefined, @@ -766,7 +750,6 @@ export class AcpAgent implements Agent { this.#cloneUsageStatistics(record.session.sessionManager.getUsageStatistics()), record.session.sessionManager.getUsageStatistics(), ), - userMessageId: promptTurn?.userMessageId, }); return; } @@ -844,7 +827,6 @@ export class AcpAgent implements Agent { this.#finishPrompt(record, { stopReason: "cancelled", usage: this.#buildTurnUsage(promptTurn.usageBaseline, record.session.sessionManager.getUsageStatistics()), - userMessageId: promptTurn.userMessageId, }); return cleanup; } @@ -1162,7 +1144,6 @@ export class AcpAgent implements Agent { this.#finishPrompt(record, { stopReason: this.#resolveStopReason(event, promptTurn.cancelRequested), usage: this.#buildTurnUsage(promptTurn.usageBaseline, record.session.sessionManager.getUsageStatistics()), - userMessageId: promptTurn.userMessageId, }); } } @@ -1385,28 +1366,6 @@ export class AcpAgent implements Agent { return configOptions; } - #buildModelState(session: AgentSession): SessionModelState | undefined { - const models = session.getAvailableModels(); - if (models.length === 0) { - return undefined; - } - - const availableModels = models.map(model => ({ - modelId: this.#toModelId(model), - name: model.name, - description: `${model.provider}/${model.id}`, - })); - const currentModelId = session.model ? this.#toModelId(session.model) : availableModels[0]?.modelId; - if (!currentModelId) { - return undefined; - } - - return { - availableModels, - currentModelId, - }; - } - #buildThinkingOptions(session: AgentSession): Array<{ value: string; name: string; description?: string }> { return [ { value: THINKING_OFF, name: "Off" }, diff --git a/packages/coding-agent/test/acp-agent.test.ts b/packages/coding-agent/test/acp-agent.test.ts index a3d0f664b..1dc44230f 100644 --- a/packages/coding-agent/test/acp-agent.test.ts +++ b/packages/coding-agent/test/acp-agent.test.ts @@ -488,13 +488,16 @@ describe("ACP agent", () => { expectAcpStructure(zNewSessionResponse, first); expectAcpStructure(zNewSessionResponse, second); - expect(first.models?.availableModels.map(model => model.modelId)).toEqual( + const modelOption = first.configOptions?.find(opt => opt.id === "model"); + expect(modelOption?.type).toBe("select"); + expect((modelOption as any).options?.map((opt: any) => opt.value)).toEqual( TEST_MODELS.map(model => `${model.provider}/${model.id}`), ); - await harness.agent.unstable_setSessionModel({ + await harness.agent.setSessionConfigOption({ sessionId: first.sessionId, - modelId: `${TEST_MODELS[1]!.provider}/${TEST_MODELS[1]!.id}`, + configId: "model", + value: `${TEST_MODELS[1]!.provider}/${TEST_MODELS[1]!.id}`, }); await harness.agent.setSessionConfigOption({ sessionId: first.sessionId, @@ -914,16 +917,14 @@ describe("ACP agent", () => { const live = await harness.agent.newSession({ cwd: harness.cwdB, mcpServers: [] }); const response = await harness.agent.prompt({ sessionId: live.sessionId, - messageId: "05b17a6f-b310-4be7-b767-6b4f3a84eb63", prompt: [{ type: "text", text: "ping" }], - } as PromptRequest); + }); expectAcpStructure(zPromptResponse, response); expectAcpNotifications(harness.updates); const liveChunks = harness.updates.filter( update => update.sessionId === live.sessionId && update.update.sessionUpdate === "agent_message_chunk", ); - expect(response.userMessageId).toBe("05b17a6f-b310-4be7-b767-6b4f3a84eb63"); expect(response.usage).toEqual({ inputTokens: 10, outputTokens: 5, @@ -1508,9 +1509,8 @@ describe("ACP agent", () => { const firstPrompt = harness.agent.prompt({ sessionId: created.sessionId, - messageId: "00000000-0000-4000-8000-000000000029", prompt: [{ type: "text", text: "wait for cleanup" }], - } as PromptRequest); + }); await idleBlocked; try { @@ -1519,8 +1519,7 @@ describe("ACP agent", () => { expect(session.waitForIdleCalls).toBe(1); unblockIdle(); - const response = await firstPrompt; - expect(response.userMessageId).toBe("00000000-0000-4000-8000-000000000029"); + await firstPrompt; } finally { unblockIdle(); harness.abortController.abort(); @@ -1548,9 +1547,8 @@ describe("ACP agent", () => { const prompt = harness.agent.prompt({ sessionId: created.sessionId, - messageId: "00000000-0000-4000-8000-000000000047", prompt: [{ type: "text", text: "wait for async delivery" }], - } as PromptRequest); + }); await deliveryBlocked.promise; try { @@ -1559,8 +1557,7 @@ describe("ACP agent", () => { expect(session.waitForIdleCalls).toBe(1); releaseDelivery(); - const response = await prompt; - expect(response.userMessageId).toBe("00000000-0000-4000-8000-000000000047"); + await prompt; expect(session.waitForIdleCalls).toBe(2); expect(drainCalls).toBe(2); } finally { @@ -1903,16 +1900,14 @@ describe("ACP agent", () => { const created = await harness.agent.newSession({ cwd: harness.cwdA, mcpServers: [] }); const session = harness.findSession(created.sessionId)!; - const response = await harness.agent.prompt({ + await harness.agent.prompt({ sessionId: created.sessionId, - messageId: "00000000-0000-4000-8000-000000000002", prompt: [{ type: "text", text: "/fast status" }], - } as PromptRequest); + }); const chunks = harness.updates.filter( update => update.sessionId === created.sessionId && update.update.sessionUpdate === "agent_message_chunk", ); - expect(response.userMessageId).toBe("00000000-0000-4000-8000-000000000002"); expect(session.promptCalls).toEqual([]); expect( chunks.some(