Skip to content

Add Hosted Agents /v2/agents OpenAPI (triggers, event stream, port-forward; supersedes #1233) - #1255

Merged
SSharma-10 merged 22 commits into
digitalocean:mainfrom
sdharavath:sdharavath/OAR-15627/agents-triggers-events-portforward
Oct 13, 2026
Merged

SSharma-10 merged 22 commits into
digitalocean:mainfrom
sdharavath:sdharavath/OAR-15627/agents-triggers-events-portforward

Conversation

@sdharavath

@sdharavath sdharavath commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Supersedes #1233 and continues @SSharma-10's work (her commits are included here). Adds the
publicly-routed /v2/agents surfaces that #1233 was missing — point 3 of the API Engineering review
(OAR-15627 / MARSOHS-1858) — and incorporates the follow-up API review of this PR.

Surfaces added on top of #1233's 38 ops:

  • Triggers Config API (13): CRUD + pause/resume on /v2/agents/triggers, webhook-secret rotation,
    execution history + get + cancel, by-session lookup, the reusable-sessions picker, the
    /v2/agents/webhook-providers registry, and the HMAC-authenticated webhook ingress.
  • Session event stream (1): GET /v2/agents/sessions/{session_id}/events (SSE).
  • Port-forward (1): GET /v2/agents/sessions/{session_id}/port-forward/{port} (WebSocket, 101).
  • Ingress (1): GET /v2/agents/sessions/{session_id}/ingress (OpenHands-only, team-gated; the URL
    that replaced the port-forward bridge for browser clients).

Full /v2/agents surface: 54 operations.

Deliberately excluded (11 ops, still under development)

Left out on purpose, not an oversight:

  • OAuth connect (6): POST /v2/agents/auth/{provider}, GET /auth/{provider}/poll,
    GET+POST /auth/{provider}/connections, GET+DELETE /auth/{provider}/connections/{id}.
  • Persistent workspaces (4): GET+POST /v2/agents/workspaces,
    GET+DELETE /v2/agents/workspaces/{workspace_id}.
  • CancelTurn (1): POST /v2/agents/sessions/{session_id}/cancel.

Error envelopes

All error responses use the DO-standard JSON {id, message, request_id} (shared/models/error.yml),
matching the Oceanus edge and cthulhu#179553 (deployed to preview + production).

Merge gate — events stream: OHP serves the events-stream errors and is moved onto the JSON
envelope by cthulhu#179797 (MARSOHS-2065, @nkurra), which is not yet deployed. Until it is
rolled out to the OHP instance behind api.digitalocean.com, the live stream still returns
text/plain on 404/429/500/503. Do not merge this PR until #179797 is deployed, or the published
events-error contract will not match the live API. Everything else matches live today.

Addressed in the API review of this PR

  • 402 added to the 16 billing-gated operations (cthulhu#179578).
  • Events errors documented as JSON matching #179797.
  • Trigger statuses completed: 422 (trigger limit) and 503 (upstream check) on create/update;
    create and update can return 403 (backing-config permission denied) and 502 (config service
    error) via writeConfigErr; 400 (body too large/unreadable) on webhook ingress; trigger reads
    return 404 (not 403) for another team's trigger.
  • stream.state added to the stream_event type enum (+ a stream_state schema).
  • Port-forward description corrected (ingress replaced it for browsers; paused sessions are woken and
    served) and the handshake 400 documented.
  • Webhook body lists concrete content types: application/json (type: object),
    application/x-www-form-urlencoded and application/octet-stream (strings).

Test plan

  • make lint → 0 errors (source + bundled ruleset).
  • make bundle → ok.
  • pydo client generation: autorest --python exits 0.
  • Spec CI green on this PR.

Follow-ups (out of scope here)

  • Rename the 54 Go-method summaries to DO reference phrasing ("List All Triggers"); cosmetic, from Add Hosted Agents /v2/agents OpenAPI (full harness surface) #1233.
  • Whether triggers warrant their own agent_harness_trigger:* scope.
  • Prism validation-proxy run against preview + a drift check vs harness.swagger.json /
    trigger.swagger.json (the contract testing in point 5 of the design review).

SSharma-10 and others added 12 commits September 24, 2026 12:06
Document all 38 OHS public endpoints (configs, sessions, checkpoints, fork, workspace transfers, and templates) for client generation and docs.

Co-authored-by: Cursor <[email protected]>
Expose a single requestBody media type so Autorest can generate agents_create_session, while documenting YAML/JSON dual forms via description and x-content-schemas to match harness swagger.

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

Adds the 15 operations missing from digitalocean#1233 (point 3 of the API Engineering review):

- Triggers Config API (13 ops): CRUD + pause/resume on /v2/agents/triggers, webhook
  secret rotation, execution history + cancel, by-session lookup, reusable-session
  picker, the webhook-providers registry, and the HMAC-authenticated webhook ingress
  endpoint. Mirrored from harness-trigger/contracts/trigger.swagger.json.
- Session event stream: GET /v2/agents/sessions/{session_id}/events (Server-Sent
  Events), mirrored from contracts/mars-dataplane-sse.openapi.yaml. Documents the
  shipping params (replay_only, replay_from, cursor, device_id, Last-Event-ID,
  X-Device-UUID).
- Port-forward: GET /v2/agents/sessions/{session_id}/port-forward/{port}, the
  WebSocket tunnel to a guest TCP port (documented as the 101 upgrade handshake).

The webhook ingress body is modeled as application/octet-stream (binary), not '*/*':
the latter crashes the autorest/modelerfour pre-namer ('Name cannot be empty') that
the pydo client-gen CI runs.

Validation: make lint 0 errors; make bundle ok; autorest --python generate exits 0.
Co-authored-by: Cursor <[email protected]>
@sdharavath
sdharavath force-pushed the sdharavath/OAR-15627/agents-triggers-events-portforward branch from a732f80 to 0a4e12a Compare October 8, 2026 07:51
sdharavath and others added 4 commits October 8, 2026 15:15
…/v2/agents errors

Anup Deka (API Eng) asked for the standard envelope; cthulhu#179553 (merged) makes
harness-api emit it and the Oceanus edge already returns it. Point the agents error
schema at shared/models/error.yml so all 128 error responses across the 53 ops use
{id, message, request_id} instead of the old nested {error:{code,message}}, removing
the split where only 401/404/429/500 used the standard shape.

make lint 0 errors; make bundle ok (old nested shape gone); autorest --python exits 0.

Co-authored-by: Cursor <[email protected]>
- digitalocean#1: events-stream errors are text/plain (OHP, not JSON) for 404/429/500/503;
  drop the 400; keep 401 JSON (edge). OHP moves to JSON under MARSOHS-2065.
- digitalocean#2: add 402 to the 16 billing-gated operations (cthulhu#179578).
- digitalocean#3: add GET /v2/agents/sessions/{id}/ingress (publicly routed; replaces the
  port-forward bridge for browser clients).
- digitalocean#4: trigger create/update return 422 (trigger limit) and 503 (upstream check);
  webhook ingress returns 400 (body too large/unreadable).
- digitalocean#5: add stream.state to the stream_event type enum + a stream_state schema.
- digitalocean#6: trigger reads return 404 not 403 for another team's trigger; create can
  return 403 (config permission denied).
- digitalocean#7: correct the port-forward description (ingress replaced it for browsers;
  paused sessions are woken and served) and document the handshake 400.
