Skip to content

[Dreamer] Curate job: empty_completion false positive — validateOutput expects text, but the curate job commits work via tool-calls #432

Description

@Sugaroverdose

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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions