From 56f2a9df3ab45fac9ff94b25f9a5aa09812ac677 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 6 Apr 2026 17:00:55 +0900 Subject: [PATCH] fix(ralph-loop): detect oracle VERIFIED tool results in session messages Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../completion-promise-detector.test.ts | 25 +++++++++++++++- .../ralph-loop/completion-promise-detector.ts | 29 ++++++++++++++----- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/hooks/ralph-loop/completion-promise-detector.test.ts b/src/hooks/ralph-loop/completion-promise-detector.test.ts index 317883a6c..814684068 100644 --- a/src/hooks/ralph-loop/completion-promise-detector.test.ts +++ b/src/hooks/ralph-loop/completion-promise-detector.test.ts @@ -89,7 +89,7 @@ describe("detectCompletionInSessionMessages", () => { }) describe("#given promise appears in tool_result part (not text part)", () => { - test("#when Oracle returns VERIFIED via task() tool_result #then should NOT detect completion", async () => { + test("#when Oracle returns VERIFIED via task() tool_result #then should detect completion", async () => { const messages = [ { info: { role: "assistant" }, @@ -116,6 +116,29 @@ describe("detectCompletionInSessionMessages", () => { sinceMessageIndex: 0, }) + expect(detected).toBe(true) + }) + + test("#when non-Oracle tool_result returns VERIFIED #then should NOT detect completion", async () => { + const messages = [ + { + info: { role: "assistant" }, + parts: [ + { type: "tool_result", text: "Agent: explore\n\nVERIFIED" }, + { type: "text", text: "Explore finished checking." }, + ], + }, + ] + const ctx = createPluginInput(messages) + + const detected = await detectCompletionInSessionMessages(ctx, { + sessionID: "session-123", + promise: "VERIFIED", + apiTimeoutMs: 1000, + directory: "/tmp", + sinceMessageIndex: 0, + }) + expect(detected).toBe(false) }) diff --git a/src/hooks/ralph-loop/completion-promise-detector.ts b/src/hooks/ralph-loop/completion-promise-detector.ts index ccfba8888..b6e8f38ec 100644 --- a/src/hooks/ralph-loop/completion-promise-detector.ts +++ b/src/hooks/ralph-loop/completion-promise-detector.ts @@ -34,6 +34,22 @@ function buildPromisePattern(promise: string): RegExp { return new RegExp(`\\s*${escapeRegex(promise)}\\s*`, "is") } +function shouldInspectSessionMessagePart( + partType: string, + promise: string, + partText: string, +): boolean { + if (partType === "text") { + return true + } + + if (partType !== "tool_result") { + return false + } + + return promise === ULTRAWORK_VERIFICATION_PROMISE && ORACLE_AGENT_PATTERN.test(partText) +} + function shouldInspectTranscriptEntry( entry: TranscriptEntry, promise: string, @@ -127,14 +143,13 @@ export async function detectCompletionInSessionMessages( const assistant = assistantMessages[index] if (!assistant.parts) continue - let responseText = "" for (const part of assistant.parts) { - if (part.type !== "text") continue - responseText += `${responseText ? "\n" : ""}${part.text ?? ""}` - } - - if (pattern.test(responseText)) { - return true + const partText = part.text ?? "" + if (!partText) continue + if (!shouldInspectSessionMessagePart(part.type, options.promise, partText)) continue + if (pattern.test(partText)) { + return true + } } }