fix(todo-continuation): cancel stale ULW countdown

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-19 09:18:00 +09:00
parent 37bd866c32
commit 6915f15299
2 changed files with 45 additions and 0 deletions
@@ -67,4 +67,35 @@ describe("handleNonIdleEvent", () => {
expect(state.wasCancelled).toBe(true)
expect(state.tokenLimitDetected).toBe(true)
})
test("given ultrawork loop continuation user message update, cancels stale todo continuation countdown", () => {
// given
const sessionID = "ses_ulw_todo_overlap"
const state = sessionStateStore.getState(sessionID)
state.countdownStartedAt = Date.now() - 10_000
state.wasCancelled = true
state.tokenLimitDetected = true
// when
handleNonIdleEvent({
eventType: "message.updated",
properties: {
sessionID,
info: { role: "user" },
parts: [
{
type: "text",
text: `ultrawork [SYSTEM DIRECTIVE: OH-MY-OPENCODE - RALPH LOOP 2/500]\ncontinue\n${OMO_INTERNAL_INITIATOR_MARKER}`,
synthetic: true,
},
],
},
sessionStateStore,
})
// then
expect(state.countdownStartedAt).toBeUndefined()
expect(state.wasCancelled).toBe(true)
expect(state.tokenLimitDetected).toBe(true)
})
})
@@ -2,6 +2,7 @@ import { resolveMessageEventSessionID, resolveSessionEventID } from "../../share
import type { InternalInitiatorTextPartLike } from "../../shared/internal-initiator-marker"
import { isSyntheticOrInternalOnlyTextParts } from "../../shared/internal-initiator-marker"
import { log } from "../../shared/logger"
import { isSystemDirective } from "../../shared/system-directive"
import { COUNTDOWN_GRACE_PERIOD_MS, HOOK_NAME } from "./constants"
import type { SessionStateStore } from "./session-state"
@@ -34,6 +35,14 @@ function resolveEventParts(
return parts
}
function hasInternalSystemDirective(parts: InternalInitiatorTextPartLike[] | undefined): boolean {
return (parts ?? []).some(
(part) => part.type === "text"
&& typeof part.text === "string"
&& isSystemDirective(part.text),
)
}
export function handleNonIdleEvent(args: {
eventType: string
properties: Record<string, unknown> | undefined
@@ -50,6 +59,11 @@ export function handleNonIdleEvent(args: {
if (role === "user") {
const parts = resolveEventParts(properties)
if (isSyntheticOrInternalOnlyTextParts(parts)) {
const state = sessionStateStore.getExistingState(sessionID)
if (state?.countdownStartedAt && hasInternalSystemDirective(parts)) {
sessionStateStore.cancelCountdown(sessionID)
log(`[${HOOK_NAME}] Cancelled countdown for internal continuation message`, { sessionID })
}
log(`[${HOOK_NAME}] Ignoring synthetic/internal user message event`, { sessionID })
return
}