ext

package
v0.17.2 Latest Latest
Warning

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

Go to latest
Published: Apr 1, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package ext provides the extension registration surface for piglet.

App is the central API through which all extensions — both compiled-in and external — register their capabilities. It supports five primitives:

Package ext provides the extension API for piglet. Extensions are plain functions that receive *App and register capabilities.

Index

Constants

View Source
const (
	StatusKeyApp          = "app"
	StatusKeyModel        = "model"
	StatusKeyMouse        = "mouse"
	StatusKeyBg           = "bg"
	StatusKeyTokens       = "tokens"
	StatusKeyCost         = "cost"
	StatusKeyQueue        = "queue"
	StatusKeyPromptBudget = "prompt-budget"
)

Built-in status section keys.

Variables

This section is empty.

Functions

func BoolArg added in v0.5.0

func BoolArg(args map[string]any, key string, fallback bool) bool

BoolArg extracts a boolean argument from a tool call's args map.

func CompactWithCircuitBreaker added in v0.17.0

func CompactWithCircuitBreaker(
	fn func(ctx context.Context, msgs []core.Message) ([]core.Message, error),
	maxFails int,
	cooldown time.Duration,
) func(ctx context.Context, msgs []core.Message) ([]core.Message, error)

CompactWithCircuitBreaker wraps a compact function with retry protection. After maxFails consecutive failures, compaction is disabled for cooldown duration.

func IntArg added in v0.5.0

func IntArg(args map[string]any, key string, fallback int) int

IntArg extracts an integer argument from a tool call's args map. JSON numbers decode as float64, so both float64 and int are handled.

func StringArg added in v0.5.0

func StringArg(args map[string]any, key string) string

StringArg extracts a string argument from a tool call's args map.

func TextResult added in v0.5.0

func TextResult(text string) *core.ToolResult

TextResult builds a ToolResult containing a single text block.

Types

type Action added in v0.4.0

type Action interface {
	// contains filtered or unexported methods
}

Action represents an intent requested by an extension command or shortcut. Commands enqueue actions; the TUI drains and applies them after the handler returns.

type ActionAttachImage added in v0.5.0

type ActionAttachImage struct{ Image any }

ActionAttachImage attaches an image to the next message. Image is typed as any to avoid importing core.ImageContent.

type ActionDetachImage added in v0.5.0

type ActionDetachImage struct{}

ActionDetachImage removes a pending image attachment.

type ActionExec added in v0.5.0

type ActionExec struct{ Cmd any }

ActionExec hands the terminal to an external process (e.g., $EDITOR). Cmd is typed as any to avoid importing os/exec from ext/. The TUI asserts *exec.Cmd and uses tea.ExecProcess.

type ActionNotify added in v0.4.0

type ActionNotify struct{ Message string }

ActionNotify sends a transient notification.

type ActionQuit added in v0.4.0

type ActionQuit struct{}

ActionQuit signals the TUI to exit.

type ActionRunAsync added in v0.5.0

type ActionRunAsync struct{ Fn func() Action }

ActionRunAsync runs Fn in a goroutine. The returned Action (if non-nil) is enqueued when Fn completes. Use for expensive event handler work.

type ActionSendMessage added in v0.16.8

type ActionSendMessage struct{ Content string }

ActionSendMessage injects a user message into the agent loop.

type ActionSetSessionTitle added in v0.5.0

type ActionSetSessionTitle struct{ Title string }

ActionSetSessionTitle sets the current session's title.

type ActionSetStatus added in v0.4.0

type ActionSetStatus struct{ Key, Text string }

ActionSetStatus updates a status bar widget (e.g. "model").

type ActionShowMessage added in v0.4.0

type ActionShowMessage struct{ Text string }

ActionShowMessage displays a message in the conversation view.

type ActionShowPicker added in v0.4.0

type ActionShowPicker struct {
	Title    string
	Items    []PickerItem
	OnSelect func(PickerItem)
}

ActionShowPicker opens a modal picker.

type ActionSwapSession added in v0.4.0

type ActionSwapSession struct{ Session any }

ActionSwapSession replaces the active session. Session is typed as any to avoid importing session/ from ext/.

type AgentAPI

