Skip to content

fix(omp): keep parent non-idle while internal task subagents run - #3371

Open
jasonhnd wants to merge 2 commits into
getpaseo:mainfrom
jasonhnd:fix/2232-omp-idle-gate
Open

fix(omp): keep parent non-idle while internal task subagents run#3371
jasonhnd wants to merge 2 commits into
getpaseo:mainfrom
jasonhnd:fix/2232-omp-idle-gate

Conversation

@jasonhnd

Copy link
Copy Markdown
Contributor

Linked issue

Closes #2232

Replaces #2245. That PR had the right idle-gate direction, but review found two defects this rewrite addresses. Do not merge #2245.

Type of change

  • Bug fix
  • New feature (with prior issue + design alignment)
  • Refactor / code improvement
  • Docs

What does this PR do

OMP can end the parent model loop (agent_end + !isStreaming && !isCompacting) while internal task children are still writing. Paseo then marks the parent idle/stopped even though work continues.

This PR:

  1. Idle gate. Do not completeTurn until the subagent index reports no running children.
  2. Snapshot reconcile. Successful get_subagents replies list only still-running children. An id that previously appeared and is now missing is treated as completed. A child seen only via lifecycle (never in a snapshot) is not killed by an empty first reply. If get_subagents is unavailable, fall back to the event index.
  3. Task cards. task tool_execution_end is a dispatch ack; children start later. Keep the parent call running and settle it from the index once a linked child exists and none remain running. completeTurn force-settles a task that never produced a child. Failed tasks still complete immediately.

Wire-order tests emit tool_execution_end before subagent_lifecycle: started, matching OMP v17.

Out of scope

How did you verify it

npx vitest run packages/server/src/server/agent/providers/omp --bail=1

Result: 19 files, 127/127 passed, including:

  • parent stays non-idle while a lifecycle child is running after agent_end
  • empty get_subagents does not complete a never-listed lifecycle child
  • a previously listed snapshot id disappearing completes the parent
  • get_subagents unavailable still honors lifecycle
  • task card stays running when the result precedes started, then completes when the child finishes
  • a task with no child is force-settled on turn complete
  • failed tasks complete immediately

Also: npm run lint and npm run typecheck --workspace=@getpaseo/server on touched files.

I did not dogfood against a live OMP 17 desktop session in this pass.

Review notes

Addresses the two findings on #2245 from @ABorakati:

  1. Card settlement no longer assumes children exist before tool_execution_end.
  2. Snapshot merge is two-directional: absence of a previously listed id is terminal, so the idle gate cannot stick forever.

Checklist

  • One focused change. Unrelated cleanups split out.
  • npm run typecheck passes
  • npm run lint passes
  • npm run format ran
  • UI changes include screenshots or video for every affected platform (N/A — server idle/card status, covered by unit tests)
  • Tests added or updated where it made sense

OMP can end the parent model loop while `task` children are still writing.
Do not complete the Paseo turn until the subagent index reports no runners.

Reconcile successful get_subagents replies so listed ids stay running, and
treat absence of a previously listed id as completed. Never-listed lifecycle
children stay running so an empty first snapshot cannot fake-idle the parent.

Closes getpaseo#2232
OMP emits tool_execution_end for `task` as a dispatch ack, then starts
children. Keep the parent call running and settle it from the subagent
index once a linked child exists and none remain running. completeTurn
force-settles a task that never produced a child.

Wire-order tests emit the result before subagent_lifecycle started.
@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds an OMP subagent-aware idle gate, reconciles lifecycle events with get_subagents snapshots, and defers successful task-card completion until linked children finish.

  • Adds validated get_subagents RPC support to the CLI runtime.
  • Tracks snapshot provenance and reconciles disappeared running children as completed.
  • Keeps successful task calls active across the dispatch acknowledgment.
  • Adds provider-level and index-level tests for lifecycle ordering, snapshot reconciliation, fallback behavior, and task-card settlement.

Confidence Score: 4/5

The multi-child task settlement race should be fixed before merging because it can mark the task and parent turn complete while a later child is still starting.

The new predicate equates zero currently indexed running children with task completion even though a task can own multiple children and the index cannot represent children whose started event has not arrived.

Files Needing Attention: packages/server/src/server/agent/providers/omp/agent.ts

Important Files Changed

Filename Overview
packages/server/src/server/agent/providers/omp/agent.ts Adds the idle gate and deferred task settlement, but the settlement predicate can close multi-child tasks during a gap between child lifecycle starts.
packages/server/src/server/agent/providers/omp/subagent-index.ts Adds running/link queries and two-way snapshot reconciliation with protection for lifecycle-only children.
packages/server/src/server/agent/providers/omp/cli-runtime.ts Adds validated get_subagents request handling through the existing JSONL RPC boundary.
packages/server/src/server/agent/providers/omp/rpc-types.ts Adds the get_subagents command and derives the snapshot type from its Zod schema.
packages/server/src/server/agent/providers/omp/agent.test.ts Covers the principal idle-gate and task-card wire ordering, but only exercises one child per task call.
packages/server/src/server/agent/providers/omp/subagent-index.test.ts Covers snapshot disappearance, lifecycle-only children, and linked-child status queries.

Sequence Diagram

sequenceDiagram
  participant OMP
  participant Session as OmpAgentSession
  participant Index as OmpSubagentIndex
  participant Parent as Parent turn
  OMP->>Session: task tool_execution_end (dispatch ack)
  Session->>Session: Keep task call running
  OMP->>Session: child lifecycle started
  Session->>Index: Record linked running child
  OMP->>Session: child lifecycle completed
  Session->>Index: Mark child completed
  Session->>Session: Settle when linked child exists and none run
  Session->>OMP: get_subagents during idle poll
  OMP-->>Session: Running-child snapshots
  Session->>Index: Reconcile snapshots
  Index-->>Session: Any child still running?
  alt child running
    Session->>Parent: Keep turn active
  else no child running
    Session->>Parent: Complete turn
  end
Loading

Reviews (1): Last reviewed commit: "fix(omp): hold task cards open until lin..." | Re-trigger Greptile

Comment on lines +1945 to +1954
private settleDeferredTaskCalls(): void {
const pendingIds = Array.from(this.deferredTaskResults.keys());
for (const toolCallId of pendingIds) {
if (
this.subagentIndex.hasLinkedChild(this.runtimeSession, toolCallId) &&
!this.subagentIndex.hasRunningLinkedTo(this.runtimeSession, toolCallId)
) {
this.finalizeDeferredTask(toolCallId);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Premature multi-child task settlement

When one task launches multiple children and an earlier child completes before a later child emits started, this predicate sees a linked child with none currently running and finalizes the task. The later child then cannot update the removed task card, and the idle poll can complete the parent turn while that child is starting.

Knowledge Base Used: Agent Orchestration

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa9fc5e624

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

state.description = snapshot.description ?? snapshot.assignment ?? state.description;
state.toolCallId = snapshot.parentToolCallId ?? state.toolCallId;
state.status = mapSnapshotStatus(snapshot.status);
events.push(this.upsert(snapshot.id, state.status, state));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Suppress unchanged subagent snapshot upserts

When an idle parent has a running task child, completeTurnAfterProviderIdle polls every 10 ms and each successful get_subagents response reaches this unconditional events.push, even when no snapshot field changed. AgentManager dispatches every resulting upsert to connected clients, so one child can generate roughly 100 redundant updates per second—and batch tasks multiply that traffic—for the child's entire runtime. Emit only when the snapshot changes or when an ID disappears.

Useful? React with 👍 / 👎.

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.

OMP parent shows idle/stopped while internal task subagents are still active; nativeHandle points at missing .jsonl

1 participant