Documentation
¶
Overview ¶
Package external defines the neutral port between the agent application layer and out-of-process runtimes: Codex, Claude Code, and ACP agents.
A Driver owns one runtime type end to end: process lifecycle inside the bot workspace, the native wire protocol, and the translation of runtime events into the shared event.StreamEvent vocabulary. The application layer stays runtime-agnostic: it resolves a Driver from the session's runtime type, hands it a PromptInput, and persists the PromptResult — the same shape for every driver. New runtimes must implement Driver instead of growing new special cases in the application layer.
Index ¶
- Constants
- Variables
- func CredentialError(err error) error
- func Fail(kind FailureKind, err error) error
- func IsFailure(err error) bool
- func LimitStreamEvent(ev event.StreamEvent, limit contextlimit.ToolOutputLimit) event.StreamEvent
- func RequireContainerWorkspace(info bridge.WorkspaceInfo, runtime string) error
- func TranscriptFromEvents(events []event.StreamEvent, fallbackText string) []sdk.Message
- func Unavailable(err error) error
- type BotAgentAuthPurger
- type BotAgentResetter
- type BotResetter
- type Command
- type CommandKind
- type CommandProvider
- type CommandResult
- type CompactionResult
- type Compactor
- type ControlCapabilities
- type Controls
- type DependencyMissingError
- type DependencyRequirement
- type DependencyRequirer
- type Driver
- type Drivers
- func (drivers Drivers) ModelCatalog(ctx context.Context, runtimeType string, request ModelCatalogRequest) (ModelCatalog, error)
- func (drivers Drivers) PurgeBotAgentAuth(ctx context.Context, runtimeType, botID, botAgentID string) error
- func (drivers Drivers) RequiredDependencies() map[string]DependencyRequirement
- func (drivers Drivers) ResetBot(botID string)
- func (drivers Drivers) ResetBotAgent(runtimeType, botID, botAgentID string)
- type EventSink
- type EventSinkFunc
- type Failure
- type FailureKind
- type Goal
- type GoalProvider
- type Image
- type Launcher
- type LauncherResolver
- type LauncherSource
- type Mode
- type ModeProvider
- type ModeState
- type ModelCatalog
- type ModelCatalogProvider
- type ModelCatalogRequest
- type ModelOption
- type PlanModeProvider
- type PromptInput
- type PromptResult
- type ReasoningEffortOption
- type RoundRollbackHandler
- type SteerInput
- type Steering
- type ThreadForker
- type TranscriptRecorder
- type VersionObserver
Constants ¶
const ( CommandTurn = turn.RuntimeCommandTurn CommandRead = turn.RuntimeCommandRead CommandOperation = turn.RuntimeCommandOperation )
Variables ¶
var ( ErrAuthRequired = errors.New("runtime authentication is required") ErrControlUnsupported = errors.New("runtime control is unsupported") )
var ErrContainerWorkspaceRequired = errors.New("direct agent runtime requires a container workspace")
ErrContainerWorkspaceRequired reports a direct runtime asked to start in a workspace that is not a container.
var ErrDependencyMissing = errors.New("workspace dependency is not installed")
ErrDependencyMissing is the errors.Is target for DependencyMissingError.
Functions ¶
func CredentialError ¶
CredentialError reports an Agent credential the user must fix (missing, of the wrong kind, revoked, or undecryptable) as a FailureCredential. Any other error is returned unchanged.
func Fail ¶ added in v0.21.0
func Fail(kind FailureKind, err error) error
Fail reports a failure of kind, caused by err when err is not nil.
func LimitStreamEvent ¶
func LimitStreamEvent(ev event.StreamEvent, limit contextlimit.ToolOutputLimit) event.StreamEvent
LimitStreamEvent bounds tool output before it enters a runtime transcript.
func RequireContainerWorkspace ¶
func RequireContainerWorkspace(info bridge.WorkspaceInfo, runtime string) error
RequireContainerWorkspace guards drivers whose executable environment and credential paths assume the container layout. The remote bridge maps file RPC paths, but it does not translate paths embedded in process environments.
func TranscriptFromEvents ¶
func TranscriptFromEvents(events []event.StreamEvent, fallbackText string) []sdk.Message
TranscriptFromEvents builds a transcript by folding a complete event sequence. Production uses the incremental builder wired into the prompt collector; this convenience exists for tests and offline tooling.
func Unavailable ¶ added in v0.21.0
Unavailable reports that the runtime could not start or serve the turn.
Types ¶
type BotAgentAuthPurger ¶
type BotAgentResetter ¶
type BotAgentResetter interface {
ResetBotAgent(botID, botAgentID string)
}
type BotResetter ¶
type BotResetter interface {
ResetBot(botID string)
}
type Command ¶
type Command = turn.RuntimeCommand
type CommandKind ¶
type CommandKind = turn.RuntimeCommandKind
type CommandProvider ¶
type CommandProvider interface {
Commands(context.Context, PromptInput) ([]Command, error)
ReadCommand(context.Context, PromptInput) (CommandResult, error)
}
CommandProvider owns both the command vocabulary and read-only dispatch. Turn commands use Driver.Prompt; operation commands use Compactor.
type CommandResult ¶
type CommandResult = turn.RuntimeCommandResult
type CompactionResult ¶
CompactionResult returns updated native session references or usage. The Agent owns compaction and persists its own conversation files.
type Compactor ¶
type Compactor interface {
Compact(context.Context, PromptInput) (CompactionResult, error)
}
Compactor completes only when the runtime finishes compaction. Cancellation interrupts the operation. The caller owns the thread's execution slot and persists returned runtime metadata, without adding conversation messages.
type ControlCapabilities ¶
type ControlCapabilities = turn.RuntimeControlCapabilities
type DependencyMissingError ¶
type DependencyMissingError struct {
DependencyID string
TaskID string
// OperationInProgress distinguishes an existing administrative operation
// from a dependency that an administrator still needs to install.
OperationInProgress bool
}
DependencyMissingError reports that no copy of the dependency exists in the workspace. TaskID is the background installation task, if one was started.
func (*DependencyMissingError) Error ¶
func (e *DependencyMissingError) Error() string
func (*DependencyMissingError) Is ¶
func (*DependencyMissingError) Is(target error) bool
Is makes errors.Is(err, ErrDependencyMissing) true for any instance.
type DependencyRequirement ¶
type DependencyRequirement struct {
DependencyID string
}
DependencyRequirement is a driver's declared workspace dependency.
type DependencyRequirer ¶
type DependencyRequirer interface {
RequiredDependency() (depID string)
}
DependencyRequirer is implemented by drivers whose CLI is provisioned as a managed workspace dependency. The CLI version is not part of the declaration: the dependency manager installs whatever version the user asks for (latest by default), and the runtime's own handshake warns when the copy it talks to drifts from its protocol snapshot.
type Driver ¶
type Driver interface {
// RuntimeType is the thread runtime type this driver serves (e.g. "codex").
RuntimeType() string
// Prompt runs one turn. Stream events flow through input.Sink while the
// turn runs; the returned result carries the transcript for persistence.
// A context cancellation is an interrupt: the driver must stop the turn
// and still return the partial transcript it has.
Prompt(ctx context.Context, input PromptInput) (PromptResult, error)
}
Driver runs turns for one external agent runtime type.
type Drivers ¶
type Drivers []Driver
func (Drivers) ModelCatalog ¶
func (drivers Drivers) ModelCatalog(ctx context.Context, runtimeType string, request ModelCatalogRequest) (ModelCatalog, error)
func (Drivers) PurgeBotAgentAuth ¶
func (Drivers) RequiredDependencies ¶
func (drivers Drivers) RequiredDependencies() map[string]DependencyRequirement
RequiredDependencies maps runtime type to the dependency each driver declares. Drivers without a declaration (the generic ACP runtime) are omitted.
func (Drivers) ResetBotAgent ¶
type EventSink ¶
type EventSink interface {
EmitStreamEvent(event.StreamEvent)
}
EventSink receives stream events during a turn.
type EventSinkFunc ¶
type EventSinkFunc func(event.StreamEvent)
EventSinkFunc adapts a function to EventSink.
func (EventSinkFunc) EmitStreamEvent ¶
func (f EventSinkFunc) EmitStreamEvent(ev event.StreamEvent)
type Failure ¶ added in v0.21.0
type Failure struct {
Kind FailureKind
Err error
}
Failure is a runtime failure the user can act on. Like a public error it hides Err from errors.Is and errors.As: callers branch on Kind, and a cancellation or sentinel beneath it does not change how they treat it. Diagnostics still reach Err through Cause.
type FailureKind ¶ added in v0.21.0
type FailureKind int
FailureKind names a runtime failure the user can act on. The application chooses the public error for each kind.
const ( FailureUnavailable FailureKind = iota + 1 // FailureAuthRequired: the runtime needs its account signed in or its // authentication configured. FailureAuthRequired // FailureSessionResumeFailed: the runtime could not resume the session's // native thread; a later turn may. FailureSessionResumeFailed // FailureGoalRequiresDefaultMode: a goal turn was asked for outside the // runtime's default mode. FailureGoalRequiresDefaultMode FailureModeUnavailable // FailureControlFailed: a runtime control the turn needed failed. FailureControlFailed // FailureCredential: the Agent's credential cannot be used. Failure.Err // is the agentcredential error that says why. FailureCredential // FailureCredentialBusy: the credential cannot change while a turn runs // on it. FailureCredentialBusy // FailureUsageLimited: the runtime's account has used up its usage // allowance; a later turn may run once it resets. FailureUsageLimited )
type Goal ¶
type Goal = turn.RuntimeGoal
type GoalProvider ¶
type GoalProvider interface {
Goal(context.Context, PromptInput) (*Goal, error)
ControlGoal(context.Context, PromptInput, string) error
}
GoalProvider requires structured state, pause/clear controls and resumption through an admitted /goal resume turn. A runtime with command-only goals exposes its native command via CommandProvider instead.
type Image ¶
type Image struct {
MimeType string
// Data is the raw image bytes (not base64, not a data URL).
Data []byte
}
Image is an inline prompt image.
type Launcher ¶
type Launcher struct {
// Path is the absolute path the driver must execute.
Path string
// Version is the observed CLI version, empty when unknown.
Version string
Source LauncherSource
}
Launcher is a resolved CLI executable inside the bot workspace.
type LauncherResolver ¶
type LauncherResolver interface {
ResolveLauncher(ctx context.Context, botID, depID string) (Launcher, error)
}
LauncherResolver picks the CLI copy a driver should execute: the managed copy first, then the image toolkit, then PATH. Discovery is read-only; a missing dependency yields a *DependencyMissingError.
type LauncherSource ¶
type LauncherSource string
LauncherSource says which copy of a CLI a Launcher points at.
const ( LauncherSourceManaged LauncherSource = "managed" LauncherSourceToolkit LauncherSource = "toolkit" LauncherSourcePath LauncherSource = "path" )
type Mode ¶
type Mode = turn.RuntimeMode
type ModeProvider ¶
type ModeState ¶
type ModeState = turn.RuntimeModeState
type ModelCatalog ¶
type ModelCatalog struct {
Models []ModelOption `json:"models"`
ConfiguredModelID string `json:"configured_model_id,omitempty"`
ConfiguredReasoningEffort string `json:"configured_reasoning_effort,omitempty"`
}
ModelCatalog is the runtime-owned model picker contract.
type ModelCatalogProvider ¶
type ModelCatalogProvider interface {
ModelCatalog(ctx context.Context, request ModelCatalogRequest) (ModelCatalog, error)
}
type ModelCatalogRequest ¶
type ModelCatalogRequest struct {
BotID string
BotAgentID string
ProjectPath string
// ModelID selects the model whose defaults the picker is displaying.
ModelID string
// Preference validation needs only capabilities; resolved defaults are a
// separate, optional observation for display.
ResolveDefaults bool
}
type ModelOption ¶
type ModelOption struct {
// ResolvedModelID preserves a runtime-advertised full model name for
// validation without duplicating its alias in the model picker.
ResolvedModelID string `json:"resolved_model_id,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Default bool `json:"default,omitempty"`
DefaultReasoningEffort string `json:"default_reasoning_effort,omitempty"`
ReasoningEfforts []ReasoningEffortOption `json:"reasoning_efforts"`
UnavailablePermissionModes []string `json:"unavailable_permission_modes,omitempty"`
}
ModelOption is one runtime model and its supported reasoning options.
type PlanModeProvider ¶
type PlanModeProvider interface {
PlanMode(context.Context, PromptInput) (ModeState, error)
SetPlanMode(context.Context, PromptInput, string) (ModeState, error)
}
PlanModeProvider declares planning independently of tool permission presets.
type PromptInput ¶
type PromptInput struct {
Steering Steering
BotID string
BotAgentID string
ChatID string
ThreadID string
RunID string
RouteID string
// Prompt is the user's message text.
Prompt string
// ContextMarkdown is the assembled Memoh context document for this turn.
ContextMarkdown string
// Images are inline user images, raw bytes with MIME types.
Images []Image
// ModelID and ReasoningEffort are per-turn overrides; empty means the
// runtime's configured default.
ModelID string
ReasoningEffort string
// SessionMode is the Memoh session mode driving this turn (chat,
// schedule, ...); tool-gateway policy keys on it.
SessionMode string
// CurrentPlatform, ReplyTarget, and ConversationType describe the surface
// the turn is answering; they flow into the trusted tool identity.
CurrentPlatform string
ReplyTarget string
ConversationType string
// Command is an exact agent-command selector matched at admission; a
// runtime that advertises commands must re-validate it at dispatch.
// Runtimes without a command vocabulary ignore it.
Command string
// CommandArgs is the original user input after the runtime command selector.
CommandArgs string
// ForceFreshRuntime asks the driver to abandon any resumable runtime
// session and start this turn on a fresh one.
ForceFreshRuntime bool
// AttachmentReferences names non-image attachments already staged for the
// runtime (paths or references it may read through its tools).
AttachmentReferences []string
// CanFallbackImagesToFiles permits a runtime without image input to
// receive the images as workspace files instead of failing the turn.
CanFallbackImagesToFiles bool
// ToolOutputLimit bounds tool output the runtime feeds back into its
// context through Memoh-owned tool surfaces.
ToolOutputLimit contextlimit.ToolOutputLimit
// RuntimeMetadata is the session's runtime metadata map. Drivers read and
// persist their own keys through it (e.g. the codex thread id).
RuntimeMetadata map[string]any
// ContextURI names the Memoh-owned context document. The budget and tool
// exchange policy flow into runtime-hosted Memoh tools.
ContextURI string
ContextBudgetMaxTokens int
ContextToolExchangePolicy *contextfrag.ToolExchangePolicy
// RuntimeOwnerAccountID is the account whose workspace authority the
// runtime executes under.
RuntimeOwnerAccountID string
// ChannelIdentityID identifies the acting user for tool attribution.
ChannelIdentityID string
// SessionToken authenticates runtime-side calls back into Memoh.
SessionToken string //nolint:gosec // session credential material, not a hardcoded secret
// ToolHTTPURL is the Memoh tool gateway (HTTP MCP) base URL, empty when
// the gateway is unavailable.
ToolHTTPURL string
// CanRequestUserInput reports whether the surface driving this turn can
// deliver interactive decisions such as approvals and ask_user.
CanRequestUserInput bool
// Sink receives stream events as the turn runs. Never nil.
Sink EventSink
}
PromptInput is one turn's worth of work for a Driver.
type PromptResult ¶
type PromptResult struct {
// Notices are UI-only runtime facts collected across startup and execution.
// They are persisted with the round, independently of the model transcript.
Notices []event.Notice
// SteerInputIDs correspond to the user messages in Output, in arrival order.
SteerInputIDs []string
// Output is the transcript to persist, in provider-message form.
Output []sdk.Message
// Text is the final assistant message text (for surfaces that report a
// plain-text result, e.g. schedule runs).
Text string
// Usage is the turn's token usage, when the runtime reported it.
Usage *sdk.Usage
// StopReason is the runtime's terminal stop reason, normalized to the
// event vocabulary where possible.
StopReason string
// AgentTurnID is the runtime's own identifier for this turn (e.g. the
// codex turn id); it anchors turn-level operations such as forking.
AgentTurnID string
// FinalTurnAnchorOnly restricts a multi-turn transcript to its final fork boundary.
FinalTurnAnchorOnly bool
// TurnCompleted reports whether the runtime finished the turn (as opposed
// to an interrupt or failure part-way).
TurnCompleted bool
// PublishHead asks the application to publish a completed round's run ID
// with its messages so warm runtimes can detect another process's progress.
PublishHead bool
// RoundMetadata carries driver-owned keys merged into the round's
// assistant-message metadata (provenance such as the ACP agent id).
RoundMetadata map[string]any
// RuntimeMetadata carries driver-owned metadata updates to merge back
// into the session's runtime metadata (e.g. a newly created thread id).
// Nil means no changes.
RuntimeMetadata map[string]any
}
PromptResult is the durable outcome of one turn.
type ReasoningEffortOption ¶
type ReasoningEffortOption struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
ReasoningEffortOption is one runtime-defined reasoning level.
type RoundRollbackHandler ¶
type RoundRollbackHandler interface {
OnRoundRolledBack(ctx context.Context, botID, threadID string)
}
RoundRollbackHandler is implemented by drivers that must repair runtime state when a completed turn's round definitively rolled back (the runtime remembers a turn the visible history lost). Drivers whose durable state is authoritative on their own side simply omit it and the divergence is logged.
type SteerInput ¶
type Steering ¶
type Steering interface {
Enable(context.Context) error
Wake() <-chan struct{}
Next(context.Context) (SteerInput, bool, error)
Accepted(context.Context, string, int) error
Close(context.Context) error
}
Steering is the application-owned, fenced queue for the current run. Drivers enable it only after their native turn is ready to accept same-turn input. Accepted means the native runtime confirmed delivery; transcript persistence remains part of the external runtime's ordinary round commit.
type ThreadForker ¶
type ThreadForker interface {
ForkThread(ctx context.Context, botID, botAgentID string, runtimeMetadata map[string]any, lastTurnID string) (map[string]any, error)
}
ThreadForker is implemented by runtimes with native conversation forking. ForkThread derives a new runtime-side conversation from the session described by runtimeMetadata, cut after lastTurnID when non-empty (empty forks at the runtime head). It returns the runtime-metadata delta that identifies the fork — only the driver-owned session keys — which the caller overlays on the source session's runtime metadata.
type TranscriptRecorder ¶
type TranscriptRecorder struct {
// contains filtered or unexported fields
}
TranscriptRecorder folds stream events into the persisted round transcript. It is not capped by the UI event buffer.
func NewTranscriptRecorder ¶
func NewTranscriptRecorder(limits ...contextlimit.ToolOutputLimit) *TranscriptRecorder
NewTranscriptRecorder creates an empty transcript recorder.
func (*TranscriptRecorder) Add ¶
func (b *TranscriptRecorder) Add(ev event.StreamEvent)
Add folds one event into the transcript in arrival order.
func (*TranscriptRecorder) AddUser ¶
func (b *TranscriptRecorder) AddUser(text string)
AddUser inserts a confirmed mid-turn input between the surrounding output.
func (*TranscriptRecorder) Messages ¶
func (b *TranscriptRecorder) Messages(fallbackText string) []sdk.Message
Messages finalizes and returns the transcript. fallbackText is used when the runtime never streamed a text delta (some agents only return final text). Safe to call more than once; finalization is idempotent on the accumulated state.
type VersionObserver ¶
type VersionObserver interface {
ObserveLauncherVersion(ctx context.Context, botID, depID, version string)
}
VersionObserver lets drivers feed the CLI version reported during the protocol handshake back into the resolver's cache.