Skip to content

parent_idle latches True #117

Description

@mohamedali-bryo

Environment

  • SDK version: 0.1.6
  • Python version: 3.13.9
  • OS / platform: Linux x86_64
  • Install method: pip install google-antigravity (local connection / bundled localharness, Vertex backend, gemini-3.5-flash)

Description

Topology: a parent fans out sub-agents (invoke_subagent, built-in self), then processes all their results and calls finish. To wait for the sub-agents, the parent goes idle and is re-woken by their completion messages.

Bug: the run ends the instant the last sub-agent goes idle — even though the parent has already been re-woken and is actively running (mid tool-call, about to finish). The parent's in-flight request is cancelled (context canceled), so it never processes the results; for structured runs structured_output() returns None.

Cause (google/antigravity/connections/local/event_processor.py): parent_idle is set True on the parent's first STATE_IDLE and never reset to False when the parent goes STATE_RUNNING again — the STATE_RUNNING handler only touches active_subagent_ids, with no branch for the main trajectory:

if tsu.state == STATE_RUNNING:
    if is_subagent:
        self.active_subagent_ids.add(tsu.trajectory_id)
    # no else: parent_idle stays latched True

elif tsu.state == STATE_IDLE:
    if is_subagent:
        self.active_subagent_ids.discard(tsu.trajectory_id)
    else:
        self.parent_idle = True
    if self.parent_idle and not self.active_subagent_ids:   # fires with a STALE parent_idle
        self.is_idle.set(); await self.step_queue.put(IDLE_SENTINEL)

So a stale parent_idle == True plus an emptied active_subagent_ids sets is_idle and enqueues IDLE_SENTINEL; local_connection.receive_steps() returns; the async with AntigravityAgent(...) block exits and cancels the parent's in-flight request.

Steps to Reproduce

  1. Create an agent with enable_subagents=True and a structured-output schema (response_schema).
  2. Give the parent a task that fans out ≥2 async sub-agents in one turn (invoke_subagent, self, Workspace: inherit), then awaits their results (each sub-agent does non-trivial work, writes a file, send_messages the parent — so the parent goes idle and is re-woken).
  3. Run via chat()structured_output().

Reliably reproduces when sub-agent work takes long enough that the last sub-agent finishes while the parent is between/inside turns.

Expected Behavior

The run stays alive while the parent is STATE_RUNNING. After the last sub-agent finishes and the parent is re-woken, the parent assembles its answer and calls finish; structured_output() returns the result.

Actual Behavior

The moment the last sub-agent goes STATE_IDLE, the stale parent_idle == True + empty active_subagent_ids ends the step stream; the session tears down and cancels the parent's in-flight streamGenerateContent; structured_output() returns None. Connection log:

RAW WS MSG {"trajectoryStateUpdate":{"trajectoryId":"<subagent>","state":"STATE_IDLE"}}
INFO Stopping Agent session                                    agent.py:159
INFO harness stderr: model response error: doRequest: error sending request:
     Post ".../gemini-3.5-flash:streamGenerateContent?alt=sse": context canceled

Additional Context

Proposed fix — clear parent_idle when the main trajectory runs, symmetric to the STATE_IDLE handler:

 if tsu.state == STATE_RUNNING:
     if is_subagent:
         self.active_subagent_ids.add(tsu.trajectory_id)
+    else:
+        self.parent_idle = False

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions