Documentation
¶
Overview ¶
Package code defines the Agent interface implemented by the in-process wingman backend (sub-package wingman) and the external ACP subprocess backend (sub-package acp). Higher layers (server, TUI) hold one active Agent and swap it on user selection.
The interface is session-id-explicit: every per-conversation operation takes a session id allocated via [Agent.NewSession]. Backends own the session catalog (wingman: on-disk; acp: the external server). Switching the active agent closes the prior one.
Index ¶
- Constants
- Variables
- func ModelName(id string) string
- func SaveAgents(defs []AgentDef) error
- func SessionIDFromContext(ctx context.Context) string
- func SessionsDir(workingDir string) string
- func WithSessionID(ctx context.Context, sid string) context.Context
- type Agent
- type AgentDef
- type Mode
- type Model
- type SessionInfo
- type UI
- type Workspace
- func (w *Workspace) Checkpoints() ([]rewind.Checkpoint, error)
- func (w *Workspace) Close()
- func (w *Workspace) Commit(msg string) error
- func (w *Workspace) Diagnostics(ctx context.Context) map[string][]lsp.Diagnostic
- func (w *Workspace) Diffs() ([]rewind.FileDiff, error)
- func (w *Workspace) InitMCP(ctx context.Context) error
- func (w *Workspace) IsGitRepo() bool
- func (w *Workspace) ManagedTools() (mcpTools, lspTools []tool.Tool)
- func (w *Workspace) MemoryContent() string
- func (w *Workspace) Restore(hash string) error
- func (w *Workspace) SyncProjectMode()
- func (w *Workspace) WarmUp()
Constants ¶
const BuiltinAgentName = "wingman"
BuiltinAgentName identifies the in-process wingman backend. It's reserved — user-defined AgentDef entries cannot share this name.
Variables ¶
var AvailableModels = []Model{
{ID: "claude-sonnet-4-6", Name: "Claude Sonnet 4.6"},
{ID: "claude-sonnet-4-5", Name: "Claude Sonnet 4.5"},
{ID: "gpt-5.5", Name: "GPT 5.5"},
{ID: "gpt-5.4", Name: "GPT 5.4"},
{ID: "gpt-5.3-codex", Name: "GPT 5.3 Codex"},
{ID: "gpt-5.2-codex", Name: "GPT 5.2 Codex"},
{ID: "claude-opus-4-8", Name: "Claude Opus 4.8"},
{ID: "claude-opus-4-7", Name: "Claude Opus 4.7"},
{ID: "claude-opus-4-6", Name: "Claude Opus 4.6"},
{ID: "claude-opus-4-5", Name: "Claude Opus 4.5"},
}
AvailableModels lives here (not pkg/agent) so the agent runtime stays provider-agnostic.
Functions ¶
func SaveAgents ¶ added in v0.7.0
SaveAgents writes the agents config. Used by the desktop app's settings page. Creating the parent directory is handled here so callers don't have to.
func SessionIDFromContext ¶ added in v0.7.0
SessionIDFromContext returns the session id set by WithSessionID, or "" when none is present.
func SessionsDir ¶ added in v0.6.9
func WithSessionID ¶ added in v0.7.0
WithSessionID stamps sid onto ctx so downstream tool calls can recover it via SessionIDFromContext. The coder agent does this in Send so elicitation UIs can route prompts back to the right session.
Types ¶
type Agent ¶
type Agent interface {
// Name returns a stable identifier for this backend. For wingman
// it's [BuiltinAgentName]; for ACP backends it's the [AgentDef.Name]
// that constructed them.
Name() string
// Workspace returns the shared workspace the agent is rooted at.
// Same instance regardless of which backend is active — its
// Rewind / LSP / MCP subsystems are wingman-only side-effects but
// the workspace itself (RootPath, Skills, MemoryPath) is shared.
Workspace() *Workspace
// Models reports the available model catalog and the currently
// selected id. Cached read — no RPC. Empty list = the backend
// doesn't expose a model selector.
Models() (available []Model, current string)
// SetModel switches the active model. Returns [errors.ErrUnsupported]
// when the backend has no model selector.
SetModel(ctx context.Context, id string) error
// Effort reports the current effort id and the available values.
// Empty options = effort selector not supported.
Effort() (current string, options []string)
SetEffort(ctx context.Context, value string) error
// Modes reports the operating modes the backend exposes for the given
// session and the currently active mode id. An empty list means the
// backend has no mode selector (the UI hides the picker). Cached read —
// no RPC.
Modes(sessionID string) (available []Mode, current string)
// SetMode switches the session's active operating mode. Returns
// [errors.ErrUnsupported] when the backend has no mode selector, or an
// error when modeID is not one of the advertised modes.
SetMode(ctx context.Context, sessionID, modeID string) error
// ListSessions returns the backend's saved session catalog scoped
// to the workspace.
ListSessions(ctx context.Context) ([]SessionInfo, error)
// NewSession allocates a fresh session and returns its id. For
// wingman this is a locally minted UUID; for ACP it's the id
// returned by the ACP server's session/new.
NewSession(ctx context.Context) (string, error)
// LoadSession restores an existing session into memory so
// [Messages] / [Usage] reflect its transcript and subsequent
// [Send] calls continue it.
LoadSession(ctx context.Context, id string) error
// DeleteSession removes a session from the catalog. Returns
// [errors.ErrUnsupported] when the backend can't delete (ACP).
DeleteSession(ctx context.Context, id string) error
// Messages returns the transcript for a session id. Returns nil
// for sessions the backend hasn't loaded.
Messages(sessionID string) []agent.Message
Usage(sessionID string) agent.Usage
// Send drives a turn. Returns nil when a turn is already running
// for this session id. The iterator yields per-update chunks; the
// implementation commits the assembled message(s) into the
// session's transcript by end-of-turn.
Send(ctx context.Context, sessionID string, input []agent.Content) iter.Seq2[agent.Message, error]
// Cancel aborts an in-flight Send for the session id. No-op when
// no turn is in flight or the id is unknown.
Cancel(sessionID string)
// Close releases all resources (subprocess, IO, file handles).
// Idempotent.
Close() error
}
Agent is the swappable backend that drives chat turns and owns its own session catalog + transcript storage. The two concrete implementations live in sub-packages wingman and acp.
All methods are safe for concurrent use across distinct session ids. Concurrent calls on the SAME session id are NOT supported — callers must ensure only one Send is in flight per session at a time.
type AgentDef ¶ added in v0.7.0
type AgentDef struct {
// Name is the user-facing label and the key used by [*Agent.SetAgent].
Name string `json:"name"`
// Command is the absolute path (or PATH-lookup name) of the ACP
// server binary to spawn.
Command string `json:"command"`
// Args are extra arguments passed to the subprocess.
Args []string `json:"args,omitempty"`
// Env adds/overrides environment variables for the subprocess. The
// parent process's environment is inherited.
Env map[string]string `json:"env,omitempty"`
}
AgentDef describes an external ACP-server backend. The selected backend is launched as a subprocess; its stdio carries the ACP JSON-RPC stream. Wingman talks to it as a client (analogous to Zed / other ACP hosts).
Defs are loaded from ~/.wingman/agents.json by LoadAgents; the built-in wingman backend (name = BuiltinAgentName) is always available and doesn't need a config entry.
func LoadAgents ¶ added in v0.7.0
func LoadAgents() []AgentDef
LoadAgents reads ~/.wingman/agents.json and returns the configured external coder backends. A missing or unreadable file means "only the built-in wingman backend is available." Entries without a Name or Command are dropped silently so a malformed line doesn't break the selection list.
type Mode ¶ added in v0.8.0
Mode is one operating mode a backend can run a session in — typically an approval/sandbox or planning level. It mirrors ACP's SessionMode, and is what the UI's mode picker is populated from.
type SessionInfo ¶ added in v0.7.0
SessionInfo is one row in the backend's session catalog (wingman's on-disk list or the ACP server's ListSessions response).
type UI ¶
type UI interface {
Ask(ctx context.Context, message string) (string, error)
Confirm(ctx context.Context, message string) (bool, error)
}
UI is the elicitation hook a frontend provides for tool ask/confirm prompts. Pass nil to NewAgent for safe defaults (Confirm → true, Ask → ""). The session id that triggered the prompt is on ctx via SessionIDFromContext for UIs that route prompts per session.
type Workspace ¶ added in v0.6.9
type Workspace struct {
Root *os.Root
RootPath string
MemoryPath string
ScratchPath string
Skills []skill.Skill
MCP *mcp.Manager
// LSP and Rewind are set by WarmUp; nil for unsupported workspaces.
LSP *lsp.Manager
Rewind *rewind.Manager
// contains filtered or unexported fields
}
func NewWorkspace ¶ added in v0.6.9
func (*Workspace) Checkpoints ¶ added in v0.6.9
func (w *Workspace) Checkpoints() ([]rewind.Checkpoint, error)
func (*Workspace) Diagnostics ¶ added in v0.6.9
func (*Workspace) IsGitRepo ¶ added in v0.6.9
IsGitRepo is re-evaluated each call so callers can react to mid-session `git init` / `rm -rf .git`.
func (*Workspace) ManagedTools ¶ added in v0.7.0
ManagedTools snapshots MCP + LSP tools under the workspace mutex so a concurrent WarmUp / InitMCP can't race the per-turn tool() callback. Exported for use by the wingman sub-package, which builds each session's tool set from baseTools + ManagedTools().
func (*Workspace) MemoryContent ¶ added in v0.6.9
MemoryContent renders an index of the memory dir for injection into the system prompt: one line per `*.md` file with a short hook. The hook is the file's frontmatter `description`, falling back to the first non-empty line of the body (heading markers stripped). Cached by per-file mtime so repeat turns don't re-read.
func (*Workspace) SyncProjectMode ¶ added in v0.6.9
func (w *Workspace) SyncProjectMode()
SyncProjectMode rebuilds LSP when the working dir's git status flips. No-op on unsupported workspaces.
func (*Workspace) WarmUp ¶ added in v0.6.9
func (w *Workspace) WarmUp()
WarmUp probes the workspace and initializes Rewind/LSP. Idempotent. Resulting modes:
- supported git repo → Rewind set, LSP set, lspTools set
- supported scratch → Rewind set, LSP nil, lspTools nil
- unsupported (huge) → Rewind nil, LSP nil