2026-02-22 17:19:39 +09:00
|
|
|
import type { OhMyOpenCodeConfig } from "../config"
|
2026-05-11 13:25:29 +09:00
|
|
|
import { isCompactionAgent } from "../shared/compaction-marker"
|
2026-05-12 11:48:18 +09:00
|
|
|
import { resolveMessageEventSessionID, resolveSessionEventID } from "../shared/event-session-id"
|
2026-04-18 01:50:41 +09:00
|
|
|
import type { ContextLimitModelCacheState } from "../shared/context-limit-resolver"
|
2026-02-16 14:58:59 +09:00
|
|
|
|
2026-03-18 16:13:23 +09:00
|
|
|
import { createPostCompactionDegradationMonitor } from "./preemptive-compaction-degradation-monitor"
|
2026-04-18 01:50:41 +09:00
|
|
|
import { runPreemptiveCompactionIfNeeded } from "./preemptive-compaction-trigger"
|
|
|
|
|
import type {
|
|
|
|
|
CachedCompactionState,
|
|
|
|
|
PreemptiveCompactionContext,
|
|
|
|
|
TokenInfo,
|
|
|
|
|
} from "./preemptive-compaction-types"
|
2026-02-02 22:40:59 +09:00
|
|
|
|
2026-02-17 10:33:17 +09:00
|
|
|
export function createPreemptiveCompactionHook(
|
2026-04-18 01:50:41 +09:00
|
|
|
ctx: PreemptiveCompactionContext,
|
2026-02-22 17:19:39 +09:00
|
|
|
pluginConfig: OhMyOpenCodeConfig,
|
2026-03-13 12:36:07 +09:00
|
|
|
modelCacheState?: ContextLimitModelCacheState,
|
2026-02-17 10:33:17 +09:00
|
|
|
) {
|
2026-02-02 22:40:59 +09:00
|
|
|
const compactionInProgress = new Set<string>()
|
|
|
|
|
const compactedSessions = new Set<string>()
|
2026-03-27 13:59:06 +09:00
|
|
|
const lastCompactionTime = new Map<string, number>()
|
2026-02-12 11:38:11 +09:00
|
|
|
const tokenCache = new Map<string, CachedCompactionState>()
|
2026-02-02 22:40:59 +09:00
|
|
|
|
2026-03-18 16:13:23 +09:00
|
|
|
const postCompactionMonitor = createPostCompactionDegradationMonitor({
|
|
|
|
|
client: ctx.client,
|
|
|
|
|
directory: ctx.directory,
|
|
|
|
|
pluginConfig,
|
|
|
|
|
tokenCache,
|
|
|
|
|
compactionInProgress,
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-02 22:40:59 +09:00
|
|
|
const toolExecuteAfter = async (
|
|
|
|
|
input: { tool: string; sessionID: string; callID: string },
|
|
|
|
|
_output: { title: string; output: string; metadata: unknown }
|
|
|
|
|
) => {
|
2026-04-18 01:50:41 +09:00
|
|
|
await runPreemptiveCompactionIfNeeded({
|
|
|
|
|
ctx,
|
|
|
|
|
pluginConfig,
|
2026-03-13 12:36:07 +09:00
|
|
|
modelCacheState,
|
2026-04-18 01:50:41 +09:00
|
|
|
sessionID: input.sessionID,
|
|
|
|
|
tokenCache,
|
|
|
|
|
compactionInProgress,
|
|
|
|
|
compactedSessions,
|
|
|
|
|
lastCompactionTime,
|
|
|
|
|
})
|
2026-02-02 22:40:59 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const eventHandler = async ({ event }: { event: { type: string; properties?: unknown } }) => {
|
|
|
|
|
const props = event.properties as Record<string, unknown> | undefined
|
2026-02-12 11:38:11 +09:00
|
|
|
|
|
|
|
|
if (event.type === "session.deleted") {
|
2026-05-12 11:48:18 +09:00
|
|
|
const sessionID = resolveSessionEventID(props)
|
2026-03-18 16:13:23 +09:00
|
|
|
if (sessionID) {
|
|
|
|
|
compactionInProgress.delete(sessionID)
|
|
|
|
|
compactedSessions.delete(sessionID)
|
2026-03-27 13:59:06 +09:00
|
|
|
lastCompactionTime.delete(sessionID)
|
2026-03-18 16:13:23 +09:00
|
|
|
tokenCache.delete(sessionID)
|
|
|
|
|
postCompactionMonitor.clear(sessionID)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.type === "session.compacted") {
|
2026-05-12 11:48:18 +09:00
|
|
|
const sessionID = resolveSessionEventID(props)
|
2026-03-18 16:13:23 +09:00
|
|
|
if (sessionID) {
|
|
|
|
|
postCompactionMonitor.onSessionCompacted(sessionID)
|
2026-02-12 11:38:11 +09:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.type === "message.updated") {
|
|
|
|
|
const info = props?.info as {
|
2026-03-18 16:13:23 +09:00
|
|
|
id?: string
|
2026-05-11 13:25:29 +09:00
|
|
|
agent?: unknown
|
2026-02-12 11:38:11 +09:00
|
|
|
role?: string
|
|
|
|
|
sessionID?: string
|
|
|
|
|
providerID?: string
|
|
|
|
|
modelID?: string
|
2026-05-19 13:48:45 +09:00
|
|
|
finish?: unknown
|
2026-02-12 11:38:11 +09:00
|
|
|
tokens?: TokenInfo
|
2026-04-28 17:59:50 +09:00
|
|
|
parts?: unknown
|
2026-02-12 11:38:11 +09:00
|
|
|
} | undefined
|
|
|
|
|
|
2026-05-12 11:48:18 +09:00
|
|
|
const sessionID = resolveMessageEventSessionID(props)
|
2026-05-19 13:48:45 +09:00
|
|
|
const finish = info?.finish
|
|
|
|
|
if (!info || info.role !== "assistant" || !finish || !sessionID) return
|
2026-05-11 13:25:29 +09:00
|
|
|
if (isCompactionAgent(info.agent)) return
|
2026-02-12 11:38:11 +09:00
|
|
|
|
2026-03-18 16:13:23 +09:00
|
|
|
if (info.providerID && info.tokens) {
|
2026-05-12 11:48:18 +09:00
|
|
|
tokenCache.set(sessionID, {
|
2026-03-18 16:13:23 +09:00
|
|
|
providerID: info.providerID,
|
|
|
|
|
modelID: info.modelID ?? "",
|
|
|
|
|
tokens: info.tokens,
|
|
|
|
|
})
|
|
|
|
|
}
|
2026-05-12 11:48:18 +09:00
|
|
|
compactedSessions.delete(sessionID)
|
2026-03-18 16:13:23 +09:00
|
|
|
|
|
|
|
|
await postCompactionMonitor.onAssistantMessageUpdated({
|
2026-05-12 11:48:18 +09:00
|
|
|
sessionID,
|
2026-03-18 16:13:23 +09:00
|
|
|
id: info.id,
|
2026-04-28 17:59:50 +09:00
|
|
|
parts: info.parts,
|
2026-03-18 16:13:23 +09:00
|
|
|
})
|
2026-02-02 22:40:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"tool.execute.after": toolExecuteAfter,
|
|
|
|
|
event: eventHandler,
|
|
|
|
|
}
|
|
|
|
|
}
|