Repository navigation
feat: add RPC server mode - #32
Merged
Merged
Conversation
`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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
codework serveboots 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:
session.execution.startedand exactly one ofsucceeded,failed, orinterrupted. 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.interruptacknowledges without waiting and returns whether there was work to stop.awaitSettlementwaits for that execution alone, whereawaitIdlefollows successors admitted during its cleanup.Plugin.define({ events }).EventRegistry.flattencollects them before the event system builds and fails the boot on a kernel collision, a duplicate across plugins, or a type outsideplugin.<plugin-id>.*.optional()used the source schema as its owndecodeTotarget, 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.messagebefore reporting — the volatile stream is never the last word.