Documentation
¶
Overview ¶
Package runtime is the runtime link's server side (WEFT-PLAYGROUND §10.3, S4.2): the registry of connected runtimes and the three routes a weft/runtime client speaks to — register, the SSE command stream, acks. Studio decides (the playground routes in studio/playground.go enqueue commands against a registered agent); the runtime in your app executes.
The package is self-contained on purpose: it reads no run data — a command's validation data is the runtime's own registration, refreshed on every reconnect, because the runtime's copy is the authoritative one (§10.4). Its one database touch is the optional write-through of its own bookkeeping (WithStore, below). Commands are at-most-once: the runtime acks before executing and ignores repeated ids; Studio refuses a reused command id outright (409) and never re-sends a lost one — the user re-issues it (§10.5).
Command lifecycle (§10.5), with its two timers:
queued ──(ack)──► accepted ──(finish)──► finished │ │ ├──(reject)──► rejected └──(no ack in 30 s, or the stream died before the ack)──► lost accepted ──(stream died, no finish in 10 min)──► lost accepted ──(no finish and no heartbeat in max(10 min, 3 beats))──► lost
The second accepted edge is for a runtime that heartbeats (Registration.HeartbeatMS): while it holds the command it re-posts the accepted ack at that cadence, and each repeated accepted ack restarts the watch — on an accepted row it is liveness, on a lost one a 409, on a finished one a no-op.
A late ack or finish still lands: the truth about a run wins over a timer's guess.
Built WithStore (studio.New passes its database when it implements obsdb.CommandStore), the server writes all of it through and a restarted Studio loads it back, deciding "lost" from the stored state — never re-sending an accepted command (store.go has the rules). Without a store everything lives in memory and a restart forgets it.
Runtime ids are the runtime's to choose: weft/runtime derives one stable per (service, cwd, agent set), so an app restarted in the same place registers under the same id and a link to /runtimes/<id> survives it. A registration under an id another process holds a live stream on is answered with `<id>-<pid>` (RegisterResponse.RuntimeID, which the runtime adopts): two replicas of one service in one cwd get distinct ids, a reconnect of the same process keeps its own.
Index ¶
- Constants
- Variables
- func ValidCommandID(id string) bool
- type Ack
- type AgentDefaults
- type AgentLimits
- type AgentRegistration
- func (a AgentRegistration) IsAllowed(tool string) bool
- func (a AgentRegistration) ManifestInstructions() string
- func (a AgentRegistration) ManifestModelName() string
- func (a AgentRegistration) ManifestToolNames() []string
- func (a AgentRegistration) ManifestToolSchemas() (schemas map[string]json.RawMessage, ok bool)
- type AgentView
- type ApprovalDecision
- type Breakpoints
- type Budget
- type CancelTarget
- type Command
- type CommandStatus
- type ManifestAgent
- type ManifestSource
- type Option
- type Overrides
- type Params
- type RegisterResponse
- type Registration
- type RuntimeServer
- func (rs *RuntimeServer) AnyOwnRuns() bool
- func (rs *RuntimeServer) AnySessions() bool
- func (rs *RuntimeServer) BreakpointsOf(runtimeID string) []string
- func (rs *RuntimeServer) Cancel(runtimeID, commandID string)
- func (rs *RuntimeServer) CancelRun(runID string) (CancelTarget, error)
- func (rs *RuntimeServer) Close()
- func (rs *RuntimeServer) Command(id string) (CommandStatus, error)
- func (rs *RuntimeServer) CommandOf(id string) (Command, bool)
- func (rs *RuntimeServer) Connected(runtimeID string) bool
- func (rs *RuntimeServer) Enqueue(runtimeID string, cmd Command) (Command, error)
- func (rs *RuntimeServer) EnqueueApproval(runtimeID string, d ApprovalDecision) (ApprovalDecision, error)
- func (rs *RuntimeServer) EnqueueSession(runtimeID string, sc SessionCommand) (SessionCommand, error)
- func (rs *RuntimeServer) Manifests() []ManifestSource
- func (rs *RuntimeServer) Mount(mux *http.ServeMux)
- func (rs *RuntimeServer) MountGuarded(mux *http.ServeMux, guard func(http.Handler) http.Handler)
- func (rs *RuntimeServer) PublicOf(runID string) (string, bool)
- func (rs *RuntimeServer) Registration(runtimeID string) (Registration, bool)
- func (rs *RuntimeServer) RuntimeOf(runID string) (string, bool)
- func (rs *RuntimeServer) SessionsRuntime(service string) (string, bool)
- func (rs *RuntimeServer) SetBreakpoints(runtimeID string, tools []string) error
- func (rs *RuntimeServer) Shutdown(ctx context.Context) error
- func (rs *RuntimeServer) Snapshot() []RuntimeView
- func (rs *RuntimeServer) Steer(runtimeID, runID, message string) error
- type RuntimeView
- type SessionCommand
- type SourceSpec
- type SteerMessage
- type ToolChoice
- type ToolView
- type TranscriptEdit
Examples ¶
Constants ¶
const ( VerbDecide = "decide" VerbSteer = "steer" VerbCancel = "cancel" )
The session frame's verbs.
const ( StateQueued = "queued" StateAccepted = "accepted" StateRejected = "rejected" StateFinished = "finished" StateLost = "lost" )
Command states (§10.5).
Variables ¶
var ( // ErrUnknownRuntime: no runtime ever registered under the id. ErrUnknownRuntime = fmt.Errorf("studio/runtime: unknown runtime") // ErrNotConnected: the runtime registered once but holds no live // command stream now. ErrNotConnected = fmt.Errorf("studio/runtime: runtime not connected") // ErrDuplicateCommand: the command id was already used (409, the // at-most-once rule's Studio side). ErrDuplicateCommand = fmt.Errorf("studio/runtime: command id already used") // ErrUnknownCommand: no command under the id. ErrUnknownCommand = fmt.Errorf("studio/runtime: unknown command") // ErrInvalidCommandID: a caller-supplied command id that is not 1 // to 128 characters of [A-Za-z0-9._:-] (400). The id is written // verbatim as the SSE frame's id line, so anything that could break // a line — and with it forge a frame on the runtime's stream — is // refused before it reaches a row or the wire. ErrInvalidCommandID = fmt.Errorf("studio/runtime: invalid command id") // ErrUnknownRun: no runtime started the run (CancelRun) — the app's // own turns, or an id no ack ever named. ErrUnknownRun = fmt.Errorf("studio/runtime: no runtime started the run") // ErrRunFinished: the run's command is over — finished (a parked // run included: approval is a run boundary), rejected or lost — so // there is nothing in flight to cancel (CancelRun). ErrRunFinished = fmt.Errorf("studio/runtime: the run's command is no longer in flight") )
Errors Enqueue and the routes answer with.
Functions ¶
func ValidCommandID ¶
ValidCommandID reports whether id may be used as a command id: 1 to 128 characters of [A-Za-z0-9._:-] (the minted "cmd_" + ULID form is inside it). The id doubles as the SSE frame id, so a control character in it would write the caller's own fields onto the runtime's command stream.
Types ¶
type Ack ¶
type Ack struct {
CommandID string `json:"command_id"`
State string `json:"state"`
RunID string `json:"run_id,omitempty"`
Status string `json:"status,omitempty"`
Error string `json:"error,omitempty"`
}
Ack is the POST /api/runtime/acks body: accepted/rejected before execution, finished at the run's end — Status succeeded | failed, and Error why (a rejection's reason, a failed run's error).
type AgentDefaults ¶ added in v0.13.0
type AgentDefaults struct {
MaxSteps int `json:"max_steps"`
Parallelism int `json:"parallelism"`
Thinking string `json:"thinking"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
MaxTokens *int `json:"max_tokens,omitempty"`
Seed *int64 `json:"seed,omitempty"`
Stop []string `json:"stop,omitempty"`
ToolChoice ToolChoice `json:"tool_choice"`
}
AgentDefaults are an agent's run defaults in the command's override vocabulary: its caps, its thinking level ("" is the provider default), the sampling knobs its core.Params set (absent: the adapter's own) and its tool choice (mode "auto" when none was set).
type AgentLimits ¶
AgentLimits are the agent's own lower-only bounds.
type AgentRegistration ¶
type AgentRegistration struct {
Name string `json:"name"`
Manifest string `json:"manifest"`
Models []string `json:"models"`
Resolver bool `json:"resolver"`
Limits AgentLimits `json:"limits"`
Defaults AgentDefaults `json:"defaults"`
SideEffects map[string]string `json:"side_effects"`
Allow []string `json:"allow"`
}
AgentRegistration is one exposed agent. Resolver says the runtime holds a runtime.ModelResolver (a command may name a model outside Models; the runtime decides); Defaults are the agent's run defaults (plan F3's option lab greys them). A runtime older than either sends neither: false and the zero value.
func (AgentRegistration) IsAllowed ¶
func (a AgentRegistration) IsAllowed(tool string) bool
IsAllowed reports whether tool is on the agent's AllowSideEffects list.
func (AgentRegistration) ManifestInstructions ¶
func (a AgentRegistration) ManifestInstructions() string
ManifestInstructions returns the agent's registered system prompt — what an experiment drawer pre-fills from (the code's own words, not a guess from the trace).
func (AgentRegistration) ManifestModelName ¶
func (a AgentRegistration) ManifestModelName() string
ManifestModelName returns the agent's own model name.
func (AgentRegistration) ManifestToolNames ¶
func (a AgentRegistration) ManifestToolNames() []string
ManifestToolNames lists the agent's tools in manifest order.
func (AgentRegistration) ManifestToolSchemas ¶ added in v0.13.0
func (a AgentRegistration) ManifestToolSchemas() (schemas map[string]json.RawMessage, ok bool)
ManifestToolSchemas returns the agent's tools' input schemas by name, as its manifest records them — what the runtime checks a tool_args edit against (its agent's own tools). ok is false when the manifest does not read; a tool listed without a schema is absent from the map.
type AgentView ¶
type AgentView struct {
Name string `json:"name"`
Models []string `json:"models"`
Resolver bool `json:"resolver"`
// Defaults are the agent's run defaults as its runtime registered
// them; nil (absent on the wire) for a runtime older than the
// option lab, which registers none — a client greys the new knobs
// on that, as Studio refuses them (playground.go).
Defaults *AgentDefaults `json:"defaults,omitempty"`
Tools []ToolView `json:"tools"`
// Instructions is the agent's registered system prompt, read from
// its manifest: the experiment drawer pre-fills from the registered
// config, never a guess from the trace (WEFT-PLAYGROUND §3).
Instructions string `json:"instructions,omitempty"`
}
AgentView is one agent of a connected runtime. Resolver and Defaults are the registration's (AgentRegistration): whether a model name outside Models may be proposed, and the run defaults the option lab shows greyed beside each override.
type ApprovalDecision ¶
type ApprovalDecision struct {
CommandID string `json:"command_id"`
RunID string `json:"run_id"`
CallID string `json:"call_id"`
Decision string `json:"decision"` // approve | deny | resolve
Reason string `json:"reason,omitempty"`
Content string `json:"content,omitempty"`
// Actor names who decided (§6 rule 4's spirit); carried for the
// record, the runtime logs it.
Actor string `json:"actor,omitempty"`
// PublicID is the public id the decided run is scoped to — the
// server's bookkeeping, never on the wire. The approval's command
// row carries it (a panel token polls its own decision) and so does
// the resumed run its acks name. Empty takes the run's recorded
// scope.
PublicID string `json:"-"`
}
ApprovalDecision is an `event: approve` frame's data (WEFT-DEVTOOLS §8.2's continue / skip / resolve): a human decision on one parked call of a run the runtime started. The runtime resumes the parked run with the matching core verb — Approve runs the handler, Deny skips it, Resolve pastes a result computed outside the process (ADR 0007).
type Breakpoints ¶
type Breakpoints struct {
Tools []string `json:"tools"`
}
Breakpoints is an `event: breakpoints` frame's data (§8.3): the runtime passes the named tools as core.ParkOn on every run it starts from then on [D7] — it cannot alter agents it did not build. An empty set clears.
type Budget ¶
type Budget struct {
MaxTokensPerExperiment int64 `json:"max_tokens_per_experiment"`
MaxRunsPerExperiment int64 `json:"max_runs_per_experiment"`
}
Budget is the runtime's per-experiment caps.
type CancelTarget ¶ added in v0.14.0
type CancelTarget struct {
RunID string `json:"run_id"`
CommandID string `json:"command_id"`
RuntimeID string `json:"runtime_id"`
}
CancelTarget is what CancelRun asked to stop: the run, the command that holds it in its runtime, and that runtime.
type Command ¶
type Command struct {
CommandID string `json:"command_id"`
Agent string `json:"agent"`
Source *SourceSpec `json:"source"`
Input *string `json:"input"`
Overrides Overrides `json:"overrides"`
TranscriptEdits []TranscriptEdit `json:"transcript_edits"`
Engine string `json:"engine"`
SideEffects string `json:"side_effects"`
Thread string `json:"thread"`
ExperimentID string `json:"experiment_id"`
Actor string `json:"actor"`
PublicID string `json:"public_id"`
// Runtime is the runtime the command was enqueued to.
Runtime string `json:"-"`
// contains filtered or unexported fields
}
Command is one `event: run` frame's data (§10.3). Runtime, seq and cancel are the server's own bookkeeping and stay off the wire.
type CommandStatus ¶
type CommandStatus struct {
CommandID string `json:"command_id"`
State string `json:"state"`
RunID string `json:"run_id"`
// Status is the finished run's own outcome, succeeded | failed, as
// the runtime's finished ack named it; absent until the command is
// finished.
Status string `json:"status,omitempty"`
// Error is why, when there is a why: a failed run's error text, a
// rejection's reason, what made the command lost. Null otherwise.
Error *string `json:"error"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}
CommandStatus is the GET /api/playground/commands/{id} row (§10.4).
type ManifestAgent ¶ added in v0.11.0
type ManifestAgent struct {
Name string `json:"name"`
// ManifestHash is sha256 of the agent's own one-agent manifest —
// the weft.manifest.hash its runs carry.
ManifestHash string `json:"manifest_hash"`
// Entry is the agent's object in its manifest's agents array.
Entry json.RawMessage `json:"-"`
}
ManifestAgent is one agent of a registered manifest.
type ManifestSource ¶ added in v0.11.0
type ManifestSource struct {
Service string `json:"service"`
// ManifestHash is sha256 (lowercase hex) of the registration's
// agents as one core.Manifest document — the bytes
// weft.Manifest(agents...) writes for the same agents in the same
// order, so a weft.json generated from them hashes the same.
ManifestHash string `json:"manifest_hash"`
// Live: a runtime holding a command stream now registered with
// this manifest. False is remembered, never live.
Live bool `json:"live"`
// RegisteredAt is the latest registration with this manifest.
RegisteredAt time.Time `json:"registered_at"`
// RuntimeID is the runtime that registered it last.
RuntimeID string `json:"runtime_id"`
Agents []ManifestAgent `json:"agents"`
// Version is the manifest documents' "weft" format version.
Version int `json:"-"`
}
ManifestSource is one remembered registration manifest.
type Option ¶ added in v0.15.0
type Option func(*RuntimeServer)
Option configures New.
func WithStore ¶ added in v0.15.0
func WithStore(s obsdb.CommandStore) Option
WithStore makes the server write its commands, routes, breakpoint sets and registrations through to s, and loads what s holds at New — a restarted Studio over the same database keeps them (plan I3). Nil keeps the server in memory only, as without the option. One Studio per command store: two Studios sharing one (a ClickHouse database) never see each other's acks and beats, so a restarted one would load the other's queued and accepted rows and decide them lost under it (nothing runs twice; live commands read lost).
type Overrides ¶
type Overrides struct {
Instructions string `json:"instructions,omitempty"`
ToolsEnabled []string `json:"tools_enabled,omitempty"`
Model string `json:"model,omitempty"`
Thinking string `json:"thinking,omitempty"`
Options map[string]float64 `json:"options,omitempty"`
Params *Params `json:"params,omitempty"`
ToolChoice *ToolChoice `json:"tool_choice,omitempty"`
ParkOn []string `json:"park_on,omitempty"`
OnlyTools []string `json:"only_tools,omitempty"`
}
Overrides are the experiment's changes (§5.1): the numeric options (max_steps, parallelism, temperature) and, beside them, the option lab's typed knobs (plan F3) — every one narrowing or neutral.
type Params ¶ added in v0.13.0
type Params struct {
TopP *float64 `json:"top_p,omitempty"`
MaxTokens *int `json:"max_tokens,omitempty"`
Stop []string `json:"stop,omitempty"`
Seed *int64 `json:"seed,omitempty"`
}
Params is the sampling override beside options.temperature: the rest of core.RequestParams. Absent fields keep the agent's own — a command cannot clear the agent's stop or max_tokens.
type RegisterResponse ¶
type RegisterResponse struct {
RuntimeID string `json:"runtime_id"`
CommandsURL string `json:"commands_url"`
}
RegisterResponse answers a registration.
type Registration ¶
type Registration struct {
RuntimeID string `json:"runtime_id"`
Host string `json:"host"`
Pid int `json:"pid"`
// Cwd is the app's working directory ("" when the runtime could not
// read it, or is older than the field): one of the inputs of its
// stable id, and shown in GET /api/runtimes under db.path's rule.
Cwd string `json:"cwd,omitempty"`
// Instance tells two runtime links of one process apart (a random
// value per link, kept across its reconnects): with host and pid it
// is what makes a registration "the same process" — whose reconnect
// keeps its id — rather than a second one colliding on it
// (serveRegister). A client must send one to keep at-most-once
// across its own restarts; one that does not is told apart by host
// and pid alone (a restarted container reusing pid 1 would then read
// as the old process), and Studio WARNs once.
Instance string `json:"instance,omitempty"`
Service string `json:"service"`
Env string `json:"env"`
WeftVersion string `json:"weft_version"`
Budget Budget `json:"budget"`
Threads bool `json:"threads"`
// Sessions says the runtime was installed with runtime.Sessions: it
// resolves the app's own sessions by public id, so the debugger's
// verbs on an app-owned turn of its service route to it as a
// `session` frame (EnqueueSession) instead of being refused. A
// runtime older than the option sends false.
Sessions bool `json:"sessions"`
// BreakpointsOwn says an agent the runtime exposes was built with
// runtime.Breakpoints(): the debugger's breakpoints park tools on
// the app's own runs of it (core.ParkSource, ADR 0030), not only on
// the runs the runtime starts. A runtime older than the option
// sends false.
BreakpointsOwn bool `json:"breakpoints_own"`
// Breakpoints is the debugger's tool set the runtime holds right
// now (WEFT-DEVTOOLS §8.3). The set lives in the runtime's process,
// so it outlives a Studio restart: a Studio that holds no set for
// the runtime adopts this one (serveRegister).
Breakpoints []string `json:"breakpoints"`
Agents []AgentRegistration `json:"agents"`
// HeartbeatMS is the cadence, in milliseconds, at which the runtime
// re-posts a command's accepted ack while the command is in its
// hands (waiting for a run slot, running) — its word that the
// command is still alive. A runtime that heartbeats gets a finish
// watch on every accepted command (serveAcks), so a lost response
// to the accepted ack cannot leave the row accepted forever while
// the stream stays open; one older than the heartbeat sends 0 and
// gets the watch only when its stream ends, as before.
HeartbeatMS int64 `json:"heartbeat_ms"`
}
Registration is the POST /api/runtime/register body.
func (Registration) Agent ¶
func (r Registration) Agent(name string) (AgentRegistration, bool)
Agent is an agent lookup on a registration.
type RuntimeServer ¶
type RuntimeServer struct {
// AckDeadline is how long a queued command may go unacked before
// it is lost (§10.5: 30 s). A field so tests can tighten it.
AckDeadline time.Duration
// FinishDeadline is how long an accepted command may stay
// unfinished with no word from its runtime before it is lost
// (§10.5: 10 min): after its runtime's stream died, or — for a
// runtime that heartbeats (Registration.HeartbeatMS) — since its
// last accepted ack, stream open or not, the watch then being
// max(FinishDeadline, three heartbeats) with the heartbeat taken at
// most FinishDeadline. A field so tests can tighten it.
FinishDeadline time.Duration
// PingEvery is the SSE keep-alive cadence (15 s).
PingEvery time.Duration
// Retention is how long a terminal command (finished, rejected,
// lost), a run's route and a disconnected runtime that still holds
// a breakpoint set stay in memory (24 h). A disconnected runtime
// with nothing pending goes after FinishDeadline: replicas and
// services come and go, and each would otherwise leave an entry in
// GET /api/runtimes forever (an app restarted in the same place
// re-registers under its stable id and reclaims its entry). A field so tests can tighten
// it.
Retention time.Duration
// WriteTimeout bounds one frame's write to the command stream
// (30 s): a runtime that stopped reading without closing must not
// hold the handler in Write forever. A field so tests can tighten
// it.
WriteTimeout time.Duration
// contains filtered or unexported fields
}
RuntimeServer is the registry of connected runtimes and their commands: it mounts the three §10.3 routes (Mount), hands the playground its views and its enqueue (Snapshot, Registration, Enqueue, Command), and runs §10.5's lost-command timers. It is the type Server.Runtime returns once the studio side wires it (S4.1; see notes-lane-c2.md for the studio.go/routes.go lines that merge applies — this lane owns neither file).
Example ¶
ExampleRuntimeServer mounts the runtime link's three routes — the block a Studio with Playground(true) serves — and answers one registration.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/weftgo/weft/studio/runtime"
)
func main() {
rs := runtime.New()
mux := http.NewServeMux()
rs.Mount(mux)
ts := httptest.NewServer(mux)
defer ts.Close()
reg := `{"runtime_id":"rt_demo","host":"laptop","pid":1,"service":"acme-api","env":"dev",
"weft_version":"v0.6.0",
"budget":{"max_tokens_per_experiment":200000,"max_runs_per_experiment":60},
"threads":false,
"agents":[{"name":"acme-support","manifest":"{}","models":["glm-5.3-flash"],
"limits":{"max_steps":10,"parallelism":4},
"side_effects":{"lookup_order":"never"},
"allow":["lookup_order"]}]}`
resp, err := http.Post(ts.URL+"/api/runtime/register", "application/json", strings.NewReader(reg))
if err != nil {
fmt.Println("register:", err)
return
}
defer func() { _ = resp.Body.Close() }()
fmt.Println("register:", resp.StatusCode)
views := rs.Snapshot()
fmt.Println("runtimes:", len(views), views[0].ID, views[0].Agents[0].Name)
}
Output: register: 200 runtimes: 1 rt_demo acme-support
func New ¶
func New(opts ...Option) *RuntimeServer
New builds a RuntimeServer with §10.5's timers: empty, or — WithStore — holding what the store kept from before a restart, decided as store.go says.
func (*RuntimeServer) AnyOwnRuns ¶ added in v0.15.0
func (rs *RuntimeServer) AnyOwnRuns() bool
AnyOwnRuns reports whether any connected runtime acts on the app's own runs: it registered runtime.Sessions (decisions, steer and cancel on the app's turns) or an agent built with runtime.Breakpoints() (breakpoints park the app's runs) — meta's debug_scope reads own+runtime then (plan F4).
func (*RuntimeServer) AnySessions ¶ added in v0.15.0
func (rs *RuntimeServer) AnySessions() bool
AnySessions reports whether any connected runtime registered runtime.Sessions — meta's debug_scope reads own+runtime then.
func (*RuntimeServer) BreakpointsOf ¶
func (rs *RuntimeServer) BreakpointsOf(runtimeID string) []string
BreakpointsOf returns the runtime's stored breakpoint set.
func (*RuntimeServer) Cancel ¶
func (rs *RuntimeServer) Cancel(runtimeID, commandID string)
Cancel sends an `event: cancel` frame for a command (the debugger rungs' verb; not a P0 route). Best effort: the runtime cancels what it started, and never the app's own runs.
func (*RuntimeServer) CancelRun ¶ added in v0.14.0
func (rs *RuntimeServer) CancelRun(runID string) (CancelTarget, error)
CancelRun asks the runtime that started runID to cancel the command holding it (an `event: cancel` frame for that command id): the runtime cancels the command's own context, and the run ends as the core ends a canceled run — its error and partial transcript in the records, its command finished failed. It asks, never kills: the frame is the whole effect, and a run that ends first simply finishes.
The command is the accepted one whose acks named the run (the experiment's own command, or the approval command a resumed run runs under). Errors: ErrUnknownRun (no runtime started the run), ErrRunFinished (its command is no longer accepted), ErrNotConnected (the runtime holds no live stream, or its feed is full — the stream is then dropped, as every enqueue drops it).
func (*RuntimeServer) Close ¶ added in v0.15.0
func (rs *RuntimeServer) Close()
Close stops the server's timers and its write-through, then drains the writer's queue (bounded at storeCloseTimeout): what was decided before Close reaches the store, and nothing after it does (a Studio closed before another opens the same database must not mark that one's commands lost from its stale timers). The memory state stays readable. Safe to call more than once.
func (*RuntimeServer) Command ¶
func (rs *RuntimeServer) Command(id string) (CommandStatus, error)
Command returns the command's lifecycle row; ErrUnknownCommand otherwise.
func (*RuntimeServer) CommandOf ¶
func (rs *RuntimeServer) CommandOf(id string) (Command, bool)
CommandOf returns the command as enqueued (its public id names the page a panel token is scoped to); ok is false for an unknown id.
func (*RuntimeServer) Connected ¶
func (rs *RuntimeServer) Connected(runtimeID string) bool
Connected reports whether the runtime holds a live command stream.
func (*RuntimeServer) Enqueue ¶
func (rs *RuntimeServer) Enqueue(runtimeID string, cmd Command) (Command, error)
Enqueue validates nothing (that is the playground route's job) and hands cmd to the named runtime's stream, minting the command id when the caller did not. Errors: ErrUnknownRuntime (404), ErrNotConnected (503), ErrDuplicateCommand (409), ErrInvalidCommandID (400: a caller's id outside ValidCommandID).
func (*RuntimeServer) EnqueueApproval ¶
func (rs *RuntimeServer) EnqueueApproval(runtimeID string, d ApprovalDecision) (ApprovalDecision, error)
EnqueueApproval forwards a human decision on one parked call of a runtime-started run (WEFT-DEVTOOLS §8.2) as an `event: approve` command, minting the command id. The runtime acks it before resuming (at-most-once like every command) and reports the resumed run in its finished ack. Errors match Enqueue's.
func (*RuntimeServer) EnqueueSession ¶ added in v0.15.0
func (rs *RuntimeServer) EnqueueSession(runtimeID string, sc SessionCommand) (SessionCommand, error)
EnqueueSession hands a debugger verb on an app-owned turn to the runtime that registered runtime.Sessions (an `event: session` frame), minting the command id. The row carries sc.PublicID (a panel token polls its own command); its acks name the app's run, and never make it a run the runtime started (serveAcks). Errors match Enqueue's.
func (*RuntimeServer) Manifests ¶ added in v0.11.0
func (rs *RuntimeServer) Manifests() []ManifestSource
Manifests lists the remembered registration manifests: live ones first, then the most recently registered. Live is computed now — a runtime holding its command stream registered with that manifest.
func (*RuntimeServer) Mount ¶
func (rs *RuntimeServer) Mount(mux *http.ServeMux)
Mount registers the three runtime-link routes (§10.3) on mux. The studio server mounts them under its /api tree, so its token gate (the Studio destination's token, S4.6) applies: nothing in-process (setup A), the bearer token otherwise.
func (*RuntimeServer) MountGuarded ¶
MountGuarded is Mount wrapping each route with guard when it is non-nil. The studio side passes its server-identity guard: the link speaks server-to-server (a runtime dials Studio with the server token, or nothing in setup A), and a panel token that passes the wall must not reach it — register overwrites any runtime's registration, the commands stream replaces its feed, and acks forge the accepted/finished state that steer and approval routing trust. None of that is public-id-shaped, so S4.6's per-public-id scoping cannot apply; the guard is the refusal.
func (*RuntimeServer) PublicOf ¶
func (rs *RuntimeServer) PublicOf(runID string) (string, bool)
PublicOf returns the public id a runtime-started run was scoped to at enqueue. ok is false when it carried none.
func (*RuntimeServer) Registration ¶
func (rs *RuntimeServer) Registration(runtimeID string) (Registration, bool)
Registration returns the runtime's current registration (the authoritative copy of its agents, allow-lists and caps) when it has registered; ok is false for an unknown id.
func (*RuntimeServer) RuntimeOf ¶
func (rs *RuntimeServer) RuntimeOf(runID string) (string, bool)
RuntimeOf names the runtime that started runID (learned from its acks). ok is false for a run no runtime started — the app's own turns, viewer-only unless a runtime registered Sessions (SessionsRuntime, plan F4).
func (*RuntimeServer) SessionsRuntime ¶ added in v0.15.0
func (rs *RuntimeServer) SessionsRuntime(service string) (string, bool)
SessionsRuntime names the connected runtime of service that registered runtime.Sessions — the one an app-owned run of that service is acted on through (the newest seen when several are). ok is false when none is connected: the app's turns are then read-only. An empty service matches no runtime: a run whose records name no service cannot be told apart from another app's.
func (*RuntimeServer) SetBreakpoints ¶
func (rs *RuntimeServer) SetBreakpoints(runtimeID string, tools []string) error
SetBreakpoints stores the runtime's breakpoint set and forwards it (an `event: breakpoints` frame): the runtime passes the named tools as core.ParkOn on every run it starts from then on. It cannot alter agents it did not build — the app's own turns are not breakable from here (PQ7), and meta says so.
func (*RuntimeServer) Shutdown ¶ added in v0.15.0
func (rs *RuntimeServer) Shutdown(ctx context.Context) error
Shutdown is Close with the drain bounded by ctx instead: ctx.Err() when the queue could not be written in time (what was left is dropped).
func (*RuntimeServer) Snapshot ¶
func (rs *RuntimeServer) Snapshot() []RuntimeView
Snapshot lists the runtimes that ever registered, newest-seen first, with their liveness and agents in §10.4's shape. A runtime that registered but holds no stream stays visible until its FinishDeadline-watched commands resolve (the UI reads last_seen), then it is pruned (pruneLocked).
func (*RuntimeServer) Steer ¶
func (rs *RuntimeServer) Steer(runtimeID, runID, message string) error
Steer forwards one user message into a runtime-started run (an `event: steer` frame): core.Steering on an ephemeral run the runtime holds, thread's Steer policy on a fork it owns. Best effort — the run may have ended between the read and the delivery.
type RuntimeView ¶
type RuntimeView struct {
ID string `json:"id"`
Host string `json:"host"`
Pid int `json:"pid"`
// Cwd is the registration's working directory — a filesystem path,
// so Studio's route serves it under db.path's rule (absent to a
// panel token).
Cwd string `json:"cwd,omitempty"`
Service string `json:"service"`
Env string `json:"env"`
ConnectedSince time.Time `json:"connected_since"`
LastSeen time.Time `json:"last_seen"`
// Connected reports whether the runtime holds a live command stream
// now. A runtime whose stream dropped stays listed (until its
// commands resolve, pruneLocked) with Connected false; a reconnect
// re-registers under the same id and reads true again.
Connected bool `json:"connected"`
// Restored marks a runtime listed from the command store after a
// Studio restart that has not registered since: its stream is gone
// with the old Studio (Connected false) until it reconnects. Absent
// otherwise.
Restored bool `json:"restored,omitempty"`
// Sessions is the registration's: the runtime acts on the app's own
// turns (runtime.Sessions) — approvals, steer and cancel on an
// app-owned run of its service reach it.
Sessions bool `json:"sessions"`
// BreakpointsOwn is the registration's: the runtime's breakpoints
// park tools on the app's own runs of an agent built with
// runtime.Breakpoints() (plan F4.2).
BreakpointsOwn bool `json:"breakpoints_own"`
Agents []AgentView `json:"agents"`
// Breakpoints is the debugger's stored tool set for this runtime
// (WEFT-DEVTOOLS §8.3) — what PUT …/breakpoints last delivered, so a
// reloaded UI shows the rule that is parking its runs. Empty, never
// null.
Breakpoints []string `json:"breakpoints"`
}
RuntimeView is one connected runtime in GET /api/runtimes (§10.4's shape, pinned): identity, liveness, and per agent the alternate models and every tool with its side-effect class and allow flag.
type SessionCommand ¶ added in v0.15.0
type SessionCommand struct {
CommandID string `json:"command_id"`
Verb string `json:"verb"` // decide | steer | cancel
PublicID string `json:"public_id"`
SessionID string `json:"session_id"`
RunID string `json:"run_id"`
CallID string `json:"call_id,omitempty"`
Decision string `json:"decision,omitempty"`
Reason string `json:"reason,omitempty"`
Content string `json:"content,omitempty"`
Message string `json:"message,omitempty"`
Actor string `json:"actor,omitempty"`
}
SessionCommand is an `event: session` frame's data (plan F4): a debugger verb on one of the app's own turns — a run no runtime started — addressed by the session behind it: PublicID (what the app's runtime.Sessions resolver answers), SessionID (checked against the Session it answers) and RunID (the app's run). Verb decide carries CallID, Decision (approve | deny | resolve), Reason and Content as ApprovalDecision does; steer carries Message; cancel nothing more. The runtime acks it like every command — accepted before it writes the session, finished after — and refuses it, before any write, with the reason: "the app is writing this session; fork instead" when the Session it was handed cannot take the writer lease, "no session for this public id", "no running turn to steer", "cancel is not supported on an app turn".
type SourceSpec ¶
SourceSpec names the run to re-run.
type SteerMessage ¶
SteerMessage is an `event: steer` frame's data (§8.4): a user message delivered into a runtime-started run — core.Steering on an ephemeral run, thread's Steer policy on a fork the runtime owns. The app's own turns are steered through a SessionCommand instead, on a runtime that registered Sessions (plan F4).
type ToolChoice ¶ added in v0.13.0
ToolChoice is a tool choice on the wire: mode auto | any | none | named, and the tool's name under named.
type ToolView ¶
type ToolView struct {
Name string `json:"name"`
SideEffects string `json:"side_effects"`
Allow bool `json:"allow"`
}
ToolView is one tool of a connected runtime's agent.
type TranscriptEdit ¶
type TranscriptEdit struct {
Kind string `json:"kind,omitempty"`
Step int `json:"step"`
ToolResult string `json:"tool_result,omitempty"`
CallID string `json:"call_id,omitempty"`
Content string `json:"content,omitempty"`
Args json.RawMessage `json:"args,omitempty"`
Index int `json:"index,omitempty"`
}
TranscriptEdit is a D2/D3 edit of the kept prefix before the experiment re-runs from from_step (ADR 0029 §8). Kind is the discriminator — tool_result | reply | user | tool_args | insert — and absent, the fields decide as before F2 (tool_result + call_id patch a result, content rewrites a call-free reply), so every body recorded before it still validates. user rewrites a user message of step Step (Content; Index picks among the step's user messages, step 0's turn prompt first); tool_args rewrites call CallID's arguments (Args, a JSON object checked against the tool's schema); insert adds a user message (Content) at the boundary before step Step's model call, 0..from_step — where a steer delivered there would land.