fix(hooks): make /stop-continuation one-time only and respect in session recovery
- Clear stop state when user sends new message (chat.message handler) - Add isContinuationStopped check to session error recovery block - Continuation resumes automatically after user interaction
This commit is contained in:
parent
e63c568c4f
commit
4b5e38f8f8
@ -103,4 +103,42 @@ describe("stop-continuation-guard", () => {
|
|||||||
expect(guard.isStopped(session1)).toBe(true)
|
expect(guard.isStopped(session1)).toBe(true)
|
||||||
expect(guard.isStopped(session2)).toBe(false)
|
expect(guard.isStopped(session2)).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should clear stopped state on new user message (chat.message)", async () => {
|
||||||
|
// #given - a session that was stopped
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const sessionID = "test-session-4"
|
||||||
|
guard.stop(sessionID)
|
||||||
|
expect(guard.isStopped(sessionID)).toBe(true)
|
||||||
|
|
||||||
|
// #when - user sends a new message
|
||||||
|
await guard["chat.message"]({ sessionID })
|
||||||
|
|
||||||
|
// #then - stop state should be cleared (one-time only)
|
||||||
|
expect(guard.isStopped(sessionID)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should not affect non-stopped sessions on chat.message", async () => {
|
||||||
|
// #given - a session that was never stopped
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const sessionID = "test-session-5"
|
||||||
|
|
||||||
|
// #when - user sends a message (session was never stopped)
|
||||||
|
await guard["chat.message"]({ sessionID })
|
||||||
|
|
||||||
|
// #then - should not throw and session remains not stopped
|
||||||
|
expect(guard.isStopped(sessionID)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should handle undefined sessionID in chat.message", async () => {
|
||||||
|
// #given - a guard with a stopped session
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
guard.stop("some-session")
|
||||||
|
|
||||||
|
// #when - chat.message is called without sessionID
|
||||||
|
await guard["chat.message"]({ sessionID: undefined })
|
||||||
|
|
||||||
|
// #then - should not throw and stopped session remains stopped
|
||||||
|
expect(guard.isStopped("some-session")).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const HOOK_NAME = "stop-continuation-guard"
|
|||||||
|
|
||||||
export interface StopContinuationGuard {
|
export interface StopContinuationGuard {
|
||||||
event: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
|
event: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
|
||||||
|
"chat.message": (input: { sessionID?: string }) => Promise<void>
|
||||||
stop: (sessionID: string) => void
|
stop: (sessionID: string) => void
|
||||||
isStopped: (sessionID: string) => boolean
|
isStopped: (sessionID: string) => boolean
|
||||||
clear: (sessionID: string) => void
|
clear: (sessionID: string) => void
|
||||||
@ -45,8 +46,20 @@ export function createStopContinuationGuardHook(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chatMessage = async ({
|
||||||
|
sessionID,
|
||||||
|
}: {
|
||||||
|
sessionID?: string
|
||||||
|
}): Promise<void> => {
|
||||||
|
if (sessionID && stoppedSessions.has(sessionID)) {
|
||||||
|
clear(sessionID)
|
||||||
|
log(`[${HOOK_NAME}] Cleared stop state on new user message`, { sessionID })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
event,
|
event,
|
||||||
|
"chat.message": chatMessage,
|
||||||
stop,
|
stop,
|
||||||
isStopped,
|
isStopped,
|
||||||
clear,
|
clear,
|
||||||
|
|||||||
@ -429,6 +429,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await stopContinuationGuard?.["chat.message"]?.(input);
|
||||||
await keywordDetector?.["chat.message"]?.(input, output);
|
await keywordDetector?.["chat.message"]?.(input, output);
|
||||||
await claudeCodeHooks["chat.message"]?.(input, output);
|
await claudeCodeHooks["chat.message"]?.(input, output);
|
||||||
await autoSlashCommand?.["chat.message"]?.(input, output);
|
await autoSlashCommand?.["chat.message"]?.(input, output);
|
||||||
@ -591,7 +592,12 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
const recovered =
|
const recovered =
|
||||||
await sessionRecovery.handleSessionRecovery(messageInfo);
|
await sessionRecovery.handleSessionRecovery(messageInfo);
|
||||||
|
|
||||||
if (recovered && sessionID && sessionID === getMainSessionID()) {
|
if (
|
||||||
|
recovered &&
|
||||||
|
sessionID &&
|
||||||
|
sessionID === getMainSessionID() &&
|
||||||
|
!stopContinuationGuard?.isStopped(sessionID)
|
||||||
|
) {
|
||||||
await ctx.client.session
|
await ctx.client.session
|
||||||
.prompt({
|
.prompt({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user