Skip to content

feat(execd): PTY session lifecycle — spec, regenerated clients, and SDK services#1054

Open
ferponse wants to merge 7 commits into
opensandbox-group:mainfrom
ferponse:feat/execd-pty-spec
Open

feat(execd): PTY session lifecycle — spec, regenerated clients, and SDK services#1054
ferponse wants to merge 7 commits into
opensandbox-group:mainfrom
ferponse:feat/execd-pty-spec

Conversation

@ferponse

@ferponse ferponse commented Jun 13, 2026

Copy link
Copy Markdown

Summary

Closes #1078.

execd already serves the interactive PTY session lifecycle (POST /pty, GET /pty/{sessionId}, DELETE /pty/{sessionId}, plus the /pty/{id}/ws WebSocket), but the REST routes were never in specs/execd-api.yaml, so the SDK clients could drift from the server protocol. This adds the routes to the source of truth, regenerates the clients that are spec-driven, and exposes a PTY session service across all SDKs.

Spec

  • Add the three /pty REST routes, matching execd's real behavior — DELETE returns 200 (empty success body) and all three document the 501 NOT_SUPPORTED platform response. The interactive /pty/{id}/ws channel stays a WebSocket and is intentionally not modelled by OpenAPI.

Generated clients (regenerated from the spec)

  • python: regenerated the execd client (openapi-python-client) — adds the api/execd/api/pty module and the CreatePtySessionRequest / CreatePtySessionResponse / PtySessionStatusResponse models.
  • javascript: regenerated execd.ts (openapi-typescript@7.13.0, the pinned version) — adds the /pty path and schema types.
  • kotlin: regenerates its execd client (PTYApi) from the spec at build time (no committed artifact).

Hand-written clients (no spec codegen — PTY transport added by hand)

  • go: the execd client (execd.go) is hand-written, so the /pty transport was added directly (sandbox_pty.go).
  • csharp: the execd client (Internal/HttpClientWrapper.cs) is hand-written, so the /pty calls were added directly in the PTY adapter.

PTY session service (create / status / delete) — wired into the execd stack and exposed on the sandbox facade in every SDK: sandbox.pty() (kotlin), sandbox.pty (python async+sync, javascript), Sandbox.CreatePtySession/... (go), Sandbox.Pty (csharp). The PTY stack member is optional in the public adapter-factory contract, with an unavailable-PTY fallback installed by Sandbox, so the change stays additive for existing custom factories.

Testing

  • Not run (explain why)
  • Unit tests
  • Integration tests
  • e2e / manual verification

Per SDK (all green):

  • spec/kotlin: :sandbox-api:generateExecdApi :sandbox-api:compileKotlin (the generator accepts the spec and PTYApi compiles) + :sandbox:test.
  • python: uv run ruff check / pyright / pytest (async + sync) after regeneration.
  • javascript: typecheck / lint / build + unit tests.
  • go: build / vet / test.
  • csharp: dotnet test.

Breaking Changes

  • None
  • Yes (describe impact and migration path)

Additive routes, types, and an optional stack member (existing adapter factories keep compiling via the unavailable-PTY fallback).

Checklist

  • Linked Issue or clearly described motivation — Add the PTY session lifecycle to the execd spec and expose it in the SDKs #1078
  • Added/updated docs (if needed) — the docs spec bundle is generated from sandbox-lifecycle.yml only, so the execd spec change needs no docs regeneration
  • Added/updated tests (if needed)
  • Security impact considered — PTY is an existing execd capability (this only adds the typed SDK surface) and is platform-gated (501 NOT_SUPPORTED on non-Unix); no new auth surface
  • Backward compatibility considered — additive; optional stack member with fallback

Co-authored-by: Atenea Agent srv_atenea_gitlab@ofidona.net

…lients

execd already serves the interactive PTY session lifecycle, but the routes
were never in the source contract, so SDK clients could drift from the server.
Add them to the source of truth and regenerate the generated clients.

- spec: add POST /pty, GET /pty/{sessionId}, DELETE /pty/{sessionId} to
  specs/execd-api.yaml, matching execd's real behavior (DELETE returns 200;
  all three document the 501 NOT_SUPPORTED platform response). The interactive
  /pty/{id}/ws channel stays a WebSocket and is intentionally not modelled.
- python: regenerate the execd client (openapi-python-client) -> adds the pty
  API module and Create/Status request/response models.
- javascript: regenerate execd.ts (openapi-typescript) -> adds the /pty path
  and schema types.

Kotlin regenerates its execd client from the spec at build time. The Go and C#
execd clients are hand-written (not generated from this spec) and can add PTY
as a follow-up feature when those SDKs choose to expose it.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR has no labels. Please add one based on the changes.

Changed directories: sdks、specs.

📋 Recommended labels (based on changed files):

  • documentation ⬅️
  • sdks ⬅️

Other available labels:

  • bug - Something isn't working
  • dependencies - Pull requests that update a dependency file
  • feature - New feature or request
  • packages - Changes for package, image and configuration

