fix: address Cubic findings for runtime fallback child sessions

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-03-12 11:07:14 +09:00
parent ba86ef0eea
commit 3caa3fcc3d
8 changed files with 35 additions and 19 deletions
+2 -1
View File
@@ -30,7 +30,7 @@ declare function clearTimeout(timeout: TimerHandle): void
>>>>>>> b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357)) >>>>>>> b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357))
export function createAutoRetryHelpers(deps: HookDeps) { export function createAutoRetryHelpers(deps: HookDeps) {
const { ctx, config, options, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, pluginConfig } = deps const { ctx, config, options, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, pluginConfig, sessionStatusRetryKeys } = deps
const abortSessionRequest = async (sessionID: string, source: string): Promise<void> => { const abortSessionRequest = async (sessionID: string, source: string): Promise<void> => {
try { try {
@@ -201,6 +201,7 @@ export function createAutoRetryHelpers(deps: HookDeps) {
sessionAwaitingFallbackResult.delete(sessionID) sessionAwaitingFallbackResult.delete(sessionID)
clearSessionFallbackTimeout(sessionID) clearSessionFallbackTimeout(sessionID)
SessionCategoryRegistry.remove(sessionID) SessionCategoryRegistry.remove(sessionID)
sessionStatusRetryKeys.delete(sessionID)
cleanedCount++ cleanedCount++
} }
} }
@@ -1,5 +1,4 @@
declare const require: (name: string) => any import { describe, expect, test } from "bun:test"
const { describe, expect, test } = require("bun:test")
import { classifyErrorType, extractAutoRetrySignal, isRetryableError } from "./error-classifier" import { classifyErrorType, extractAutoRetrySignal, isRetryableError } from "./error-classifier"
+1 -2
View File
@@ -11,8 +11,7 @@ import { dispatchFallbackRetry } from "./fallback-retry-dispatcher"
import { createSessionStatusHandler } from "./session-status-handler" import { createSessionStatusHandler } from "./session-status-handler"
export function createEventHandler(deps: HookDeps, helpers: AutoRetryHelpers) { export function createEventHandler(deps: HookDeps, helpers: AutoRetryHelpers) {
const { config, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts } = deps const { config, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps
const sessionStatusRetryKeys = new Map<string, string>()
const sessionStatusHandler = createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) const sessionStatusHandler = createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys)
const handleSessionCreated = (props: Record<string, unknown> | undefined) => { const handleSessionCreated = (props: Record<string, unknown> | undefined) => {
@@ -32,6 +32,20 @@ export function resolveFallbackBootstrapModel(
return agentModel return agentModel
} }
const agentCategory = typeof agentConfig?.category === "string" ? agentConfig.category : undefined
if (agentCategory) {
const agentCategoryModel = options.pluginConfig?.categories?.[agentCategory]?.model
if (typeof agentCategoryModel === "string" && agentCategoryModel.length > 0) {
log(`[${HOOK_NAME}] Derived model from agent category config for ${options.source}`, {
sessionID: options.sessionID,
agent: options.resolvedAgent,
category: agentCategory,
model: agentCategoryModel,
})
return agentCategoryModel
}
}
const sessionCategory = SessionCategoryRegistry.get(options.sessionID) const sessionCategory = SessionCategoryRegistry.get(options.sessionID)
const categoryModel = sessionCategory const categoryModel = sessionCategory
? options.pluginConfig?.categories?.[sessionCategory]?.model ? options.pluginConfig?.categories?.[sessionCategory]?.model
+1
View File
@@ -43,6 +43,7 @@ export function createRuntimeFallbackHook(
sessionRetryInFlight: new Set(), sessionRetryInFlight: new Set(),
sessionAwaitingFallbackResult: new Set(), sessionAwaitingFallbackResult: new Set(),
sessionFallbackTimeouts: new Map(), sessionFallbackTimeouts: new Map(),
sessionStatusRetryKeys: new Map(),
} }
const helpers = createAutoRetryHelpers(deps) const helpers = createAutoRetryHelpers(deps)
+1 -2
View File
@@ -1,5 +1,4 @@
declare const require: (name: string) => any import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"
const { describe, expect, test, beforeEach, afterEach, spyOn } = require("bun:test")
import { createRuntimeFallbackHook } from "./index" import { createRuntimeFallbackHook } from "./index"
import type { RuntimeFallbackConfig, OhMyOpenCodeConfig } from "../../config" import type { RuntimeFallbackConfig, OhMyOpenCodeConfig } from "../../config"
import * as sharedModule from "../../shared" import * as sharedModule from "../../shared"
@@ -1,14 +1,22 @@
type SessionMessagesResponse = { type MessageItem = {
data?: Array<{ info?: Record<string, unknown>
info?: Record<string, unknown> parts?: Array<{ type?: string; text?: string }>
parts?: Array<{ type?: string; text?: string }> }
}>
function extractMessages(messagesResponse: unknown): MessageItem[] | undefined {
if (messagesResponse == null) return undefined
if (Array.isArray(messagesResponse)) return messagesResponse as MessageItem[]
if (typeof messagesResponse === "object") {
const data = (messagesResponse as Record<string, unknown>).data
if (Array.isArray(data)) return data as MessageItem[]
}
return undefined
} }
export function getLastUserRetryParts( export function getLastUserRetryParts(
messagesResponse: unknown, messagesResponse: unknown,
): Array<{ type: "text"; text: string }> { ): Array<{ type: "text"; text: string }> {
const messages = (messagesResponse as SessionMessagesResponse).data const messages = extractMessages(messagesResponse)
const lastUserMessage = messages?.filter((message) => message.info?.role === "user").pop() const lastUserMessage = messages?.filter((message) => message.info?.role === "user").pop()
const lastUserParts = const lastUserParts =
lastUserMessage?.parts lastUserMessage?.parts
+1 -6
View File
@@ -72,11 +72,6 @@ export interface HookDeps {
sessionLastAccess: Map<string, number> sessionLastAccess: Map<string, number>
sessionRetryInFlight: Set<string> sessionRetryInFlight: Set<string>
sessionAwaitingFallbackResult: Set<string> sessionAwaitingFallbackResult: Set<string>
<<<<<<< HEAD
sessionFallbackTimeouts: Map<string, RuntimeFallbackTimeout> sessionFallbackTimeouts: Map<string, RuntimeFallbackTimeout>
||||||| parent of b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357)) sessionStatusRetryKeys: Map<string, string>
sessionFallbackTimeouts: Map<string, ReturnType<typeof setTimeout>>
=======
sessionFallbackTimeouts: Map<string, TimerHandle>
>>>>>>> b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357))
} }