Short description
The curate job commits curation work via ctx_memory tool-calls (not text), but the validateOutput function expects text output. When the latest assistant message is a tool-call (no text), extractLatestAssistantText returns null → validateOutput throws "Dreamer returned no assistant output." → recorded as empty_completion (with resultChars=0), even though the curation work was actually committed.
What happened?
Bug
The dreamer's curate job is an agentic task: the model commits curation work directly via ctx_memory tool calls (merge, archive, add). It does NOT produce a final assistant text (the latest assistant message is a tool call, with no text parts).
But the validateOutput function (dist/index.js:19800) expects text:
validateOutput: (messages) => {
const text = extractLatestAssistantText(messages);
if (!text)
throw new Error("Dreamer returned no assistant output.");
return task === "curate" ? validateCurateAssistantText(text) : text;
}
extractLatestAssistantText (dist/index-0y2mcg5y.js:16220) returns the text parts of the latest assistant message. In an agentic curation run, the latest assistant message is a tool call (no text parts), so it returns null. The validateOutput function throws an error ("Dreamer returned no assistant output."), which is recorded as empty_completion (with resultChars=0).
So the curate job DID commit the curation work (via ctx_memory tool calls), but the validateOutput function throws an error (because the latest assistant message is a tool call, not text). The empty_completion bug is a false positive.
Root cause
The validateOutput function is designed for a text-output job (not a tool-call job). But the curate job is a tool-call job (it commits work via ctx_memory tool calls, not text). So the validateOutput function should count tool-calls as output (not just text).
The fix
The validateOutput function should count tool-calls as output for the curate job:
validateOutput: (messages) => {
const text = extractLatestAssistantText(messages);
if (text)
return task === "curate" ? validateCurateAssistantText(text) : text;
// curate-empty-completion fix: the curate job commits work via tool-calls (ctx_memory), not text
if (task === "curate") {
const hasToolCalls = messages.some(m => m?.parts?.some(p => p?.type === 'tool'));
if (hasToolCalls) return 'tool-calls-committed';
}
throw new Error("Dreamer returned no assistant output.");
}
Live validation (2026-09-09, patched 0.41.4 dist, real /ctx-dream run, local 27B model)
- Dream run 123 (the curate task):
tasks_succeeded = 1, tasks_failed = 0. The curate task processed 8 memories (backlog: 224 → 216, processed 8). No empty_completion error.
So the fix worked! The empty_completion bug is gone (the curate job is now finishing successfully, processing memories via tool-calls).
Diagnostics
Plugin version
0.41.4
OpenCode version
1.18.30
Platform
macos arm64
Client
OpenCode Desktop
Log output (optional)
Short description
The curate job commits curation work via ctx_memory tool-calls (not text), but the validateOutput function expects text output. When the latest assistant message is a tool-call (no text), extractLatestAssistantText returns null → validateOutput throws "Dreamer returned no assistant output." → recorded as empty_completion (with resultChars=0), even though the curation work was actually committed.
What happened?
Bug
The dreamer's curate job is an agentic task: the model commits curation work directly via
ctx_memorytool calls (merge, archive, add). It does NOT produce a final assistant text (the latest assistant message is a tool call, with no text parts).But the
validateOutputfunction (dist/index.js:19800) expects text:extractLatestAssistantText(dist/index-0y2mcg5y.js:16220) returns the text parts of the latest assistant message. In an agentic curation run, the latest assistant message is a tool call (no text parts), so it returnsnull. ThevalidateOutputfunction throws an error ("Dreamer returned no assistant output."), which is recorded asempty_completion(withresultChars=0).So the curate job DID commit the curation work (via
ctx_memorytool calls), but thevalidateOutputfunction throws an error (because the latest assistant message is a tool call, not text). Theempty_completionbug is a false positive.Root cause
The
validateOutputfunction is designed for a text-output job (not a tool-call job). But the curate job is a tool-call job (it commits work viactx_memorytool calls, not text). So thevalidateOutputfunction should count tool-calls as output (not just text).The fix
The
validateOutputfunction should count tool-calls as output for the curate job:Live validation (2026-09-09, patched 0.41.4 dist, real
/ctx-dreamrun, local 27B model)tasks_succeeded = 1,tasks_failed = 0. The curate task processed 8 memories (backlog: 224 → 216, processed 8). Noempty_completionerror.So the fix worked! The
empty_completionbug is gone (the curate job is now finishing successfully, processing memories via tool-calls).Diagnostics
Plugin version
0.41.4
OpenCode version
1.18.30
Platform
macos arm64
Client
OpenCode Desktop
Log output (optional)