Skip to content

hosted agents: read session events from the data-plane /events endpoint (stack on OHS_endpoints) - #1072

Merged
SSharma-10 merged 4 commits into
OHS_endpointsfrom
atomer/ohp-events-endpoint
Aug 18, 2026
Merged

hosted agents: read session events from the data-plane /events endpoint (stack on OHS_endpoints)#1072
SSharma-10 merged 4 commits into
OHS_endpointsfrom
atomer/ohp-events-endpoint

Conversation

@logwolvy

Copy link
Copy Markdown

Re-applies the two commits reverted from OHS_endpoints by eae8993 and ab376b8 (see #1021), unchanged, on a branch of their own.

The point of the separate branch is to let the matching doctl work (digitalocean/doctl, stacked on feat/agents-subcommands) pin a godo revision that actually has /events, so doctl builds can be tested against the OHP data plane without moving either shared PR branch.

What the commits do

StreamSession moves off the control plane's /v2/agents/sessions/{id}/stream onto the data plane's /v2/agents/sessions/{id}/events, for both reads:

  • Live (the default) delivers forward-only from the moment of attach and holds the connection open. ReplayFrom is sent as the standard Last-Event-ID header.
  • ReplayOnly adds ?replay_only=true: the server writes the session's stored history and then ends the stream, so the read terminates on its own. ReplayFrom stays on the replay_from query parameter, since it is an explicit pagination cursor here rather than a resume hint the server may widen.

Both reads carry stream.state transport control frames reporting connection health (live / catching_up / degraded / superseded). They arrive in the same canonical envelope as an event, so HostedAgentEventKindStreamState and HostedAgentStreamState name the kind and its payload, letting callers identify and skip them instead of rendering them as session activity.

Testing

go build ./..., go vet ./... and go test ./... all pass, including the four StreamSession tests covering the live path, replay-only, replay-only history-then-EOF, and stream.state decoding.

logwolvy and others added 2 commits July 31, 2026 20:07
Live session streaming moved off the control plane's
`/v2/agents/sessions/{id}/stream` onto the data plane's
`/v2/agents/sessions/{id}/events`, so point StreamSession there.

The two surfaces are not interchangeable, so the move is a split rather than
a rename:

  - Live reads go to `/events`. Delivery is forward-only from the moment of
    attach, and the resume cursor rides in the standard `Last-Event-ID`
    header instead of a query parameter.
  - `ReplayOnly` reads stay on `/stream?replay_only=true`. The control plane
    owns the stored event history; the data-plane endpoint holds none, so
    sending a replay-only read there would hang on a stream that never
    yields the history it was asked for.

The live stream also carries `stream.state` transport control frames
reporting connection health (live / catching_up / degraded / superseded).
They arrive in the same canonical envelope as an event, so they decode
through the same parser; name the kind and its payload so callers can
identify and skip them rather than rendering them as session activity.

Co-Authored-By: Claude Opus 5 <[email protected]>
…dpoint

The previous commit moved live streaming onto the data plane but left
replay-only reads (`doctl agents logs`) on the control plane's
`/stream?replay_only=true`, because the data-plane endpoint held no history
of its own -- it delivered forward-only from the moment of attach.

The data plane now stores and serves that history, so both reads move onto
`/v2/agents/sessions/{id}/events` and the split disappears:

  - Live is unchanged: forward-only from attach, connection held open,
    ReplayFrom sent as Last-Event-ID.
  - ReplayOnly adds `?replay_only=true`. The server writes the session's
    stored history and then ends the stream, so the read terminates on its
    own rather than blocking on a connection that never closes.

ReplayFrom keeps riding the `replay_from` query parameter on a replay-only
read rather than moving to Last-Event-ID with the live lane. The two are not
the same cursor: on a live attach it is a resume hint the server is free to
widen for context, while on a history read it is an explicit pagination
point, and collapsing them would let a history query silently return events
the caller already has.

`stream.state` frames now appear on both reads. A replay-only read reports
`catching_up` and then simply ends -- it never reaches `live`, since there is
no live tail to join.

Co-Authored-By: Claude Opus 5 <[email protected]>
Co-authored-by: Cursor <[email protected]>
logwolvy and others added 2 commits August 14, 2026 15:22
The base branch added backward history paging (Before/Limit, HasMore) on
the control-plane .../stream path while this branch moved both session
reads to the data-plane .../events path. Keep both: the paging query
parameters now ride the replay-only mode of the events endpoint, which is
the only mode that can start in the past.

Co-authored-by: Cursor <[email protected]>
The base branch added include_raw (native protocol frames), provider OAuth,
and checkpoint/fork/rollback while this branch serves both session reads
from the data-plane .../events path. Keep both: include_raw rides as a
query parameter on /events for live and replay-only, and live ReplayFrom
still uses Last-Event-ID.

Co-authored-by: Cursor <[email protected]>
@SSharma-10
SSharma-10 merged commit 1d4643b into OHS_endpoints Aug 18, 2026
2 checks passed
@SSharma-10
SSharma-10 deleted the atomer/ohp-events-endpoint branch August 18, 2026 08:28
SSharma-10 added a commit to digitalocean/doctl that referenced this pull request Aug 18, 2026
…ck on feat/agents-subcommands) (#1899)

* agents: pin godo to the data-plane /events build

Points godo at digitalocean/godo#1072, which re-applies the two commits
reverted from `OHS_endpoints`, so `StreamSession` reads both live and
replay-only streams from the data plane's
`/v2/agents/sessions/{id}/events` again.

Vendor-only in effect: the sole diff under vendor/ is the 89 lines that
the earlier pin to the released v1.202.0-beta.1 had dropped from
hosted_agents.go. The next commit moves doctl's own code back onto it.

Co-authored-by: Cursor <[email protected]>

* agents: read live and replay streams from the data-plane /events endpoint

With godo back on `/events`, undo the three local accommodations that
were made while the endpoint was unavailable.

`commands/agents.go` drops its local copies of the `stream.state` kind
and payload and uses godo's `HostedAgentEventKindStreamState` /
`HostedAgentStreamState` again, so the wire contract lives in one place
rather than being restated here.

The reconnect test reads the resume cursor from the `Last-Event-ID`
header instead of a `replay_from` query parameter, matching where the
live lane actually carries it. `replay_from` stays the cursor for
replay-only reads, which are a different lane.

The agentproxy harness serves `/events` and opens every stream with a
`stream.state` frame, so the codex facade tests exercise a stream shaped
like the real one. The control plane's `/stream` is deliberately left
unregistered: no agentproxy caller makes a replay-only read, so a
request landing there is a bug worth failing on rather than quietly
serving.

Co-authored-by: Cursor <[email protected]>

* agents: default the agents surface to the hosted-agents host

Hosted agents are fronted by their own host, which serves both the session
control plane and the data-plane event stream, so reaching them meant
exporting DIGITALOCEAN_API_URL by hand -- and that redirects every other
doctl command along with it.

Give the `doctl agents` services their own client pinned to that host, and
apply caller-supplied client options ahead of the --api-url override so an
endpoint the user named explicitly still wins. That is what keeps a
non-production environment reachable (the preview host, for instance) and
leaves the rest of doctl on api.digitalocean.com.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: SSharma-10 <[email protected]>
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.

2 participants