Skip to content

[Bug] AgentTool: untracked background tasks and shared _event_queue causes cross-talk between concurrent calls #90

@Prodesire

Description

@Prodesire

Bug Description

Two related issues in AgentTool affecting background task lifecycle management and concurrent execution correctness.

1. Background task not tracked — lifecycle uncontrollable

File: src/iac_code/agent/agent_tool.py:179-185

asyncio.create_task(self._run_background(...)) does not save the task handle, register a done callback, or provide a shutdown/cancel path. While _run_background() internally catches Exception, BaseException subclasses like KeyboardInterrupt or SystemExit would produce an "unhandled exception in Task" warning. More importantly, the task lifecycle cannot be awaited — on process exit or session close, in-flight background agents may lose state.

This directly relates to the /tasks stop issue : since the asyncio task is not retained, TaskManager.stop() can only set a status flag but cannot cancel the running coroutine. The agent continues executing tools and consuming tokens after the user believes it was stopped.

2. Shared _event_queue causes cross-talk between concurrent agent calls

Files: src/iac_code/agent/agent_tool.py:131,187-199,247-248, src/iac_code/tools/tool_executor.py:69-75,131-144

ToolExecutor writes tool._event_queue = call.event_queue on the shared registry tool instance, then concurrently executes tools marked as concurrency_safe. AgentTool.is_concurrency_safe() unconditionally returns True, so two concurrent agent tool calls in the same turn can overwrite each other's _event_queue.

Verified: Two concurrent calls to a shared tool instance with different queues — both calls end up reading from the second queue's ID. AgentTool.execute() passes self._event_queue to run_sub_agent() and writes the end sentinel to it.

Impact: Sub-agent tool progress events may go to the wrong call's queue, or the wrong queue may receive a premature None end marker. CLI/ACP/A2A progress streams will cross-talk, lose events, or cause the parent agent to wait for events that never arrive.

Expected Behavior

  1. Background tasks should be tracked and cancellable via TaskManager.
  2. Per-call state (event queue) should not be stored on shared tool instances.

Actual Behavior

  1. Background tasks are fire-and-forget with no cancel path.
  2. Concurrent agent calls overwrite each other's event queue on the shared instance.

Suggested Fix

  1. Store the asyncio.Task in TaskManager; stop()/cancel() should call task.cancel() and handle CancelledError in _run_background().
  2. Move event queue to ToolContext instead of storing on the tool instance. Until fixed, AgentTool.is_concurrency_safe() should return False.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions