diff --git a/src/hooks/ralph-loop/completion-promise-detector.test.ts b/src/hooks/ralph-loop/completion-promise-detector.test.ts
index 6e2dae816..b63640457 100644
--- a/src/hooks/ralph-loop/completion-promise-detector.test.ts
+++ b/src/hooks/ralph-loop/completion-promise-detector.test.ts
@@ -108,4 +108,80 @@ describe("detectCompletionInSessionMessages", () => {
expect(detected).toBe(true)
})
})
+
+ describe("#given promise appears in tool_result part (not text part)", () => {
+ test("#when Oracle returns VERIFIED via task() tool_result #then should detect completion", async () => {
+ const messages: SessionMessage[] = [
+ {
+ info: { role: "assistant" },
+ parts: [
+ { type: "text", text: "Consulting Oracle for verification." },
+ { type: "tool_use", text: '{"subagent_type":"oracle"}' },
+ ],
+ },
+ {
+ info: { role: "assistant" },
+ parts: [
+ { type: "tool_result", text: 'Task completed.\n\nAgent: oracle\n\nVERIFIED\n\n\nsession_id: ses_abc123\n' },
+ { type: "text", text: "Oracle verified the task." },
+ ],
+ },
+ ]
+ const ctx = createPluginInput(messages)
+
+ const detected = await detectCompletionInSessionMessages(ctx, {
+ sessionID: "session-123",
+ promise: "VERIFIED",
+ apiTimeoutMs: 1000,
+ directory: "/tmp",
+ sinceMessageIndex: 0,
+ })
+
+ expect(detected).toBe(true)
+ })
+
+ test("#when DONE appears only in tool_result part #then should detect completion", async () => {
+ const messages: SessionMessage[] = [
+ {
+ info: { role: "assistant" },
+ parts: [
+ { type: "tool_result", text: 'Background task output DONE' },
+ { type: "text", text: "Task completed successfully." },
+ ],
+ },
+ ]
+ const ctx = createPluginInput(messages)
+
+ const detected = await detectCompletionInSessionMessages(ctx, {
+ sessionID: "session-123",
+ promise: "DONE",
+ apiTimeoutMs: 1000,
+ directory: "/tmp",
+ })
+
+ expect(detected).toBe(true)
+ })
+
+ test("#when promise appears in tool_use part (not tool_result) #then should NOT detect completion", async () => {
+ const messages: SessionMessage[] = [
+ {
+ info: { role: "assistant" },
+ parts: [
+ { type: "tool_use", text: 'prompt containing VERIFIED as instruction' },
+ { type: "text", text: "Calling Oracle." },
+ ],
+ },
+ ]
+ const ctx = createPluginInput(messages)
+
+ const detected = await detectCompletionInSessionMessages(ctx, {
+ sessionID: "session-123",
+ promise: "VERIFIED",
+ apiTimeoutMs: 1000,
+ directory: "/tmp",
+ })
+
+ 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 81f061bad..40e9e1af7 100644
--- a/src/hooks/ralph-loop/completion-promise-detector.ts
+++ b/src/hooks/ralph-loop/completion-promise-detector.ts
@@ -93,7 +93,7 @@ export async function detectCompletionInSessionMessages(
let responseText = ""
for (const part of assistant.parts) {
- if (part.type !== "text") continue
+ if (part.type !== "text" && part.type !== "tool_result") continue
responseText += `${responseText ? "\n" : ""}${part.text ?? ""}`
}
diff --git a/src/hooks/ralph-loop/pending-verification-handler.ts b/src/hooks/ralph-loop/pending-verification-handler.ts
index 31dc7295c..00878ca91 100644
--- a/src/hooks/ralph-loop/pending-verification-handler.ts
+++ b/src/hooks/ralph-loop/pending-verification-handler.ts
@@ -25,7 +25,7 @@ function collectAssistantText(message: OpenCodeSessionMessage): string {
let text = ""
for (const part of message.parts) {
- if (part.type !== "text") {
+ if (part.type !== "text" && part.type !== "tool_result") {
continue
}
text += `${text ? "\n" : ""}${part.text ?? ""}`