Skip to content

[Bug]: Antigravity ACP background tasks are marked completed when the agent stops to wait, hiding active background tasks and dropping agent wakeups in the UI #28415

Closed as duplicate

Description

@mahmoodnizamani94-png

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Open an Antigravity ACP thread in T3 Code (e.g., gemini-4-argon / gemini-4-argon-high).
  2. Ask the agent to run a command that exceeds WaitMsBeforeAsync (e.g., WaitMsBeforeAsync: 5000 on a command that takes ~30–45 seconds, such as a deployment or test suite) so Antigravity moves the command to a background task (Tool is running as a background task with task id: <conversation-id>/task-<N>) and the agent ends its turn to wait for the background task to complete.
  3. Observe the T3 Code thread UI while the background task is still running, and after the background task completes and wakes the agent in the harness.

Expected behavior

  1. While the background task is running and the agent is waiting for it:
    • The open background command (run_command / shell / schedule / run_workflow) should remain in a running/pending background state rather than being forcibly marked completed (Ran command) when the initial turn settles.
    • thread.pendingBackgroundTasks should include the running background task so the UI displays the background task indicator/banner (and keeps the thread in a waiting/working state rather than looking completely finished).
  2. When the background task finishes and wakes the agent:
    • T3 Code should start a continuation run (or stream the wakeup turn items and assistant messages live into the thread) so the user sees the agent wake up, execute follow-up tool calls, and output its final response without needing to manually send a follow-up prompt.

Actual behavior

  1. Thread immediately shows as Done and hides the running background task:
    • As soon as the agent outputs its "waiting for task-<N> to complete" message and ends its turn, finalizeTurn marks the run completed and terminalizeOpenForegroundTools forcibly overwrites the still-running command_execution turn item from in_progress (running) to completed (Ran command).
    • Because all turn items are now completed and providerThread.pendingBackgroundTasks is [], derivePendingBackgroundWork returns []. The UI shows no running background tasks at all, making it impossible to tell whether the agent finished, died, or is waiting on a background task.
  2. Agent wakes up in the background when the task finishes, but the UI never shows the wakeup or response:
    • When the background task finishes (e.g., 26 seconds later), the Antigravity harness delivers the <SYSTEM_MESSAGE> task completion wakeup to the model. The agent wakes up in the background, runs follow-up tools, and generates its final completion response (verified in .system_generated/logs/transcript.jsonl), but nothing appears in the T3 Code UI until the user manually sends another prompt.

Root Cause Analysis (on main @ 5e9d56416f6b395095c772c597e79cd5f94e3052)

There are three interacting causes across AntigravityAdapterV2.ts, packages/provider-acp/src/server/adapter.ts, and antigravity-acp:

1. makeAntigravityAcpAdapterFlavor omits enablePostSettleContinuation: true despite AntigravityDriver wiring continuationRequests