type AgentAPI interface {
	AgentReader
	AgentWriter
}

AgentAPI is the subset of *core.Agent that the extension runtime needs. Using an interface keeps ext/ from depending on agent implementation details.

Removed from interface (used only via *core.Agent in tui/ and cmd/):

  • SetTools, SetTurnContext, IsRunning

type AgentReader added in v0.16.8

type AgentReader interface {
	Messages() []core.Message
	StepMode() bool
	Provider() core.StreamProvider
	System() string
}

AgentReader provides read-only access to agent state.

type AgentWriter added in v0.16.8

type AgentWriter interface {
	Steer(msg core.Message)
	FollowUp(msg core.Message)
	SetModel(m core.Model)
	SetProvider(p core.StreamProvider)
	SetMessages(msgs []core.Message)
	SetStepMode(on bool)
}

AgentWriter provides mutation access to the agent.

type App

type App struct {
	// contains filtered or unexported fields
}

App is the single extension API surface. Extensions receive this in their factory function and call methods to register tools, commands, etc.

After registration, Bind() wires the runtime references (agent, session) so runtime methods like SendMessage() and Model() work.

CAPABILITY GATE — before adding a new callback or field to App, answer:

  1. Can this be a Tool? → RegisterTool (agent-callable action)
  2. Can this be a Command? → RegisterCommand (user-invoked /slash)
  3. Can this be a Shortcut? → RegisterShortcut (keyboard binding)
  4. Can this be a PromptSection? → RegisterPromptSection (system prompt injection)
  5. Can this be an Interceptor? → RegisterInterceptor (before/after tool hook)
  6. Can this be a MessageHook? → RegisterMessageHook (before user message reaches LLM)

Only add a new BindOption/callback when NONE of the above apply — typically for TUI-specific lifecycle that extensions cannot express through existing primitives (e.g. session swapping, background agent management). See ext/architecture_test.go for automated boundary enforcement.

func NewApp

func NewApp(cwd string) *App

NewApp creates an extension App for the given working directory.

func (*App) AvailableModels added in v0.4.0

func (a *App) AvailableModels() []core.Model

AvailableModels returns all registered models.

func (*App) BackgroundSafeTools added in v0.4.0

func (a *App) BackgroundSafeTools() []core.Tool

BackgroundSafeTools returns core.Tool slice filtered to tools marked BackgroundSafe.

func (*App) Bind

func (a *App) Bind(agent AgentAPI, opts ...BindOption)

Bind wires the runtime references after the agent and session are created. Must be called before runtime methods (SendMessage, Model, etc.) are used.

func (*App) CWD

func (a *App) CWD() string

CWD returns the working directory.

func (*App) CancelBackground added in v0.4.0

func (a *App) CancelBackground()

CancelBackground stops the running background agent. No-op if not bound or not running.

func (*App) Commands

func (a *App) Commands() map[string]*Command

Commands returns all registered commands.

func (*App) Compactor added in v0.5.0

func (a *App) Compactor() *Compactor

Compactor returns the registered compactor, or nil.

func (*App) ConversationMessages

func (a *App) ConversationMessages() []core.Message

ConversationMessages returns a snapshot of the conversation history.

func (*App) CoreTools

func (a *App) CoreTools() []core.Tool

CoreTools converts registered ToolDefs into core.Tool slice for the agent. Wraps each tool's Execute with the interceptor chain.

func (*App) DispatchEvent added in v0.5.0

func (a *App) DispatchEvent(ctx context.Context, evt core.Event)

DispatchEvent sends an agent event to all registered event handlers. Called by the TUI or runPrint as events are drained from the agent channel. Handlers run synchronously in priority order. Returned actions are enqueued.

func (*App) EnqueueAction added in v0.5.0

func (a *App) EnqueueAction(action Action)

EnqueueAction adds an action to the pending queue. Used by the TUI to re-enqueue results from ActionRunAsync.

func (*App) ExtInfos added in v0.2.0

func (a *App) ExtInfos() []ExtInfo

ExtInfos returns metadata about all loaded extensions.

func (*App) FindTool added in v0.5.0

func (a *App) FindTool(name string) *core.Tool

