Talk to a running soothe-daemon over WebSocket — send prompts, stream agent turns, run jobs.
go get github.com/mirasoth/soothe-client-go@latestRequires a local daemon (default ws://127.0.0.1:8765).
package main
import (
"context"
"fmt"
"github.com/mirasoth/soothe-client-go/appkit"
)
func main() {
ctx := context.Background()
session := appkit.NewDaemonSession("ws://127.0.0.1:8765", nil)
defer session.Close()
if _, err := session.Connect(ctx, ""); err != nil {
panic(err)
}
if err := session.SendTurn(ctx, "Summarize this in one sentence: agents need tools.", nil); err != nil {
panic(err)
}
chunks, errCh := session.IterTurnChunks(ctx, 0)
for chunk := range chunks {
fmt.Println(chunk.Mode, chunk.Data)
}
if err := <-errCh; err != nil {
panic(err)
}
}More patterns: examples/ (hello → streaming → multi-turn → pool → jobs).
| Need | Use |
|---|---|
| One conversation, stream replies | appkit.DaemonSession |
| Jobs / cron one-shots | soothe.CommandClient |
| Raw WebSocket / custom RPCs | soothe.Client |
| Many users / HTTP backend | appkit.ConnectionPool + TurnRunner |
DaemonSession uses a dual-socket layout (stream + RPC sidecar): peels leftover
prior-goal terminals, ignores premature soothe.stream.end until the turn has
progress, drains a short post-idle window, and sends delivery_ack on terminal
frames for daemon drain gating.
import (
"time"
soothe "github.com/mirasoth/soothe-client-go"
)
cc := soothe.NewCommandClient("ws://127.0.0.1:8765", 30*time.Second)
created, err := cc.JobCreate(ctx, "Echo: smoke job", "/tmp/workspace")
// … JobStatus / JobCancel / CronAdd / CronList …soothe-client-go/
├── client.go, protocol.go, session.go, … — transport + Client RPCs
├── command_client.go, stream_terminal.go — CommandClient + turn helpers
└── appkit/ — DaemonSession, pool, TurnRunner, …
Product backends that map chat sessions to daemon loops use ConnectionPool +
QueryGate + TurnRunner + EventClassifier + SessionStore.
| Knob | Default | Notes |
|---|---|---|
IdleTimeout |
off | Silence watchdog between events |
MinIdleTimeoutWithAttachments |
off | Floor when attachments are present |
OnIdleTimeout / OnQueryTimeout / OnStreamClose |
fail | Or soft-complete |
CompactAttachmentsBeforeSend |
false | Optional image downscale |
TreatStatusIdleAsComplete (classifier) |
false | Opt-in idle deliverable |
SessionStore methods take context.Context as the first parameter so
cancellation and deadlines reach the storage backend. See
docs/impl/sessionstore-context.md and
docs/impl/turn-lifecycle.md.
Autopilot control is WebSocket-only (protocol-1 autopilot_* / job_*
request RPCs). Prefer CommandClient for job/cron/autopilot one-shots so they
do not share a streaming socket. Worker event streams use
Client.AutopilotSubscribe on a long-lived connection.
go test ./... # unit + example tests (mock daemon)
go test ./examples/progressive/ # 01–06 ladder (offline)
go test -short ./... # skip live-daemon integrationSame protocol-1 WebSocket contract as soothe-client-python and
@mirasoth/soothe-client.
MIT — see LICENSE.