fix(runtime-fallback): inject pluginConfig and defer cleanup interval to first event

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-04-18 14:12:41 +09:00
parent d8e00ebfbc
commit 76e8508fa4
2 changed files with 22 additions and 17 deletions
+2 -1
View File
@@ -107,9 +107,10 @@ describe("createRuntimeFallbackHook dispose", () => {
globalThis.clearTimeout = originalClearTimeout globalThis.clearTimeout = originalClearTimeout
}) })
test("#given runtime-fallback hook created #when dispose() is called #then cleanup interval is cleared", () => { test("#given runtime-fallback hook handles its first event #when dispose() is called #then cleanup interval is cleared", async () => {
// given // given
const hook = createRuntimeFallbackHook(createMockContext(), { pluginConfig: {} }) const hook = createRuntimeFallbackHook(createMockContext(), { pluginConfig: {} })
await hook.event({ event: { type: "session.created", properties: {} } })
// when // when
hook.dispose?.() hook.dispose?.()
+20 -16
View File
@@ -1,7 +1,5 @@
import type { HookDeps, RuntimeFallbackHook, RuntimeFallbackInterval, RuntimeFallbackOptions, RuntimeFallbackPluginInput, RuntimeFallbackTimeout } from "./types" import type { HookDeps, RuntimeFallbackHook, RuntimeFallbackInterval, RuntimeFallbackOptions, RuntimeFallbackPluginInput, RuntimeFallbackTimeout } from "./types"
import { DEFAULT_CONFIG, HOOK_NAME } from "./constants" import { DEFAULT_CONFIG } from "./constants"
import { log } from "../../shared/logger"
import { loadPluginConfig } from "../../plugin-config"
import { createAutoRetryHelpers } from "./auto-retry" import { createAutoRetryHelpers } from "./auto-retry"
import { createEventHandler } from "./event-handler" import { createEventHandler } from "./event-handler"
import { createMessageUpdateHandler } from "./message-update-handler" import { createMessageUpdateHandler } from "./message-update-handler"
@@ -24,20 +22,11 @@ export function createRuntimeFallbackHook(
notify_on_fallback: options?.config?.notify_on_fallback ?? DEFAULT_CONFIG.notify_on_fallback, notify_on_fallback: options?.config?.notify_on_fallback ?? DEFAULT_CONFIG.notify_on_fallback,
} }
let pluginConfig = options?.pluginConfig
if (!pluginConfig) {
try {
pluginConfig = loadPluginConfig(ctx.directory, ctx)
} catch {
log(`[${HOOK_NAME}] Plugin config not available`)
}
}
const deps: HookDeps = { const deps: HookDeps = {
ctx, ctx,
config, config,
options, options,
pluginConfig, pluginConfig: options?.pluginConfig,
sessionStates: new Map(), sessionStates: new Map(),
sessionLastAccess: new Map(), sessionLastAccess: new Map(),
sessionRetryInFlight: new Set(), sessionRetryInFlight: new Set(),
@@ -51,10 +40,23 @@ export function createRuntimeFallbackHook(
const messageUpdateHandler = createMessageUpdateHandler(deps, helpers) const messageUpdateHandler = createMessageUpdateHandler(deps, helpers)
const chatMessageHandler = createChatMessageHandler(deps) const chatMessageHandler = createChatMessageHandler(deps)
const cleanupInterval = setInterval(helpers.cleanupStaleSessions, 5 * 60 * 1000) let cleanupInterval: RuntimeFallbackInterval | null = null
cleanupInterval.unref() let intervalStarted = false
const ensureInterval = (): void => {
if (intervalStarted) return
intervalStarted = true
cleanupInterval = setInterval(helpers.cleanupStaleSessions, 5 * 60 * 1000)
if (typeof cleanupInterval.unref === "function") {
cleanupInterval.unref()
}
}
const eventHandler = async ({ event }: { event: { type: string; properties?: unknown } }) => { const eventHandler = async ({ event }: { event: { type: string; properties?: unknown } }) => {
ensureInterval()
if (event.type === "message.updated") { if (event.type === "message.updated") {
if (!config.enabled) return if (!config.enabled) return
const props = event.properties as Record<string, unknown> | undefined const props = event.properties as Record<string, unknown> | undefined
@@ -65,7 +67,9 @@ export function createRuntimeFallbackHook(
} }
const dispose = () => { const dispose = () => {
clearInterval(cleanupInterval) if (cleanupInterval) {
clearInterval(cleanupInterval)
}
for (const fallbackTimeout of deps.sessionFallbackTimeouts.values()) { for (const fallbackTimeout of deps.sessionFallbackTimeouts.values()) {
clearTimeout(fallbackTimeout) clearTimeout(fallbackTimeout)