FindTool looks up a tool by name and returns a core.Tool with interceptors applied. Returns nil if no tool with that name is registered.

func (*App) ForkSession added in v0.4.0

func (a *App) ForkSession() (string, int, error)

ForkSession forks the current session into a new branch.

func (*App) IsBackgroundRunning added in v0.4.0

func (a *App) IsBackgroundRunning() bool

IsBackgroundRunning returns whether a background agent is currently active.

func (*App) LoadSession added in v0.4.0

func (a *App) LoadSession(path string) error

LoadSession opens a session by path and enqueues a swap.

func (*App) Notify

func (a *App) Notify(msg string)

Notify sends a notification to the TUI.

func (*App) PendingActions added in v0.4.0

func (a *App) PendingActions() []Action

PendingActions returns and clears all queued actions.

func (*App) PromptSections

func (a *App) PromptSections() []PromptSection

PromptSections returns all registered prompt sections, sorted by order.

func (*App) Provider added in v0.5.0

func (a *App) Provider() core.StreamProvider

Provider returns the current agent's streaming provider.

func (*App) Providers

func (a *App) Providers() map[string]*ProviderConfig

Providers returns all registered provider configs.

func (*App) RegisterCommand

func (a *App) RegisterCommand(c *Command)

RegisterCommand adds a slash command.

func (*App) RegisterCompactor added in v0.5.0

func (a *App) RegisterCompactor(c Compactor)

RegisterCompactor sets the conversation compactor. Only one compactor is active at a time (last-write-wins).

func (*App) RegisterEventHandler added in v0.5.0

func (a *App) RegisterEventHandler(h EventHandler)

RegisterEventHandler adds a handler that reacts to agent lifecycle events. Sorted by priority ascending (lower = earlier).

func (*App) RegisterExtInfo added in v0.2.0

func (a *App) RegisterExtInfo(info ExtInfo)

RegisterExtInfo records metadata about a loaded extension.

func (*App) RegisterInterceptor

func (a *App) RegisterInterceptor(i Interceptor)

RegisterInterceptor adds a tool interceptor. Sorted by priority descending.

func (*App) RegisterMessageHook added in v0.5.0

func (a *App) RegisterMessageHook(h MessageHook)

RegisterMessageHook adds a hook that runs before user messages reach the LLM. Sorted by priority ascending (lower = earlier).

func (*App) RegisterPromptSection

func (a *App) RegisterPromptSection(s PromptSection)

RegisterPromptSection adds a section to the system prompt.

func (*App) RegisterProvider

func (a *App) RegisterProvider(name string, cfg *ProviderConfig)

RegisterProvider adds a custom LLM provider.

func (*App) RegisterRenderer

func (a *App) RegisterRenderer(typ string, r Renderer)

RegisterRenderer adds a custom message type renderer.

func (*App) RegisterShortcut

func (a *App) RegisterShortcut(s *Shortcut)

RegisterShortcut adds a keyboard shortcut.

func (*App) RegisterStatusSection added in v0.5.0

func (a *App) RegisterStatusSection(s StatusSection)

RegisterStatusSection adds a status bar section. Overwrites if a section with the same key already exists.

func (*App) RegisterStreamProvider added in v0.16.11

func (a *App) RegisterStreamProvider(api string, factory StreamProviderFactory)

RegisterStreamProvider registers a factory that creates StreamProviders for the given API type.

func (*App) RegisterTool

func (a *App) RegisterTool(t *ToolDef)

RegisterTool adds a tool. Overwrites if name already exists.

func (*App) Renderers

func (a *App) Renderers() map[string]Renderer

Renderers returns all registered renderers.

func (*App) RequestQuit

func (a *App) RequestQuit()

RequestQuit signals the TUI to quit.

func (*App) ResolveModel added in v0.5.0

func (a *App) ResolveModel(id string) (core.Model, core.StreamProvider, error)

ResolveModel returns a model and configured provider for the given model ID without switching the main agent. Used by sub-agents to run on different models.

func (*App) RunBackground added in v0.4.0

func (a *App) RunBackground(prompt string) error

RunBackground starts a background agent with the given prompt. Returns an error if not bound or if a background agent is already running.

func (*App) RunMessageHooks added in v0.5.0

