fix(agent-switch): clear fallback markers on session.error

processedFallbackMessages was only cleaned up on session.deleted, not session.error. This could leak memory for errored sessions. Mirrors the existing session.deleted cleanup pattern.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
ismeth
2026-02-19 13:53:17 +01:00
committed by YeonGyu-Kim
parent 74e519e545
commit d30d80abbd
2 changed files with 63 additions and 0 deletions

View File

@@ -139,6 +139,54 @@ describe("agent-switch hook", () => {
expect(getPendingSwitch("ses-4")).toBeUndefined()
})
test("clears pending switch on session.error with info.id", async () => {
const ctx = {
client: {
session: {
promptAsync: async () => {},
messages: async () => ({ data: [] }),
message: async () => ({ data: { parts: [] } }),
},
},
} as any
setPendingSwitch("ses-10", "atlas", "fix this")
const hook = createAgentSwitchHook(ctx)
await hook.event({
event: {
type: "session.error",
properties: { info: { id: "ses-10" } },
},
})
expect(getPendingSwitch("ses-10")).toBeUndefined()
})
test("clears pending switch on session.error with sessionID property", async () => {
const ctx = {
client: {
session: {
promptAsync: async () => {},
messages: async () => ({ data: [] }),
message: async () => ({ data: { parts: [] } }),
},
},
} as any
setPendingSwitch("ses-11", "atlas", "fix this")
const hook = createAgentSwitchHook(ctx)
await hook.event({
event: {
type: "session.error",
properties: { sessionID: "ses-11" },
},
})
expect(getPendingSwitch("ses-11")).toBeUndefined()
})
test("recovers missing switch_agent tool call from Athena handoff text", async () => {
const promptAsyncCalls: Array<Record<string, unknown>> = []
let switched = false

View File

@@ -56,6 +56,21 @@ export function createAgentSwitchHook(ctx: PluginInput) {
return
}
if (input.event.type === "session.error") {
const props = input.event.properties as Record<string, unknown> | undefined
const info = props?.info as Record<string, unknown> | undefined
const erroredSessionID = info?.id ?? props?.sessionID
if (typeof erroredSessionID === "string") {
clearPendingSwitchRuntime(erroredSessionID)
for (const key of Array.from(processedFallbackMessages)) {
if (key.startsWith(`${erroredSessionID}:`)) {
processedFallbackMessages.delete(key)
}
}
}
return
}
if (input.event.type === "message.updated") {
const props = input.event.properties as Record<string, unknown> | undefined
const info = props?.info as Record<string, unknown> | undefined