fix(runtime-events): honor OpenCode progress shapes

This commit is contained in:
YeonGyu-Kim
2026-05-19 13:48:45 +09:00
parent 38462aa9d2
commit 44ef5dec1d
6 changed files with 93 additions and 7 deletions
+56
View File
@@ -31,6 +31,7 @@ describe("createEventHandler - model fallback", () => {
const createHandler = (args?: {
hooks?: any
pluginConfig?: any
abort?: (input: { path: { id: string } }) => Promise<unknown>
promptAsync?: (input: { path: { id: string } }) => Promise<unknown>
}) => {
setupConnectedProviderCacheMocks()
@@ -41,6 +42,9 @@ describe("createEventHandler - model fallback", () => {
const sessionClient = {
abort: async ({ path }: { path: { id: string } }) => {
abortCalls.push(path.id)
if (args?.abort) {
return args.abort({ path })
}
return {}
},
prompt: async ({ path }: { path: { id: string } }) => {
@@ -305,6 +309,58 @@ describe("createEventHandler - model fallback", () => {
expect(abortCalls).toEqual([sessionID])
})
test("#given abort fails before model-fallback continuation #when fallback handles assistant error #then it does not inject another prompt", async () => {
//#given
const sessionID = "ses_model_fallback_abort_failure"
setMainSession(sessionID)
let pendingFallbackArms = 0
const modelFallback = unsafeTestValue({
setSessionFallbackChain: () => {},
setPendingModelFallback: () => {
pendingFallbackArms += 1
return true
},
})
const { handler, abortCalls, promptAsyncCalls } = createHandler({
hooks: { modelFallback },
abort: async () => {
throw new Error("abort transport failed")
},
promptAsync: async () => ({}),
})
const assistantError = {
name: "APIError",
data: {
message:
"Bad Gateway: {\"error\":{\"message\":\"unknown provider for model claude-opus-4-7-thinking\"}}",
isRetryable: true,
},
}
//#when
await handler({
event: {
type: "message.updated",
properties: {
info: {
id: "msg_err_abort_failure",
sessionID,
role: "assistant",
error: assistantError,
modelID: "claude-opus-4-7-thinking",
providerID: "anthropic",
agent: "Sisyphus - Ultraworker",
},
},
},
})
//#then
expect(pendingFallbackArms).toBe(1)
expect(abortCalls).toEqual([sessionID])
expect(promptAsyncCalls).toEqual([])
})
test("does not collapse fallback continuations for different providers with the same model id", async () => {
//#given
const sessionID = "ses_model_fallback_same_model_different_provider"
+7 -3
View File
@@ -474,9 +474,12 @@ export function createEventHandler(args: {
modelFallbackContinuationsInFlight.add(sessionID);
let dispatched = false;
try {
await pluginContext.client.session.abort({ path: { id: sessionID } }).catch((error) => {
try {
await pluginContext.client.session.abort({ path: { id: sessionID } });
} catch (error) {
log("[event] model-fallback abort failed", { sessionID, source, error });
});
return;
}
releasePromptAsyncReservation(sessionID, `model-fallback-abort:${source}`, {
reservedBy: [`model-fallback:${source}`, `model-fallback:${source}:sync`],
reservedByPrefix: "model-fallback:",
@@ -755,7 +758,8 @@ export function createEventHandler(args: {
const sessionID = resolveMessageEventSessionID(props);
const agent = info?.agent as string | undefined;
const role = info?.role as string | undefined;
if (sessionID && info?.finish === true) {
const finish = info?.finish;
if (sessionID && ((typeof finish === "string" && finish.length > 0) || finish === true)) {
invalidateContextWindowUsageCache(pluginContext as PluginInput, sessionID);
}
if (sessionID && role === "user") {