func (a *App) RunMessageHooks(ctx context.Context, msg string) ([]string, error)

RunMessageHooks executes all message hooks in priority order. Returns collected non-empty context strings for ephemeral injection.

func (*App) SendMessage

func (a *App) SendMessage(content string)

SendMessage enqueues an ActionSendMessage that the TUI will pick up and feed into the agent loop as a follow-up user message.

func (*App) SessionTitle added in v0.5.0

func (a *App) SessionTitle() string

SessionTitle returns the current session's title (empty if not set).

func (*App) Sessions added in v0.4.0

func (a *App) Sessions() ([]SessionSummary, error)

Sessions returns all sessions, newest first.

func (*App) SetConversationMessages

func (a *App) SetConversationMessages(msgs []core.Message)

SetConversationMessages replaces the conversation history.

func (*App) SetModel

func (a *App) SetModel(m core.Model)

SetModel updates the agent's model.

func (*App) SetProvider added in v0.3.0

func (a *App) SetProvider(p core.StreamProvider)

SetProvider swaps the agent's streaming provider.

func (*App) SetSessionTitle added in v0.4.0

func (a *App) SetSessionTitle(title string) error

SetSessionTitle updates the current session's title.

func (*App) SetStatus

func (a *App) SetStatus(key, text string)

SetStatus updates a status bar widget.

func (*App) Shortcuts

func (a *App) Shortcuts() map[string]*Shortcut

Shortcuts returns all registered shortcuts.

func (*App) ShowMessage

func (a *App) ShowMessage(text string)

ShowMessage displays a message in the TUI.

func (*App) ShowPicker

func (a *App) ShowPicker(title string, items []PickerItem, onSelect func(PickerItem))

ShowPicker shows a picker/modal in the TUI.

func (*App) StatusSections added in v0.5.0

func (a *App) StatusSections() []StatusSection

StatusSections returns all registered status sections.

func (*App) Steer

func (a *App) Steer(content string)

Steer injects a steering message that interrupts the current turn.

func (*App) StreamProvider added in v0.16.11

func (a *App) StreamProvider(api string, model core.Model) (core.StreamProvider, bool)

StreamProvider returns a provider for the given API type and model, if a factory is registered.

func (*App) SwitchModel added in v0.4.0

func (a *App) SwitchModel(id string) error

SwitchModel activates a model by its "provider/id" key. Updates the agent's model and provider, and enqueues a status update.

func (*App) SyncModels added in v0.4.0

func (a *App) SyncModels() (int, error)

SyncModels updates the model catalog from an external source.

func (*App) SystemPrompt added in v0.5.0

func (a *App) SystemPrompt() string

SystemPrompt returns the current agent's system prompt.

func (*App) ToggleStepMode

func (a *App) ToggleStepMode() bool

ToggleStepMode toggles step mode and returns the new state.

func (*App) ToolDefs

func (a *App) ToolDefs() []*ToolDef

ToolDefs returns all registered tool definitions.

func (*App) Tools

func (a *App) Tools() []string

Tools returns the names of all registered tools.

func (*App) UnregisterExtension added in v0.16.11

func (a *App) UnregisterExtension(name string)

UnregisterExtension removes all registrations associated with the named extension. Used by the supervisor when restarting a crashed extension process.

type BindOption

type BindOption func(*App)

BindOption configures optional runtime callbacks for sync operations that cannot be expressed as fire-and-forget actions.

func WithCancelBackground added in v0.4.0

func WithCancelBackground(fn func()) BindOption

WithCancelBackground sets the callback to cancel the running background agent.

func WithIsBackgroundRunning added in v0.4.0

func WithIsBackgroundRunning(fn func() bool) BindOption

WithIsBackgroundRunning sets the callback to check if a background agent is active.

func WithModelManager added in v0.4.0

func WithModelManager(mm ModelManager) BindOption

WithModelManager binds the model manager.

func WithRunBackground added in v0.4.0

func WithRunBackground(fn func(prompt string) error) BindOption

WithRunBackground sets the callback to start a background agent.

func WithSessionManager added in v0.4.0

func WithSessionManager(sm SessionManager) BindOption

WithSessionManager binds the session manager.

type Command

