Skip to content

[Bug]: False "Task complete" notifications while the agent is still working (agent started after the setup script finishes) #26014

Open

Description

@whisperice

Operating system

macOS 26.6.2 (25G83), Apple Silicon (arm64)

Orca version

1.4.221 (build e65bbc95cec14b365e5cb22e3fdfdff413c1f82b)

Agent

OMP (oh-my-pi) 18.6.1. The login shell is zsh. Orca's setup sequence ran under Homebrew bash 5.3.15.

Details

Short summary

When a worktree has a setup script and the agent waits for it, Orca starts the agent through bash -lc 'eval "$ORCA_SEQUENCED_STARTUP_SCRIPT"' after setup finishes. That bash has no job control. The agent therefore runs inside bash's process group, and the terminal's foreground process group leader is bash, not the agent.

Orca's terminal daemon then reads the pane as "foreground is bash, no child processes" whenever its cached agent identity is older than 1 s. A hidden pane is polled every 3 s, so every poll reads that stale result. Orca concludes the agent has exited and posts a generic notification while the agent is still working:

Task complete in <worktree>
<repo> · omp

I see this with OMP. Any TUI agent started this way is probably affected, but I only checked OMP.

What happened

  • On 1.4.221, I got 13 generic Task complete in <worktree> / <repo> · omp notifications in about 75 minutes. All 13 came from the 3 panes whose agent was started by the setup sequence.
  • There were none from 9 other OMP panes in the same period. In those panes OMP had been started at the zsh prompt, so it led its own process group.
  • Each time, the agent was still working: either mid-turn or waiting on background subagents.
  • The notifications appear after I switch away from the worktree. While I am in it, delivery is suppressed (suppressed-focus, notification-delivery-service.ts:139).
  • On 1.4.218 this happened about once per new session. On 1.4.221 it repeats within the same session.

How to reproduce

  1. On macOS, use a repo with a setup script, and make the agent wait for setup to finish.
  2. Create a worktree with OMP as the agent, and give it a task that runs for more than ~30 s.
  3. Switch to another worktree.
  4. A Task complete in <worktree> notification arrives within a few seconds while the agent is still running.

To confirm the cause:

$ ps -o pid,ppid,pgid,tpgid,stat,comm -t <pane tty>
  zsh   (pgid A)
  bash  (pgid B, tpgid B)   <- foreground group leader
  omp   (pgid B)            <- agent, not a group leader

I also queried the daemon read-only over its control socket. I used a separate client ID, sent inspectProcess and getForegroundProcess, and kept the agent process alive and working throughout:

Pane Read every 3 s (hidden poll rate) Read every ~0.8 s
Agent started by the setup sequence always foregroundProcess: "bash", hasChildProcesses: false alternates between bash/false and omp/true
OMP typed at the zsh prompt (control) always omp/true always omp/true

Code path (at e65bbc9)

  1. src/shared/setup-agent-sequencing.ts:134: the success branch runs eval "$ORCA_SEQUENCED_STARTUP_COMMAND"; exit "$?". In a non-interactive bash without job control, the agent stays in bash's process group. The exec form at :157 is used only as a fallback.
  2. src/main/daemon/pty-subprocess/foreground-process-tracker.ts:33,244-265: the node-pty name is the group leader, bash. A recognised agent is cached for FOREGROUND_AGENT_CACHE_TTL_MS = 1000 and refreshed only asynchronously, on reads. Once the TTL expires, a POSIX shell fallback is returned unchanged. The Windows branch already keeps the cached agent for a shell fallback (process.platform === 'win32' && isShellProcess(fallbackProcess)).
  3. src/main/daemon/terminal-host-process-inspection.ts:127-139: hasChildProcesses is derived from that shell name, so it is false. In my panes, the evidence verdict was unverifiable with reason tty_boundary (agent-foreground-process-remote-evidence.ts:78), so it did not correct the result.
  4. src/renderer/src/components/terminal-pane/agent-completion-poll-cadence.ts:6 and agent-completion-poll-scheduler.ts:50-51: a hidden pane polls every 3000 ms. That is longer than the 1 s cache, so every hidden poll reads bash.
  5. src/renderer/src/components/terminal-pane/agent-completion-inspection-result.ts:148-182: two misses more than 1.5 s apart dispatch process-exit with the title omp.
  6. src/renderer/src/components/terminal-pane/use-notification-dispatch.ts:109-115: a process-exit drops a hook status that is not done. The notification therefore has no agent fields, and falls back to the generic shape.
  7. Probably related to why it is more frequent on 1.4.221: since fix(opencode): report OpenCode 2 status from each pane's own TUI #23722, agent-completion-hook-observer.ts:119-121 calls clearProcessExitCompletion() on every hook working. This appears to remove the old once-per-pane dedupe. I inferred this from the code and did not A/B test it.

Suggested fix

  1. Primary. In the POSIX startup script (buildPosixStartupScript), turn on job control before starting the agent, so the agent leads its own foreground process group. The same applies to the startupSuccessCommand branch.
    - ... then eval "$ORCA_SEQUENCED_STARTUP_COMMAND"; exit "$?"; ...
    + ... then set -m; eval "$ORCA_SEQUENCED_STARTUP_COMMAND"; exit "$?"; ...
    I tested this in a PTY under an interactive zsh, with macOS /bin/bash 3.2.57 and Homebrew bash 5.3.15. I tried a simple command, the fresh-session guard form, and a function-wrapped command. In every case:
    • The agent became the foreground group leader. Without the change, bash was the leader.
    • Exit codes were preserved (tested with 7).
    • zsh got the terminal back after the agent exited.
    • Without a tty, set -m printed nothing and the exit code was unchanged.
  2. Defense in depth. In foreground-process-tracker.ts, when the fallback is a POSIX shell and an agent identity is cached, keep returning the cached identity until the scheduled refresh retires it. This is what the Windows branch already does. The existing retireStaleForegroundIdentity() still clears the identity when a scan finds no agent. The cost is that a real exit is detected one refresh later.
  3. Optional. Do not report hasChildProcesses: false based on the tracker's fallback name when the process-table evidence is unverifiable.

Workaround I am using

I set an agent command override (Settings › Agents › OMP › Command). It points to a small wrapper named omp. Before the wrapper execs the real binary, it moves into its own process group and takes the terminal foreground (setpgid + tcsetpgrp). It does nothing if it already leads its group, has no tty, or is in the background.

With the wrapper in place:

  • A new worktree started by the setup sequence reads omp / hasChildProcesses: true on every 3 s probe.
  • The false notifications stopped.

The basename must stay omp, because shared/omp-fresh-launch.ts only adds --config "$ORCA_OMP_FRESH_CONFIG" for that executable name.

Note on my local setup

I run a locally modified copy of Orca's OMP status extension, which is the hook reporter. It is not what causes the false exit:

  • The daemon readings above come from the process table, not from hooks.
  • I ran a controlled A/B in a fresh pane with ORCA_PANE_KEY unset, which disables the extension, so it posted no hooks. With the plain launch, getForegroundProcess read bash every time and inspectProcess reported hasChildProcesses: false. The wrapped launch read omp / true.

The extension only affects how the notification looks. A mid-turn hook state of working makes Orca drop the snapshot. I expect Orca's own managed extension to report working mid-turn as well, but I have not checked.

Image

Activity

  1. AmethystLiang commented on Oct 7, 2026

    @AmethystLiang
    Contributor

    Thanks for reporting. Looking into it

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

Metadata

Metadata

Assignees

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