Skip to content

fix: add waiting status so agents can end turns without continuation spam - #8

Open
hieusats wants to merge 1 commit into
Michaelliv:mainfrom
hieusats:fix/goal-waiting-status
Open

hieusats wants to merge 1 commit into
Michaelliv:mainfrom
hieusats:fix/goal-waiting-status

Conversation

@hieusats

Copy link
Copy Markdown

Problem

While a goal is active, agent_end unconditionally queues a continuation turn. When the only correct action is to end the turn and passively wait for an external wake — exactly what pi advises for background task notifications — this creates an infinite no-op loop:

agent: "Worker still running — waiting for notification, not polling."
agent_end -> continuation queued -> new turn -> same reply -> agent_end -> ...

Observed in a real session: 383 continuation events at ~5 second intervals, each one a paid API call, for as long as the background task ran. The agent behaved correctly (no polling); the extension punished that behavior. Worse, because the wake notification arrives as a pending message and then the loop resumes, every external event adds another no-op cycle.

There is currently no way for the model to declare "I am blocked on an external event — do not re-trigger me".

Fix

  • New waiting goal status, set by the model via update_goal({ status: "waiting" })
  • agent_end does not queue a continuation while waiting
  • Any externally triggered turn (user message or event notification) auto-resumes the goal to active at turn_start, so normal continuation resumes after the wake
  • The continuation prompt teaches the model to mark waiting instead of replying with no-op status messages
  • statusLine / event labels cover the new status; README documents it

Contract note

waiting is a scheduling hint, not lifecycle control: pause, resume, clear, and budget limiting remain user/runtime-owned. The source-contract test is updated to pin the new schema (enum: ["complete", "waiting"]) while keeping the "no pause/resume/abandon/budget-limit via update_goal" assertion. Usage is still accounted on waiting turns, and budget_limited still flips on the next active turn_end.

Tests

  • New test/agent-loop.test.cjs: runtime harness driving the real extension code through jiti with mocked ExtensionAPI/ExtensionContext — asserts exactly-one continuation while active, zero continuation while waiting, auto-resume on the next turn, continuation resuming after the wake, idempotent waiting, rejection of lifecycle statuses, and usage accounting during waiting
  • test/goal-state.test.cjs: statusLine + goalEventStatus cases for waiting
  • test/source-contract.test.cjs: updated update_goal schema contract
  • npm test → 28/28 pass; npm run check passes

…spam

While a goal is active, agent_end unconditionally queues a continuation
turn. When the only correct action is to end the turn and passively wait
for an external wake (e.g. a background task terminal notification), this
creates a no-op loop: the agent replies 'still waiting', agent_end fires,
a new continuation turn starts ~5s later, forever. Observed in practice:
383 continuation events at ~5s intervals burning API calls.

- add a 'waiting' goal status, set by the model via
  update_goal({ status: 'waiting' })
- agent_end does not queue a continuation while waiting
- any externally triggered turn (user message or event notification)
  auto-resumes the goal to active at turn_start
- the continuation prompt teaches the model to mark waiting instead of
  replying with no-op status messages

waiting is a scheduling hint, not lifecycle control: pause, resume,
clear, and budget limiting remain user/runtime-owned. The final turn is
still accounted while waiting; budget_limited still flips on the next
active turn_end.
@hieusats
hieusats force-pushed the fix/goal-waiting-status branch from 1c98bb9 to c5824fe Compare August 27, 2026 15:10
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.

1 participant