type Command struct {
	Name        string
	Description string
	Handler     func(args string, app *App) error
	Complete    func(prefix string) []string // tab completion; nil = no completion
	Immediate   bool                         // if true, executes during streaming without queuing
}

Command is a slash command registered by an extension.

type Compactor added in v0.5.0

type Compactor struct {
	Name      string
	Threshold int // token threshold for auto-compact; 0 = use config default
	Compact   func(ctx context.Context, messages []core.Message) ([]core.Message, error)
}

Compactor controls conversation compaction (summarization to save tokens). Register one via App.RegisterCompactor. The Compact function receives all messages and returns the compacted set — giving full control over what to keep and how to summarize.

type EventHandler added in v0.5.0

type EventHandler struct {
	Name     string
	Priority int

	// Filter limits which events this handler sees. nil = all events.
	Filter func(core.Event) bool

	// Handle processes the event. Returns an optional Action to enqueue.
	// Return nil for no action.
	Handle func(ctx context.Context, evt core.Event) Action
}

EventHandler reacts to agent lifecycle events (Observe primitive). Lower priority runs first. Handle must be fast (<50ms) — return ActionRunAsync for expensive work.

type ExtInfo added in v0.2.0

type ExtInfo struct {
	Name            string   // human-readable name
	Version         string   // semver or empty
	Kind            string   // "builtin" or "external"
	Runtime         string   // "go", "bun", "node", "python", etc.
	Tools           []string // tool names registered by this extension
	Commands        []string // command names registered by this extension
	Interceptors    []string // interceptor names
	EventHandlers   []string // event handler names
	Shortcuts       []string // shortcut keys
	MessageHooks    []string // message hook names
	Compactor       string   // compactor name, empty if none
	PromptSections  []string // prompt section titles
	StreamProviders []string // stream provider API types (e.g. "openai")
}

ExtInfo describes a loaded extension for /extensions listing.

type Extension

type Extension func(app *App) error

Extension is a function that registers tools, commands, interceptors, etc. Built-in tools and external extensions use the same signature.

type Interceptor

type Interceptor struct {
	Name     string
	Priority int

	// Before is called before tool execution.
	// Return allow=false to block the tool call.
	// Return modified args to transform input.
	Before func(ctx context.Context, toolName string, args map[string]any) (allow bool, modifiedArgs map[string]any, err error)

	// After is called after tool execution.
	// Return modified result to transform output.
	After func(ctx context.Context, toolName string, result any) (any, error)
}

Interceptor wraps tool execution with before/after hooks. Higher priority runs first. Use priority 2000+ for security, 1000+ for logging.

type InterruptBehavior added in v0.17.0

type InterruptBehavior int

InterruptBehavior controls how a tool reacts to steering (mid-turn user input).

const (
	// InterruptCancel cancels the tool on steer and discards its result (default).
	InterruptCancel InterruptBehavior = iota
	// InterruptBlock keeps the tool running when steered; the steer is queued
	// for after the tool completes.
	InterruptBlock
)

type MessageHook added in v0.5.0

type MessageHook struct {
	Name     string
	Priority int

	// OnMessage sees the user message before the LLM call.
	// Returns additional context to inject for this turn only (ephemeral).
	// Empty string = no injection. Error = abort the message.
	OnMessage func(ctx context.Context, msg string) (string, error)
}

MessageHook runs before a user message is sent to the LLM. Lower priority runs first (same convention as prompt sections).

type ModelManager added in v0.4.0

type ModelManager interface {
	// Available returns all registered models.
	Available() []core.Model

	// Switch activates a model by its "provider/id" key.
	// Returns the model and a configured streaming provider.
	// The implementation handles provider creation and auth.
	Switch(id string) (core.Model, core.StreamProvider, error)

	// Sync updates the model catalog from an external source (e.g. models.dev).
	// Returns the number of models updated.
	Sync() (updated int, err error)
}

ModelManager provides model operations to commands and extensions. Implemented by the wiring layer which imports provider/ and config/.

type PickerItem

type PickerItem struct {
	ID    string
	Label string
	Desc  string
}

PickerItem is an item in a picker/modal list. Used by commands to request a selection UI from the TUI.

type PromptSection

