fix(todo-continuation-enforcer): only show countdown when incomplete todos exist in main session
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) Changes: - Add main session check: skip toast for subagent sessions - Move todo validation BEFORE countdown: only start countdown when incomplete todos actually exist - Improve toast message to show remaining task count This fixes the issue where countdown toast was showing on every idle event, even when no todos existed or in subagent sessions.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { existsSync, readdirSync } from "node:fs"
|
import { existsSync, readdirSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
|
import { getMainSessionID } from "../features/claude-code-session-state"
|
||||||
import {
|
import {
|
||||||
findNearestMessageWithFields,
|
findNearestMessageWithFields,
|
||||||
MESSAGE_STORAGE,
|
MESSAGE_STORAGE,
|
||||||
@@ -112,6 +113,12 @@ export function createTodoContinuationEnforcer(ctx: PluginInput): TodoContinuati
|
|||||||
|
|
||||||
log(`[${HOOK_NAME}] session.idle received`, { sessionID })
|
log(`[${HOOK_NAME}] session.idle received`, { sessionID })
|
||||||
|
|
||||||
|
const mainSessionID = getMainSessionID()
|
||||||
|
if (mainSessionID && sessionID !== mainSessionID) {
|
||||||
|
log(`[${HOOK_NAME}] Skipped: not main session`, { sessionID, mainSessionID })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const existingCountdown = pendingCountdowns.get(sessionID)
|
const existingCountdown = pendingCountdowns.get(sessionID)
|
||||||
if (existingCountdown) {
|
if (existingCountdown) {
|
||||||
clearInterval(existingCountdown.intervalId)
|
clearInterval(existingCountdown.intervalId)
|
||||||
@@ -119,21 +126,6 @@ export function createTodoContinuationEnforcer(ctx: PluginInput): TodoContinuati
|
|||||||
log(`[${HOOK_NAME}] Cancelled existing countdown`, { sessionID })
|
log(`[${HOOK_NAME}] Cancelled existing countdown`, { sessionID })
|
||||||
}
|
}
|
||||||
|
|
||||||
const showCountdownToast = async (seconds: number): Promise<void> => {
|
|
||||||
await ctx.client.tui.showToast({
|
|
||||||
body: {
|
|
||||||
title: "Todo Continuation",
|
|
||||||
message: `Resuming in ${seconds}s...`,
|
|
||||||
variant: "warning" as const,
|
|
||||||
duration: TOAST_DURATION_MS,
|
|
||||||
},
|
|
||||||
}).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
const executeAfterCountdown = async (): Promise<void> => {
|
|
||||||
pendingCountdowns.delete(sessionID)
|
|
||||||
log(`[${HOOK_NAME}] Countdown finished, checking conditions`, { sessionID })
|
|
||||||
|
|
||||||
// Check if session is in recovery mode - if so, skip entirely without clearing state
|
// Check if session is in recovery mode - if so, skip entirely without clearing state
|
||||||
if (recoveringSessions.has(sessionID)) {
|
if (recoveringSessions.has(sessionID)) {
|
||||||
log(`[${HOOK_NAME}] Skipped: session in recovery mode`, { sessionID })
|
log(`[${HOOK_NAME}] Skipped: session in recovery mode`, { sessionID })
|
||||||
@@ -142,10 +134,9 @@ export function createTodoContinuationEnforcer(ctx: PluginInput): TodoContinuati
|
|||||||
|
|
||||||
const shouldBypass = interruptedSessions.has(sessionID) || errorSessions.has(sessionID)
|
const shouldBypass = interruptedSessions.has(sessionID) || errorSessions.has(sessionID)
|
||||||
|
|
||||||
|
if (shouldBypass) {
|
||||||
interruptedSessions.delete(sessionID)
|
interruptedSessions.delete(sessionID)
|
||||||
errorSessions.delete(sessionID)
|
errorSessions.delete(sessionID)
|
||||||
|
|
||||||
if (shouldBypass) {
|
|
||||||
log(`[${HOOK_NAME}] Skipped: error/interrupt bypass`, { sessionID })
|
log(`[${HOOK_NAME}] Skipped: error/interrupt bypass`, { sessionID })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -155,6 +146,7 @@ export function createTodoContinuationEnforcer(ctx: PluginInput): TodoContinuati
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for incomplete todos BEFORE starting countdown
|
||||||
let todos: Todo[] = []
|
let todos: Todo[] = []
|
||||||
try {
|
try {
|
||||||
log(`[${HOOK_NAME}] Fetching todos for session`, { sessionID })
|
log(`[${HOOK_NAME}] Fetching todos for session`, { sessionID })
|
||||||
@@ -182,16 +174,38 @@ export function createTodoContinuationEnforcer(ctx: PluginInput): TodoContinuati
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`[${HOOK_NAME}] Found incomplete todos`, { sessionID, incomplete: incomplete.length, total: todos.length })
|
log(`[${HOOK_NAME}] Found incomplete todos, starting countdown`, { sessionID, incomplete: incomplete.length, total: todos.length })
|
||||||
remindedSessions.add(sessionID)
|
|
||||||
|
|
||||||
// Re-check if abort occurred during the delay/fetch
|
const showCountdownToast = async (seconds: number): Promise<void> => {
|
||||||
if (interruptedSessions.has(sessionID) || errorSessions.has(sessionID) || recoveringSessions.has(sessionID)) {
|
await ctx.client.tui.showToast({
|
||||||
log(`[${HOOK_NAME}] Abort occurred during delay/fetch`, { sessionID })
|
body: {
|
||||||
remindedSessions.delete(sessionID)
|
title: "Todo Continuation",
|
||||||
|
message: `Resuming in ${seconds}s... (${incomplete.length} tasks remaining)`,
|
||||||
|
variant: "warning" as const,
|
||||||
|
duration: TOAST_DURATION_MS,
|
||||||
|
},
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const executeAfterCountdown = async (): Promise<void> => {
|
||||||
|
pendingCountdowns.delete(sessionID)
|
||||||
|
log(`[${HOOK_NAME}] Countdown finished, executing continuation`, { sessionID })
|
||||||
|
|
||||||
|
// Re-check conditions after countdown
|
||||||
|
if (recoveringSessions.has(sessionID)) {
|
||||||
|
log(`[${HOOK_NAME}] Abort: session entered recovery mode during countdown`, { sessionID })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (interruptedSessions.has(sessionID) || errorSessions.has(sessionID)) {
|
||||||
|
log(`[${HOOK_NAME}] Abort: error/interrupt occurred during countdown`, { sessionID })
|
||||||
|
interruptedSessions.delete(sessionID)
|
||||||
|
errorSessions.delete(sessionID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
remindedSessions.add(sessionID)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get previous message's agent info to respect agent mode
|
// Get previous message's agent info to respect agent mode
|
||||||
const messageDir = getMessageDir(sessionID)
|
const messageDir = getMessageDir(sessionID)
|
||||||
|
|||||||
Reference in New Issue
Block a user