fix(acp): surface prompt errors (#34061)
This commit is contained in:
parent
971518c6d9
commit
6e8e772582
3 changed files with 136 additions and 26 deletions
|
|
@ -10,7 +10,7 @@ import type {
|
|||
SessionConfigSelectOption,
|
||||
SetSessionConfigOptionResponse,
|
||||
} from "@agentclientprotocol/sdk"
|
||||
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
|
||||
import type { AssistantMessage, OpencodeClient } from "@opencode-ai/sdk/v2"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { Effect } from "effect"
|
||||
|
|
@ -144,7 +144,10 @@ const provider: Provider.Info = {
|
|||
describe("ACP service sessions", () => {
|
||||
const makeService = (
|
||||
messages: readonly { info: unknown; parts: readonly unknown[] }[] = [],
|
||||
options?: { abort?: (input: { sessionID: string }) => Promise<{ data: boolean }> },
|
||||
options?: {
|
||||
abort?: (input: { sessionID: string }) => Promise<{ data: boolean }>
|
||||
prompt?: (input: unknown) => Promise<{ data: { info: ReturnType<typeof assistantInfo> } }>
|
||||
},
|
||||
) => {
|
||||
const updates: SessionNotification[] = []
|
||||
const mcpAdds: string[] = []
|
||||
|
|
@ -193,19 +196,21 @@ describe("ACP service sessions", () => {
|
|||
data: input.directory ? sessions.filter((session) => session.directory === input.directory) : sessions,
|
||||
}),
|
||||
messages: () => Promise.resolve({ data: messages }),
|
||||
prompt: (input: unknown) => {
|
||||
prompts.push(input)
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
info: assistantInfo({
|
||||
input: 100,
|
||||
output: 40,
|
||||
reasoning: 7,
|
||||
cache: { read: 11, write: 13 },
|
||||
}),
|
||||
},
|
||||
})
|
||||
},
|
||||
prompt:
|
||||
options?.prompt ??
|
||||
((input: unknown) => {
|
||||
prompts.push(input)
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
info: assistantInfo({
|
||||
input: 100,
|
||||
output: 40,
|
||||
reasoning: 7,
|
||||
cache: { read: 11, write: 13 },
|
||||
}),
|
||||
},
|
||||
})
|
||||
}),
|
||||
command: (input: unknown) => {
|
||||
commands.push(input)
|
||||
return Promise.resolve({
|
||||
|
|
@ -994,6 +999,52 @@ describe("ACP service sessions", () => {
|
|||
expect(usageUpdates).toEqual([session.sessionId])
|
||||
})
|
||||
|
||||
it("maps assistant prompt errors to request errors instead of end turn", async () => {
|
||||
const { service } = makeService([], {
|
||||
prompt: () =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
info: assistantInfo(
|
||||
{ input: 8, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
{ name: "APIError", data: { message: "Provider request failed", isRetryable: false } },
|
||||
),
|
||||
},
|
||||
}),
|
||||
})
|
||||
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||
|
||||
const error = await Effect.runPromise(
|
||||
service
|
||||
.prompt({ sessionId: session.sessionId, prompt: [{ type: "text", text: "hello" }] })
|
||||
.pipe(Effect.mapError(ACPError.toRequestError), Effect.flip),
|
||||
)
|
||||
|
||||
expect(error.code).toBe(-32603)
|
||||
expect(error.message).toBe("Internal error: Provider request failed")
|
||||
expect(error.data).toEqual({ service: "session", errorName: "APIError" })
|
||||
})
|
||||
|
||||
it("maps aborted assistant prompt errors to cancelled", async () => {
|
||||
const { service } = makeService([], {
|
||||
prompt: () =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
info: assistantInfo(
|
||||
{ input: 8, output: 1, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
{ name: "MessageAbortedError", data: { message: "Aborted" } },
|
||||
),
|
||||
},
|
||||
}),
|
||||
})
|
||||
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||
|
||||
const result = await Effect.runPromise(
|
||||
service.prompt({ sessionId: session.sessionId, prompt: [{ type: "text", text: "hello" }] }),
|
||||
)
|
||||
|
||||
expect(result.stopReason).toBe("cancelled")
|
||||
})
|
||||
|
||||
it("prompt maps assistant and user audience annotations", async () => {
|
||||
const { service, prompts } = makeService()
|
||||
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||
|
|
@ -1145,13 +1196,17 @@ describe("ACP service sessions", () => {
|
|||
})
|
||||
})
|
||||
|
||||
function assistantInfo(tokens: UsageService.AssistantTokenCost["tokens"]): UsageService.AssistantMessage {
|
||||
function assistantInfo(
|
||||
tokens: UsageService.AssistantTokenCost["tokens"],
|
||||
error?: AssistantMessage["error"],
|
||||
): UsageService.AssistantMessage & Pick<AssistantMessage, "error"> {
|
||||
return {
|
||||
role: "assistant",
|
||||
providerID: "test",
|
||||
modelID: "test-model",
|
||||
cost: 0,
|
||||
tokens,
|
||||
...(error ? { error } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue