Files
oh-my-opencode/src/hooks/runtime-fallback/session-status-handler.ts
T

96 lines
3.2 KiB
TypeScript

import type { HookDeps } from "./types"
import type { AutoRetryHelpers } from "./auto-retry"
import { HOOK_NAME } from "./constants"
import { log } from "../../shared/logger"
import { extractAutoRetrySignal } from "./error-classifier"
import { createFallbackState } from "./fallback-state"
import { getFallbackModelsForSession } from "./fallback-models"
import { normalizeRetryStatusMessage, extractRetryAttempt } from "../../shared/retry-status-utils"
import { resolveFallbackBootstrapModel } from "./fallback-bootstrap-model"
import { dispatchFallbackRetry } from "./fallback-retry-dispatcher"
export function createSessionStatusHandler(
deps: HookDeps,
helpers: AutoRetryHelpers,
sessionStatusRetryKeys: Map<string, string>,
) {
const {
pluginConfig,
sessionStates,
sessionLastAccess,
sessionRetryInFlight,
} = deps
return async (props: Record<string, unknown> | undefined) => {
const sessionID = props?.sessionID as string | undefined
const status = props?.status as { type?: string; message?: string; attempt?: number } | undefined
const agent = props?.agent as string | undefined
const model = props?.model as string | undefined
if (!sessionID || status?.type !== "retry") return
const retryMessage = typeof status.message === "string" ? status.message : ""
const retrySignal = extractAutoRetrySignal({ status: retryMessage, message: retryMessage })
if (!retrySignal) return
const retryKey = `${extractRetryAttempt(status.attempt, retryMessage)}:${normalizeRetryStatusMessage(retryMessage)}`
if (sessionStatusRetryKeys.get(sessionID) === retryKey) {
return
}
sessionStatusRetryKeys.set(sessionID, retryKey)
if (sessionRetryInFlight.has(sessionID)) {
log(`[${HOOK_NAME}] session.status retry skipped — retry already in flight`, { sessionID })
return
}
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent)
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig)
if (fallbackModels.length === 0) return
let state = sessionStates.get(sessionID)
if (!state) {
const initialModel = resolveFallbackBootstrapModel({
sessionID,
source: "session.status",
eventModel: model,
resolvedAgent,
pluginConfig,
})
if (!initialModel) {
log(`[${HOOK_NAME}] session.status retry missing model info, cannot fallback`, { sessionID })
return
}
state = createFallbackState(initialModel)
sessionStates.set(sessionID, state)
}
sessionLastAccess.set(sessionID, Date.now())
if (state.pendingFallbackModel) {
log(`[${HOOK_NAME}] session.status retry skipped (pending fallback in progress)`, {
sessionID,
pendingFallbackModel: state.pendingFallbackModel,
})
return
}
log(`[${HOOK_NAME}] Detected provider auto-retry signal in session.status`, {
sessionID,
model: state.currentModel,
retryAttempt: status.attempt,
})
await helpers.abortSessionRequest(sessionID, "session.status.retry-signal")
await dispatchFallbackRetry(deps, helpers, {
sessionID,
state,
fallbackModels,
resolvedAgent,
source: "session.status",
})
}
}