oh-my-opencode/src/plugin/hooks/create-continuation-hooks.ts
ismeth 5a72f21fc8 refactor(athena): rename session_handoff to switch_agent to avoid confusion with /handoff command
Rename across all layers to eliminate naming ambiguity:
- Tool: session_handoff → switch_agent
- Hook: agent-handoff → agent-switch
- Feature: agent-handoff/ → agent-switch/
- Types: SessionHandoffArgs → SwitchAgentArgs, PendingHandoff → PendingSwitch
- Functions: setPendingHandoff → setPendingSwitch, consumePendingHandoff → consumePendingSwitch

/handoff = inter-session context summary (existing command)
switch_agent = intra-session active agent change (our new tool)
2026-02-24 22:20:54 +09:00

131 lines
4.6 KiB
TypeScript

import type { HookName, OhMyOpenCodeConfig } from "../../config"
import type { BackgroundManager } from "../../features/background-agent"
import type { PluginContext } from "../types"
import {
createTodoContinuationEnforcer,
createBackgroundNotificationHook,
createStopContinuationGuardHook,
createCompactionContextInjector,
createCompactionTodoPreserverHook,
createAtlasHook,
createAgentSwitchHook,
} from "../../hooks"
import { safeCreateHook } from "../../shared/safe-create-hook"
import { createUnstableAgentBabysitter } from "../unstable-agent-babysitter"
export type ContinuationHooks = {
stopContinuationGuard: ReturnType<typeof createStopContinuationGuardHook> | null
compactionContextInjector: ReturnType<typeof createCompactionContextInjector> | null
compactionTodoPreserver: ReturnType<typeof createCompactionTodoPreserverHook> | null
todoContinuationEnforcer: ReturnType<typeof createTodoContinuationEnforcer> | null
unstableAgentBabysitter: ReturnType<typeof createUnstableAgentBabysitter> | null
backgroundNotificationHook: ReturnType<typeof createBackgroundNotificationHook> | null
atlasHook: ReturnType<typeof createAtlasHook> | null
agentSwitchHook: ReturnType<typeof createAgentSwitchHook> | null
}
type SessionRecovery = {
setOnAbortCallback: (callback: (sessionID: string) => void) => void
setOnRecoveryCompleteCallback: (callback: (sessionID: string) => void) => void
} | null
export function createContinuationHooks(args: {
ctx: PluginContext
pluginConfig: OhMyOpenCodeConfig
isHookEnabled: (hookName: HookName) => boolean
safeHookEnabled: boolean
backgroundManager: BackgroundManager
sessionRecovery: SessionRecovery
}): ContinuationHooks {
const {
ctx,
pluginConfig,
isHookEnabled,
safeHookEnabled,
backgroundManager,
sessionRecovery,
} = args
const safeHook = <T>(hookName: HookName, factory: () => T): T | null =>
safeCreateHook(hookName, factory, { enabled: safeHookEnabled })
const stopContinuationGuard = isHookEnabled("stop-continuation-guard")
? safeHook("stop-continuation-guard", () => createStopContinuationGuardHook(ctx))
: null
const compactionContextInjector = isHookEnabled("compaction-context-injector")
? safeHook("compaction-context-injector", () => createCompactionContextInjector(backgroundManager))
: null
const compactionTodoPreserver = isHookEnabled("compaction-todo-preserver")
? safeHook("compaction-todo-preserver", () => createCompactionTodoPreserverHook(ctx))
: null
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer")
? safeHook("todo-continuation-enforcer", () =>
createTodoContinuationEnforcer(ctx, {
backgroundManager,
isContinuationStopped: stopContinuationGuard?.isStopped,
}))
: null
const unstableAgentBabysitter = isHookEnabled("unstable-agent-babysitter")
? safeHook("unstable-agent-babysitter", () =>
createUnstableAgentBabysitter({ ctx, backgroundManager, pluginConfig }))
: null
if (sessionRecovery) {
const onAbortCallbacks: Array<(sessionID: string) => void> = []
const onRecoveryCompleteCallbacks: Array<(sessionID: string) => void> = []
if (todoContinuationEnforcer) {
onAbortCallbacks.push(todoContinuationEnforcer.markRecovering)
onRecoveryCompleteCallbacks.push(todoContinuationEnforcer.markRecoveryComplete)
}
if (onAbortCallbacks.length > 0) {
sessionRecovery.setOnAbortCallback((sessionID: string) => {
for (const callback of onAbortCallbacks) callback(sessionID)
})
}
if (onRecoveryCompleteCallbacks.length > 0) {
sessionRecovery.setOnRecoveryCompleteCallback((sessionID: string) => {
for (const callback of onRecoveryCompleteCallbacks) callback(sessionID)
})
}
}
const backgroundNotificationHook = isHookEnabled("background-notification")
? safeHook("background-notification", () => createBackgroundNotificationHook(backgroundManager))
: null
const atlasHook = isHookEnabled("atlas")
? safeHook("atlas", () =>
createAtlasHook(ctx, {
directory: ctx.directory,
backgroundManager,
isContinuationStopped: (sessionID: string) =>
stopContinuationGuard?.isStopped(sessionID) ?? false,
agentOverrides: pluginConfig.agents,
}))
: null
const agentSwitchHook = isHookEnabled("agent-switch")
? safeHook("agent-switch", () => createAgentSwitchHook(ctx))
: null
return {
stopContinuationGuard,
compactionContextInjector,
compactionTodoPreserver,
todoContinuationEnforcer,
unstableAgentBabysitter,
backgroundNotificationHook,
atlasHook,
agentSwitchHook,
}
}