Documentation
¶
Index ¶
- Variables
- func MessageToAPI(m Message) api.Message
- func MessagesToAPI(messages []Message) []api.Message
- type Conversation
- type ConversationList
- type ConversationListItem
- type Message
- type Repo
- type Thread
- func (t *Thread) Append(msgs ...Message)
- func (t *Thread) ApplySystemPrompt()
- func (t *Thread) ConfirmTools(confirmations []api.ToolConfirmation, sandboxCfg *sandbox.ToolSandboxConfig) (allRejected bool, err error)
- func (t *Thread) IsDirty(id uint) bool
- func (t *Thread) Persist() error
- func (t *Thread) Reload() error
- func (t *Thread) RequestCompletion(ctx context.Context, temperature float32, maxTokens int, ...) (<-chan api.Event, context.CancelFunc)
- func (t *Thread) SetAgent(agent *api.Agent)
- func (t *Thread) SetContent(i int, content string)
- func (t *Thread) SetMessage(i int, msg Message)
- func (t *Thread) SetParts(i int, parts []api.ContentPart)
Constants ¶
This section is empty.
Variables ¶
var LocalAgentsFileNames = []string{"AGENT.md", "AGENTS.md", "CLAUDE.md"}
LocalAgentsFileNames contains the filenames to check for local agent instructions.
Functions ¶
func MessageToAPI ¶
func MessagesToAPI ¶
Types ¶
type Conversation ¶
type Conversation struct {
ID uint `gorm:"primaryKey"`
ShortName sql.NullString
Title string
Agent sql.NullString
Workspace sql.NullString
SelectedRootID *uint
SelectedRoot *Message `gorm:"foreignKey:SelectedRootID"`
RootMessages []Message `gorm:"-:all"`
LastMessageAt time.Time
}
type ConversationList ¶
type ConversationList struct {
Total int
Items []ConversationListItem
HasMore bool
}
type ConversationListItem ¶
type Message ¶
type Message struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
Metadata api.MessageMeta
ConversationID *uint `gorm:"index"`
Conversation *Conversation `gorm:"foreignKey:ConversationID"`
ParentID *uint
Parent *Message `gorm:"foreignKey:ParentID"`
Replies []Message `gorm:"foreignKey:ParentID"`
SelectedReplyID *uint
SelectedReply *Message `gorm:"foreignKey:SelectedReplyID"`
Role api.MessageRole
Content string
ReasoningContent string
Parts []api.ContentPart `gorm:"serializer:json-compat"`
ToolCalls []api.ToolCall `gorm:"serializer:json-compat"` // a json array of tool calls (from the model)
ToolResults []api.ToolResult `gorm:"serializer:json-compat"` // a json array of tool results
}
func MessageFromAPI ¶
func MessagesFromAPI ¶
func (*Message) AfterFind ¶
AfterFind is a GORM hook that runs after loading a Message from the database. For legacy rows that lack Parts, it builds Parts from the Content/ReasoningContent fields.
func (*Message) BeforeSave ¶
BeforeSave is a GORM hook that clears the legacy Content and ReasoningContent fields before writing, so only Parts is persisted.
func (*Message) SetTextContent ¶
SetTextContent updates the first text-type part, or appends one if none exists.
func (*Message) TextContent ¶
TextContent returns the message's text content, extracted from text-type parts. AfterFind guarantees Parts is always populated.
func (*Message) ThinkingContent ¶
ThinkingContent returns the message's thinking/reasoning content, extracted from thinking-type parts. AfterFind guarantees Parts is always populated.
type Repo ¶
type Repo interface {
LoadConversationList(limit int) (ConversationList, error)
SearchConversations(query string, limit int) (ConversationList, error)
FindConversationByShortName(shortName string) (*Conversation, error)
FindLatestConversationByWorkspace(workspace string) (*Conversation, error)
ConversationShortNameCompletions(search string) []string
GetConversationByID(id uint) (*Conversation, error)
GetRootMessages(conversationID uint) ([]Message, error)
CreateConversation(title string) (*Conversation, error)
UpdateConversation(*Conversation) error
DeleteConversation(id uint) error
GetMessageByID(messageID uint) (*Message, error)
SaveMessage(message Message) (*Message, error)
UpdateMessage(message *Message) error
CloneBranch(toClone Message) (*Message, uint, error)
Reply(to *Message, messages ...Message) ([]Message, error)
// BranchMessage creates a new message at the same position as msg (same
// parent and conversation) with the given content, without copying any
// descendants, and sets it as the selected path. Returns the new message.
BranchMessage(msg Message, content string) (*Message, error)
// BranchRootMessage creates a new root message in oldRoot's conversation
// with the given content. The old root's selected reply is re-parented to
// the new root and the conversation's selected root is updated. This
// avoids mutating a shared root in-place when the system prompt changes.
// Returns the new root message.
BranchRootMessage(oldRoot *Message, content string) (*Message, error)
PathToRoot(message *Message) ([]Message, error)
PathToLeaf(message *Message) ([]Message, error)
// GetSelectedThread returns the "selected thread" of the conversation:
// a chain of messages starting from SelectedRoot, following each
// message's SelectedReply until the leaf is reached.
GetSelectedThread(*Conversation) ([]Message, error)
// SetSelected marks message as the selected choice at its position in the
// tree. If the message has no parent, it becomes the conversation's
// SelectedRoot; otherwise it becomes its parent's SelectedReply.
SetSelected(message *Message) error
// DeleteMessage deletes the given message. If withDescendants is false,
// its children are re-parented to the deleted message's parent (or made
// root messages), preserving the selected path where possible. If
// withDescendants is true, the message and all its descendants are
// deleted, and the parent's selected reply is updated to a sibling if
// one exists.
DeleteMessage(msg Message, withDescendants bool) error
// CycleSelectedRoot advances (forward=true) or retreats (forward=false)
// the conversation's SelectedRoot among the available root messages.
// Returns nil without error when there is only one root message.
CycleSelectedRoot(conv *Conversation, forward bool) (*Message, error)
// CycleSelectedReply advances or retreats the message's SelectedReply
// among its available replies.
// Returns nil without error when there is only one reply.
CycleSelectedReply(message *Message, forward bool) (*Message, error)
// StartConversation creates a new conversation with the given messages.
StartConversation(messages ...Message) (*Conversation, []Message, error)
CloneConversation(toClone Conversation) (*Conversation, uint, error)
}
Repo exposes message and conversation management operations.
type Thread ¶
type Thread struct {
Conversation Conversation
Messages []Message
// DefaultSystemPrompt is the fallback system prompt when no agent
// is set or the agent has no system prompt.
DefaultSystemPrompt string
// Agent is the currently active agent. If set, its SystemPrompt is used
// (falling back to DefaultSystemPrompt if empty). If the agent has
// Code=true, the system prompt is enhanced with local agents file
// content and workspace directory information.
Agent *api.Agent
// Persistent controls whether produced messages are persisted
// to the repo by the caller after receiving events from the
// completion loop.
Persistent bool
// Sandbox is the user-facing sandbox configuration. When set, it is
// used to compute the effective workspace path that appears in the
// system prompt (e.g. /workspace when remapping is enabled).
Sandbox *sandbox.SandboxConfig
// Model is the resolved model for running completions.
Model *api.Model
// Effort is the reasoning effort level for completions.
Effort api.ReasoningEffort
// contains filtered or unexported fields
}
Thread manages an in-memory conversation thread, its persistence, and configuration for the agent completion loop. It provides a unified data structure for both CLI and TUI.
To run a completion, configure the thread's Config field and call thread.Run(ctx), which returns event and approval channels.
func LoadThread ¶
func LoadThread(repo Repo, conv *Conversation) (*Thread, error)
LoadThread loads an existing conversation's selected thread from the repo.
func (*Thread) ApplySystemPrompt ¶
func (t *Thread) ApplySystemPrompt()
ApplySystemPrompt ensures the thread's messages start with the appropriate system prompt based on the configured agent and default system prompt. If no system prompt is needed, any existing system message is removed.
func (*Thread) ConfirmTools ¶ added in v0.6.0
func (t *Thread) ConfirmTools(confirmations []api.ToolConfirmation, sandboxCfg *sandbox.ToolSandboxConfig) (allRejected bool, err error)
ConfirmTools validates that the last message is a ToolCall, executes the tool calls with the given confirmations, and appends a ToolResult message to the thread. Returns allRejected=true if every tool call was rejected. Returns an error if the last message is not a ToolCall or if execution fails.
func (*Thread) IsDirty ¶
IsDirty reports whether the message with the given database ID has been modified since it was last persisted.
func (*Thread) Persist ¶
Persist saves all pending changes to the repo:
- If the conversation is new (ID==0), it creates the conversation and all messages atomically via StartConversation.
- If the conversation exists, it updates the conversation metadata, updates any dirty (modified) messages, and appends new (unsaved) messages via Reply.
It also syncs the current Thread.Agent to Conversation.Agent and, for code agents, ensures Conversation.Workspace is set.
func (*Thread) Reload ¶
Reload re-fetches the conversation and its selected thread from the repo. Useful after operations like branching, cycling, or deleting that modify the tree structure externally.
func (*Thread) RequestCompletion ¶ added in v0.6.0
func (t *Thread) RequestCompletion(ctx context.Context, temperature float32, maxTokens int, sandboxCfg *sandbox.ToolSandboxConfig) (<-chan api.Event, context.CancelFunc)
RequestCompletion applies the system prompt and starts a single completion step in a goroutine. It returns an event channel for the caller to consume and a cancel function to stop the step.
The caller is responsible for appending produced messages to the thread, executing any tool calls, and triggering persistence and subsequent steps as needed.
Cancelling ctx stops the step.
func (*Thread) SetAgent ¶
SetAgent sets the agent for the thread. If the agent is a code agent and the conversation doesn't already have a workspace set, it initializes the workspace to the current working directory. It also applies the system prompt to the thread's messages.
func (*Thread) SetContent ¶
SetContent modifies a message's text content in-place and marks it dirty if it has already been saved. It updates the first text-type part, or appends one if none exists.
func (*Thread) SetMessage ¶
SetMessage replaces a message at the given index. If the message at that index was already saved, it is marked dirty.