Skip to content

feat(go): native Go server — parity with the Rust/C# server - #61

Merged
brentrager merged 1 commit into
mainfrom
server-go
Jun 24, 2026
Merged

feat(go): native Go server — parity with the Rust/C# server#61
brentrager merged 1 commit into
mainfrom
server-go

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

Today only Rust and C# have a smooth-operator server. Go shipped only a client (go/protocol). This fills the gap with a native Go server that consumes the Go engine in-process per turn.

Solution

New module go/server (github.com/SmooAI/smooth-operator/go/server), a parity port of the proven C# server, verified against the Rust reference.

  • FrameDispatcher / SessionStore / TurnRunner / ProtocolEvents — ported from dotnet/server/src/{FrameDispatcher,TurnRunner,SessionStore,ProtocolEvents,Auth}.cs, cross-checked against rust/smooth-operator-server/src/{server,handler,runner,protocol,local}.rs.
  • WS transport (github.com/coder/websocket, already the client's dep) — one /ws endpoint, per-connection read loop + a single outbound writer goroutine fed by a channel (the Rust sink_tx/writer split, the C# channel/writer task).
  • Graceful SIGTERM/SIGINT drain — one shared drain context; the read loop selects drain-vs-frame with the turn dispatch awaited inside the frame branch (drain checked first; Go select is random on ties, so the cancel is preferred explicitly), the writer kept on a separate teardown-only context so an in-flight turn flushes its terminal event, then a backplane detach-after-loop always runs.
  • Auth verifier seam — default permissive (anonymous/org-public) + an HS256 LocalTokenVerifier (fail-closed), chosen at connect from ?token= (mirrors the Rust verifier).
  • ServeLocal / SpawnLocal — in-memory, loopback, auth-off entrypoint (mirrors Rust local.rs), embeddable in-process.
  • Consumes the published engine github.com/SmooAI/smooth-operator-core/go/core (NewSmoothAgent + RunStream; MockLlmProvider in tests). Engine stream events (text/tool_call/tool_result/done) map to protocol stream_token / stream_chunk → terminal eventual_response.
  • No-engine case settles as a clean protocol error (the keyless path) — never a panic or dropped socket.

Stubbed seams (MVP): in-memory Backplane only (Redis/NATS cross-pod fan-out is the seam), ACL-filtered retrieval + rerank in the turn, and HITL tool-confirmation.

The client/protocol module (go/protocol, go/go.mod) is untouched; the server is its own module so the published client stays dependency-light, consuming the protocol locally via a replace directive.

Verification

go test -race ./... — all pass:

  • Protocol conformance — every server→client event the server emits validated against the canonical spec/events/*.schema.json and round-tripped through the Go client's protocol.ParseServerEvent.
  • BootSpawnLocal starts on an ephemeral port and accepts a connection (ping round-trips).
  • Turn round-trip — client → create_conversation_sessionsend_message (engine on MockLlmProvider) → streamed stream_tokens concatenate to the scripted reply + terminal eventual_response carries it.
  • No-engine / unknown-session — clean protocol error / NOT_FOUND.
  • Graceful drain — turn provably in-flight, Shutdown() lets it finish (terminal event still arrives), the loop exits, and the backplane detach runs (verified under -race).

🤖 Generated with Claude Code

…g smooth-operator-core/go/core

Fills the Go server gap: until now Go shipped only a protocol client. This adds a
native Go smooth-operator service mirroring the proven C# server and the Rust
reference, in a new module `go/server`.

- FrameDispatcher / SessionStore / TurnRunner / ProtocolEvents ported from the C#
  server (dotnet/server/src), verified against the Rust reference
  (rust/smooth-operator-server/src/{server,handler,runner,protocol,local}.rs).
- WebSocket transport (coder/websocket): per-connection read loop + single outbound
  writer goroutine fed by a channel.
- Graceful SIGTERM/SIGINT drain: one shared drain context; an in-flight turn finishes
  and flushes its terminal event before the loop exits and detaches from the backplane.
- Auth verifier seam (permissive + HS256 LocalTokenVerifier, fail-closed) and an
  in-memory Backplane stub (Redis/NATS seam left open).
- ServeLocal / SpawnLocal in-memory loopback entrypoint (mirrors Rust local.rs).
- Consumes the published engine module github.com/SmooAI/smooth-operator-core/go/core
  (NewSmoothAgent + RunStream; MockLlmProvider in tests).

Tests (go test -race): spec conformance (every emitted event validated against the
spec/ schemas + round-tripped through the Go client's parser), boot, turn round-trip
on MockLlmProvider, no-engine clean-error path, unknown-session NOT_FOUND, and the
graceful-drain guarantee (in-flight turn finishes → detach runs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U7Mn93HpqhSgEmX6tRdPAv
@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d2506d3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 81b82c2 into main Jun 24, 2026
brentrager added a commit that referenced this pull request Jun 24, 2026
… shared parity corpus (#69)

The TS/Python/Go servers landed (#59/#60/#61) and all five now run the shared
spec/conformance/scenarios corpus (#62-#67) — tested protocol parity. Updates the
servers row + tests section from 'in progress' to the real state.


Claude-Session: https://claude.ai/code/session_01U7Mn93HpqhSgEmX6tRdPAv

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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