runner

package
v0.7.17 Latest Latest
Warning

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

Go to latest
Published: Mar 6, 2026 License: AGPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package runner implements the execution loop and I/O orchestration for the Trellis engine.

It acts as the bridge between the core state machine (Engine) and the outside world. The runner manages session persistence, handles user input/output through pluggable handlers, and integrates with the 'lifecycle' library for signal handling and supervision.

Key Components

  • Runner: The main orchestrator that implements the lifecycle.Worker interface.
  • InputHandler: Decouples how the engine receives inputs (CLI, JSON, etc.).
  • TextHandler: A standard implementation for interactive CLI usage.

Usage

r := runner.NewRunner(
	runner.WithEngine(engine),
	runner.WithSessionID("user-1"),
	runner.WithInputHandler(runner.NewTextHandler(os.Stdout, runner.WithStdin())),
)

if err := r.Run(ctx); err != nil {
	log.Fatal(err)
}

Index

Constants

View Source
const DefaultInputBufferSize = 64

DefaultInputBufferSize is the default number of lines to buffer for input handlers.

Variables

View Source
var (
	// DefaultMaxInputSize is 4KB (conservative default)
	DefaultMaxInputSize = 4096
	// EnvMaxInputSize is the environment variable to override the default
	EnvMaxInputSize = "TRELLIS_MAX_INPUT_SIZE"
)
View Source
var (
	ErrInputTooLarge = errors.New("input exceeds maximum allowed size")
	ErrInvalidUTF8   = errors.New("input contains invalid UTF-8 sequences")
)

Functions

func SanitizeInput added in v0.5.2

func SanitizeInput(input string) (string, error)

SanitizeInput cleans user input by enforcing size limits, validating UTF-8, and stripping dangerous control characters.

Types

type ContentRenderer

type ContentRenderer func(content string) (string, error)

ContentRenderer transforms a raw string (e.g. markdown) into a rendered string (e.g. ANSI).

type IOHandler

type IOHandler interface {
	// Output presents the actions to the user.
	// Returns true if the output requires user input (e.g. asking a question),
	// or if the handler expects to read input after this.
	Output(ctx context.Context, actions []domain.ActionRequest) (bool, error)

	// Input reads a response from the user.
	Input(ctx context.Context) (string, error)

	// Signal notifies the handler of an event (e.g. "thinking", "typing").
	// This is used for visual feedback without blocking input.
	Signal(ctx context.Context, name string, args map[string]any) error

	// HandleTool executes a side-effect requested by the engine.
	// In a text/CLI context, this might just log the request or ask for confirmation.
	HandleTool(ctx context.Context, call domain.ToolCall) (domain.ToolResult, error)

	// SystemOutput presents a meta-message to the user (e.g. system logs, status updates).
	// This is distinct from content rendering.
	SystemOutput(ctx context.Context, msg string) error
}

IOHandler defines the strategy for interacting with the user. This allows switching between Text (CLI/TUI) and JSON (Structured) modes.

type JSONHandler

type JSONHandler struct {
	Writer   io.Writer
	Encoder  *json.Encoder
	Registry *registry.Registry
	// contains filtered or unexported fields
}

JSONHandler implements the IOHandler interface for structured JSON-Lines communication.

func NewJSONHandler

func NewJSONHandler(w io.Writer, opts ...JSONHandlerOption) *JSONHandler

NewJSONHandler creates a handler for JSON IO.

func (*JSONHandler) FeedInput added in v0.7.3

func (h *JSONHandler) FeedInput(line string, err error)

FeedInput feeds a line of input into the handler. This is called by the bridge when lifecycle detects input.

func (*JSONHandler) HandleTool added in v0.4.0

func (h *JSONHandler) HandleTool(ctx context.Context, call domain.ToolCall) (domain.ToolResult, error)

HandleTool for JSONHandler emits the tool call as JSON.

func (*JSONHandler) Input

func (h *JSONHandler) Input(ctx context.Context) (string, error)

func (*JSONHandler) Output

func (h *JSONHandler) Output(ctx context.Context, actions []domain.ActionRequest) (bool, error)

func (*JSONHandler) Signal added in v0.7.3

func (h *JSONHandler) Signal(ctx context.Context, name string, args map[string]any) error

func (*JSONHandler) SystemOutput added in v0.5.0

func (h *JSONHandler) SystemOutput(ctx context.Context, msg string) error

type JSONHandlerOption added in v0.6.0

type JSONHandlerOption func(*JSONHandler)

