fix(agent-api): reject non-task- ids in /task-done to stop Task list pollution#1875
fix(agent-api): reject non-task- ids in /task-done to stop Task list pollution#1875bassilkhilo-ag2 wants to merge 2 commits into
Conversation
…st pollution (closes sonichi#1786) task-bridge.ts feeds voice-* and proactive-* result files through the /task-done endpoint (they all pass _shouldFallthrough's match set). Since each notification file carries a per-fire timestamp id, every re-fire adds a NEW row to the in-memory task_history dict — duplicate rows pile up in the web Task list with each pending-question reminder or proactive ping. Fix: add an early-return guard before the task_history write in the /task-done handler: if not tid.startswith("task-"): self.send_json(200, {"ok": True}) return The 200 response keeps the caller happy; the guard prevents voice-* and proactive-* ids from ever entering task_history. Genuine task-* ids are unaffected. Structural regression test added: tests/agent-api-task-done-id-guard.test.py verifies the guard exists, returns 200, and precedes the task_history write. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KXQJogmVSdYrKtYX1LwzwT
|
Cold-review — LGTM, one context note. Server-side validation at Context worth noting: #1788 (merged) already gates the Nit (non-blocking): the test is structural (regex-greps Also: mergeState is BEHIND — needs an |
|
@cla-assistant check |
Closes #1786.
Root cause
task-bridge.ts_shouldFallthrough(L187) matchestask-,voice-, andproactive-result files — all three pass to the/task-donePOST handler. Sincevoice-*andproactive-*files carry per-fire timestamp ids, each re-fire creates a newtask_historyentry, accumulating duplicate rows in the web Task list (one per reminder fire, per proactive ping, etc.).Note: #1788 (merged) already closes this at the source —
task-bridge.ts:750gates the/task-donePOST itself behindif (taskId.startsWith('task-')), sovoice-*/proactive-*no longer reach this endpoint from the primary caller. This PR is the server-side belt-and-suspenders half: it makes/task-donerefuse non-task-*ids regardless of caller, so a second caller (or a future regression in the TS-side gate) can't reintroduce the pollution.Fix
One early-return guard in the
/task-donehandler, before anytask_historywrite:The caller (
task-bridge.ts) receives 200 and is satisfied. The guard preventsvoice-*andproactive-*ids from reachingtask_history. Genuinetask-*ids are unaffected.Why here (not at task-bridge.ts)
The issue description (Reproduce C) traced the write to
/task-done do_POST (~L697)— this is the correct layer. Fixing at the ingestion point is the smallest targeted change with no side-effects on the task-bridge caller, and provides defense-in-depth independent of the TS-side gate in #1788.Test
tests/agent-api-task-done-id-guard.test.py— structural check:if not tid.startswith("task-"):task_historywritetask_history[tid]write follows the guard (correct ordering)/task-donehandler🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01KXQJogmVSdYrKtYX1LwzwT