Skip to content

fix: keep the agent bridge alive while tasks are running (v0.6.9) - #49

Open
luyang950 wants to merge 2 commits into
mainfrom
release/officedex-0.6.9
Open

fix: keep the agent bridge alive while tasks are running (v0.6.9)#49
luyang950 wants to merge 2 commits into
mainfrom
release/officedex-0.6.9

Conversation

@luyang950

Copy link
Copy Markdown
Contributor

Hotfix released as v0.6.9, branched from v0.6.8. Merging it back so main carries the fix.

The bug

Generating two documents in a row could leave the first one spinning forever. ensureBridgeForCwd closes the current bridge client whenever the requested working directory differs from the running one, and two callers disagree about that directory: Generate with noProject resolves to the app workspace, while ensureBridge (ListImageTemplates, GetCapabilities, the PPTist planner) resolves to the active project. Either one therefore killed the child process mid-task, and since Client.Stop only fails pending RPCs — task/invoke has already returned running by then — the task never got a terminal event and stayed running in the local store for good.

Reproduced on the shipped 0.6.8 build: three image tasks 10s apart, the first two stuck at generate_llm with no further events, the third (nothing started after it) completed normally. Only one officecli agent-bridge process was left alive, started at the third task's timestamp.

The fix

  • retireBridge parks a replaced client that still has work in flight, keeping its process and listeners alive until its last task finishes. A grace timer bounds the parking lot.
  • bridge.Client tracks the tasks it still owes an answer for and synthesizes task.failed for them when the process goes away — Stop, Close, or an unexpected exit. Interactive waits (task.question / task.plan) do not count: stale-respond recovery replays those against a fresh process.
  • Metadata-only calls reuse whichever client is connected instead of resolving a working directory of their own.
  • A workspace-only settings change retires the bridge instead of closing it. Binary / provider / proxy changes still close it, but now report the interrupted tasks as failed.
  • Startup marks tasks left running by a previous session as failed. No bridge child survives a restart, so those cards could only ever spin.

This also covers quitting the app or changing settings mid-generation, which used to leave the same permanently-running rows.

Testing

  • 10 new regression cases in internal/bridge/client_stranded_test.go and app_bridge_retire_test.go, mutation-checked: reverting retireBridge to an unconditional Close() fails TestRetireBridgeKeepsTaskRunningUntilItFinishes, and dropping failStrandedTasks from Stop fails TestCloseReportsStrandedTasksAsFailed.
  • go test ./... green under both build tags, 479 renderer tests green, tsc --noEmit clean.

Also on develop/1.0 via fix/bridge-task-lifecycle.

🤖 Generated with Claude Code

卢阳 and others added 2 commits August 19, 2026 11:20
Generating two documents in a row could leave the first one spinning
forever. ensureBridgeForCwd closes the current bridge client whenever the
requested working directory differs from the running one, and the working
directory two callers ask for is not the same: Generate with noProject
resolves to the app workspace, while ensureBridge (ListImageTemplates,
GetCapabilities, the PPTist planner) resolves to the active project. Any
of those calls therefore killed the child process mid-task, and because
Client.Stop only fails pending RPCs -- task/invoke has already returned
"running" by then -- the task never received a terminal event and stayed
`running` in the local store for good.

- retireBridge parks a replaced client that still has work in flight,
  keeping its process and listeners alive until its last task finishes,
  instead of killing it. A grace timer bounds the parking lot.
- Client tracks the tasks it still owes an answer for and synthesizes
  task.failed for them when the process goes away, whether that is Stop,
  Close, or an unexpected exit. Interactive waits do not count: stale
  respond recovery replays those against a fresh process.
- Metadata-only calls reuse whichever client is connected rather than
  resolving a working directory of their own.
- A workspace-only settings change now retires the bridge instead of
  closing it; binary, provider and proxy changes still close it, but now
  report the interrupted tasks as failed.
- Startup marks tasks left `running` by a previous session as failed. No
  bridge child survives a restart, so those cards could only ever spin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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