Documentation
¶
Overview ¶
Package agentapi is the provider-neutral contract between the uam web service and the structured provider integrations (the Copilot SDK and the OpenCode server). It carries only what the web interface needs; provider specifics stay inside the adapters.
Lifetime rules every implementation must follow:
- A Conversation belongs to the web service, never to an HTTP request or a browser connection. The context passed to a method bounds only that call; turns keep running after the call returns.
- Events are delivered through the EventSink supplied at Open until Close returns. Emit never blocks on browsers, so adapters may call it from their event goroutines.
- Adapters never replay a prompt, never pick "the latest" conversation, and never answer a pending interaction on the user's behalf. Only the web service may, for permission requests of a Task in yolo mode.
Index ¶
- Constants
- Variables
- type Answer
- type Capabilities
- type Context
- type ContextSize
- type Conversation
- type Delta
- type Event
- type EventKind
- type EventSink
- type FileDiff
- type History
- type Interaction
- type InteractionKind
- type InteractionState
- type Item
- type ItemKind
- type Model
- type OpenRequest
- type Option
- type Provider
- type Question
- type Subagent
- type SubagentStatus
- type ToolCall
- type ToolStatus
- type Turn
- type TurnState
Constants ¶
const ( ProviderCopilot = "copilot" ProviderOpenCode = "opencode" )
Provider names match the uam agent names used in sessions.json.
const DeliverySteer = "steer"
DeliverySteer marks a user item that arrived as a steer.
Variables ¶
var ( // ErrConversationNotFound reports that an exact conversation ID does not // exist in the provider's store. The caller must not create a replacement. ErrConversationNotFound = errors.New("provider conversation not found") // ErrInteractionGone reports that the provider no longer considers an // interaction pending (answered elsewhere, expired, or the turn ended). ErrInteractionGone = errors.New("interaction is no longer pending") // ErrUnsupported reports an operation the provider does not offer. ErrUnsupported = errors.New("operation not supported by this provider") // ErrClosed reports use of a Conversation after Close or after its // provider runtime exited. ErrClosed = errors.New("conversation is closed") // ErrSubmissionUncertain wraps a Send failure after which the provider may // or may not have accepted the prompt. The caller must surface the // uncertainty and must not resubmit automatically. ErrSubmissionUncertain = errors.New("prompt submission outcome is unknown") // ErrBusy reports that the provider rejected a prompt because a turn is // still running. ErrBusy = errors.New("a turn is already running") )
Functions ¶
This section is empty.
Types ¶
type Answer ¶
type Answer struct {
Decision string `json:"decision,omitempty"`
Answers [][]string `json:"answers,omitempty"`
Reject bool `json:"reject,omitempty"`
// Auto marks an answer UAM gave on its own (yolo), not one a person chose.
// It is internal: a client can never set it.
Auto bool `json:"-"`
}
Answer responds to an interaction. Permission answers set Decision; question answers set Answers (one slice per Question, holding chosen choices and/or a custom text). Reject declines a question without answering it.
type Capabilities ¶
type Capabilities struct {
Cancel bool `json:"cancel"`
Permissions bool `json:"permissions"`
Questions bool `json:"questions"`
// SessionDiff is true when the provider records per-conversation file
// changes (Conversation.Diff). Workspace Git diffs are separate.
SessionDiff bool `json:"session_diff"`
History bool `json:"history"`
// ContextSize is the per-Task context-tier exception to provider parity.
ContextSize bool `json:"context_size"`
}
Capabilities advertises what an adapter really supports. The UI hides or disables controls for unsupported operations instead of pretending.
type ContextSize ¶
ContextSize is a selectable provider tier and its prompt token budget.
type Conversation ¶
type Conversation interface {
// ID returns the exact provider conversation ID to persist.
ID() string
// History returns the provider-recorded transcript and subagents of a
// reopened conversation.
History(ctx context.Context) (History, error)
// Send submits one user prompt that starts a turn and returns once the
// provider accepted or rejected it. The turn continues asynchronously and
// is reported through events. Ambiguous failures wrap
// ErrSubmissionUncertain. A provider that would otherwise fold a prompt
// sent during a turn into that turn must run it after the turn instead.
Send(ctx context.Context, prompt string) error
// Steer adds prompt to the turn that is running, before the provider's
// next model call, and returns once the provider accepted or rejected
// it. The caller steers only while a turn runs; it never starts a turn.
// An accepted steer cannot be withdrawn. When the provider uses it, its
// user item carries Delivery DeliverySteer; when the turn ends without
// the provider using it, the adapter emits an ItemNotice saying so.
// Ambiguous failures wrap ErrSubmissionUncertain and are never resent.
// ErrUnsupported means the provider cannot steer.
Steer(ctx context.Context, prompt string) error
// Cancel aborts the current turn. The conversation stays open.
Cancel(ctx context.Context) error
// CancelSubagent stops only the exact agent instance. Its final status
// arrives through EventSubagent; the parent and siblings stay running.
CancelSubagent(ctx context.Context, agentID string) error
// Respond answers a pending interaction. It returns ErrInteractionGone
// when the provider no longer considers the interaction pending.
Respond(ctx context.Context, interactionID string, answer Answer) error
// Diff returns provider-recorded file changes for this conversation, or
// ErrUnsupported when Capabilities().SessionDiff is false.
Diff(ctx context.Context) ([]FileDiff, error)
// SetModel applies the complete selection from the next turn on. Empty
// effort means Default; contextSize is default or a catalog tier ID.
// The caller validates the selection and never switches during a turn.
SetModel(ctx context.Context, model, effort, contextSize string) error
// Close disconnects from the conversation without deleting it. Pending
// interactions end without an answer being fabricated.
Close(ctx context.Context) error
}
Conversation is one open provider conversation owned by the web service.
type Delta ¶
type Delta struct {
ItemID string `json:"item_id"`
Kind ItemKind `json:"kind"`
Text string `json:"text"`
// AgentID is the item's agent, as in Item.AgentID.
AgentID string `json:"agent_id,omitempty"`
}
Delta appends Text to the item ItemID of kind Kind.
type Event ¶
type Event struct {
Kind EventKind
Item *Item
Delta *Delta
Turn *Turn
Interaction *Interaction
Subagent *Subagent
Context *Context
// Error is the sanitized reason for EventExit.
Error string
// Title is the untrusted provider title for EventTitle.
Title string
}
Event is one adapter notification. Exactly one payload matches Kind.
type EventKind ¶
type EventKind string
EventKind discriminates Event payloads.
const ( // EventItem upserts a transcript item; a later event with the same // AgentID and Item.ID replaces the earlier one (partial tool updates never // duplicate). EventItem EventKind = "item" // EventDelta appends streamed text to Item.ID, creating it when absent. EventDelta EventKind = "delta" // EventTurn reports a turn state transition. EventTurn EventKind = "turn" // EventInteraction upserts a permission request or question by ID. EventInteraction EventKind = "interaction" // EventExit reports that the conversation or its runtime became unusable // (process exit, event-stream failure). The conversation is then closed. EventExit EventKind = "exit" // EventTitle reports the provider-generated conversation title in // Event.Title. EventTitle EventKind = "title" // EventSubagent upserts a subagent record by Subagent.ID. EventSubagent EventKind = "subagent" // EventContext reports main-agent context usage; it is never persisted. EventContext EventKind = "context" )
type EventSink ¶
type EventSink interface {
Emit(Event)
}
EventSink receives adapter events. Implementations must not block.
type FileDiff ¶
type FileDiff struct {
Path string `json:"path"`
Status string `json:"status,omitempty"`
Additions int `json:"additions"`
Deletions int `json:"deletions"`
Before string `json:"before,omitempty"`
After string `json:"after,omitempty"`
Patch string `json:"patch,omitempty"`
}
FileDiff is one provider-recorded file change. Before/After hold full file text when available; Patch holds a unified diff when that is what the provider returns.
type History ¶
type History struct {
// Items is the transcript, oldest first. Subagent items carry AgentID.
Items []Item
// Subagents are the subagent records the recorded events allow to
// rebuild.
Subagents []Subagent
}
History is a reopened conversation's provider-recorded state.
type Interaction ¶
type Interaction struct {
ID string `json:"id"`
Kind InteractionKind `json:"kind"`
Title string `json:"title"`
// Detail is display text: command, paths, patterns, or tool arguments.
Detail string `json:"detail,omitempty"`
// Options lists permission decisions the provider accepts. Answer.Decision
// must be one of these IDs.
Options []Option `json:"options,omitempty"`
// Questions lists question prompts. Answer.Answers must have one entry per
// question.
Questions []Question `json:"questions,omitempty"`
State InteractionState `json:"state"`
Resolution string `json:"resolution,omitempty"`
Time time.Time `json:"time"`
// AgentID names the subagent that asked, when the provider says so.
AgentID string `json:"agent_id,omitempty"`
}
Interaction is a provider request that needs the user.
type InteractionKind ¶
type InteractionKind string
InteractionKind separates permission requests from questions.
const ( InteractionPermission InteractionKind = "permission" InteractionQuestion InteractionKind = "question" )
type InteractionState ¶
type InteractionState string
InteractionState is the lifecycle of one interaction.
const ( InteractionPending InteractionState = "pending" InteractionAnswered InteractionState = "answered" InteractionRejected InteractionState = "rejected" // InteractionExpired means the provider withdrew the request (turn ended, // timeout, conversation closed) without an answer from this service. InteractionExpired InteractionState = "expired" )
type Item ¶
type Item struct {
ID string `json:"id"`
Kind ItemKind `json:"kind"`
Text string `json:"text,omitempty"`
Tool *ToolCall `json:"tool,omitempty"`
Time time.Time `json:"time"`
// AgentID is empty for the main agent, otherwise the exact subagent
// instance ID from the provider.
AgentID string `json:"agent_id,omitempty"`
// Delivery is DeliverySteer on a user item that joined a running turn
// as a steer, and empty otherwise.
Delivery string `json:"delivery,omitempty"`
}
Item is one transcript entry. Text is untrusted provider output.
type Model ¶
type Model struct {
ID string `json:"id"`
Name string `json:"name"`
Efforts []string `json:"efforts"`
ContextSizes []ContextSize `json:"context_sizes"`
}
Model is one selectable model.
type OpenRequest ¶
type OpenRequest struct {
// SessionID is the uam managed-session ID (a UUID).
SessionID string
// ConversationID is the exact provider conversation ID, or "" to create.
ConversationID string
// Workdir is the absolute, canonical project directory.
Workdir string
// Title is the user-visible session name.
Title string
// Model is the model ID for a new conversation; "" means the provider
// default. It is ignored on reopen, so reopening never changes the model.
Model string
// Effort and ContextSize apply only when creating a conversation.
Effort string
ContextSize string
// Events receives every event for this conversation until Close returns.
Events EventSink
}
OpenRequest identifies the managed session and its project.
type Option ¶
type Option struct {
ID string `json:"id"`
Label string `json:"label"`
// Reject marks decisions that deny the request.
Reject bool `json:"reject,omitempty"`
// AllowOnce marks the decision that allows this one request and nothing
// more. A provider leaves it off when its policy says a person must
// decide: the web service's yolo mode answers only with this option.
AllowOnce bool `json:"allow_once,omitempty"`
}
Option is one permission decision.
type Provider ¶
type Provider interface {
// Name returns ProviderCopilot or ProviderOpenCode.
Name() string
// DisplayName returns a human label, e.g. "GitHub Copilot".
DisplayName() string
Capabilities() Capabilities
// Check reports whether the installed runtime is present and compatible,
// without starting a conversation. The error text is shown to the user.
Check(ctx context.Context) error
// Models returns the models the signed-in account can select. An empty
// list or ErrUnsupported means only the provider default is offered. A
// provider that lists models must support Conversation.SetModel.
Models(ctx context.Context) ([]Model, error)
// Open creates a conversation when req.ConversationID is empty, otherwise
// reopens exactly that conversation (ErrConversationNotFound when it no
// longer exists). ctx bounds the open call only.
Open(ctx context.Context, req OpenRequest) (Conversation, error)
// Shutdown closes every conversation and stops runtimes this provider
// started. It never stops a runtime it did not start.
Shutdown(ctx context.Context) error
}
Provider creates and reopens conversations for one provider runtime.
type Question ¶
type Question struct {
Text string `json:"text"`
Header string `json:"header,omitempty"`
Choices []string `json:"choices,omitempty"`
// Multiple allows selecting more than one choice.
Multiple bool `json:"multiple,omitempty"`
// Custom allows a free-form answer.
Custom bool `json:"custom"`
}
Question is one prompt inside a question interaction.
type Subagent ¶
type Subagent struct {
ID string `json:"id"`
Model string `json:"model,omitempty"`
Effort string `json:"effort,omitempty"`
// ParentToolCallID is the ID of the tool call item that spawned it.
ParentToolCallID string `json:"parent_tool_call_id,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Status SubagentStatus `json:"status"`
// Error is the provider's reason for SubagentFailed.
Error string `json:"error,omitempty"`
StartedAt time.Time `json:"started_at,omitzero"`
EndedAt time.Time `json:"ended_at,omitzero"`
}
Subagent is one agent instance the main agent delegated work to. Its transcript items carry AgentID == ID.
type SubagentStatus ¶
type SubagentStatus string
SubagentStatus is the lifecycle of one subagent.
const ( SubagentRunning SubagentStatus = "running" SubagentCompleted SubagentStatus = "completed" SubagentFailed SubagentStatus = "failed" SubagentCancelled SubagentStatus = "cancelled" )
func (SubagentStatus) Terminal ¶
func (s SubagentStatus) Terminal() bool
Terminal reports whether s is final. Once a subagent is terminal, later updates for it are ignored: a provider may report the end more than once.
type ToolCall ¶
type ToolCall struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Status ToolStatus `json:"status"`
Input string `json:"input,omitempty"`
Output string `json:"output,omitempty"`
}
ToolCall describes one tool invocation. Input and Output are display text (JSON or plain) that adapters truncate to a bounded size.
type ToolStatus ¶
type ToolStatus string
ToolStatus is the lifecycle of one tool call.
const ( ToolPending ToolStatus = "pending" ToolRunning ToolStatus = "running" ToolCompleted ToolStatus = "completed" ToolFailed ToolStatus = "failed" )
type Turn ¶
type Turn struct {
State TurnState `json:"state"`
// Error is the sanitized provider error for TurnFailed.
Error string `json:"error,omitempty"`
// Model is the model the provider reported for this turn, when known.
Model string `json:"model,omitempty"`
}
Turn reports a turn transition with evidence from the provider.