11import type { PluginInput } from "@opencode-ai/plugin"
22
3+ export interface TodoContinuationEnforcer {
4+ handler : ( input : { event : { type : string ; properties ?: unknown } } ) => Promise < void >
5+ markRecovering : ( sessionID : string ) => void
6+ markRecoveryComplete : ( sessionID : string ) => void
7+ }
8+
39interface Todo {
410 content : string
511 status : string
@@ -32,13 +38,22 @@ function detectInterrupt(error: unknown): boolean {
3238 return false
3339}
3440
35- export function createTodoContinuationEnforcer ( ctx : PluginInput ) {
41+ export function createTodoContinuationEnforcer ( ctx : PluginInput ) : TodoContinuationEnforcer {
3642 const remindedSessions = new Set < string > ( )
3743 const interruptedSessions = new Set < string > ( )
3844 const errorSessions = new Set < string > ( )
45+ const recoveringSessions = new Set < string > ( )
3946 const pendingTimers = new Map < string , ReturnType < typeof setTimeout > > ( )
4047
41- return async ( { event } : { event : { type : string ; properties ?: unknown } } ) => {
48+ const markRecovering = ( sessionID : string ) : void => {
49+ recoveringSessions . add ( sessionID )
50+ }
51+
52+ const markRecoveryComplete = ( sessionID : string ) : void => {
53+ recoveringSessions . delete ( sessionID )
54+ }
55+
56+ const handler = async ( { event } : { event : { type : string ; properties ?: unknown } } ) : Promise < void > => {
4257 const props = event . properties as Record < string , unknown > | undefined
4358
4459 if ( event . type === "session.error" ) {
@@ -73,6 +88,11 @@ export function createTodoContinuationEnforcer(ctx: PluginInput) {
7388 const timer = setTimeout ( async ( ) => {
7489 pendingTimers . delete ( sessionID )
7590
91+ // Check if session is in recovery mode - if so, skip entirely without clearing state
92+ if ( recoveringSessions . has ( sessionID ) ) {
93+ return
94+ }
95+
7696 const shouldBypass = interruptedSessions . has ( sessionID ) || errorSessions . has ( sessionID )
7797
7898 interruptedSessions . delete ( sessionID )
@@ -111,7 +131,7 @@ export function createTodoContinuationEnforcer(ctx: PluginInput) {
111131 remindedSessions . add ( sessionID )
112132
113133 // Re-check if abort occurred during the delay/fetch
114- if ( interruptedSessions . has ( sessionID ) || errorSessions . has ( sessionID ) ) {
134+ if ( interruptedSessions . has ( sessionID ) || errorSessions . has ( sessionID ) || recoveringSessions . has ( sessionID ) ) {
115135 remindedSessions . delete ( sessionID )
116136 return
117137 }
@@ -158,6 +178,7 @@ export function createTodoContinuationEnforcer(ctx: PluginInput) {
158178 remindedSessions . delete ( sessionInfo . id )
159179 interruptedSessions . delete ( sessionInfo . id )
160180 errorSessions . delete ( sessionInfo . id )
181+ recoveringSessions . delete ( sessionInfo . id )
161182
162183 // Cancel pending continuation
163184 const timer = pendingTimers . get ( sessionInfo . id )
@@ -168,4 +189,10 @@ export function createTodoContinuationEnforcer(ctx: PluginInput) {
168189 }
169190 }
170191 }
192+
193+ return {
194+ handler,
195+ markRecovering,
196+ markRecoveryComplete,
197+ }
171198}
0 commit comments