fix(todo-continuation-enforcer): add plan agent to DEFAULT_SKIP_AGENTS to prevent infinite loop
The todo-continuation-enforcer injects continuation prompts when sessions go idle with pending todos. When Plan Mode agents (which are read-only) create todo items, the continuation prompt contradicts Plan Mode's STRICTLY FORBIDDEN directive, causing an infinite loop where the agent acknowledges the conflict then goes idle, triggering another injection. Adding 'plan' to DEFAULT_SKIP_AGENTS prevents continuation injection into Plan Mode sessions, matching the same exclusion pattern already used for prometheus and compaction agents. Fixes #2526
This commit is contained in:
@@ -38,7 +38,7 @@ session.idle
|
||||
## CONSTANTS
|
||||
|
||||
```typescript
|
||||
DEFAULT_SKIP_AGENTS = ["prometheus", "compaction"]
|
||||
DEFAULT_SKIP_AGENTS = ["prometheus", "compaction", "plan"]
|
||||
CONTINUATION_COOLDOWN_MS = 30_000 // 30s between injections
|
||||
MAX_CONSECUTIVE_FAILURES = 5 // Then 5min pause (exponential backoff)
|
||||
FAILURE_RESET_WINDOW_MS = 5 * 60_000 // 5min window for failure reset
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createSystemDirective, SystemDirectiveTypes } from "../../shared/system
|
||||
|
||||
export const HOOK_NAME = "todo-continuation-enforcer"
|
||||
|
||||
export const DEFAULT_SKIP_AGENTS = ["prometheus", "compaction"]
|
||||
export const DEFAULT_SKIP_AGENTS = ["prometheus", "compaction", "plan"]
|
||||
|
||||
export const CONTINUATION_PROMPT = `${createSystemDirective(SystemDirectiveTypes.TODO_CONTINUATION)}
|
||||
|
||||
|
||||
@@ -47,4 +47,38 @@ describe("injectContinuation", () => {
|
||||
expect(capturedTools).toEqual({ question: false, bash: true })
|
||||
expect(capturedText).toContain(OMO_INTERNAL_INITIATOR_MARKER)
|
||||
})
|
||||
|
||||
test("skips injection when agent is plan (prevents Plan Mode infinite loop)", async () => {
|
||||
// given
|
||||
let injected = false
|
||||
const ctx = {
|
||||
directory: "/tmp/test",
|
||||
client: {
|
||||
session: {
|
||||
todo: async () => ({ data: [{ id: "1", content: "todo", status: "pending", priority: "high" }] }),
|
||||
promptAsync: async () => {
|
||||
injected = true
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const sessionStateStore = {
|
||||
getExistingState: () => ({ inFlight: false, lastInjectedAt: 0, consecutiveFailures: 0 }),
|
||||
}
|
||||
|
||||
// when
|
||||
await injectContinuation({
|
||||
ctx: ctx as never,
|
||||
sessionID: "ses_plan_skip",
|
||||
resolvedInfo: {
|
||||
agent: "plan",
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
|
||||
},
|
||||
sessionStateStore: sessionStateStore as never,
|
||||
})
|
||||
|
||||
// then
|
||||
expect(injected).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user