runtime

package
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 14 Imported by: 0

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 holds no database and reads nothing — 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). 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

A late ack or finish still lands: the truth about a run wins over a timer's guess.

Index

Examples

Constants

View Source
const (
	StateQueued   = "queued"
	StateAccepted = "accepted"
	StateRejected = "rejected"
	StateFinished = "finished"
	StateLost     = "lost"
)

Command states (§10.5).

Variables

View Source
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")
)

Errors Enqueue and the routes answer with.

Functions

func ValidCommandID

func ValidCommandID(id string) bool

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 AgentLimits

type AgentLimits struct {
	MaxSteps    int `json:"max_steps"`
	Parallelism int `json:"parallelism"`
}

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"`
	Limits      AgentLimits       `json:"limits"`
	SideEffects map[string]string `json:"side_effects"`
	Allow       []string          `json:"allow"`
}

AgentRegistration is one exposed agent.

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.

type AgentView

type AgentView struct {
	Name   string     `json:"name"`
	Models []string   `json:"models"`
	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.

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 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 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"`
}

Overrides are the experiment's changes (§5.1).

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"`
	Service     string `json:"service"`
	Env         string `json:"env"`
	WeftVersion string `json:"weft_version"`
	Budget      Budget `json:"budget"`
	Threads     bool   `json:"threads"`
	// 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"`
}

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 after its runtime's stream died before it is lost
	// (§10.5: 10 min). 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: a runtime id
	// lives one process, so every app restart would otherwise leave an
	// entry in GET /api/runtimes forever. 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() *RuntimeServer

New builds an empty RuntimeServer with §10.5's timers.

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) 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) 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

func (rs *RuntimeServer) MountGuarded(mux *http.ServeMux, guard func(http.Handler) http.Handler)

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, which stay viewer-only (D7, PQ7).

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) 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"`
	Service        string      `json:"service"`
	Env            string      `json:"env"`
	ConnectedSince time.Time   `json:"connected_since"`
	LastSeen       time.Time   `json:"last_seen"`
	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 SourceSpec

type SourceSpec struct {
	RunID    string `json:"run_id"`
	FromStep int    `json:"from_step"`
}

SourceSpec names the run to re-run.

type SteerMessage

type SteerMessage struct {
	RunID   string `json:"run_id"`
	Message string `json:"message"`
}

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 never steerable from here (PQ7).

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 {
	Step       int    `json:"step"`
	ToolResult string `json:"tool_result,omitempty"`
	CallID     string `json:"call_id,omitempty"`
	Content    string `json:"content,omitempty"`
}

TranscriptEdit is a D2/D3 edit: a step's recorded tool result or message replaced before the experiment re-runs from that step.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL