conversation

package
v0.6.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var LocalAgentsFileNames = []string{"AGENT.md", "AGENTS.md", "CLAUDE.md"}

LocalAgentsFileNames contains the filenames to check for local agent instructions.

Functions

func MessageToAPI

func MessageToAPI(m Message) api.Message

func MessagesToAPI

func MessagesToAPI(messages []Message) []api.Message

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 ConversationListItem struct {
	ID            uint
	ShortName     string
	Title         string
	Workspace     string
	LastMessageAt time.Time
}

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 MessageFromAPI(m api.Message) Message

func MessagesFromAPI

func MessagesFromAPI(messages []api.Message) []Message

func (*Message) AfterFind

func (m *Message) AfterFind(tx *gorm.DB) error

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

func (m *Message) BeforeSave(tx *gorm.DB) error

BeforeSave is a GORM hook that clears the legacy Content and ReasoningContent fields before writing, so only Parts is persisted.

func (*Message) SetTextContent

func (m *Message) SetTextContent(content string)

SetTextContent updates the first text-type part, or appends one if none exists.

func (*Message) TextContent

func (m *Message) TextContent() string

TextContent returns the message's text content, extracted from text-type parts. AfterFind guarantees Parts is always populated.

func (*Message) ThinkingContent

func (m *Message) ThinkingContent() string

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.

func NewRepo

func NewRepo(db *gorm.DB) (Repo, error)

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 NewThread

func NewThread(repo Repo) *Thread

NewThread creates a new, empty thread (for starting new conversations).

func (*Thread) Append

func (t *Thread) Append(msgs ...Message)

Append adds messages to the in-memory thread without persisting.

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

func (t *Thread) IsDirty(id uint) bool

IsDirty reports whether the message with the given database ID has been modified since it was last persisted.

func (*Thread) Persist

func (t *Thread) Persist() error

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

func (t *Thread) Reload() error

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

func (t *Thread) SetAgent(agent *api.Agent)

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

func (t *Thread) SetContent(i int, content string)

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

func (t *Thread) SetMessage(i int, msg Message)

SetMessage replaces a message at the given index. If the message at that index was already saved, it is marked dirty.

func (*Thread) SetParts

func (t *Thread) SetParts(i int, parts []api.ContentPart)

SetParts replaces the content parts of the message at index i and marks it dirty if it has already been saved.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL