Documentation
¶
Overview ¶
Package composition owns session wiring: registries, dispatchers, hooks, MCP merge, session construction. internal/cli must not hold this logic; it parses arguments and renders output.
Index ¶
- func AttachMCPServers(reg *tools.Registry, cfg config.MCPConfig, redactionPolicy *redact.Policy, ...) (*mcp.Manager, func(), error)
- func BuildDispatcher(in DispatcherInput) (*runtime.Dispatcher, error)
- func BuildRegistry(in RegistryInput) (*tools.Registry, error)
- func BuildSession(in SessionInput) (*chat.Session, *storage.SQLite, contextstate.Principal, error)
- func HookPolicyFuncs(in DispatcherInput) (func(context.Context, runtime.Request) runtime.HookVerdict, ...)
- func MergeMCPTools(reg *tools.Registry, manager *mcp.Manager, serverIDs []string) error
- func NewMCPManager(cfg config.MCPConfig, redactionPolicy *redact.Policy) (*mcp.Manager, error)
- type DispatcherInput
- type RegistryInput
- type SessionInput
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AttachMCPServers ¶
func AttachMCPServers(reg *tools.Registry, cfg config.MCPConfig, redactionPolicy *redact.Policy, serverIDs []string) (*mcp.Manager, func(), error)
AttachMCPServers builds an MCP manager for cfg and merges serverIDs' tools into reg (see MergeMCPTools). It returns the manager (nil when MCP is disabled) and a cleanup that closes it. On error the manager, if created, is closed before returning so a failed attach leaks no client.
func BuildDispatcher ¶
func BuildDispatcher(in DispatcherInput) (*runtime.Dispatcher, error)
BuildDispatcher assembles the runtime.Dispatcher with hook gates. It fills Policy.PreInvokeHook/PostInvokeHook exactly as internal/cli/hooks_runner.go did before this move: nil when no hooks are configured, otherwise closures that run the session's hook groups through a hooks.Runner rooted at WorkspaceRoot.
func BuildRegistry ¶
func BuildRegistry(in RegistryInput) (*tools.Registry, error)
BuildRegistry constructs the tool registry. It produces a registry identical to what the cli path built before the composition-root move (internal/cli's buildWorkflowToolOpts/configureChatWorkspace and workflowDefaultRegistry). It never returns a non-nil error today because tools.NewDefaultRegistry cannot fail; the error return is kept so a future validation step does not change this function's signature.
func BuildSession ¶
func BuildSession(in SessionInput) (*chat.Session, *storage.SQLite, contextstate.Principal, error)
BuildSession wires a chat.Session end to end: the completer chat.NewSession takes directly, the tool registry (BuildRegistry), the dispatcher (BuildDispatcher), the event bus, and a SQLite-backed checkpoint store. It returns the checkpoint principal actually installed (in.Principal as-is, or a freshly minted one), so a caller can read its own checkpoint back through the returned store. The caller owns the store's Close; the session holds no reference that outlives it.
func HookPolicyFuncs ¶
func HookPolicyFuncs(in DispatcherInput) ( func(context.Context, runtime.Request) runtime.HookVerdict, func(context.Context, runtime.Request, runtime.Result) runtime.HookResult, )
HookPolicyFuncs returns the dispatcher's lifecycle-hook fields, or nils when no hook is configured at all. Exported so a caller that needs the funcs without a full dispatcher (e.g. a session that rebuilds Policy directly) can still delegate to this package rather than reimplement the hook-execution logic.
Nil is not an optimisation, it is the contract: with no hooks configured the dispatcher does one nil compare per invocation and behaves exactly as it did before this layer existed.
func MergeMCPTools ¶
MergeMCPTools discovers each of serverIDs' tools through manager and registers them into reg under the manager's mcp__<server>__<tool> name encoding (see internal/mcp's discoveredTool.Name). A contained server outage never fails the merge; the session continues without that server's tools, and the operator is warned by server ID only - external error text can carry request content (DC-14). A discovered tool whose name collides with a name reg already holds is rejected as an error unless manager owns that name (an idempotent re-merge of the same server).
func NewMCPManager ¶
NewMCPManager builds an MCP manager from cfg. It returns (nil, nil) when cfg.Enabled is false, matching the historical cli behavior: a disabled MCP configuration is a no-op, not an error.
Types ¶
type DispatcherInput ¶
type DispatcherInput struct {
// Registry is the tool registry the dispatcher registers handlers
// against.
Registry *tools.Registry
MaxDepth, MaxRetries, MaxInputBytes, MaxOutputBytes int
MaxBudget int
// Allow is the per-Kind, per-name allow map. Nil allows every
// registered handler, the same default runtime.Policy carries today.
Allow map[runtime.Kind]map[string]bool
// Sink, when set, receives one runtime.Event per invocation lifecycle
// step.
Sink func(runtime.Event)
// WorkspaceRoot is the directory lifecycle hooks execute in. Empty
// means no hooks are wired at all, the same as HooksConfigured false.
WorkspaceRoot string
// HooksConfigured reports whether this session has any hook armed,
// evaluated once at build time. False (or an empty WorkspaceRoot)
// means Policy.PreInvokeHook/PostInvokeHook stay nil: one nil compare
// per invocation, no hook overhead at all - the same contract
// internal/cli/hooks_runner.go's hookPolicyFuncs held before this
// move.
HooksConfigured bool
// HookGroups returns the runnable hook groups for the current session.
// It is read fresh on every hook invocation rather than closed over at
// build time, so what the caller's session state lists is what the
// next tool call runs without a dispatcher rebuild. Required when
// HooksConfigured is true.
HookGroups func() []hooks.Group
// NoteHookWarnings receives runtime diagnostics from hooks that
// actually executed, for the caller to surface (e.g. a /hooks
// listing). Nil is safe: warnings are simply dropped.
NoteHookWarnings func([]string)
}
DispatcherInput carries the tool registry, dispatcher policy, and hook wiring BuildDispatcher needs to assemble a runtime.Dispatcher. The policy fields mirror runtime.Policy field for field; BuildDispatcher is the only place that translates one into the other.
type RegistryInput ¶
type RegistryInput struct {
Workspace *workspace.Root
RunAllowlist, RunAllowlistOnly, RunBlocklist, DisableTools []string
RunTimeoutSec, MaxReadBytes, MaxEditFileBytes, MaxOutputBytes, MaxWriteKB, MaxListDirEntries int
MaxToolResultBytes int
MaxTavilyResponseBytes int
MaxFetchKB int
MemoryBackstopBytes int
TavilyAPIKey string
EnvAllowlist, EnvAllowlistOnly, EnvBlocklist []string
EnvAllowKeywordBlocklist []string
SecretPathPatterns, SecretPathExceptions []string
WritePathDenylist []string
SearchIgnorePatterns []string
MaxInspectRepositoryBytes int
DiagnosticsCommands map[string][]string
WorkflowTools []tools.Tool
Memory memory.Store
}
RegistryInput carries every value the CLI or tests supply to build the default tool registry. Keep it a plain struct. No methods. No behavior. Fields mirror tools.DefaultOptions field for field: BuildRegistry is the only place that translates one into the other.
type SessionInput ¶
type SessionInput struct {
// Config is the resolved workspace configuration. Required:
// chat.NewSession reads it for the model, prompt, and token budgets.
Config *config.Resolved
// Completer is the provider completer the session's initial binding
// uses. May be nil (chat.NewSession accepts a nil completer for
// construction), but a nil completer cannot run a turn.
Completer provider.Completer
// Registry configures the tool registry BuildRegistry builds. Its
// Workspace field may be nil, which yields a registry with no
// filesystem tools.
Registry RegistryInput
// PrebuiltRegistry, when non-nil, skips BuildRegistry(in.Registry)
// entirely and assigns sess.Tools = in.PrebuiltRegistry directly. Use
// this when the caller has already merged additional tools (e.g. MCP
// wrappers via AttachMCPServers) into a registry it owns. The dispatcher's
// view matches regardless of the path taken: dispatcher.Registry is set
// to whatever sess.Tools ends up as.
PrebuiltRegistry *tools.Registry
// Dispatcher configures the dispatcher BuildDispatcher builds.
// Dispatcher.Registry is overwritten with the registry BuildSession just
// built, so the caller does not need to set it.
Dispatcher DispatcherInput
// EventBus is the session's event bus. Nil mints a fresh events.New().
EventBus *events.Bus
// StorePath is the SQLite file BuildSession opens for the session's
// checkpoint store. Required: chat.Session has no durable checkpoint
// history without one.
StorePath string
// WorkspaceID identifies the checkpoint principal's workspace scope.
// Ignored when Principal is already bound.
WorkspaceID string
// SubjectID identifies the checkpoint principal's subject scope. Empty
// defaults to the session's own SessionID. Ignored when Principal is
// already bound.
SubjectID string
// Principal, when bound (IsBound() true), is used as-is instead of
// minting a fresh one from WorkspaceID/SubjectID. A caller that needs to
// read its own checkpoint back later (contextstate.Principal's capability
// is random per mint - see contextstate.NewPrincipal - and store.Load
// rejects a principal whose capability does not match what was written)
// must supply the same Principal value it will read with.
Principal contextstate.Principal
}
SessionInput carries every value BuildSession needs to construct a working chat.Session: the resolved config and completer chat.NewSession itself takes, the registry and dispatcher inputs BuildRegistry/BuildDispatcher already accept, and the checkpoint store BuildSession opens and wires as the session's context store.