💡 Tip: Use feature for new functionality or improvements, bug for fixes.

cc @ferponse

Ferran Pons Serra and others added 3 commits June 14, 2026 01:41
…YApi

Expose execd's PTY session lifecycle in the Kotlin SDK as `sandbox.pty()`,
implemented on top of the OpenAPI-generated PTYApi (this PR adds /pty to the
execd spec): createSession / getSession / deleteSession. No handwritten
transport — request/response mapping and error handling go through the
generated client and the shared exception converter, like the other execd
adapters. The PTY service is wired after construction via an internal
bindPtyService() so the public Sandbox constructor signature is unchanged.

The interactive WebSocket attach helper (ws/wss URL + handshake headers) is
intentionally out of scope here and will follow in a branch stacked on this one.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>
…Y client

Expose execd's PTY session lifecycle in the Python SDK as `sandbox.pty`
(async and sync variants): create_session / get_session / delete_session,
implemented on the openapi-python-client generated PTY API. Adds the Pty /
PtySync service protocols, PtyAdapter / PtyAdapterSync, PtySession /
PtySessionStatus domain models, factory wiring and the Sandbox accessor.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>
Add CreatePtySession / GetPtySession / DeletePtySession to the hand-written
execd client and expose them on the Sandbox, mirroring the existing session
helpers. The PtySession / PtySessionStatus types map execd's /pty REST
responses (session_id / running / output_offset). The interactive WebSocket
channel is driven separately and is out of scope here.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>
@ferponse ferponse changed the title feat(execd): add PTY session routes to the spec and regenerate clients feat(execd): PTY session lifecycle — spec, regenerated clients, and SDK services Jun 14, 2026
@ferponse

Copy link
Copy Markdown
Author

Scope update: this PR now carries the full auto-generatable PTY lifecycle:

  • spec: /pty create/status/delete in execd-api.yaml (DELETE 200, 501 NOT_SUPPORTED).
  • generated clients regenerated from the spec: Python (openapi-python-client) and JS (openapi-typescript@7.13.0); Kotlin regenerates PTYApi at build time.
  • SDK PTY services on top of the generated/native client: Kotlin (sandbox.pty(), on the generated PTYApi), Python (sandbox.pty, async + sync), Go (Sandbox.CreatePtySession/GetPtySession/DeletePtySession). All verified locally: Kotlin :sandbox:test, Python ruff/pyright/pytest, Go build/vet/test.
  • C# and JS PTY services (the hand-written adapter layer) are not included yet — only their generated execd types. The interactive WebSocket attach helper is intentionally deferred to a follow-up branch stacked on this one.

Expose execd's PTY session lifecycle (create / status / delete) in the
remaining SDKs, on top of the clients generated/hand-written from the
/pty spec:
- JavaScript: ExecdPty service + PtyAdapter (openapi-fetch over execd.ts),
  wired into the execd stack and exposed as `sandbox.pty`.
- C#: IExecdPty + PtyAdapter (HttpClientWrapper), wired into ExecdStack and
  exposed as `Sandbox.Pty`.

Adds PtySession / PtySessionStatus models and unit tests in both SDKs.
DELETE is read as a stream in JS to avoid JSON-parsing its empty 200 body.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>

@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: e2a31a6282

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread sdks/sandbox/javascript/src/factory/adapterFactory.ts Outdated
Comment thread sdks/sandbox/csharp/src/OpenSandbox/Factory/IAdapterFactory.cs Outdated
Comment thread sdks/sandbox/javascript/src/adapters/ptyAdapter.ts Outdated
Address Codex review on opensandbox-group#1054:

- Make the execd stack's `pty`/`Pty` member optional in the public
  `AdapterFactory` / `IAdapterFactory` contract, and install an
  unavailable-PTY fallback in `Sandbox` when a custom factory omits it
  (mirrors the existing unavailable-credential-vault fallback). Existing
  factories and test doubles keep compiling and running; `sandbox.pty`
  stays defined and fails loudly only on use.
- JS: stop forcing the PTY DELETE response through the stream parser. The
  empty 200 body (Content-Length: 0) is already skipped by openapi-fetch,
  while error responses (404 CONTEXT_NOT_FOUND, 501 NOT_SUPPORTED) keep
  their JSON code/message for throwOnOpenApiFetchError to surface.

Tests cover the JSON-error path, the empty-success path, and the
unavailable-PTY fallback; the C# stack tests no longer set Pty, proving
the change is additive.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>

@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: 9908a47b3f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread sdks/sandbox/javascript/src/sandbox.ts
`sandbox.pty` is part of the root public API, so export `ExecdPty`,
`PtySession`, and `PtySessionStatus` alongside `ExecdCommands` instead of
leaving them reachable only via inference or internal paths.

Co-authored-by: Atenea Agent <srv_atenea_gitlab@ofidona.net>
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.

Add the PTY session lifecycle to the execd spec and expose it in the SDKs

1 participant