Documentation
¶
Overview ¶
Package plugins implements tau's process-isolated plugin host.
The plugin protocol is gRPC over an anonymous Unix-domain socket, with hashicorp/go-plugin handling process lifecycle (spawn, heartbeat, crash detection, graceful shutdown). A panicking plugin cannot panic the host.
Two sides:
- Host side (this package): spawns plugin subprocesses, dispatches tool calls, observes crashes, restarts per the policy.
- Plugin side (an external Go binary): calls plugin.Serve with a Server implementation of the proto.Plugin service.
Both sides reference HandshakeConfig and PluginMap. Host-side code uses HostClient (host.go) to wrap a single spawned plugin. Manager (manager.go) discovers, owns, and recovers HostClients.
Index ¶
- Constants
- Variables
- func Handshake(ctx context.Context, pluginName string, client tauproto.PluginClient) error
- func HostGRPCRegistrar(host tauproto.HostServer) func(opts []grpc.ServerOption) *grpc.Server
- type ConfigSource
- type ConfigSourceFunc
- type DiscoveredPath
- type ErrAlreadyShutdown
- type ErrNotRunning
- type ErrProtocolMismatch
- type ErrUnknownPluginTool
- type GRPCPlugin
- type HostClient
- func (h *HostClient) CrashSignals() <-chan struct{}
- func (h *HostClient) Handshake(ctx context.Context) error
- func (h *HostClient) ListTools(ctx context.Context) ([]*PluginTool, error)
- func (h *HostClient) Name() string
- func (h *HostClient) Path() string
- func (h *HostClient) Shutdown(ctx context.Context) error
- func (h *HostClient) Spawn(ctx context.Context) error
- func (h *HostClient) State() PluginState
- type HostConfig
- type HostServer
- func (s *HostServer) GetConfig(_ context.Context, req *tauproto.GetConfigRequest) (*tauproto.GetConfigResponse, error)
- func (s *HostServer) Log(_ context.Context, req *tauproto.LogRequest) (*tauproto.Empty, error)
- func (s *HostServer) Notify(_ context.Context, req *tauproto.NotifyRequest) (*tauproto.Empty, error)
- func (s *HostServer) SetLogWriter(w io.Writer)
- type Manager
- func (m *Manager) Discover() ([]DiscoveredPath, error)
- func (m *Manager) Execute(ctx context.Context, call tools.ToolCall) (tools.ToolResult, error)
- func (m *Manager) Shutdown(ctx context.Context) error
- func (m *Manager) SpawnAll(ctx context.Context) (spawned int, firstErr error)
- func (m *Manager) Tools() []tools.HeadlessTool
- type PluginAdapter
- type PluginSource
- type PluginState
- type PluginTool
- func (t *PluginTool) Description() string
- func (t *PluginTool) Execute(ctx context.Context, call tools.ToolCall) (tools.ToolResult, error)
- func (t *PluginTool) LocalName() string
- func (t *PluginTool) Name() string
- func (t *PluginTool) Parameters() jsonschema.Schema
- func (t *PluginTool) PluginShortName() string
- func (t *PluginTool) RenderCall(args json.RawMessage, theme *tools.Theme) string
- func (t *PluginTool) RenderResult(result tools.ToolResult, theme *tools.Theme) string
- type RestartPolicy
Constants ¶
const ( MagicCookieKey = "TAU_PLUGIN_MAGIC_COOKIE" MagicCookieValue = "tau-plugin-protocol-v1" )
MagicCookieKey is the environment variable name the host sets before spawning a plugin. The plugin MUST verify the value matches MagicCookieValue before initializing; this prevents random binaries from being treated as plugins when misused as go-plugin servers.
const ( RestartAlwaysMaxAttempts = 5 RestartAlwaysWindow = 30 * time.Second )
RestartAlwaysMaxAttempts caps the respawn attempts under RestartAlways within RestartAlwaysWindow. Prevents an infinite loop on a panic-at-startup plugin.
const PluginName = "tau"
PluginName is the key the host and plugin use to register the gRPC service in their respective PluginMaps. go-plugin supports multiple plugins per process; tau ships exactly one (the Plugin service) so a fixed name suffices.
const PluginPrefix = "tau-plugin-"
PluginPrefix is the executable-name prefix the spec requires for discovery. Files in a plugins directory that do not start with this prefix are ignored.
const ProtocolVersion uint = 1
ProtocolVersion is the wire-protocol version this host build expects. Plugins announce their version via Plugin.Handshake; the host rejects mismatches. Bumping this constant is a breaking change and requires a tau minor-version bump.
History:
1 = initial release.
The type is `uint` to satisfy go-plugin's HandshakeConfig.ProtocolVersion field; handshake comparisons cast to int32 to match the proto response.
const SpawnDefaultTimeout = 15 * time.Second
SpawnDefaultTimeout is the default HostConfig.StartTimeout. Generous because plugin startup may include BPE encoding table loads or schema reflection.
Variables ¶
var HandshakeConfig = plugin.HandshakeConfig{ ProtocolVersion: ProtocolVersion, MagicCookieKey: MagicCookieKey, MagicCookieValue: MagicCookieValue, }
HandshakeConfig is the go-plugin handshake shared by host and plugin. Both sides reference this exact value.
var PluginFileExt = windowsExt()
PluginFileExt is the Windows executable extension. On non-Windows it is empty; on Windows it is appended to PluginPrefix when matching.
var PluginMap = map[string]plugin.Plugin{ PluginName: &GRPCPlugin{}, }
PluginMap is the host-side PluginMap. The plugin side builds its own equivalent via NewPluginAdapter (server.go).
Functions ¶
func Handshake ¶
Handshake calls Plugin.Handshake on the client and verifies the version. Returns ErrProtocolMismatch on version drift, errNoHandshake on RPC failure. Caller is responsible for emitting diagnostics and deciding whether to skip-and-continue or fail-fast.
func HostGRPCRegistrar ¶
func HostGRPCRegistrar(host tauproto.HostServer) func(opts []grpc.ServerOption) *grpc.Server
HostGRPCRegistrar is the hook go-plugin's GRPCServer uses to register the host-side Host service on the same gRPC server as the broker. Without this, plugin→host RPCs fail with "unknown service Host".
The Manager sets this up by wrapping plugin.DefaultGRPCServer with a registrar that calls tauproto.RegisterHostServer.
Types ¶
type ConfigSource ¶
type ConfigSource interface {
// GetConfig returns the value for the dotted key, or found=false
// if not present.
GetConfig(key string) (value string, found bool)
}
ConfigSource resolves a dotted-path configuration key (e.g. "compaction.reserveTokens") to a string value. Returns found=false if the key is absent. Implementations are responsible for redacting sensitive keys (e.g. API keys, OAuth tokens) — they should return found=false rather than leak the value.
func NoopConfigSource ¶
func NoopConfigSource() ConfigSource
NoopConfigSource returns found=false for every key. Used in tests and as a safe default when no real ConfigSource is available.
type ConfigSourceFunc ¶
ConfigSourceFunc is the func adapter for ConfigSource.
type DiscoveredPath ¶
type DiscoveredPath struct {
ShortName string // e.g. "git"; what follows PluginPrefix
AbsPath string // absolute path to the executable
Source PluginSource
}
DiscoveredPath is one entry returned by Discover's enumeration. The Manager uses these to spawn HostClients after applying the project-shadows-global rule.
type ErrAlreadyShutdown ¶
type ErrAlreadyShutdown struct{ Name string }
ErrAlreadyShutdown is returned by Spawn (and other stateful methods) after Shutdown has been called on a HostClient. The client is dead.
func (ErrAlreadyShutdown) Error ¶
func (e ErrAlreadyShutdown) Error() string
type ErrNotRunning ¶
ErrNotRunning is returned when a method requires PluginStateRunning but the subprocess is not currently alive. The Manager may interpret this as a signal to apply the on-next-call restart policy.
func (ErrNotRunning) Error ¶
func (e ErrNotRunning) Error() string
type ErrProtocolMismatch ¶
ErrProtocolMismatch is returned by Handshake when a plugin announces a protocol version incompatible with this host's ProtocolVersion.
func (*ErrProtocolMismatch) Error ¶
func (e *ErrProtocolMismatch) Error() string
type ErrUnknownPluginTool ¶
type ErrUnknownPluginTool struct{ Name string }
ErrUnknownPluginTool is returned by Execute when no plugin owns the requested tool name. The agent loop surfaces this to the model as a tool-call failure.
func (ErrUnknownPluginTool) Error ¶
func (e ErrUnknownPluginTool) Error() string
type GRPCPlugin ¶
type GRPCPlugin struct {
plugin.NetRPCUnsupportedPlugin
// ServerImpl is the proto.PluginServer the plugin process exposes.
// nil on the host side.
ServerImpl tauproto.PluginServer
}
GRPCPlugin is the bridge between go-plugin's plugin.Plugin interface and the proto Plugin service. The same type is registered in both the host's and the plugin's PluginMap:
- On the plugin side, ServerImpl returns the proto.PluginServer implementation. The plugin author passes it via NewPluginAdapter.
- On the host side, GRPCClient returns a Client that wraps the gRPC connection; the host uses it via HostClient (host.go).
The split exists because go-plugin's net/rpc and gRPC subtypes want one factory per "plugin name", but the host and plugin processes occupy opposite ends of the same wire.
func (*GRPCPlugin) GRPCClient ¶
func (p *GRPCPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (any, error)
GRPCClient is called by go-plugin on the host to wrap the dispensed gRPC client. Plugin side never calls this. The returned value is what HostClient.pluginClient holds.
func (*GRPCPlugin) GRPCServer ¶
func (p *GRPCPlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error
GRPCServer is called by go-plugin on the plugin process to register the gRPC service. Host side never calls this.
type HostClient ¶
type HostClient struct {
// contains filtered or unexported fields
}
HostClient owns the lifecycle of one plugin subprocess. It is the boundary between the Manager (which coordinates discovery, restart, and tool registration) and go-plugin's per-process primitives.
HostClient is goroutine-safe: concurrent Execute and ListTools calls are serialized by an internal mutex, and crash detection runs in its own goroutine.
func NewHostClient ¶
func NewHostClient(cfg HostConfig) (*HostClient, error)
NewHostClient constructs a HostClient in PluginStateNew. Spawn must be called separately to launch the subprocess.
func (*HostClient) CrashSignals ¶
func (h *HostClient) CrashSignals() <-chan struct{}
CrashSignals returns a channel that receives one signal per unexpected subprocess exit. The Manager uses this to apply the restart policy. The channel is buffered(1) and never closes; observers should select non-blocking or drain in a loop.
func (*HostClient) Handshake ¶
func (h *HostClient) Handshake(ctx context.Context) error
Handshake verifies the plugin's announced protocol version. Must be called after Spawn; calling before Spawn returns ErrNotRunning.
func (*HostClient) ListTools ¶
func (h *HostClient) ListTools(ctx context.Context) ([]*PluginTool, error)
ListTools calls Plugin.ListTools and caches the result. Subsequent calls return the cache without an RPC. The cache is cleared on re-spawn so schema drift is detected on restart.
Returns PluginTool instances whose clientProvider points back at this HostClient's live gRPC client.
func (*HostClient) Name ¶
func (h *HostClient) Name() string
Name returns the plugin's short identifier.
func (*HostClient) Path ¶
func (h *HostClient) Path() string
Path returns the plugin executable path.
func (*HostClient) Shutdown ¶
func (h *HostClient) Shutdown(ctx context.Context) error
Shutdown calls Plugin.Shutdown, waits up to grace for a clean exit, then SIGKILLs the subprocess if still alive and transitions to PluginStateShutdown. Idempotent; second and later calls are no-ops.
func (*HostClient) Spawn ¶
func (h *HostClient) Spawn(ctx context.Context) error
Spawn launches the subprocess and establishes the gRPC channel. It is idempotent for PluginStateNew and PluginStateCrashed; on PluginStateRunning it is a no-op; on PluginStateShutdown it returns ErrAlreadyShutdown.
func (*HostClient) State ¶
func (h *HostClient) State() PluginState
State returns the current lifecycle state. Safe to call from any goroutine; the value may change asynchronously as the subprocess runs.
type HostConfig ¶
type HostConfig struct {
// Name is the plugin's short identifier (e.g. "git"). Used in
// diagnostics, tool namespacing ("<name>.<localName>"), and the
// PluginMap key.
Name string
// Path is the absolute path to the plugin executable.
Path string
// HostServer is the host-side Host service implementation the plugin
// will call back to for Log/Notify/GetConfig. Required: plugins
// legitimately fail when their Host callbacks return "not
// implemented".
HostServer tauproto.HostServer
// Stderr captures the plugin's raw stderr stream. Plugin logs (via
// the host's Host.Log RPC) and go-plugin's own diagnostics land
// here. Defaults to io.Discard.
Stderr io.Writer
// Restart is the policy applied when the subprocess exits
// unexpectedly. See RestartPolicy docs for semantics.
Restart RestartPolicy
// StartTimeout caps how long HostClient.Spawn waits for go-plugin's
// handshake (magic cookie negotiation) before giving up. Defaults
// to SpawnDefaultTimeout.
StartTimeout time.Duration
}
HostConfig carries the host-side inputs to a HostClient. It is captured at construction time and reused across restarts so a recovered plugin comes back with the same environment.
type HostServer ¶
type HostServer struct {
tauproto.UnimplementedHostServer
// LogWriter is the destination for Host.Log calls. Defaults to
// io.Discard; tests inject io.Discard or a buffer to assert.
LogWriter io.Writer
// Logger is structured logger used when LogWriter is nil but
// Logger is non-nil. If both are nil, Host.Log is silently
// discarded.
Logger *log.Logger
// ConfigSource resolves dotted config keys for Host.GetConfig.
// Plugins should not read secrets through this RPC; the
// ConfigSource is responsible for redacting sensitive keys.
ConfigSource ConfigSource
// NotifyHandler is invoked per Host.Notify call. NotifyHandler
// should be non-blocking; long work should be queued off-thread.
NotifyHandler func(severity, message string)
// contains filtered or unexported fields
}
HostServer implements the proto Host service that the host exposes to plugins. Plugins call Host.Log, Host.Notify, and Host.GetConfig to surface diagnostics, request user notifications, and read host config.
HostServer is registered on the same gRPC server that go-plugin sets up for plugin→host callbacks via the broker. The plugin side dials it through the broker-supplied connection.
func NewHostServer ¶
func NewHostServer(logWriter io.Writer, cfg ConfigSource, notify func(string, string)) *HostServer
NewHostServer constructs a HostServer. nil ConfigSource becomes NoopConfigSource; nil NotifyHandler is a no-op.
func (*HostServer) GetConfig ¶
func (s *HostServer) GetConfig(_ context.Context, req *tauproto.GetConfigRequest) (*tauproto.GetConfigResponse, error)
GetConfig implements Host.GetConfig. Returns the value or found=false if the key is absent (or redacted by the ConfigSource).
func (*HostServer) Log ¶
func (s *HostServer) Log(_ context.Context, req *tauproto.LogRequest) (*tauproto.Empty, error)
Log implements Host.Log. Plugins call this for diagnostic output. The level controls prefix only; the host does not rate-limit.
func (*HostServer) Notify ¶
func (s *HostServer) Notify(_ context.Context, req *tauproto.NotifyRequest) (*tauproto.Empty, error)
Notify implements Host.Notify. Routes a user-facing notification to the host's event bus via the configured handler.
func (*HostServer) SetLogWriter ¶
func (s *HostServer) SetLogWriter(w io.Writer)
SetLogWriter swaps the log destination. Safe to call after the host has started; concurrent Log calls serialize on the internal mutex.
type Manager ¶
type Manager struct {
// ProjectPluginsDir is the absolute path to <cwd>/plugins/. May be
// empty if the cwd has no plugins/ dir; Manager still scans
// GlobalPluginsDir.
ProjectPluginsDir string
// GlobalPluginsDir is the absolute path to ~/.config/tau/plugins/.
// May be empty.
GlobalPluginsDir string
// HostServer is the host-side Host service implementation passed
// to every HostClient. Required.
HostServer tauproto.HostServer
// HostServerLogWriter is the writer HostServer uses for plugin
// log lines. May be nil (defaults to io.Discard via NewHostServer).
HostServerLogWriter io.Writer
// DefaultRestartPolicy is applied to plugins that don't override
// it. Defaults to RestartOnNextCall — the least-surprising policy
// for a long-lived agent.
DefaultRestartPolicy RestartPolicy
// Diagnostics receives one message per significant lifecycle event
// (collision, shadow, version mismatch, restart, crash). May be nil;
// the Manager silently drops diagnostics then. Each message is a
// single line ending in \n.
Diagnostics chan<- string
// contains filtered or unexported fields
}
Manager owns the set of HostClients for a session. It is the public boundary the agent runtime and CLI use: callers Discover() once at startup, then call Tools() to get a flat list of namespaced tools to register with the agent's tool registry, then call Execute() for each model-issued tool call.
Manager also coordinates restart policies, collision detection, and clean shutdown across all plugins.
func NewManager ¶
func NewManager(projectDir, globalDir string, hostServer tauproto.HostServer) (*Manager, error)
NewManager constructs a Manager in New state. Discover must be called separately. Returns an error if required fields are missing.
func (*Manager) Discover ¶
func (m *Manager) Discover() ([]DiscoveredPath, error)
Discover scans ProjectPluginsDir and GlobalPluginsDir for executable files whose names start with PluginPrefix. Project-local shadows global: when both directories have a same-named plugin, the project one wins and a diagnostic is emitted. Non-matching and non-executable files are silently skipped.
Discover is the canonical "is the on-disk layout what the spec requires" check. Callers should run it once at startup; the result is cached by SpawnAll.
func (*Manager) Execute ¶
Execute routes a tool call to the plugin that owns its name. Namespaced names ("git.status") are decomposed into plugin name + local name; the plugin receives the local name (PluginTool.Execute strips the prefix before sending the RPC).
When the full name was advertised via ListTools, the cached PluginTool is used. When the full name was not advertised but the prefix matches a known plugin, Execute still routes the call to that plugin — the plugin is responsible for rejecting unknown tool names. This matches the spec invariant that the host treats plugin tools uniformly whether or not they appear in ListTools (ListTools is informational, not a gate). Returns ErrUnknownPluginTool only when no plugin's short name matches the prefix.
func (*Manager) Shutdown ¶
Shutdown terminates every plugin subprocess. Caller passes a context whose deadline bounds total shutdown (per-plugin shutdown grace is shutdownGrace=5s). Returns the first error encountered; remaining plugins are still attempted.
func (*Manager) SpawnAll ¶
SpawnAll discovers plugins and spawns a HostClient for each. Tools are loaded (via HostClient.ListTools) so Tools() can return them without further RPCs. Returns the number of plugins spawned and the first error encountered (subsequent plugins are still attempted — a single bad plugin should not poison the rest).
func (*Manager) Tools ¶
func (m *Manager) Tools() []tools.HeadlessTool
Tools returns the namespaced tools across all plugins, sorted by name. Tools from the same plugin retain their natural order. Collisions across plugins are resolved first-wins; subsequent registrants are dropped and a diagnostic is emitted.
The return type is []tools.HeadlessTool so the runtime can merge plugin tools onto a HeadlessTool-capable registry without conversion. Plugin-dispatched tools are gRPC proxies and never implement the TUI rendering hooks, so HeadlessTool is the correct contract.
type PluginAdapter ¶
type PluginAdapter struct {
// Server is the plugin's proto.PluginServer implementation.
Server tauproto.PluginServer
// HostCallbacks is dialed at startup via the go-plugin broker; the
// plugin uses it to call Host.Log/Notify/GetConfig. May be nil;
// plugin code should nil-check before calling.
HostCallbacks tauproto.HostClient
}
PluginAdapter is the plugin-side glue between a Server implementation of proto.PluginServer and go-plugin's plugin.Serve entrypoint. Plugin authors construct one with their Server and pass it to plugin.Serve.
func (*PluginAdapter) PluginMap ¶
func (a *PluginAdapter) PluginMap() map[string]plugin.Plugin
PluginMap returns the plugin-side PluginMap for plugin.Serve. Exactly one entry keyed by PluginName; its GRPCPlugin carries the Server.
func (*PluginAdapter) Serve ¶
func (a *PluginAdapter) Serve()
Serve is the convenience entrypoint. It calls plugin.Serve with the adapter's config and never returns (plugin.Serve exits the process). Authors who need a different exit behavior should use ServeConfig() directly.
func (*PluginAdapter) ServeConfig ¶
func (a *PluginAdapter) ServeConfig() plugin.ServeConfig
ServeConfig assembles the plugin.Serve config. Plugins call:
adapter := &plugins.PluginAdapter{Server: myServer}
plugin.Serve(&adapter.ServeConfig())
or use the Serve helper below.
func (*PluginAdapter) ServerFactory ¶
func (a *PluginAdapter) ServerFactory() *GRPCPlugin
ServerFactory is the name go-plugin dispenses (we registered a single "tau" plugin in PluginMap on the host side; the plugin's PluginMap uses the same name so the wire handshake matches).
type PluginSource ¶
type PluginSource int
PluginSource identifies which scan directory a plugin came from.
const ( // PluginSourceGlobal means the plugin came from ~/.config/tau/plugins/. PluginSourceGlobal PluginSource = iota // PluginSourceProject means the plugin came from <cwd>/plugins/. PluginSourceProject )
func (PluginSource) String ¶
func (s PluginSource) String() string
String returns "global" or "project" for diagnostics.
type PluginState ¶
type PluginState int
PluginState reports where in its lifecycle a HostClient's subprocess is. The Manager uses this to decide whether to restart, fail the call, or proceed.
const ( // PluginStateNew means the HostClient has been constructed but Spawn // has not been called (or the previous subprocess was killed). PluginStateNew PluginState = iota // PluginStateRunning means the subprocess is alive and the gRPC // channel is established. Handshake may or may not have completed. PluginStateRunning // PluginStateCrashed means the subprocess exited unexpectedly. The // Manager's restart policy decides whether Spawn is called again. PluginStateCrashed // PluginStateShutdown means Shutdown was called and the subprocess // was killed cleanly. The HostClient should not be restarted. PluginStateShutdown )
func (PluginState) String ¶
func (s PluginState) String() string
String returns a lowercase identifier matching the diagnostic vocabulary.
type PluginTool ¶
type PluginTool struct {
// contains filtered or unexported fields
}
PluginTool is the host-side tools.Tool implementation that routes Execute to a Plugin.Execute RPC. The Registry treats it identically to built-in tools; the agent loop is unaware a tool is remote.
PluginTool instances are constructed by HostClient after ListTools returns. They hold an indirect clientProvider so a restarted plugin can swap its HostClient without rebuilding every PluginTool instance.
Naming convention: the plugin advertises fully-qualified names like "git.status" in its ToolSchema; the host stores the tool under that name verbatim. Cross-plugin collisions (two plugins expose the same qualified name) are resolved first-wins by the Manager, with a diagnostic emitted for the loser.
func NewPluginTool ¶
func NewPluginTool(pluginShortName, toolName, description, jsonSchema string, provider func() (tauproto.PluginClient, error)) *PluginTool
NewPluginTool constructs a PluginTool. toolName is the fully-qualified name the plugin advertised (e.g. "git.status"); pluginShortName is the plugin's discovery name for routing and diagnostics. The clientProvider returns the live gRPC client; it is called on every Execute so restarts swap transparently.
func (*PluginTool) Description ¶
func (t *PluginTool) Description() string
Description returns the plugin-advertised description verbatim.
func (*PluginTool) Execute ¶
func (t *PluginTool) Execute(ctx context.Context, call tools.ToolCall) (tools.ToolResult, error)
Execute routes the call to the plugin via the gRPC client. RPC errors (network, plugin crash, context cancellation) are converted to a ToolResult with IsError=true; the agent loop treats them as tool failures. The model sees the error text and can retry or proceed.
Context cancellation propagates to the plugin via gRPC; the plugin is responsible for honoring ctx.Done() promptly.
func (*PluginTool) LocalName ¶
func (t *PluginTool) LocalName() string
LocalName returns the un-namespaced name sent to the plugin on the wire. Everything after the first "." in the advertised name; if there is no ".", the full name is returned as-is. The spec mandates the plugin receive the un-namespaced form.
func (*PluginTool) Name ¶
func (t *PluginTool) Name() string
Name returns the fully-qualified tool name the plugin advertised.
func (*PluginTool) Parameters ¶
func (t *PluginTool) Parameters() jsonschema.Schema
Parameters parses the plugin-advertised JSON Schema string into the jsonschema.Schema shape. The parse is cached after the first call.
func (*PluginTool) PluginShortName ¶
func (t *PluginTool) PluginShortName() string
PluginShortName returns the plugin's discovery short name. Used by the Manager for routing and diagnostics.
func (*PluginTool) RenderCall ¶
func (t *PluginTool) RenderCall(args json.RawMessage, theme *tools.Theme) string
RenderCall renders the invocation for the TUI. Plugin tools render their namespaced name plus the raw args JSON, matching the style of the built-in tools.
func (*PluginTool) RenderResult ¶
func (t *PluginTool) RenderResult(result tools.ToolResult, theme *tools.Theme) string
RenderResult renders the result for the TUI. Errors get an "error: " prefix; otherwise the content blocks are concatenated via the shared tools.RenderContentBlocks helper.
type RestartPolicy ¶
type RestartPolicy string
RestartPolicy governs how the Manager reacts when a plugin's subprocess exits unexpectedly. Matches the spec values exactly.
const ( // RestartNever marks the plugin's tools as permanently unavailable // after a crash. Subsequent Execute calls fail with ErrNotRunning. RestartNever RestartPolicy = "never" // RestartOnNextCall leaves the plugin down until a tool of its is // invoked; the Manager re-spawns on the next Execute call. RestartOnNextCall RestartPolicy = "on-next-call" // RestartAlways triggers an immediate respawn when the subprocess // exits, capped at RestartAlwaysMaxAttempts in RestartAlwaysWindow. RestartAlways RestartPolicy = "always" )