type PromptSection struct {
	Title     string // section heading (e.g. "Code Style")
	Content   string // markdown content
	Order     int    // lower = earlier in prompt; default 0
	TokenHint int    // estimated token count; 0 = unknown
}

PromptSection is a block of text injected into the system prompt. Extensions use this to add instructions, guidelines, or context.

type ProviderConfig

type ProviderConfig struct {
	BaseURL string
	APIKey  string
	API     core.API
	Models  []core.Model
	Headers map[string]string
}

ProviderConfig registers a custom LLM provider from an extension.

type Renderer

type Renderer func(message any, expanded bool) string

Renderer renders a custom message type for display. expanded controls whether the message is shown in full or collapsed.

type SessionManager added in v0.4.0

type SessionManager interface {
	// List returns all sessions, newest first.
	List() ([]SessionSummary, error)

	// Load opens a session by path.
	// Returns an opaque session handle for ActionSwapSession.
	Load(path string) (any, error)

	// Fork creates a branch of the current session.
	// Returns the parent short ID, forked session handle, message count, and any error.
	Fork() (parentID string, forked any, count int, err error)

	// SetTitle updates the current session's title.
	SetTitle(title string) error

	// Title returns the current session's title (empty if not set).
	Title() string
}

SessionManager provides session operations to commands and extensions. Implemented by the wiring layer (main.go/tui) which imports session/.

type SessionSummary added in v0.4.0

type SessionSummary struct {
	ID        string
	Path      string
	Title     string
	Model     string
	CWD       string
	CreatedAt time.Time
	Messages  int
	ParentID  string
}

SessionSummary is the ext-layer view of a session. Mirrors session.Summary without importing session/.

type Shortcut

type Shortcut struct {
	Key         string // e.g. "ctrl+g"
	Description string
	Handler     func(app *App) (Action, error)
}

Shortcut is a keyboard shortcut registered by an extension.

type StatusSection added in v0.5.0

type StatusSection struct {
	Key   string     // unique key (e.g. "model", "tokens", "cost")
	Side  StatusSide // left or right side of status bar
	Order int        // lower = rendered first within the side
}

StatusSection defines an extensible status bar segment. Register via App.RegisterStatusSection. The TUI renders all registered sections sorted by Order within each side (left/right).

type StatusSide added in v0.5.0

type StatusSide int

StatusSide determines which side of the status bar a section appears on.

const (
	StatusLeft StatusSide = iota
	StatusRight
)

type StreamProviderFactory added in v0.16.11

type StreamProviderFactory func(model core.Model) core.StreamProvider

StreamProviderFactory creates a StreamProvider configured for a specific model. Registered per API type; called each time the agent selects a model served by that API.

type ToolDef

type ToolDef struct {
	core.ToolSchema

	// Execute runs the tool. Same signature as core.ToolExecuteFn.
	Execute func(ctx context.Context, id string, args map[string]any) (*core.ToolResult, error)

	// PromptHint is a one-liner injected into the system prompt.
	// Example: "Read file contents with line numbers"
	PromptHint string

	// PromptGuides are bullets injected into the system prompt guidelines.
	// Example: ["Use offset/limit for files >2000 lines", "Prefer grep to find content"]
	PromptGuides []string

	// BackgroundSafe marks this tool as safe for background agent use (read-only).
	BackgroundSafe bool

	// ConcurrencySafe returns true if this tool call is safe to run concurrently
	// with other tool calls. Return false for destructive/mutating operations.
	// nil means always safe (default: parallel execution).
	ConcurrencySafe func(args map[string]any) bool

	// InterruptBehavior controls how this tool reacts to steering.
	// InterruptBlock keeps the tool running; InterruptCancel (default) cancels it.
	InterruptBehavior InterruptBehavior

	// Deferred marks this tool as rarely used. Only name+description sent in API schemas.
	// Full schema available via tool_search.
	Deferred bool
}

ToolDef extends core.ToolSchema with execution and optional UI rendering.

Directories

Path Synopsis
Package external implements the JSON-RPC stdio protocol for external piglet extensions.
Package external implements the JSON-RPC stdio protocol for external piglet extensions.

Jump to

Keyboard shortcuts

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