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
+ }
}
}