- digitalocean#8: webhook body lists concrete content types (json/form/octet-stream).
- digitalocean#9: drop the ignored Content-Type response header on the events 200.

Deferred (cosmetic): renaming the 53 Go-method summaries to DO reference style.

make lint 0 errors; make bundle ok; autorest --python exits 0.

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

Matches OHP after cthulhu#179797 (MARSOHS-2065): OHP routes its refusals through
common/httpapi.WriteError + httpapi.RequestID and emits {id, message, request_id}
on 404/429/500/503 (the 401 is the edge). GATED ON #179797's DEPLOY: until it is
rolled out to the OHP instance behind api.digitalocean.com the live stream still
returns text/plain, so digitalocean#1255 must merge only after #179797 is deployed.

make lint 0 errors; make bundle ok.

Co-authored-by: Cursor <[email protected]>
anup-deka
anup-deka previously approved these changes Oct 8, 2026

@anup-deka anup-deka left a comment

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.

LGTM

- create and update trigger document 502 (config service error); update also
  documents 403 (config permission denied) - both via writeConfigErr's default
  and PermissionDenied branches (handlers.go:1244/1248).
- webhook ingress application/json body is type: object (additionalProperties:
  true) so real JSON deliveries validate under a contract check; form and
  octet-stream stay strings.

make lint 0 errors; make bundle ok; autorest --python exits 0.

Co-authored-by: Cursor <[email protected]>
anup-deka
anup-deka previously approved these changes Oct 8, 2026
@SSharma-10
SSharma-10 merged commit 0dd8470 into digitalocean:main Oct 13, 2026
6 checks passed
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.

4 participants