JSONHandlerOption defines configuration for JSONHandler.

func WithJSONHandlerRegistry added in v0.6.0

func WithJSONHandlerRegistry(reg *registry.Registry) JSONHandlerOption

WithJSONHandlerRegistry configures the tool registry.

func WithJSONInputBufferSize added in v0.7.3

func WithJSONInputBufferSize(size int) JSONHandlerOption

WithJSONInputBufferSize sets the size of the input buffer.

type Option added in v0.6.0

type Option func(*Runner)

Option defines a functional option for configuring the Runner.

func WithEngine added in v0.7.5

func WithEngine(engine *trellis.Engine) Option

WithEngine configures the Trellis engine for execution. This is required for the Runner to operate as a lifecycle.Worker.

func WithHeadless added in v0.6.0

func WithHeadless(headless bool) Option

WithHeadless sets the runner to headless mode.

func WithInitialState added in v0.7.5

func WithInitialState(state *domain.State) Option

WithInitialState configures the initial state for the Runner. If not provided, the Runner will call Engine.Start() to create one.

func WithInputHandler added in v0.6.0

func WithInputHandler(handler IOHandler) Option

WithInputHandler configures a custom IOHandler.

func WithInterceptor added in v0.6.0

func WithInterceptor(interceptor ToolInterceptor) Option

WithInterceptor configures the tool execution middleware.

func WithInterruptSource added in v0.7.3

func WithInterruptSource(ch <-chan struct{}) Option

WithInterruptSource sets a channel that signals the runner to interrupt current execution.

func WithLogger added in v0.6.0

func WithLogger(logger *slog.Logger) Option

WithLogger configures the structured logger.

func WithRenderer added in v0.6.0

func WithRenderer(renderer ContentRenderer) Option

WithRenderer configures the content renderer (e.g. TUI, Markdown).

func WithSessionID added in v0.6.0

func WithSessionID(id string) Option

WithSessionID sets the session ID for persistence context. This is required if WithStore is used.

func WithStore added in v0.6.0

func WithStore(store ports.StateStore) Option

WithStore configures the StateStore for persistence.

func WithToolRunner added in v0.7.0

func WithToolRunner(tr ToolRunner) Option

WithToolRunner configures the strategy for executing side-effects.

type RichResponse added in v0.7.10

type RichResponse struct {
	State    *domain.State          `json:"state"`
	Actions  []domain.ActionRequest `json:"actions,omitempty"`
	Terminal bool                   `json:"terminal"`
}

RichResponse combines state and rendering actions for rich clients (Web, MCP, etc). This encapsulates the common pattern of: Navigate -> Render -> Return Actions.

func NavigateAndRender(ctx context.Context, engine ports.StatelessEngine, currentState *domain.State, input any) (*RichResponse, error)

NavigateAndRender performs a navigation step and immediately renders the resulting state. This ensures that rich clients always receive the content/instructions for the node they just entered.

func SignalAndRender added in v0.7.10

func SignalAndRender(ctx context.Context, engine ports.StatelessEngine, currentState *domain.State, signalName string) (*RichResponse, error)

SignalAndRender performs a signal transition and immediately renders the resulting state.

type Runner

type Runner struct {
	Handler         IOHandler
	Store           ports.StateStore
	Logger          *slog.Logger
	Headless        bool
	SessionID       string
	Renderer        ContentRenderer
	Interceptor     ToolInterceptor
	ToolRunner      ToolRunner
	InterruptSource <-chan struct{}
	// contains filtered or unexported fields
}

Runner handles the execution loop of the Trellis engine using provided IO. This allows for easy testing and integration with different frontends (CLI, TUI, etc).

Runner implements the lifecycle.Worker interface.

Note: Runner is designed for single-use execution. It is NOT thread-safe for concurrent usage. Create a new Runner instance for each execution.

func NewRunner

func NewRunner(opts ...Option) *Runner

NewRunner creates a new Runner with options.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context) error

Run executes the engine loop until termination. This method implements the lifecycle.Worker interface (Run(context.Context) error).

func (*Runner) State added in v0.7.5

func (r *Runner) State() *domain.State

State returns a snapshot of the current execution state. It implements the introspection.TypedWatcher interface.

func (*Runner) Watch added in v0.7.5

func (r *Runner) Watch(ctx context.Context) <-chan introspection.StateChange[*domain.State]

Watch returns a channel of state changes for the introspection system.

type SessionManager added in v0.6.0

type SessionManager struct {
	Store ports.StateStore
}

