From 3caa3fcc3d22d7404b419d0d24d564b60627b032 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 12 Mar 2026 11:07:14 +0900 Subject: [PATCH] fix: address Cubic findings for runtime fallback child sessions Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/hooks/runtime-fallback/auto-retry.ts | 3 ++- .../runtime-fallback/error-classifier.test.ts | 3 +-- src/hooks/runtime-fallback/event-handler.ts | 3 +-- .../fallback-bootstrap-model.ts | 14 +++++++++++++ src/hooks/runtime-fallback/hook.ts | 1 + src/hooks/runtime-fallback/index.test.ts | 3 +-- .../runtime-fallback/last-user-retry-parts.ts | 20 +++++++++++++------ src/hooks/runtime-fallback/types.ts | 7 +------ 8 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/hooks/runtime-fallback/auto-retry.ts b/src/hooks/runtime-fallback/auto-retry.ts index 66e402b98..9679961ab 100644 --- a/src/hooks/runtime-fallback/auto-retry.ts +++ b/src/hooks/runtime-fallback/auto-retry.ts @@ -30,7 +30,7 @@ declare function clearTimeout(timeout: TimerHandle): void >>>>>>> b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357)) 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 => { try { @@ -201,6 +201,7 @@ export function createAutoRetryHelpers(deps: HookDeps) { sessionAwaitingFallbackResult.delete(sessionID) clearSessionFallbackTimeout(sessionID) SessionCategoryRegistry.remove(sessionID) + sessionStatusRetryKeys.delete(sessionID) cleanedCount++ } } diff --git a/src/hooks/runtime-fallback/error-classifier.test.ts b/src/hooks/runtime-fallback/error-classifier.test.ts index 163ccf745..4719737fc 100644 --- a/src/hooks/runtime-fallback/error-classifier.test.ts +++ b/src/hooks/runtime-fallback/error-classifier.test.ts @@ -1,5 +1,4 @@ -declare const require: (name: string) => any -const { describe, expect, test } = require("bun:test") +import { describe, expect, test } from "bun:test" import { classifyErrorType, extractAutoRetrySignal, isRetryableError } from "./error-classifier" diff --git a/src/hooks/runtime-fallback/event-handler.ts b/src/hooks/runtime-fallback/event-handler.ts index daf3519af..71bd1af06 100644 --- a/src/hooks/runtime-fallback/event-handler.ts +++ b/src/hooks/runtime-fallback/event-handler.ts @@ -11,8 +11,7 @@ import { dispatchFallbackRetry } from "./fallback-retry-dispatcher" import { createSessionStatusHandler } from "./session-status-handler" export function createEventHandler(deps: HookDeps, helpers: AutoRetryHelpers) { - const { config, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts } = deps - const sessionStatusRetryKeys = new Map() + const { config, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps const sessionStatusHandler = createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) const handleSessionCreated = (props: Record | undefined) => { diff --git a/src/hooks/runtime-fallback/fallback-bootstrap-model.ts b/src/hooks/runtime-fallback/fallback-bootstrap-model.ts index eacb13fbd..78437d594 100644 --- a/src/hooks/runtime-fallback/fallback-bootstrap-model.ts +++ b/src/hooks/runtime-fallback/fallback-bootstrap-model.ts @@ -32,6 +32,20 @@ export function resolveFallbackBootstrapModel( 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 categoryModel = sessionCategory ? options.pluginConfig?.categories?.[sessionCategory]?.model diff --git a/src/hooks/runtime-fallback/hook.ts b/src/hooks/runtime-fallback/hook.ts index a3dfc9334..bbf8d439b 100644 --- a/src/hooks/runtime-fallback/hook.ts +++ b/src/hooks/runtime-fallback/hook.ts @@ -43,6 +43,7 @@ export function createRuntimeFallbackHook( sessionRetryInFlight: new Set(), sessionAwaitingFallbackResult: new Set(), sessionFallbackTimeouts: new Map(), + sessionStatusRetryKeys: new Map(), } const helpers = createAutoRetryHelpers(deps) diff --git a/src/hooks/runtime-fallback/index.test.ts b/src/hooks/runtime-fallback/index.test.ts index 8bb506e00..3956d26c8 100644 --- a/src/hooks/runtime-fallback/index.test.ts +++ b/src/hooks/runtime-fallback/index.test.ts @@ -1,5 +1,4 @@ -declare const require: (name: string) => any -const { describe, expect, test, beforeEach, afterEach, spyOn } = require("bun:test") +import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test" import { createRuntimeFallbackHook } from "./index" import type { RuntimeFallbackConfig, OhMyOpenCodeConfig } from "../../config" import * as sharedModule from "../../shared" diff --git a/src/hooks/runtime-fallback/last-user-retry-parts.ts b/src/hooks/runtime-fallback/last-user-retry-parts.ts index 1f7f086bb..e845914df 100644 --- a/src/hooks/runtime-fallback/last-user-retry-parts.ts +++ b/src/hooks/runtime-fallback/last-user-retry-parts.ts @@ -1,14 +1,22 @@ -type SessionMessagesResponse = { - data?: Array<{ - info?: Record - parts?: Array<{ type?: string; text?: string }> - }> +type MessageItem = { + info?: Record + 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).data + if (Array.isArray(data)) return data as MessageItem[] + } + return undefined } export function getLastUserRetryParts( messagesResponse: unknown, ): 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 lastUserParts = lastUserMessage?.parts diff --git a/src/hooks/runtime-fallback/types.ts b/src/hooks/runtime-fallback/types.ts index 686053257..ecf18a4f5 100644 --- a/src/hooks/runtime-fallback/types.ts +++ b/src/hooks/runtime-fallback/types.ts @@ -72,11 +72,6 @@ export interface HookDeps { sessionLastAccess: Map sessionRetryInFlight: Set sessionAwaitingFallbackResult: Set -<<<<<<< HEAD sessionFallbackTimeouts: Map -||||||| parent of b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357)) - sessionFallbackTimeouts: Map> -======= - sessionFallbackTimeouts: Map ->>>>>>> b6f740ed (fix: enable runtime fallback for delegated child sessions (#2357)) + sessionStatusRetryKeys: Map }