Bug: 15+ turn tasks can exit without calling start_long_term_update

Author: kjoreCreated Sep 2, 2026Updated Sep 2, 2026

Problem

The tool schema states that start_long_term_update must be called when a task taking 15+ turns completes. However, a task can return its final response and exit without calling the tool because this requirement is not enforced by the runtime.

This is a completion-time requirement, not a request to perform memory consolidation periodically every 15 turns.

Reproduction

I ran two long travel-planning tasks using the same model and the unmodified upstream code.

Experiment LLM turns start_long_term_update calls
Beijing → Lanzhou A 39 0
Beijing → Lanzhou B 20 0

Both tasks completed successfully and returned their final responses, but neither session called start_long_term_update.

This occurred even though both tasks exceeded the documented long-task threshold.

Expected Behavior

When a normal task attempts to complete after reaching the long-task threshold, GenericAgent should perform exactly one long-term memory evaluation unless:

  • long-term settlement has already started;
  • the task is running in an exempt autonomous flow; or
  • start_long_term_update is unavailable, such as when using --no-user-tools.

The evaluation may conclude that no information qualifies for storage. The requirement should force evaluation, not unconditional memory modification or SOP creation.

Actual Behavior

For an ordinary final response:

  1. The model returns no tool call.
  2. do_no_tool() returns StepOutcome(response, next_prompt=None).
  3. agent_runner_loop() treats the missing next prompt as CURRENT_TASK_DONE.
  4. The loop exits without checking whether the documented long-term memory evaluation occurred.

do_start_long_term_update() rejects early calls below 10 turns, but there is no corresponding runtime check that starts settlement when a 15+ turn task completes.

Root Cause

The requirement exists only in the tool description:

Must call when a task that took 15+ turns is completed.

Tool descriptions are advisory to the LLM. The task-exit path does not track whether long-term settlement was requested or completed.

As a result, compliance depends entirely on the model remembering and following the tool description after a long execution trace.

Suggested Direction

Add a one-time completion gate around the existing handler completion boundary:

  1. Track whether long-term settlement has started.
  2. When do_no_tool() receives a normal completion at or above the threshold, start the settlement phase instead of immediately exiting.
  3. Reuse the existing start_long_term_update implementation rather than duplicating its prompt and SOP-loading logic.
  4. Allow normal exit after evaluation, including when no memory qualifies for storage.
  5. Preserve the autonomous-flow and --no-user-tools exemptions.
  6. Ensure the gate cannot trigger recursively.

This can likely remain localized to GenericAgentHandler and its construction without adding dependencies or coupling the generic agent loop to a specific tool name.

Threshold Wording

The Chinese schema says 超15轮, while the English schema says 15+ turns. These have different boundary semantics: > 15 versus >= 15.

The wording and implementation should use one definition consistently. I suggest 15+ turns / 达到15轮, implemented as turn >= 15.

Suggested Tests

  • A task completing on turn 14 exits normally.
  • A task completing on turn 15 enters settlement once.
  • A task completing after turn 15 enters settlement once.
  • A prior explicit start_long_term_update call prevents another gate.
  • A no-op evaluation can exit without modifying memory.
  • --no-user-tools does not request an unavailable tool.
  • Autonomous flows preserve their documented exemption.
  • Settlement cannot recursively trigger itself.

Environment

  • OS: Windows
  • Python: 3.12.7
  • Model: qwen3.7-max

Privacy Note

The full response logs contain unrelated browser and conversation content, so they are not attached. The reported turn counts and tool-call results were extracted from the local logs.