Skip to content

feat: add RPC server mode - #32

Merged
sanchitrk merged 7 commits into
devfrom
feat-rpc-server
Sep 17, 2026
Merged

sanchitrk merged 7 commits into
devfrom
feat-rpc-server

Conversation

@sanchitrk

Copy link
Copy Markdown
Contributor

codework serve boots the harness once and serves session, sandbox, and event RPCs over a WebSocket; codework run --server <url> drives a session remotely through the same contract.

Harness changes this required:

  • Execution lifecycle events: a busy period publishes session.execution.started and exactly one of succeeded, failed, or interrupted. A failure carries a namespaced category rather than a squashed cause string, and an interruption carries the reason its caller gave instead of one inferred from the cause.
  • Control.interrupt acknowledges without waiting and returns whether there was work to stop. awaitSettlement waits for that execution alone, where awaitIdle follows successors admitted during its cleanup.
  • Plugins declare the events they publish with Plugin.define({ events }). EventRegistry.flatten collects them before the event system builds and fails the boot on a kernel collision, a duplicate across plugins, or a type outside plugin.<plugin-id>.*.
  • optional() used the source schema as its own decodeTo target, so nested transforms ran twice: an Option encoded to null, then null re-encoded as an Option.

Events reach clients through one feed that encodes each event once and fans it out, so a slow client fails alone and a type nothing declared never costs a client its queue capacity. Sessions are shared, so a terminal event arriving before the client's own prompt is promoted is remembered rather than raised, and the client reconciles against session.message before reporting — the volatile stream is never the last word.

`codework serve` boots the harness once and serves session, sandbox, and event
RPCs over a WebSocket; `codework run --server <url>` drives a session remotely
through the same contract.

Harness changes this required:

- Execution lifecycle events: a busy period publishes `session.execution.started`
  and exactly one of `succeeded`, `failed`, or `interrupted`. A failure carries a
  namespaced category rather than a squashed cause string, and an interruption
  carries the reason its caller gave instead of one inferred from the cause.
- `Control.interrupt` acknowledges without waiting and returns whether there was
  work to stop. `awaitSettlement` waits for that execution alone, where
  `awaitIdle` follows successors admitted during its cleanup.
- Plugins declare the events they publish with `Plugin.define({ events })`.
  `EventRegistry.flatten` collects them before the event system builds and fails
  the boot on a kernel collision, a duplicate across plugins, or a type outside
  `plugin.<plugin-id>.*`.
- `optional()` used the source schema as its own `decodeTo` target, so nested
  transforms ran twice: an Option encoded to null, then null re-encoded as an
  Option.

Events reach clients through one feed that encodes each event once and fans it
out, so a slow client fails alone and a type nothing declared never costs a
client its queue capacity. Sessions are shared, so a terminal event arriving
before the client's own prompt is promoted is remembered rather than raised, and
the client reconciles against `session.message` before reporting — the volatile
stream is never the last word.
- one hung remote sandbox like Daytona or Vercel request can prevent the server process from exiting indefinitely.
- shutdown interrupts every active session and awaits settlement.
- mount finalizers release their sandbox leases.
- `Sandbox.stop` rechecks the current refCount while holding the sandbox lifecycle gate.
- If refCount > 0, it returns SandboxBusyError; shutdown logs that failure and continues.
- add location-level errors for missing paths and paths that are not directories,
 and expose them through the session.create and session.relink RPC contracts.
- preserve Runner.SandboxDirectoryNotFoundError for persisted session directories that disappear during execution.
- mark sandbox filesystem adapter layers as fresh so long-running servers do not reuse a memory or SQLite filesystem for later local mounts.
- add RPC coverage for relative sandbox paths, invalid local paths, relinking, and filesystem isolation across sandbox mounts.
@sanchitrk
sanchitrk merged commit fe21399 into dev Sep 17, 2026
@sanchitrk
sanchitrk deleted the feat-rpc-server branch September 22, 2026 13:15
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.

1 participant