SessionManager handles the lifecycle of a durable session. It coordinates between the Runner, the Engine, and the StateStore.

func NewSessionManager added in v0.6.0

func NewSessionManager(store ports.StateStore) *SessionManager

NewSessionManager creates a new SessionManager.

func (*SessionManager) LoadOrStart added in v0.6.0

func (sm *SessionManager) LoadOrStart(
	ctx context.Context,
	engine *trellis.Engine,
	sessionID string,
	initialContext map[string]any,
) (*domain.State, bool, error)

LoadOrStart attempts to load an existing session. If not found, it starts a new one. Returns the state and a boolean indicating if it was loaded (true) or new (false).

func (*SessionManager) Save added in v0.6.0

func (sm *SessionManager) Save(ctx context.Context, sessionID string, state *domain.State) error

Save persists the state.

type TextHandler

type TextHandler struct {
	Writer   io.Writer
	Renderer ContentRenderer
	Registry *registry.Registry
	// contains filtered or unexported fields
}

TextHandler implements the standard text-based interface.

func NewTextHandler

func NewTextHandler(w io.Writer, opts ...TextHandlerOption) *TextHandler

NewTextHandler creates a handler for standard text IO.

func (*TextHandler) FeedInput added in v0.7.3

func (h *TextHandler) FeedInput(text string, err error)

FeedInput feeds a line of input into the handler. This is called by the bridge when lifecycle detects input.

func (*TextHandler) HandleTool added in v0.4.0

func (h *TextHandler) HandleTool(ctx context.Context, call domain.ToolCall) (domain.ToolResult, error)

HandleTool for TextHandler mocks the execution by printing to stdout.

func (*TextHandler) Input

func (h *TextHandler) Input(ctx context.Context) (string, error)

func (*TextHandler) Output

func (h *TextHandler) Output(ctx context.Context, actions []domain.ActionRequest) (bool, error)

func (*TextHandler) Signal added in v0.7.3

func (h *TextHandler) Signal(ctx context.Context, name string, args map[string]any) error

func (*TextHandler) SystemOutput added in v0.5.0

func (h *TextHandler) SystemOutput(ctx context.Context, msg string) error

type TextHandlerOption added in v0.6.0

type TextHandlerOption func(*TextHandler)

TextHandlerOption defines configuration for TextHandler.

func WithStdin added in v0.7.7

func WithStdin() TextHandlerOption

WithStdin enables reading from os.Stdin for simple library usage.

func WithTextHandlerRegistry added in v0.6.0

func WithTextHandlerRegistry(reg *registry.Registry) TextHandlerOption

WithTextHandlerRegistry configures the tool registry.

func WithTextHandlerRenderer added in v0.6.0

func WithTextHandlerRenderer(renderer ContentRenderer) TextHandlerOption

WithTextHandlerRenderer configures the content renderer.

func WithTextInputBufferSize added in v0.7.3

func WithTextInputBufferSize(size int) TextHandlerOption

WithTextInputBufferSize sets the size of the input buffer.

type ToolInterceptor added in v0.5.0

type ToolInterceptor func(ctx context.Context, call domain.ToolCall) (bool, domain.ToolResult, error)

ToolInterceptor is a middleware that can intercept, modify, or block a tool call. It returns true if execution should proceed, or false to block it. If blocked, it should return a ToolResult describing the denial (typically an error).

func AutoApproveMiddleware added in v0.5.0

func AutoApproveMiddleware() ToolInterceptor

AutoApproveMiddleware allows everything.

func ConfirmationMiddleware added in v0.5.0

func ConfirmationMiddleware(handler IOHandler) ToolInterceptor

ConfirmationMiddleware prompts the user via the provided Handler before allowing execution. It is "aware" of the IOHandler to use its Input/Output methods, but keeps the policy logic separate.

Note: This leverages the IOHandler.SystemOutput capability to send meta-messages. This allows the prompt ("Allow execution?") to be distinct from the flow content.

func MultiInterceptor added in v0.5.0

func MultiInterceptor(interceptors ...ToolInterceptor) ToolInterceptor

MultiInterceptor chains multiple interceptors.

type ToolRunner added in v0.7.0

type ToolRunner interface {
	Execute(ctx context.Context, call domain.ToolCall) (domain.ToolResult, error)
}

ToolRunner defines the strategy for executing side-effects. It decouples execution logic (Process, HTTP, MCP) from IO logic (Text, JSON).

Jump to

Keyboard shortcuts

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