2. makeAntigravityAcpAdapterFlavor omits extractBackgroundTaskId, causing terminalizeOpenForegroundTools to mark still-running background commands as completed at turn end

  • When an Antigravity run_command tool call goes to the background, antigravity-acp (server.py:5490-5495 and 6090-6093) intentionally keeps the tool_call in in_progress state (moving it to session_info.persisted_open_tool_calls without emitting a terminal tool_call_update until the background process actually exits).
  • However, when the turn settles in T3 Code, finalizeTurn calls terminalizeOpenForegroundTools (packages/provider-acp/src/server/adapter.ts#L6938-L6947), which checks acpCompletedTurnShouldTerminalizeTool (adapter.ts#L1223-L1231):
    export function acpCompletedTurnShouldTerminalizeTool(
      tool: AcpToolCallState,
      flavor: Pick<AcpAdapterV2Flavor, "extractBackgroundTaskId" | "extractSubagentUpdate">,
    ): boolean {
      const status = toolStatus(tool.status);
      if (status !== "pending" && status !== "running") return false;
      if (flavor.extractBackgroundTaskId?.(tool) !== undefined) return false;
      return flavor.extractSubagentUpdate?.(tool) === undefined;
    }
  • Because makeAntigravityAcpAdapterFlavor does not provide extractBackgroundTaskId (e.g., for open execute tools that remain inProgress at turn end, see isAntigravityOpenCommand in apps/server/src/provider/acp/AntigravityProtocol.ts#L317-L319), acpCompletedTurnShouldTerminalizeTool returns true and terminalizeOpenForegroundTools forcibly emits status: "completed" (Ran command) for the still-running background command!
  • Because the command_execution turn item is now marked completed, pendingBackgroundTurnItems and derivePendingBackgroundWork (packages/shared/src/orchestrationV2PendingBackgroundWork.ts#L174-L243) return [], so the UI hides the running background command and marks the thread completely idle/done.

3. antigravity-acp (server.py / event_processor.py) stops draining agent.conversation.receive_steps() once session/prompt returns

  • In antigravity-acp (google3/cloud/developer_experience/antigravity_extensions/acp_server/server.py:5483), steps from localharness_external are only forwarded to self._stream_live_step(...) inside the async for step in agent.conversation.receive_steps(): loop during an active session/prompt RPC.
  • Meanwhile, in localharness_external, when a run_command exceeds WaitMsBeforeAsync, the RUN_COMMAND step remains in STATE_RUNNING (status = 2) while the planner receives the background task ID (<conv>/task-<N>) and finishes its turn, causing localharness_external to emit TrajectoryStateUpdate(state=STATE_WAITING_FOR_TASKS) (4).
  • If session/prompt exits on STATE_WAITING_FOR_TASKS, nothing drains agent.conversation.receive_steps() when the background task later finishes and wakes the planner in localharness_external—so the wakeup turn (e.g., steps 253–256 below) runs inside localharness_external without emitting ACP session/update notifications until the next session/prompt call. Conversely, if session/prompt stays open across STATE_WAITING_FOR_TASKS (or spawns a background step drainer that triggers post-settle continuation), T3 Code needs both extractBackgroundTaskId / open background command tracking and enablePostSettleContinuation: true so the UI shows the pending background task while waiting and streams the wakeup turn when it fires.

Impact

Major degradation or frequent failure

Version or commit

0.1.7-nightly.20261031.3991 (and main @ 5e9d56416f6b395095c772c597e79cd5f94e3052)

Environment

Linux x86_64 (CachyOS), T3 Code 0.1.7-nightly.20261031.3991, Provider: Antigravity ACP (gemini-4-argon / gemini-4-argon-high)

Logs or stack traces

Concrete timeline from thread 6af2808e-6d27-43fa-91d1-eda0daffacc3 (Antigravity session c7b20c6f-f402-4c58-ad7b-6f56e7fa6f9c, run ordinal 19):

  1. In .t3/userdata/statev2.sqlite (orchestration_v2_projection_runs and orchestration_v2_projection_turn_items):

    • At 2026-10-31T23:15:23Z, the agent launches background command task-251 (tool item ...tool:c7b20c6f-f402-4c58-ad7b-6f56e7fa6f9c:236).
    • At 2026-10-31T23:15:33Z, the agent outputs: "I have launched task-251 in the background to confirm dpl_2a6G1s8uQYFF7a4QnqnxaRrvvKJE reaches READY (PROMOTED) and will report back as soon as it finishes."
    • At 2026-10-31T23:15:48.720Z, terminalizeOpenForegroundTools marks tool:...:236 as status = 'completed' (title = 'Ran command') even though task-251 is still running, and at 23:15:48.749Z run ordinal: 19 is marked status = 'completed' with pendingBackgroundTasks = [].
  2. In antigravity-acp/brain/c7b20c6f-f402-4c58-ad7b-6f56e7fa6f9c/.system_generated/logs/transcript.jsonl (steps 251–256):

    • 23:15:23Z (step 251, RUN_COMMAND, status: "RUNNING"): Tool is running as a background task with task id: c7b20c6f-f402-4c58-ad7b-6f56e7fa6f9c/task-251
    • 23:15:33Z (step 252, PLANNER_RESPONSE, status: "DONE"): Agent stops and waits for task-251.
    • 23:16:14Z (step 253, SYSTEM_MESSAGE, status: "DONE"): Task id "c7b20c6f-f402-4c58-ad7b-6f56e7fa6f9c/task-251" finished with result: The command exited with code 0...
    • 23:16:14Z (step 254, PLANNER_RESPONSE, status: "DONE"): Agent wakes up automatically in the background and calls run_command (curl -sS https://text2voice-pi.vercel.app/api/health).
    • 23:16:25Z (step 255, RUN_COMMAND, status: "DONE"): Health check returns {"status":"ok","ok":true,"database":"ok"}.
    • 23:16:28Z (step 256, PLANNER_RESPONSE, status: "DONE"): Agent writes its complete 1,048-token final summary (Everything is now 100% resolved, migrated, merged, and promoted to live production...).
    • In T3 Code (statev2.sqlite): Zero messages or turn items were recorded after 23:15:48.749Z! Steps 253–256 never appeared in the UI.

Workaround

Manually sending a follow-up prompt (e.g., "where are we at?") forces a new session/prompt turn so the agent can report what happened in the background.

Activity

  1. eliophan commented on Nov 3, 2026

    @eliophan

    Evidence: on runtime 1.4.5, session/prompt stays open until background tasks finish, so a non-exiting server keeps the turn "Working" forever

    Environment: Antigravity ACP runtime 1.4.5 (darwin-arm64, release sha256 886fff77…), T3 Code Nightly 0.1.7-nightly.20261103.4119.

    I drove the runtime directly with a minimal ACP stdio client (fs.readTextFile: false, as T3 sends for 1.4.5) and asked the agent to start a background command and end its turn. Prompt: run sleep 50 && echo BGDONE with WaitMsBeforeAsync: 1000, reply with one sentence that it is launched, and do nothing else.

    t (s) Message
    12.0 tool_call / tool_call_update → in_progress for the command
    15.3 agent_message_chunk: "The background task has been launched." (the model considers its turn over)
    62.0 tool_call_update → completed for the command
    65.6 runtime wakes the model; agent_message_chunk: "The background task has finished successfully…"
    65.7 session/prompt returns stopReason: end_turn

    So the prompt was held open for ~50 s after the agent's final message, until the background task ended, and the wake-up happened inside the same prompt.

    This seems to differ from the assumption in #28450 that Antigravity returns end_turn while the command is still running. I only tried this one scenario (a short background command), so it may not cover the cases that PR was written for.

    Effect in a real thread with a server that never exits (pnpm start):

    • The agent wrote its final answer, but the thread stayed "Working" and the final assistant_message item stayed running, with no session/prompt result logged.
    • When the task was stopped by hand, the runtime injected a system message ("Task … finished with result: … exit code 143") and woke the model. The model restarted pnpm start as a new background task, and the turn stayed open again.
    • On runtime 1.4.1 the same pattern ended at the runtime's 600 s command timeout instead (inferred from the thread's history, not re-run).

    Stop ends the turn but not the background task (#16442), so the server keeps running.

    LOG:

    C-1.4.5-bg-task.sanitized.json

  2. Q3yp commented on Nov 11, 2026

    @Q3yp
    Contributor

    Live agy acp 1.5.5 wire findings (Windows ARM64) + working reference fix

    Sharing empirical wire behavior from real authenticated Antigravity CLI 1.5.5 (agy acp, gemini-4-carbon) on T3 Code Nightly (0.1.9-nightly.20261110.4491 / main @ b883156e2), since #28450 noted that live agy delivery after early end_turn was still unverified.

    What happens on real agy acp 1.5.5

    Driving agy acp 1.5.5 directly over stdio JSON-RPC with a minimal harness reproduces two behaviors that prevent passive post-settle listeners alone from receiving the wakeup:

    1. Zero session/update notifications are emitted after session/prompt returns end_turn: When a foreground run_command (IsDaemon: false, such as chained npm run typecheck; npm run lint; npx vitest run ...) exceeds WaitMsBeforeAsync (capped at 10s), agy emits tool_call with kind: "execute", status: "in_progress" (no tool_call_update) and instructs the planner to end its turn immediately (<messaging> reactive wakeup). As soon as the planner ends its step, agy acp responds to session/prompt with {"stopReason": "end_turn"} while conversation_summaries.db remains not_fully_idle = 1. When the backgrounded shell process exits seconds later with no active session/prompt RPC attached, agy wakes the planner internally in its transcript (SYSTEM_MESSAGE -> PLANNER_RESPONSE), but sends 0 session/update frames over ACP stdout. A client waiting passively for post-settle session/update frames never receives the command completion or the wakeup reply.
    2. Detached session/request_permission hangs agy indefinitely on follow-up commands in default mode: agy acp also binds its session/request_permission broker strictly to an active session/prompt call. In default mode, when the internal wakeup triggers a follow-up tool call (such as a second run_command) while no session/prompt is open, agy sends 0 session/request_permission frames over ACP and blocks forever internally waiting for permission (not_fully_idle = 1 permanently).
    3. Contrast with agy 1.4.5 (Antigravity: a command still running after the reply is marked completed, and Stop cannot end it #16442): Unlike 1.4.5 (which held session/prompt open across all background tasks, causing IsDaemon: true dev servers to hang the turn forever), 1.5.5 returns end_turn immediately for both IsDaemon: false and IsDaemon: true.

    Verified behavior when session/prompt stays attached or re-attaches

    Testing both cases against live agy acp 1.5.5:

    • Holding session/prompt open via an MCP tool (t3_background_wait): While session/prompt remains active, agy streams tool_call_update(status: "completed", rawOutput) live the instant the backgrounded command exits, delivers <SYSTEM_MESSAGE> in the next step of the same turn, and routes follow-up session/request_permission prompts normally.
    • Re-attaching session/prompt immediately after early end_turn: Sending a continuation session/prompt while the IsDaemon: false command is still in_progress re-attaches both session/update and session/request_permission (and replays gap completions if the command finished between the two RPCs).

    Working reference implementation (live-tested on Windows 11 ARM64)

    Reference branch against main (b883156e2):

    Summary of the reference approach (for maintainer design reference; not opened as a PR per CONTRIBUTING.md):

    1. Extract IsDaemon in AntigravityProtocol.ts so IsDaemon: true servers/watchers never hold turns (Antigravity: a command still running after the reply is marked completed, and Stop cannot end it #16442).
    2. Wire awaitBackgroundTasks (t3_background_wait) on AcpAdapterV2 flavors opting into foregroundCommandWait (plus MCP guidance when hasT3Mcp is active) so foreground run_command tasks hold the active session/prompt stream open until tool_call_update(completed) arrives (or waiting: false releases them).
    3. Add a <t3_background_continuation> prompt loop in AcpAdapterV2 as a fallback if the model still ends its turn early while an unreleased foreground command (IsDaemon: false) is inProgress, and drain the Antigravity update semaphore before resolving runtime.prompt.

    Environment tested: Windows 11 ARM64, Node v24.22.1, Antigravity CLI 1.5.5 (gemini-4-carbon), T3 Code desktop 0.1.9-nightly build.

    (Investigated and drafted with gemini-4-carbon via Antigravity in T3 Code.)

  3. juliusmarminge commented on Nov 12, 2026

    @juliusmarminge
    Member

    This is the same problem as #16442, so I'm closing it in favour of that one. If it is different, say how and it will be reopened.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions