Skip to content

[Bug]: A run's last conversation events never reach enterprise because the sandbox is deleted before the agent-server flushes them #616

Open

Description

@simonrosenberg

Bug Description

On OHE and Cloud, enterprise never receives the last events of an automation run's conversation. Its copy of the conversation stops before the agent's final reply and the finished state. The cause is a race:

  • The agent-server sends events to enterprise's webhook in batches, and flushes a partial batch only after a delay.
  • The service deletes the run's sandbox within seconds of the run completing, so that batch is never sent.

This affects every run whose last events don't fill a batch. Since #614, a plugin-spec run's conversation is started through enterprise, so this truncated copy is the record users open from their conversation list.

Expected Behavior

Once a run is COMPLETED or FAILED, enterprise has the run conversation's full event history, including the agent's final message and the last state update.

Actual Behavior

Reproduced on OHE Replicated (fleet instance shared-8, 2026-10-20):

A plugin-spec prompt-task automation was dispatched manually. While it ran, I read its events from the sandbox's agent-server (GET {AGENT_SERVER}/api/conversations/{id}/events/search). After it completed, I read enterprise's copy (GET /api/v1/conversation/{id}/events/search).

Sandbox agent-server (just before completion) Enterprise (after completion)
events 13: 12 listed by the agent-server, plus the start-time state update that only enterprise lists 10
last events ObservationEvent (task) 14:25:02, ConversationStateUpdateEvent 14:25:04, agent MessageEvent 14:25:04 (the final reply), ConversationStateUpdateEvent finished 14:25:04 ends at the ObservationEvent at 14:25:02

The run completed at 14:25:09, and the sandbox was gone right after. An earlier run of the same automation also stopped at exactly 10 stored events, before the final reply.

Why this happens:

  • Agent-server. The agent-server's WebhookSpec defaults to event_buffer_size=5 and flush_delay=30.0 s (config.py).
    • It sends a batch when 5 events are queued, and sends a partial batch only after 30 s without a new event (__call__).
    • OHE doesn't override either setting; it only sets OH_WEBHOOKS_0_BASE_URL.
    • So in the run above, 13 events made 2 full batches and the last 3 were still queued.
  • Service. POST /v1/runs/{run_id}/complete starts sandbox deletion right away (router.py). With sandbox_cleanup_delay_seconds set it pauses the sandbox instead; I haven't checked whether a pause lets the agent-server flush.
    • The runner calls /complete as soon as the conversation finishes, about 5 s after the last event.

Acceptance Criteria

  • After a run completes on OHE/Cloud, enterprise's events for the run's conversation include the agent's final message and the terminal ConversationStateUpdateEvent.
  • This holds both when the sandbox is deleted at completion and when sandbox_cleanup_delay_seconds is set.
  • A regression test covers the ordering: the sandbox is not deleted or paused before the conversation's buffered events have been delivered.

Additional Context

Places the fix could go, for discussion:

  • Service: don't delete or pause the sandbox until a buffered batch would have been sent, for example by deferring deletion past flush_delay. This is simple but couples the service to an agent-server default.
  • Runner: before calling /complete, wait until enterprise's copy has the conversation's final event.
  • Agent-server (software-agent-sdk): flush webhook subscribers as soon as a conversation reaches a terminal execution status. This fixes it for every consumer, but needs an SDK release that OHE then ships.

Related: #600, #614 (its "Known gap" note).


I'm an AI agent (Claude Code) helping @simonrosenberg.

Activity

  1. all-hands-bot commented on Oct 20, 2026

    @all-hands-bot
    Contributor

    The following comments were added by the OpenHands AI agent.

    Decision needed

    The bug, its root cause, and the reproduction are clear. What is not settled is which of the three candidate fix locations owns the change, and that choice determines this issue's scope and whether it can land in this repository at all.

    1. Which location should own the fix? The service's teardown (complete_run in router.py / the watchdog deleting or pausing the sandbox), the plugin-spec runner waiting for enterprise's copy before it calls /complete, or the agent-server flushing webhook subscribers when a conversation reaches a terminal execution status. The first two land here; the third needs a software-agent-sdk change plus an OHE release, which would make this issue wait on that release. The body lists all three "for discussion" and notes a different downside for each, so the choice is yours to make.

    2. On the sandbox_cleanup_delay_seconds path: should the fix also guarantee the tail is delivered there, or is deferring to the agent-server's flush window acceptable? The body records that whether a pause lets the agent-server flush was not checked, so the expected behavior on that path is currently unknown.

    Once the location and the pause-path expectation are settled, observable acceptance criteria can be written for the outcome the body already describes, without prescribing an approach.

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

    bugSomething isn't workingpriority:mediumModerate impact; schedule after higher-priority work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions