Skip to content

agent_end: auto-complete in_progress tasks when LLM finishes turn#10

Closed
ZGltYQ wants to merge 1 commit into
tintinweb:masterfrom
ZGltYQ:agent-end-auto-complete
Closed

agent_end: auto-complete in_progress tasks when LLM finishes turn#10
ZGltYQ wants to merge 1 commit into
tintinweb:masterfrom
ZGltYQ:agent-end-auto-complete

Conversation

@ZGltYQ
Copy link
Copy Markdown

@ZGltYQ ZGltYQ commented Apr 20, 2026

Adds two related fixes:

  1. Auto-complete on agent_end: When the LLM stops producing tool calls (agent_end event), mark any in_progress tasks as completed. This matches the natural semantic of 'the LLM is done for this turn, so tasks it was working on are done'.

    Previously, only subagent-executed tasks were auto-completed via subagents:completed. Regular tasks required an explicit TaskUpdate call, which the LLM often forgot, leaving tasks stuck in_progress.

    The very first agent_end of the session (fired on startup before the user sends any message) is skipped via the isFirstAgentCycle guard.

  2. Fix task loss across store upgrade: In session scope, the store starts in-memory before the session ID is known, then upgrades to file-backed once upgradeStoreIfNeeded() fires. Previously, the upgrade created a brand new empty TaskStore and the old in-memory tasks were GC'd \u2014 so any task created before the first turn_start was silently lost.

    Now we persist the old store's tasks to the new session file (atomic temp-file + rename, same pattern as TaskStore.save()) before swapping the store reference.

Without fix #2, fix #1 is only partially effective: agent_end fires but the in-memory task created in the same turn is in a different store instance than the file-backed one used later.

Adds two related fixes:

1. Auto-complete on agent_end:
   When the LLM stops producing tool calls (agent_end event), mark any
   in_progress tasks as completed. This matches the natural semantic of
   'the LLM is done for this turn, so tasks it was working on are done'.

   Previously, only subagent-executed tasks were auto-completed via
   subagents:completed. Regular tasks required an explicit TaskUpdate
   call, which the LLM often forgot, leaving tasks stuck in_progress.

   The very first agent_end of the session (fired on startup before the
   user sends any message) is skipped via the isFirstAgentCycle guard.

2. Fix task loss across store upgrade:
   In session scope, the store starts in-memory before the session ID is
   known, then upgrades to file-backed once upgradeStoreIfNeeded() fires.
   Previously, the upgrade created a brand new empty TaskStore and the
   old in-memory tasks were GC'd \u2014 so any task created before the first
   turn_start was silently lost.

   Now we persist the old store's tasks to the new session file (atomic
   temp-file + rename, same pattern as TaskStore.save()) before swapping
   the store reference.

Without fix tintinweb#2, fix tintinweb#1 is only partially effective: agent_end fires
but the in-memory task created in the same turn is in a different store
instance than the file-backed one used later.
@tintinweb
Copy link
Copy Markdown
Owner

Thanks for the PR. going to close this one.

The premise that "LLM stops producing tool calls ⇒ tasks are done" doesn't hold. agent_end also fires when the LLM pauses for a clarifying question, reports partial progress, hits an error, or gets interrupted — in all those cases this would silently flip unfinished work to completed, which defeats the point of the task widget surfacing a mismatch between claimed and actual status.

@tintinweb tintinweb closed this May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants