core

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: Apache-2.0 Imports: 69 Imported by: 0

Documentation

Overview

Package core is Godwit's mail engine.

Index

Constants

View Source
const (
	MaxCacheBytes   = 2 << 30  // attachment cache budget; oldest files are evicted beyond it
	MaxMessageBytes = 25 << 20 // largest attachment or message accepted
)
View Source
const (
	WarningPlugin     = "plugin_warning"
	WarningNoResponse = "no_response"
)
View Source
const (
	AuditLocalAPI = "local_api"
	AuditMCP      = "local_mcp"
)

External audit sources: the Local API's full role, and its mcp role (the godwit mcp bridge; "local_mcp", unlike the "mcp" of MCP-server tools).

View Source
const (
	ProviderOpenAI    = "openai"
	ProviderAnthropic = "anthropic"
	ProviderTypeSafe  = "typesafe"
	ProviderOther     = "other"
)

Providers: which formats a service offers (the add dialog's tiles).

View Source
const (
	CapGen   = "gen"
	CapJudge = "judge"
)

Capabilities, the keys of ModelService.Status.

View Source
const (
	KeyKeep    = "keep"    // reuse the stored key; only while kind and address are unchanged
	KeyReplace = "replace" // use APIKey
	KeyClear   = "clear"   // no key
)

Key actions for ModelServiceInput.KeyAction.

View Source
const (
	SignatureImageMax  = 512 << 10
	SignatureImagesMax = 4
)

Signature images: png, jpeg, gif or webp, sniffed (never named), at most SignatureImageMax bytes each and SignatureImagesMax per signature. They are stored per account, content-addressed, and referenced in the signature as cid:sig-<hash>@godwit; a sent mail carries them as inline parts. Their metadata (EXIF with its GPS, XMP, text) is stripped before storing.

View Source
const (
	SecurityTLS      = conn.TLS
	SecurityStartTLS = conn.StartTLS
	SecurityNone     = conn.None
)
View Source
const KindInvalid conn.FailureKind = "invalid"

KindInvalid marks a failed settings step: the input itself is not valid.

View Source
const MaxLabelDescription = 500

MaxLabelDescription is the most runes a label description has: every classification sends every description to the model.

View Source
const MaxLabelName = 32

MaxLabelName is the most runes a label name has.

View Source
const SignatureHTMLPrefix = "<!--godwit:html-->"

SignatureHTMLPrefix marks a stored signature as the settings editor's HTML; any other signature (the CLI's, the Local API's, an older one) is plain text.

Variables

View Source
var (
	ErrTooLarge       = errors.New("attachment exceeds the size limit")
	ErrNotOnServerYet = errors.New("message has not reached the server yet")
)
View Source
var (
	ErrNeedsGrant     = errors.New("permissions are not granted")
	ErrPluginNotFound = errors.New("plugin not found")
)
View Source
var (
	ErrAINotConfigured      = errors.New("no model configured")
	ErrModelServiceNotFound = errors.New("model service not found")
	ErrModelNotFound        = errors.New("model not found")
)
View Source
var (
	ErrModelAuth       = ai.ErrModelAuth
	ErrBaseURLInvalid  = ai.ErrBaseURLInvalid
	ErrBaseURLScheme   = ai.ErrBaseURLScheme
	ErrKeyNeedsConfirm = errors.New("the address or protocol changed: enter the key again or choose no key")
)
View Source
var (
	// ErrSignatureImage is an image that is not png, jpeg, gif or webp, or is over SignatureImageMax.
	ErrSignatureImage = errors.New("a signature image must be a png, jpeg, gif or webp of at most 512 KB")
	// ErrSignatureImageCount is a signature with more than SignatureImagesMax images.
	ErrSignatureImageCount = errors.New("a signature has at most 4 images")
)
View Source
var (
	ErrSkillDisabled = errors.New("skill is disabled")
	ErrSkillNotFound = errors.New("skill not found")
)
View Source
var ErrAccountNotFound = errors.New("account not found")

ErrAccountNotFound is returned for an account id that does not exist.

View Source
var ErrClosed = errors.New("service is closed")

ErrClosed is returned by calls made after Close.

View Source
var ErrInvalidState = errors.New("item is not waiting for a decision")

ErrInvalidState is returned by ResolveOutbox and ResolveApproval for an item that is not waiting for a decision.

View Source
var ErrKeychainUnavailable = errors.New("system keychain is unavailable")

ErrKeychainUnavailable means the system keychain cannot be used (locked, no secret service, access denied). Credentials never fall back to a file.

View Source
var ErrLabelExists = errors.New("a label with this name exists")

ErrLabelExists is a label name already taken, ignoring case and outer spaces.

View Source
var ErrMCPNotFound = errors.New("MCP server not found")
View Source
var ErrNotGrantable = errors.New("this tool cannot be allowed for the whole conversation")

ErrNotGrantable: the approval's tool cannot be allowed for the whole conversation.

View Source
var ErrNotSynced = mailsync.ErrNotSynced

ErrNotSynced is LoadOlder on a folder whose first sync has not completed.

View Source
var ErrProviderMismatch = errors.New("signed in with a different provider")

ErrProviderMismatch: AccountInput.Provider names another provider than the one the OAuth flow signed in with.

View Source
var ErrRunActive = errors.New("the conversation already has an active run")

ErrRunActive: the conversation has a run that is running, waiting for approvals or resuming.

View Source
var ErrStaleApproval = errors.New("the tool changed since this was proposed")

ErrStaleApproval: what an external approval was bound to (plugin code, permissions or config; an MCP server or tool definition) changed since it was proposed; it was not run.

View Source
var ErrStaleDraft = errors.New("draft changed since it was loaded")

ErrStaleDraft means the draft changed since the revision the caller saved against.

View Source
var ErrUnknownFlow = errors.New("no such sign-in flow")

ErrUnknownFlow: no such flow ID, or it was already awaited, cancelled or expired.

View Source
var ErrUseAssist = errors.New("draft skills run through AssistSkill")

ErrUseAssist: skills on drafts run through AssistSkill, which makes proposals.

View Source
var ErrWrongAccount = errors.New("signed in with a different account")

ErrWrongAccount: the OAuth sign-in was for another address than the account's.

View Source
var LabelColors = []string{"gray", "red", "orange", "amber", "green", "blue", "violet", "pink"}

LabelColors are the colors a label may take, in the picker's order.

View Source
var LabelIcons = []string{"shield-keyhole", "bill", "truck", "plane", "calendar-event", "notification-3", "newspaper", "shopping-bag-3",
	"folder-3", "money-dollar-circle", "book-2", "graduation-cap", "pencil", "quill-pen", "braces", "terminal-box",
	"music-2", "movie-2", "tools", "palette", "stethoscope", "first-aid-kit", "seedling", "briefcase-4",
	"bar-chart-box", "run", "sticky-note", "scales-3", "earth", "global", "hammer", "footprint",
	"flask", "brain", "heart-3", "plant", "team", "home-4", "key-2", "chat-3"}

LabelIcons are the icons a label may take, in the picker's order; the GUI draws each as "label-<name>".

View Source
var PurgeDays = []int{0, 1, 7, 30}

PurgeDays are the purge periods offered: never, a day, a week and a month.

Functions

func ModelEndpoint

func ModelEndpoint(kind, base string) string

ModelEndpoint is the URL requests to a service at base will hit.

func NormalizeModelBaseURL

func NormalizeModelBaseURL(raw string) (string, error)

NormalizeModelBaseURL is the address a service is saved with, whatever its formats: ErrBaseURLInvalid or ErrBaseURLScheme says why raw is refused.

func ProviderFormats

func ProviderFormats(p string) (gen, judge []string)

ProviderFormats are the generate kinds and judge formats provider p offers, in the dialog's order.

func SerializeKeyringForTest

func SerializeKeyringForTest()

SerializeKeyringForTest makes keychain calls one at a time. Tests only: go-keyring's mock (keyring.MockInit) is not safe for concurrent use, and sync workers read credentials while a test writes them. Call it before any Service starts; later calls do nothing.

func SignatureEditorHTML

func SignatureEditorHTML(sig string, cids map[string]bool) string

SignatureEditorHTML is sig as safe compose HTML, without the draft marker or the "-- " delimiter: a marked one sanitized (user markers stripped, the images in cids kept), a plain one escaped into a paragraph; "" for none.

func SignatureImageURL

func SignatureImageURL(data []byte) (string, error)

SignatureImageURL is data as a data: URL for the signature editor, or ErrSignatureImage.

Types

type Account

type Account struct {
	ID                                      int64
	Email, Name, Alias, Signature, Username string
	IsDefault                               bool
	Sync                                    SyncRange
	Folders                                 int // folders synced so far: 0 before the first sync
	AuthType, Status                        string
	IMAPHost                                string
	IMAPPort                                int
	IMAPSecurity                            Security
	SMTPHost                                string
	SMTPPort                                int
	SMTPSecurity                            Security
	Provider, StatusDetail                  string
}

Account is a stored account. The password lives only in the system keychain. Status is ok; reauth_required when the password is missing from the keychain (StatusDetail credentials_missing) or the IMAP or SMTP server refuses it (auth_rejected) or the provider revoked an OAuth grant (token_revoked); or error when the keychain is unavailable (keychain_unavailable). An OAuth account's keychain entry holds its refresh token; access tokens stay in memory.

type AccountInput

type AccountInput struct {
	Email, Name, Alias, Signature, Username, Password string
	IsDefault                                         bool
	Sync                                              *SyncRange // nil: the newest 2000 per folder
	IMAPHost                                          string
	IMAPPort                                          int
	IMAPSecurity                                      Security
	SMTPHost                                          string
	SMTPPort                                          int
	SMTPSecurity                                      Security
	Provider                                          string // auth.Provider ID; empty is stored as other
	// OAuthFlowID names a finished AwaitOAuth flow: the account signs in with its
	// tokens instead of Password, and Email must be the signed-in address.
	OAuthFlowID string
}

AccountInput is what the user supplies to add or test an account.

type AccountPatch

type AccountPatch struct {
	Name, Alias, Signature *string
	IsDefault              *bool
	Sync                   *SyncRange
	Servers                *ServerSettings
}

AccountPatch changes the account settings that are not nil.

type ActionInfo

type ActionInfo struct {
	ID, Plugin string
	Label      plugins.Text
	On         []string
}

ActionInfo is an action a running plugin registered; ID is <plugin>/<id>.

type ActionResult

type ActionResult struct {
	Text, HTML string
	DraftID    int64
}

ActionResult is what an action returned: text, HTML sanitized for reading, or a draft it made for the UI to open.

type AppliedLabel added in v0.3.0

type AppliedLabel struct {
	LabelID int64
	Auto    bool
}

AppliedLabel is a label on a message or thread. Auto: the model set it, and no message of the thread has it set by hand.

type Approval

type Approval struct {
	ID, RunID, ConversationID int64
	Seq                       int
	CallID, Tool              string
	Args                      json.RawMessage
	Summary, State, Error     string
	Destinations              []string
	// SessionAllow: 本次对话都允许 can approve it (ResolveApprovalForConversation).
	SessionAllow bool
}

Approval is a call waiting for the user or decided. State goes pending → executing → done or failed, or pending → rejected or stale. Error is what the model was told for any end state but done. Destinations are the hosts (host[:port]) the call sends data to, if any. CallID is the tool call it answers ("skill" for a skill's proposal).

type AttachmentInfo

type AttachmentInfo struct {
	ID                    int64
	Filename, ContentType string
	Size                  int
	Inline                bool
}

AttachmentInfo describes one attachment; its bytes are fetched lazily.

type Avatar added in v0.3.0

type Avatar struct {
	Data           []byte
	Format, Source string
	Width, Height  int
}

Avatar is a sender image; empty Data means there is no permitted image.

type CapStatus

type CapStatus struct {
	State      string // "ok" or "fail"
	LatencyMs  int
	HTTPStatus int    // the status that failed it, when known
	Code       string // why it failed: auth, forbidden, no_list, timeout, bad_answer, error, unreachable
	At         time.Time
}

CapStatus is a capability's last test. It holds codes, never error text: errors can echo a key, a response body or mail.

type Card

type Card struct {
	Plugin, Title string
	Fields        []CardField
	Buttons       []CardButton
}

Card is a plugin's declarative card for a message.

type CardButton

type CardButton struct{ Label, Kind, Value string }

CardButton: Kind copy (Value is the text), open-url (an http or https URL) or run-action (Value is the action's full ID, <plugin>/<id>).

type CardField

type CardField struct{ Label, Value string }

type CheckError

type CheckError struct {
	Report CheckReport
	// contains filtered or unexported fields
}

CheckError is returned by TestAccount and AddAccount when a check fails.

func (*CheckError) Error

func (e *CheckError) Error() string

func (*CheckError) Unwrap

func (e *CheckError) Unwrap() error

type CheckReport

type CheckReport struct {
	Steps   []CheckStep
	Folders int
}

Folders is how many selectable folders the imap step listed (0 if LIST failed).

func (CheckReport) OK

func (r CheckReport) OK() bool

OK reports whether every step passed.

type CheckStep

type CheckStep struct {
	Name    string
	OK      bool
	Skipped bool
	Kind    conn.FailureKind
	Detail  string
}

CheckStep is one item of a connection check. Name is settings, imap or smtp. Detail comes from conn's already redacted errors: no password, token or server text.

type Citation

type Citation struct {
	N             int
	MessageID     int64
	Subject, From string
	Date          time.Time
	Suspect       bool
	Quote         string // the suspect text, from where it starts (ai.InjectionQuote): show as plain text
	RunID         int64
	CallID        string
}

Citation is a message a run's tools showed the model: RunID's call CallID (its tool_result), or its context when CallID is "". N is the message's citation number in the conversation (from 1, by first citation): the model sees it as the mail's "ref" and cites it as [N].

type ConfigValue

type ConfigValue struct {
	Key   string
	Value json.RawMessage
	Set   bool
}

ConfigValue is a plugin setting as the settings form shows it: the stored value, or with Set false the default. A secret's Value is always empty.

type Conversation

type Conversation struct {
	ID                 int64
	Kind, Title, Model string
	DraftID            int64
	Messages           []ai.Message
	Citations          []Citation
	Runs               []RunSpan
	ActiveRunID        int64
	UpdatedAt          time.Time
}

Conversation is a Wit chat: kind "ask" (the mailbox panel) or "assist" (one draft's compose panel, DraftID set). Messages is decoded from the saved model state, for display; Citations are the mail its runs showed the model, in the order they came, each with the run and tool call that showed it (a message shown twice is there twice); Runs are the parts of Messages each run added, in run order; ActiveRunID is its run that is running, resuming or waiting for approvals (0: none). ListConversations leaves those four empty.

type Draft

type Draft struct {
	ID, AccountID          int64
	Mode                   string
	SourceMessageID        int64
	To, Cc, Bcc            []string
	ReplyTo, Subject, HTML string
	Attachments            []DraftAttachment
	UpdatedAt              time.Time
	// Revision grows by one on every update, an attachment added or removed
	// included (refetch it after one). SaveDraft with a non-zero Revision
	// fails with ErrStaleDraft unless it is still the stored one.
	Revision int64
}

Draft is an unsent message. Mode is new, reply, reply_all or forward.

type DraftAttachment

type DraftAttachment struct {
	ID                    int64
	Filename, ContentType string
	Size                  int
}

DraftAttachment is a file copied into the draft's directory.

type EgressApprovalRequired

type EgressApprovalRequired struct {
	Plugin string
	Hosts  []string
}

EgressApprovalRequired: the run may send mail content to Hosts (every host the plugin declares); ask the user, then run again with ApproveEgress.

func (*EgressApprovalRequired) Error

func (e *EgressApprovalRequired) Error() string

type ErrNoRoleFolder

type ErrNoRoleFolder struct{ Role string }

ErrNoRoleFolder is MoveToRole's error for an account without a folder of Role.

func (*ErrNoRoleFolder) Error

func (e *ErrNoRoleFolder) Error() string

type Folder

type Folder struct {
	ID, AccountID int64
	Name, Role    string
	Unread        int
}

Folder is a mailbox of an account. Role is inbox, sent, trash, drafts, junk, archive or empty.

type Label added in v0.3.0

type Label struct {
	ID                                   int64
	Slug, Name, Description, Color, Icon string
	Builtin                              bool
	Position                             int
	Count, Unread                        int
}

Label is a mail label. Count is the messages it is on, Unread those unread.

type LabelInput added in v0.3.0

type LabelInput struct{ Name, Description, Color, Icon string }

LabelInput is what the user sets on a label.

type MCPServer

type MCPServer struct {
	ID                        int64
	Name, Transport, Command  string
	Args, EnvKeys, HeaderKeys []string
	URL                       string
	Version                   int64
	Enabled, Granted          bool
	Tools                     []MCPTool
	Error                     string
}

MCPServer is a configured server. Granted means the current Version is granted; Tools are the last listed for this version; Error is why it last failed to connect or was stopped.

type MCPServerInput

type MCPServerInput struct {
	Name, Transport, Command string
	Args                     []string
	Env                      map[string]string
	URL                      string
	Headers                  map[string]string
}

MCPServerInput configures a server. Transport is "stdio" (Command, Args, Env) or "http" (URL, Headers). Env and Header values go to the keychain; an empty value in an update keeps the stored one, a key left out is removed.

type MCPTool

type MCPTool struct {
	Name, Description     string
	ReadOnlyHint, Trusted bool
	DefinitionHash        string
}

MCPTool is a tool of a server. ReadOnlyHint is the server's own claim, for display only; Trusted holds while the user trusted this DefinitionHash.

type Message

type Message struct {
	MessageSummary
	ReplyTo             string
	HTML, Text          string
	Truncated           bool
	RemoteImagesBlocked int
	// InJunk: the folder is junk, detected or given (even where another
	// folder holds the role), so its sender's images are never trusted.
	InJunk      bool
	Attachments []AttachmentInfo
}

Message is a fully loaded message. HTML is always sanitized. Truncated means HTML and Text hold only the start of the body; GetRawMessage has all of it.

type MessagePage

type MessagePage struct {
	Items      []MessageSummary
	NextCursor string
}

MessagePage is one page of a flat message list.

type MessageSummary

type MessageSummary struct {
	ID, ThreadID, FolderID int64
	Subject, From, Snippet string
	To, Cc                 []string
	Date                   time.Time
	Flags                  []string
	HasAttachments         bool
}

MessageSummary is a message as listed.

type MessageText

type MessageText struct {
	MessageSummary
	AccountID   int64
	ReplyTo     string
	Text        string
	Truncated   bool
	Attachments []AttachmentInfo
}

MessageText is a message as stored: its plain text and metadata, with the attachments listed but never fetched.

type ModelService

type ModelService struct {
	ID                      int64
	Name, Kind, BaseURL     string
	Provider                string // ProviderOpenAI, …: the formats it offers; fixed once added
	Slug                    string // the service part of its "slug/model" references; kept on rename
	Models                  []string
	JudgeFormat, JudgeModel string
	HasKey                  bool
	Version                 int64
	// Status is the last test of each capability (CapGen, CapJudge), absent
	// when untested or when what it tested changed since.
	Status map[string]CapStatus
	// contains filtered or unexported fields
}

ModelService is a model service and its two capabilities. Kind is the generate format: ai.KindOpenAI (chat), ai.KindResponses or ai.KindAnthropic (messages); "" turns generate off. JudgeFormat is ai.FormatDecisions or ai.FormatSystemOne; "" turns judge off. BaseURL is one base for both, normalized the same for every format.

func (ModelService) Input

func (m ModelService) Input() ModelServiceInput

Input is m as it is saved, keeping its key.

type ModelServiceInput

type ModelServiceInput struct {
	Name, Kind, BaseURL, APIKey string
	KeyAction                   string
	Models                      []string
	Provider                    string
	JudgeFormat, JudgeModel     string
}

ModelServiceInput is a service as the user entered it. An empty KeyAction means replace when APIKey is set, else keep (update) or none (add). Provider "" is the stored one, or for a new service the one its address implies (inferProvider). A capability that is on needs its models.

type Op

type Op struct {
	ID          int64
	Kind, State string // Kind: flags, move or purge. State: pending, awaiting_claim, conflict or failed
	Attempts    int
	LastError   string
}

Op is a queued local change that has not reached the server yet, or could not.

type Options

type Options struct {
	DataDir  string
	AutoSync bool // run a background sync worker per account
	// LabelMail runs the label classifier in the background (the apps set it;
	// tests call it by hand or leave it off so their scripted models stay theirs).
	LabelMail bool
	// SystemLanguage is the operating system language (BCP 47) the GUI passes in; empty means "en".
	SystemLanguage string
	// ImageClient downloads remote mail images; nil is imagefetch.NewClient(false)
	// (public hosts only). Tests give one that reaches loopback.
	ImageClient *http.Client
}

Options configures a Service.

type OutboxItem

type OutboxItem struct {
	ID               int64
	Subject          string
	State, LastError string
	CreatedAt        time.Time
}

OutboxItem is a message that has not finished sending and archiving. State is queued, sending, sent, archiving, send_uncertain, archive_uncertain or failed.

type PluginInfo

type PluginInfo struct {
	Name, Version         string
	Description           plugins.Text
	Permissions           plugins.Permissions
	Enabled, NeedsGrant   bool
	DisabledReason, Error string
	Config                []plugins.ConfigField
}

PluginInfo is an installed plugin. Error is why it cannot run (its files are damaged or an update was interrupted); such a plugin is listed, not hidden.

type Purge

type Purge struct {
	Days    int
	Blocked bool
}

Purge is an account's junk or trash purge. Days 0 is never. Blocked is set while the server lacks UIDPLUS, so the purge is skipped.

type QuickReply

type QuickReply struct{ Preview, HTML string }

QuickReply is one suggested reply: sanitized HTML and a plain-text preview.

type RunActionOptions

type RunActionOptions struct {
	// ApproveEgress is the user's approval of the plugin's declared hosts
	// (EgressApprovalRequired) for this one run.
	ApproveEgress bool
}

type RunSpan

type RunSpan struct {
	ID       int64
	From, To int
}

RunSpan is the part of a conversation's Messages that run ID added, its question included: Messages[From:To]. A resume extends its run's span; a run that saved nothing (failed, cancelled while running) has none.

type SearchQuery

type SearchQuery struct {
	AccountID      int64
	Text           string
	From           string    // a substring of the From header, any case
	Since, Until   time.Time // the date from Since, before Until
	HasAttachments bool
	LabelID        int64 // mail with this label on
}

SearchQuery is a search: free text ANDed with the filters set. Zero fields filter nothing; AccountID 0 is every account.

func (SearchQuery) Filtered

func (q SearchQuery) Filtered() bool

Filtered reports whether a filter beyond the account is set: such a query needs no text.

type Security

type Security = conn.Security

Security is the transport protection of an IMAP or SMTP connection.

type Selection

type Selection struct {
	Revision   int64
	Start, End int
}

Selection is a byte range [Start, End) of the draft's editable HTML (the first value mail.SplitDraft returns) at Revision. Neither end may fall inside a tag or a character reference.

type SendBlockedError

type SendBlockedError struct{ Plugin, Message string }

SendBlockedError: a plugin refused the draft. It cannot be acknowledged; the user edits the draft or disables the plugin.

func (*SendBlockedError) Error

func (e *SendBlockedError) Error() string

type SendCheck

type SendCheck struct {
	DraftID, Revision int64
	Plugins           []string
	Warnings          []SendWarning
	Digest            string
}

SendCheck is a draft revision's beforeSend outcome with warnings. Plugins are the checking plugins as name@source_hash@config_version, sorted by name. Digest binds all of it: SendAcknowledged(DraftID, Digest) queues that revision without checking again while the draft and the plugins are unchanged.

type SendWarning

type SendWarning struct{ Plugin, Code, Message string }

SendWarning is a plugin's warning about a draft; the user may still send. Code is WarningPlugin (Message is the plugin's) or WarningNoResponse (the check failed or timed out; Message is a fallback in English).

type SendWarningsError

type SendWarningsError struct{ Check SendCheck }

SendWarningsError: nothing was queued; show the warnings and call SendAcknowledged with Check.Digest to send anyway.

func (*SendWarningsError) Error

func (e *SendWarningsError) Error() string

type ServerSettings

type ServerSettings struct {
	Username     string
	IMAPHost     string
	IMAPPort     int
	IMAPSecurity Security
	SMTPHost     string
	SMTPPort     int
	SMTPSecurity Security
}

ServerSettings replaces an account's username and IMAP and SMTP servers. An empty Username means the email address.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service owns the database and event bus of one Godwit data directory.

func New

func New(ctx context.Context, opts Options) (*Service, error)

New creates DataDir if needed and opens DataDir/godwit.db.

func (*Service) AddAccount

func (s *Service) AddAccount(ctx context.Context, in AccountInput) (Account, error)

AddAccount tests in, stores the account and saves the password in the keychain. With OAuthFlowID it uses up the flow's grant and saves its refresh token instead.

func (*Service) AddAttachment

func (s *Service) AddAttachment(ctx context.Context, draftID int64, srcPath string) (DraftAttachment, error)

AddAttachment copies the file at srcPath into the draft.

func (*Service) AddAttachmentAt

func (s *Service) AddAttachmentAt(ctx context.Context, draftID, revision int64, srcPath string) (DraftAttachment, int64, error)

AddAttachmentAt is AddAttachment for exactly that revision of the draft (else ErrStaleDraft, nothing added); it returns the new revision.

func (*Service) AddMCPServer

func (s *Service) AddMCPServer(ctx context.Context, in MCPServerInput) (MCPServer, error)

AddMCPServer adds a server, disabled and not granted.

func (*Service) AddModelService

func (s *Service) AddModelService(ctx context.Context, in ModelServiceInput) (ModelService, error)

AddModelService stores the service, then its key in the keychain (entry 1); if the keychain fails the row is removed again. A crash between the two leaves a service without a key.

func (*Service) ApplyEditedProposal

func (s *Service) ApplyEditedProposal(ctx context.Context, approvalID int64, edited string) error

ApplyEditedProposal accepts a pending draft proposal with html, the editable part the user settled on in the GUI, in place of the proposed one: markers removed, sanitized, then applied like ResolveApproval (same revision check: a changed draft makes it stale, ErrStaleDraft).

func (*Service) Ask

func (s *Service) Ask(ctx context.Context, conversationID int64, input string) (<-chan WitEvent, error)

Ask runs input in the "ask" conversation with the mailbox tools. Mail the tools show the model comes back as Citations on their tool_result events; create_draft calls become approvals. The first input titles the conversation. The channel ends with done, error or interrupted (see startRun).

func (*Service) AskWith

func (s *Service) AskWith(ctx context.Context, conversationID int64, input string, quoted []int64) (<-chan WitEvent, error)

AskWith is Ask with the mail the user quoted (message IDs, at most maxQuoted after dedupe): the run reads it through mailContext, in its scope, and the model gets it before the question, cited like the tools' mail (a "context" event first). An ID that does not exist fails AskWith.

func (*Service) Assist

func (s *Service) Assist(ctx context.Context, draftID int64, input string) (<-chan WitEvent, error)

Assist runs input in the draft's assist conversation. The model sees the draft's editable part, the email it replies to or forwards, the names of its attachments and the latest mail with its recipients; it can read mail and propose a new editable part (update_draft). A proposal for an empty draft is applied at once (see applyInitial); the others wait for the user. The channel ends with done, error or interrupted, as Ask's.

func (*Service) AssistSkill

func (s *Service) AssistSkill(ctx context.Context, draftID int64, skill string, opts SkillOptions) (<-chan WitEvent, error)

AssistSkill runs a replace-draft skill (tone, length, proofreading, translate-draft) on the draft's editable part, or on opts.Selection of it if the skill runs on selections; the model sees that and nothing else. The skill has no tools, as every skill: the host turns its output into an update_draft proposal in the draft's assist conversation, which the user decides like any other (an empty draft's is applied at once). The conversation's history is left as it is. The channel streams the skill's text and ends with interrupted (the proposal) or error.

func (*Service) AssistWith

func (s *Service) AssistWith(ctx context.Context, draftID int64, input string, quoted []int64) (<-chan WitEvent, error)

AssistWith is Assist with the mail the user quoted, as AskWith.

func (*Service) AttachmentCacheUsage

func (s *Service) AttachmentCacheUsage(ctx context.Context, accountID int64) (int64, error)

AttachmentCacheUsage is the bytes the account's downloaded attachments take on disk.

func (*Service) AuditExternal

func (s *Service) AuditExternal(ctx context.Context, source, action string, target int64) error

AuditExternal writes an audit_log row for a call made from outside the app: source (AuditLocalAPI or AuditMCP), the method as action, and the target's ID (0 = none). No content is stored.

func (*Service) AwaitOAuth

func (s *Service) AwaitOAuth(ctx context.Context, flowID string) (email string, err error)

AwaitOAuth waits for the browser to come back and returns the signed-in email. The flow is over either way; on success its Grant stays in memory for grantTTL. Cancelling ctx cancels the flow.

func (*Service) BeginOAuth

func (s *Service) BeginOAuth(ctx context.Context, providerID, loginHint string) (flowID, authURL string, err error)

BeginOAuth starts an authorization flow for the provider and returns its ID and the URL to open in the browser. The flow ends after auth.FlowTimeout.

func (*Service) CancelOAuth

func (s *Service) CancelOAuth(flowID string)

CancelOAuth ends the flow (a pending AwaitOAuth returns an error) and drops its grant. Unknown IDs are ignored.

func (*Service) CancelRun

func (s *Service) CancelRun(ctx context.Context, runID int64) error

CancelRun ends a run when its panel closes. A run in flight is cancelled; an approved call executing outside the lock (external mode) is waited for; an interrupted one has its pending approvals rejected ("cancelled") and those results added to the conversation state without asking the model, so the conversation can take a new run. A run already over is left as it is.

func (*Service) CheckAccount

func (s *Service) CheckAccount(ctx context.Context, in AccountInput) (CheckReport, error)

CheckAccount runs the settings, imap and smtp checks in order; after a failure the remaining steps are Skipped.

func (*Service) CheckModelService

func (s *Service) CheckModelService(ctx context.Context, id int64, in ModelServiceInput) (ServiceCheck, error)

CheckModelService probes in as service id (0: a new one) would be saved, without saving: the key action decides the key, and a kept key never goes to a changed address or protocol (ErrKeyNeedsConfirm, no request).

func (*Service) ClearAttachmentCache

func (s *Service) ClearAttachmentCache(ctx context.Context, accountID int64) error

ClearAttachmentCache deletes the account's downloaded attachments; opening one downloads it again. Other accounts' caches stay.

func (*Service) ClearImageCache added in v0.1.0

func (s *Service) ClearImageCache() error

ClearImageCache deletes the cached remote images.

func (*Service) Close

func (s *Service) Close() error

Close refuses new calls with ErrClosed, cancels the explicit network calls and background workers, waits for them (each records its outcome) and releases the database. Concurrent and repeated calls all wait for the same shutdown.

func (*Service) CreateLabel added in v0.3.0

func (s *Service) CreateLabel(ctx context.Context, in LabelInput) (Label, error)

CreateLabel adds a label at the end; its slug is "u" and its id.

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, ids []int64) error

Delete moves the messages to their account's Trash; ones already there stay.

func (*Service) DeleteConversation

func (s *Service) DeleteConversation(ctx context.Context, id int64) error

DeleteConversation deletes the conversation with its runs and approvals. A run in flight or an approval executing makes it ErrRunActive; cancel first.

func (*Service) DeleteDraft

func (s *Service) DeleteDraft(ctx context.Context, id int64) error

DeleteDraft deletes the draft and its attachment files. A draft already queued is in the outbox, not here, and stays.

func (*Service) DeleteDraftIfEmpty

func (s *Service) DeleteDraftIfEmpty(ctx context.Context, id, revision int64) (deleted bool, err error)

DeleteDraftIfEmpty deletes the draft, as DeleteDraft, if it is still at revision and holds nothing the user added: no attachments, no text outside the quote and the signature, and the addresses and subject a new draft of its mode starts with (none for a new one; a reply's own). A draft Wit is working on (an assist run active, or an approval pending) is in use, never empty. deleted reports whether it went.

func (*Service) DeleteLabel added in v0.3.0

func (s *Service) DeleteLabel(ctx context.Context, id int64) error

DeleteLabel removes a label and takes it off every message.

func (*Service) DetectProvider

func (s *Service) DetectProvider(ctx context.Context, email string) (auth.Provider, auth.Source, error)

DetectProvider finds the provider for an email address; DNS gets 5 seconds in total.

func (*Service) DraftFromQuickReply

func (s *Service) DraftFromQuickReply(ctx context.Context, messageID int64, r QuickReply) (Draft, error)

DraftFromQuickReply starts a reply draft to messageID with r's HTML, sanitized again, at the start of the editable part, above the quote and signature.

func (*Service) FetchModels

func (s *Service) FetchModels(ctx context.Context, id int64) ([]string, error)

FetchModels lists the service's models, sorted. It saves nothing.

func (*Service) GetAttachment

func (s *Service) GetAttachment(ctx context.Context, attachmentID int64) (string, AttachmentInfo, error)

GetAttachment returns the attachment's cached file, downloading it first if needed.

func (*Service) GetConversation

func (s *Service) GetConversation(ctx context.Context, id int64) (Conversation, error)

GetConversation returns the conversation with its messages.

func (*Service) GetDraft

func (s *Service) GetDraft(ctx context.Context, id int64) (Draft, error)

GetDraft returns the draft with its attachments. HTML is sanitized again on read, covering rows stored before SaveDraft sanitized.

func (*Service) GetMessage

func (s *Service) GetMessage(ctx context.Context, id int64, loadRemoteImages bool) (Message, error)

GetMessage returns the message with sanitized HTML. Inline images are downloaded first if needed and embedded.

func (*Service) GetMessageText

func (s *Service) GetMessageText(ctx context.Context, id int64) (MessageText, error)

GetMessageText reads the message's stored text and metadata. Unlike GetMessage it processes no HTML and reads or downloads no attachment or inline image.

func (*Service) GetPluginConfig

func (s *Service) GetPluginConfig(ctx context.Context, name string) ([]ConfigValue, error)

GetPluginConfig returns the plugin's settings in declaration order.

func (*Service) GetRawMessage

func (s *Service) GetRawMessage(ctx context.Context, messageID int64) ([]byte, error)

GetRawMessage downloads the message's full original from the server, for a Truncated message or for saving. Messages over MaxMessageBytes give ErrTooLarge.

func (*Service) GetSettings

func (s *Service) GetSettings(ctx context.Context) (Settings, error)

GetSettings returns the stored settings, defaults for anything unset. Fields a newer build added are ignored.

func (*Service) GetThread

func (s *Service) GetThread(ctx context.Context, id int64) (Thread, error)

GetThread returns the thread's messages by date ascending.

func (*Service) GrantMCPServer

func (s *Service) GrantMCPServer(ctx context.Context, id int64) error

GrantMCPServer grants the server's current version: it may then be started.

func (*Service) GrantPermission

func (s *Service) GrantPermission(ctx context.Context, name string) error

GrantPermission grants the plugin's current permissions.

func (*Service) ImageCache added in v0.1.0

func (s *Service) ImageCache() *imagecache.Cache

ImageCache is the shared disk cache of remote mail images, which the reader's image proxy serves from and prefetch fills.

func (*Service) ImageCacheSize added in v0.1.0

func (s *Service) ImageCacheSize() int64

ImageCacheSize is the bytes the cached remote images take on disk.

func (*Service) ImagesAllowed added in v0.1.0

func (s *Service) ImagesAllowed(ctx context.Context, message int64) (bool, error)

ImagesAllowed is whether message's remote images load without asking, by the RemoteImages setting: never in a junk folder (From is unauthenticated); "auto" otherwise; "trusted" for the trusted From addresses; "ask" never.

func (*Service) InlineSignatureImages

func (s *Service) InlineSignatureImages(ctx context.Context, accountID int64, html string) (string, error)

InlineSignatureImages is html (sanitized compose HTML of a draft of the account) with its signature images as data: URLs, for showing only: never save it, as saving drops data: sources.

func (*Service) InstallPlugin

func (s *Service) InstallPlugin(ctx context.Context, srcDir string) (PluginInfo, error)

InstallPlugin installs or updates the plugin in srcDir: it snapshots the directory, builds the snapshot (a plugin that does not build is not installed), moves it into a new version directory and points the row at it. An update with other permissions is disabled until they are granted; with the same ones a running plugin restarts on the new code, and is disabled if that fails.

func (*Service) KeychainStatus

func (s *Service) KeychainStatus(ctx context.Context) error

KeychainStatus writes, reads back and deletes a fresh probe entry; any failure is ErrKeychainUnavailable. Onboarding calls it before showing the account form.

func (*Service) LabelModelReady added in v0.3.0

func (s *Service) LabelModelReady(ctx context.Context) bool

LabelModelReady reports whether a model can classify mail.

func (*Service) ListAccounts

func (s *Service) ListAccounts(ctx context.Context) ([]Account, error)

ListAccounts returns all accounts in creation order.

func (*Service) ListActions

func (s *Service) ListActions(ctx context.Context, kind string) ([]ActionInfo, error)

ListActions lists the running plugins' actions that run on kind (message, thread, selection or draft; "" for all), by ID.

func (*Service) ListApprovals

func (s *Service) ListApprovals(ctx context.Context, conversationID int64) ([]Approval, error)

ListApprovals returns the conversation's approvals by run and seq, so the cards survive a restart.

func (*Service) ListConversations

func (s *Service) ListConversations(ctx context.Context, kind string) ([]Conversation, error)

ListConversations returns the conversations of kind, most recent first.

func (*Service) ListDrafts

func (s *Service) ListDrafts(ctx context.Context, accountID int64) ([]Draft, error)

ListDrafts returns the account's drafts, most recently saved first.

func (*Service) ListFolders

func (s *Service) ListFolders(ctx context.Context, accountID int64) ([]Folder, error)

ListFolders returns the account's folders with their unread counts. Each role is on one folder at most, the one mailsync.RoleFolder resolves; inbox is detected only.

func (*Service) ListLabels added in v0.3.0

func (s *Service) ListLabels(ctx context.Context) ([]Label, error)

ListLabels returns the labels in the user's order.

func (*Service) ListMCPServers

func (s *Service) ListMCPServers(ctx context.Context) ([]MCPServer, error)

ListMCPServers lists the configured servers by ID.

func (*Service) ListMessages

func (s *Service) ListMessages(ctx context.Context, v View, cursor string, limit int) (MessagePage, error)

ListMessages lists the view's messages as flat rows, newest first.

func (*Service) ListModelServices

func (s *Service) ListModelServices(ctx context.Context) ([]ModelService, error)

ListModelServices lists the services in the order they were added.

func (*Service) ListOutbox

func (s *Service) ListOutbox(ctx context.Context, accountID int64) ([]OutboxItem, error)

ListOutbox returns the account's unfinished outbox items, oldest first.

func (*Service) ListPlugins

func (s *Service) ListPlugins(ctx context.Context) ([]PluginInfo, error)

ListPlugins lists the installed plugins, including ones that cannot run.

func (*Service) ListProviders

func (s *Service) ListProviders() []auth.Provider

func (*Service) ListSkills

func (s *Service) ListSkills(ctx context.Context) ([]SkillInfo, error)

ListSkills lists built-in and user skills, including user skills that failed to load.

func (*Service) ListThreads

func (s *Service) ListThreads(ctx context.Context, v View, cursor string, limit int) (ThreadPage, error)

ListThreads lists threads with at least one message in the view, newest first.

func (*Service) LoadOlder

func (s *Service) LoadOlder(ctx context.Context, folderID int64) (more bool, err error)

LoadOlder imports one page (about 200) of the folder's mail that the sync range left out, newest first, and keeps it. more is false once the server holds nothing more the folder lacks. It takes the account's sync lock, so a sync runs before or after it, never alongside; cancelling ctx stops it.

func (*Service) MessageLabels added in v0.3.0

func (s *Service) MessageLabels(ctx context.Context, ids []int64) (map[int64][]AppliedLabel, error)

MessageLabels returns the labels on each of the messages, in the labels' order.

func (*Service) Move

func (s *Service) Move(ctx context.Context, ids []int64, folderID int64) error

Move moves the messages to folderID, which must belong to their account. The local message IDs stay the same.

func (*Service) MoveToRole

func (s *Service) MoveToRole(ctx context.Context, ids []int64, role string) error

MoveToRole moves the messages to their account's folder of role (trash, junk or archive); ones already there stay.

func (*Service) NewConversation

func (s *Service) NewConversation(ctx context.Context, kind string, draftID int64) (Conversation, error)

NewConversation starts an empty conversation: kind "ask" without a draft, or "assist" on draftID.

func (*Service) NewDraft

func (s *Service) NewDraft(ctx context.Context, accountID int64, mode string, sourceMessageID int64) (Draft, error)

NewDraft starts and saves a draft. reply and reply_all address the source's sender (and everyone, for reply_all) and quote it; forward quotes it and copies its non-inline attachments, downloading them if needed. The account signature ends the body.

func (*Service) PendingLabels added in v0.3.0

func (s *Service) PendingLabels(ctx context.Context) (int, error)

PendingLabels counts the rescan targets not classified yet.

func (*Service) PendingOps

func (s *Service) PendingOps(ctx context.Context, accountID int64) ([]Op, error)

PendingOps lists the account's ops that are not done (or cancelled), oldest first.

func (*Service) PluginCards

func (s *Service) PluginCards(ctx context.Context, messageID int64) ([]Card, error)

PluginCards runs each running plugin's card on the message, by plugin name: match(message), then render(message) if it matched, within one 5 s budget, read-only and with no egress. A failed, late or invalid card is left out and counts as a plugin failure.

func (*Service) Purges

func (s *Service) Purges(ctx context.Context, accountID int64) (map[string]Purge, error)

Purges returns the account's junk and trash purges by role.

func (*Service) QuickReplies

func (s *Service) QuickReplies(ctx context.Context, messageID int64) ([]QuickReply, error)

QuickReplies suggests up to three replies to a message with the default model. It returns none, without asking the model, when Settings.QuickReplies is off or the message is from the account itself. Results are cached like RunSkill's; an answer that is not the requested JSON is an error.

func (*Service) ReadingLanguage

func (s *Service) ReadingLanguage(ctx context.Context) string

ReadingLanguage is the language Wit reads and writes in: the setting, else the UI language, else the system language, else "en".

func (*Service) Reauthorize

func (s *Service) Reauthorize(ctx context.Context, id int64, flowID string) error

Reauthorize gives an OAuth account the tokens of a finished flow, which must have signed in to the account's address with its provider (ErrWrongAccount, ErrProviderMismatch; the old tokens stay). It clears reauth_required and a paused outbox; the worker resumes.

func (*Service) RebuildSearchIndex

func (s *Service) RebuildSearchIndex(ctx context.Context) error

RebuildSearchIndex recreates the full-text index from the messages table in one transaction, repairing an index that drifted or was damaged.

func (*Service) RecordCapStatus

func (s *Service) RecordCapStatus(ctx context.Context, id, version int64, c string, st CapStatus) error

RecordCapStatus stores st, a TestCapability result for capability c of what service id now is at version: the dialog tests before it saves.

func (*Service) Redact

func (s *Service) Redact(text string) string

Redact replaces every credential and extension secret this Service knows of in text. Not for RPC. Values under 8 characters are skipped (ai.Redact): replacing them would mangle ordinary text, and the ones that matter (keys, tokens) are longer; a short password is a known gap, not leaked by core's own errors, which never carry credentials.

func (*Service) RefreshMCPTools

func (s *Service) RefreshMCPTools(ctx context.Context, id int64) error

RefreshMCPTools connects to the server again and lists its tools; a disabled server is disconnected afterwards. Needs the current version granted.

func (*Service) RemoveAccount

func (s *Service) RemoveAccount(ctx context.Context, id int64) error

RemoveAccount deletes the account, its keychain entry and its cache directory. It first cancels the account's explicit calls and worker and waits for them.

func (*Service) RemoveAttachment

func (s *Service) RemoveAttachment(ctx context.Context, draftID, attachmentID int64) error

RemoveAttachment deletes the attachment from the draft and its file.

func (*Service) RemoveAttachmentAt

func (s *Service) RemoveAttachmentAt(ctx context.Context, draftID, attachmentID, revision int64) (int64, error)

RemoveAttachmentAt is RemoveAttachment for exactly that revision of the draft (else ErrStaleDraft, nothing removed); it returns the new revision.

func (*Service) RemoveMCPServer

func (s *Service) RemoveMCPServer(ctx context.Context, id int64) error

RemoveMCPServer stops a server and deletes it with its keychain entries; its pending approvals become stale.

func (*Service) RemoveModelService

func (s *Service) RemoveModelService(ctx context.Context, id int64) error

func (*Service) ReorderLabels added in v0.3.0

func (s *Service) ReorderLabels(ctx context.Context, ids []int64) error

ReorderLabels puts the labels in the order of ids, which names every label once.

func (*Service) RescanLabels added in v0.3.0

func (s *Service) RescanLabels(ctx context.Context, days int, all bool) (int, error)

RescanLabels makes the Inbox mail of the last days (0: all of it) the classifier's targets again: all of it, its model labels dropped, or only mail with no label on. Hand-set labels stay. It returns how many.

func (*Service) ResolveAll

func (s *Service) ResolveAll(ctx context.Context, runID int64, accept bool) error

ResolveAll resolves the run's pending approvals one by one in seq order.

func (*Service) ResolveApproval

func (s *Service) ResolveApproval(ctx context.Context, id int64, accept bool) error

ResolveApproval accepts or rejects a pending approval, exactly once: the call that claims it (pending → executing) decides it; any other gets ErrInvalidState. Accepting applies the recorded arguments and marks it done in one transaction, after checking the target draft's revision (changed → stale, ErrStaleDraft). An external tool's approval runs outside the lock instead and records its outcome afterwards (see decideExternal; a changed binding makes it stale, ErrStaleApproval). The call that ends the run's last open approval resumes the run in the background; SubscribeConversation streams it.

func (*Service) ResolveApprovalForConversation

func (s *Service) ResolveApprovalForConversation(ctx context.Context, id int64) error

ResolveApprovalForConversation accepts pending approval id and allows its tool for the rest of its conversation (本次对话都允许): the run's other pending calls of the tool are accepted too, and later ones as soon as they come (applyInitial), audited auto:session. The tool and the conversation come from the approval. Only sessionTools qualify, else ErrNotGrantable.

func (*Service) ResolveOutbox

func (s *Service) ResolveOutbox(ctx context.Context, id int64, action string) error

ResolveOutbox decides an item in send_uncertain, archive_uncertain or failed: confirm_sent treats it as sent (it is archived next), resend queues it again and discard drops it.

func (*Service) RewriteSelection

func (s *Service) RewriteSelection(ctx context.Context, draftID int64, sel Selection, instruction string) (<-chan WitEvent, error)

RewriteSelection rewrites sel of the draft's editable part as instruction says, in a new assist conversation of its own. The model gets only the selected HTML and up to 500 characters of read-only context on each side, and answers with update_selection; the proposal splices its HTML into the range, so nothing outside it can change. A sel made on another revision is ErrStaleDraft.

func (*Service) RunAction

func (s *Service) RunAction(ctx context.Context, id string, t Target, opts RunActionOptions) (ActionResult, error)

RunAction runs action id (<plugin>/<id>) on t. The plugin gets {kind, id} and, for a selection, text: never other mail content (it reads that itself with mail:read). A plugin holding net that may hold mail content (mail:read, or a selection) returns *EgressApprovalRequired unless opts approves. A failed run or an invalid result counts as a plugin failure.

func (*Service) RunSkill

func (s *Service) RunSkill(ctx context.Context, name string, t Target) (<-chan ai.Event, error)

RunSkill runs an enabled skill on t with no tools. The first event is "context": Text is a JSON list of {message_id, suspect} for the mail the model sees, so the UI can flag suspected prompt injection, and Model is the "slug/model" the run uses (skill godwit-model, else SmallModel for the small tier, else DefaultModel). message and thread runs are cached by everything that changes their output; a hit replays context, one text event and done without asking the model. The channel ends with exactly one of done or error and is closed after it; cancel ctx to stop early.

func (*Service) SaveDraft

func (s *Service) SaveDraft(ctx context.Context, d Draft) (Draft, error)

SaveDraft inserts d (ID 0) or updates its addresses, subject and body. The HTML is sanitized for composing (no scripts, handlers or remote resources). Attachments are changed with AddAttachment and RemoveAttachment.

func (*Service) Search

func (s *Service) Search(ctx context.Context, accountID int64, query string, limit int) ([]MessageSummary, error)

Search finds synced messages; accountID 0 searches every account.

func (*Service) SearchWith

func (s *Service) SearchWith(ctx context.Context, q SearchQuery, limit int) ([]MessageSummary, error)

SearchWith finds synced messages matching q, newest first. A query with neither text nor a filter finds nothing.

func (*Service) Send

func (s *Service) Send(ctx context.Context, draftID int64) error

Send moves the draft into the outbox; the next sync submits it. Nothing is sent and the draft stays when validation or building fails. Plugins' message.beforeSend handlers check it first (hooks.go): a block returns *SendBlockedError, warnings *SendWarningsError (see SendAcknowledged). A draft changed during the checks is checked again; changed twice in a row, Send returns ErrStaleDraft.

func (*Service) SendAcknowledged

func (s *Service) SendAcknowledged(ctx context.Context, draftID int64, digest string) error

SendAcknowledged sends the draft despite the warnings of the check with digest: if the draft's revision and the checking plugins are still the checked ones it is queued without checking again; otherwise (or with no such check, e.g. expired) it is a plain Send, which may warn again. A block has no check, so it can never be acknowledged.

func (*Service) SendAcknowledgedRevision

func (s *Service) SendAcknowledgedRevision(ctx context.Context, draftID, revision int64, digest string) error

SendAcknowledgedRevision is SendAcknowledged for exactly that revision of the draft: if the stored one differs, nothing is sent and the error is ErrStaleDraft.

func (*Service) SendRevision

func (s *Service) SendRevision(ctx context.Context, draftID, revision int64) error

SendRevision is Send for exactly that revision of the draft: if the stored one differs, nothing is sent and the error is ErrStaleDraft.

func (*Service) SenderAvatar added in v0.3.0

func (s *Service) SenderAvatar(ctx context.Context, messageID int64, allowRemote bool) (Avatar, error)

SenderAvatar loads the ordinary sender avatar under the current message's remote-image policy. allowRemote is the user's explicit permission for this message. Missing or unavailable sources are normal empty results.

func (*Service) SetDraftAccount

func (s *Service) SetDraftAccount(ctx context.Context, id, accountID, revision int64) (Draft, error)

SetDraftAccount moves the draft to accountID: the body's signature becomes that account's, attachments and the reply's source stay, and the revision grows by one. revision must be the stored one (ErrStaleDraft).

func (*Service) SetFlags

func (s *Service) SetFlags(ctx context.Context, ids []int64, add, remove []string) error

SetFlags adds then removes flags on the messages locally and queues the change for the server.

func (*Service) SetLabel added in v0.3.0

func (s *Service) SetLabel(ctx context.Context, messageIDs []int64, labelID int64, on bool) error

SetLabel puts the label on the messages (on) or takes it off, by hand: the classifier leaves these rows alone from then on. All or none are written.

func (*Service) SetMCPServerEnabled

func (s *Service) SetMCPServerEnabled(ctx context.Context, id int64, enabled bool) error

SetMCPServerEnabled starts (connects and lists tools) or stops a server. Starting needs its current version granted (ErrNeedsGrant).

func (*Service) SetMCPToolTrusted

func (s *Service) SetMCPToolTrusted(ctx context.Context, id int64, tool string, trusted bool) error

SetMCPToolTrusted trusts (or stops trusting) a tool as listed now: trust is bound to its definition hash, so any change to the definition ends it.

func (*Service) SetPluginConfig

func (s *Service) SetPluginConfig(ctx context.Context, name string, values map[string]json.RawMessage) error

SetPluginConfig saves the settings form. Non-secret fields take the given value; absent, null or "" unsets one (its default applies). A secret absent or "" keeps its value and null clears it. Fields their when hides are ignored and keep what they had. Nothing is written unless every shown field is valid and every shown required one has a value; a write bumps config_version.

func (*Service) SetPluginEnabled

func (s *Service) SetPluginEnabled(ctx context.Context, name string, enabled bool) error

SetPluginEnabled starts or stops a plugin. Starting needs its current permissions granted (ErrNeedsGrant) and intact files; it resets the failure count.

func (*Service) SetPluginNetForTest

func (s *Service) SetPluginNetForTest(n plugins.NetOptions)

SetPluginNetForTest replaces the network plugins and MCP servers over HTTP use (resolver, dialer, plain HTTP and loopback allowed or not). Tests only: it lets fixture plugins reach a local server. Call it right after New, before any plugin or MCP server starts.

func (*Service) SetPurge

func (s *Service) SetPurge(ctx context.Context, accountID int64, role string, days int) error

SetPurge sets how many days mail stays in the account's junk or trash folder before it is expunged on the server; 0 is never. Any change restarts the clock: only mail that stays that long from now on is purged.

func (*Service) SetRoleFolder

func (s *Service) SetRoleFolder(ctx context.Context, accountID int64, role string, folderID int64) error

SetRoleFolder gives role (drafts, sent, junk, trash or archive) to the folder; 0 leaves the account without a folder of the role. The choice survives sync; once the folder is gone the role follows detection again. INBOX takes no role, and a folder holding another role cannot take this one (junk and trash would purge its mail): that role moves elsewhere first.

func (*Service) SetSettings

func (s *Service) SetSettings(ctx context.Context, st Settings) (Settings, error)

SetSettings validates and stores st, then announces settings.changed.

func (*Service) SetSkillEnabled

func (s *Service) SetSkillEnabled(ctx context.Context, name string, enabled bool) error

SetSkillEnabled turns a skill on or off.

func (*Service) SignatureEditor

func (s *Service) SignatureEditor(ctx context.Context, accountID int64) (string, error)

SignatureEditor is the account's signature as the settings editor's HTML: sanitized, a plain one escaped into a paragraph, its images as data: URLs.

func (*Service) SignatureImageURLs

func (s *Service) SignatureImageURLs(ctx context.Context, accountID int64, html string) ([]string, error)

SignatureImageURLs are the data: URLs of the images html shows, in order: the compose page's read-only signature preview.

func (*Service) StopRescan added in v0.3.0

func (s *Service) StopRescan(ctx context.Context) error

StopRescan gives up on the rescan targets not classified yet.

func (*Service) Subscribe

func (s *Service) Subscribe() (<-chan events.Event, func())

Subscribe returns UI notifications and a func that unsubscribes.

func (*Service) SubscribeConversation

func (s *Service) SubscribeConversation(id int64) (<-chan WitEvent, func())

SubscribeConversation streams the events of the conversation's runs, including resumes after approvals, until the returned func is called or the Service closes; either closes the channel and discards what is still queued. A slow reader loses the oldest text but never an event that ends a run. After Close the channel is closed.

func (*Service) SyncNow

func (s *Service) SyncNow(ctx context.Context, accountID int64) error

SyncNow connects to the account's IMAP server, pushes queued local changes, sends and files the outbox, and fetches new mail. Calls for the same account run one at a time. Afterwards the background worker is woken; if it was parked on reauth_required and this call logged in, it resumes.

func (*Service) TaskModel

func (s *Service) TaskModel(ctx context.Context, skill string) (string, error)

TaskModel is the "slug/model" skill runs on (quick replies when skill is ""), resolved as RunSkill and QuickReplies resolve it, without running it. ErrAINotConfigured, ErrModelServiceNotFound or ErrModelNotFound: it has none.

func (*Service) TestAccount

func (s *Service) TestAccount(ctx context.Context, in AccountInput) error

TestAccount runs the connection check; a failure is a *CheckError.

func (*Service) TestCapability

func (s *Service) TestCapability(ctx context.Context, id int64, in ModelServiceInput, c string) (CapStatus, error)

TestCapability tests capability c (CapGen, CapJudge) of in as service id (0: a new one) would be saved, without saving it: generate runs the shortest real generation on its first model (or the first one it lists), judge asks a yes/no about a fixed sample. A failed test returns its status and the error; an invalid in returns no status. When in is service id as saved (same address, stored key, and format), the status is stored, unless the service changed while the test ran.

func (*Service) ThreadLabels added in v0.3.0

func (s *Service) ThreadLabels(ctx context.Context, threadIDs []int64) (map[int64][]AppliedLabel, error)

ThreadLabels returns the labels on any message of each thread, in the labels' order.

func (*Service) TrustImageSender

func (s *Service) TrustImageSender(ctx context.Context, from string) error

TrustImageSender trusts the address of from (a From header) to load remote images; from must parse as an address.

func (*Service) UndoLastAssist

func (s *Service) UndoLastAssist(ctx context.Context, draftID int64) error

UndoLastAssist puts back the editable part the draft's latest applied proposal (by approval ID, not by clock) replaced. ErrInvalidState: there is none, or the draft changed since it was written (an undo counts as a change).

func (*Service) UninstallPlugin

func (s *Service) UninstallPlugin(ctx context.Context, name string) error

UninstallPlugin stops the plugin and removes its row (with its config, storage and cursor) and its files.

func (*Service) UntrustImageSender

func (s *Service) UntrustImageSender(ctx context.Context, addr string) error

UntrustImageSender stops trusting addr.

func (*Service) UpdateAccount

func (s *Service) UpdateAccount(ctx context.Context, id int64, p AccountPatch) (Account, error)

UpdateAccount writes p's fields in one transaction. New Servers are first checked with the stored credentials (a failure is a *CheckError and nothing is saved). IsDefault true makes the account the only default; false fails on the default account, as one account is always the default. Sync is a Limit of 1–100000, Months of 1–120, or neither (all).

func (*Service) UpdateLabel added in v0.3.0

func (s *Service) UpdateLabel(ctx context.Context, id int64, in LabelInput) (Label, error)

UpdateLabel changes a label's name, description, color and icon; its slug stays.

func (*Service) UpdateMCPServer

func (s *Service) UpdateMCPServer(ctx context.Context, id int64, in MCPServerInput) (MCPServer, error)

UpdateMCPServer changes a server: its version goes up, its connection closes, its grant and tool trust are cleared, it is disabled, and its pending approvals become stale.

func (*Service) UpdateModelService

func (s *Service) UpdateModelService(ctx context.Context, id int64, in ModelServiceInput) (ModelService, error)

UpdateModelService replaces the service's fields, applies the key action and bumps its version. A new key goes to a new keychain entry first; then one transaction switches the address and the entry together; then the old entry is deleted. A crash at any point never pairs a key with an address it was not saved for: the row names the old entry or the new one, never a half-written one, and the next start deletes the other (sweepModelKeys).

func (*Service) UpdatePassword

func (s *Service) UpdatePassword(ctx context.Context, id int64, password string) error

UpdatePassword checks password against the account's servers and, if both accept it, saves it and clears reauth_required and a paused outbox; the worker resumes and the outbox sends in its next round. A failed check is a *CheckError and leaves the keychain unchanged.

type ServiceCheck

type ServiceCheck struct {
	BaseURL, Endpoint string
	Models            []string
	ListSupported     bool // false: the service has no model list; models are typed by hand
	NoListStatus      int  // the HTTP status that said so, when known
	JudgeEndpoint     string
	JudgeModels       []string // nil until there is a key, except for ProviderOther
}

ServiceCheck is what probing a service before saving found. Endpoint and Models are generate's ("" and nil when it is off); JudgeEndpoint and JudgeModels judge's.

type Settings

type Settings struct {
	Theme, Density                                   string
	FontSize, BodyFontSize                           int // BodyFontSize 0 follows FontSize
	UILanguage, ReadingLanguage, ReplyLanguage       string
	NoTranslate                                      []string
	LaunchAtLogin, KeepInBackground, UnreadInMenuBar bool
	UnreadOnAppIcon                                  bool
	NotifyNewMail, NotifySmartOnly, NotifyPreview    bool
	QuickReplies, Summary, ThreadView                bool
	DefaultModel, SmallModel                         string
	// JudgeService is the slug of the service whose judge answers typed
	// questions; "" (or one gone or with judge off): the DefaultModel does.
	JudgeService string
	LocalAPI     bool // the GUI serves the Local API; off by default
	// AutoLabel lets the classifier label new Inbox mail; on by default.
	// A rescan runs either way.
	AutoLabel bool
	// SidebarWidth is the page sidebars' width in DIPs, the user's drag:
	// 0 for the default, else 240 to 480.
	SidebarWidth int
	// ListWidth is the mail list's width in DIPs, the user's drag: 0 for the
	// default, else 280 to 560.
	ListWidth int
	// TrustedImageSenders are the lower-cased From addresses whose remote
	// images load at once (始终信任此发件人). Only TrustImageSender and
	// UntrustImageSender change it; SetSettings keeps the stored list.
	TrustedImageSenders []string
	// RemoteImages is when a message's remote images and sender avatars load, through the image
	// proxy: "auto" (default, "" too) at once, "trusted" for the trusted
	// senders only, "ask" after 加载图片. Mail in Junk never loads them by itself.
	RemoteImages string
	// SettingsVersion is 2 once the default text grew from 13 to 14 px and a
	// stored FontSize was raised by one to keep its sizes; 3 once a stored
	// BodyFontSize of 14, the old default, became 0 (follow FontSize).
	SettingsVersion int
}

Settings are the app-wide preferences, stored as one JSON row. ReplyLanguage is "match" (the mail's language) or "reading".

type SkillInfo

type SkillInfo struct {
	Name, Description string
	On                []string
	Output            string
	Model             string // godwit-model; "" is the default model
	Tier              string // godwit-model-tier: "small" or ""
	Builtin, Enabled  bool
	UI                bool // has godwit-action-*
	Error             string
}

type SkillOptions

type SkillOptions struct {
	Selection      *Selection
	TargetLanguage string
}

SkillOptions are AssistSkill's options. Selection, if set, limits the skill to that range; TargetLanguage is translate-draft's target (empty: the reading language).

type SyncRange

type SyncRange struct{ Limit, Months int }

SyncRange is how much of each folder a sync fetches: the newest Limit messages, the mail of the last Months months, or (both 0) all of it. Narrowing it only stops fetching older mail; nothing stored is deleted.

type Target

type Target struct {
	Kind string
	ID   int64
	Text string
}

Target is what a skill runs on: message, thread, selection (ID is the message the text was selected in) or draft.

type Thread

type Thread struct {
	ID       int64
	Subject  string
	Messages []MessageSummary
}

Thread is a conversation with its messages, oldest first.

type ThreadPage

type ThreadPage struct {
	Items      []ThreadSummary
	NextCursor string
}

ThreadPage is one page of threads.

type ThreadSummary

type ThreadSummary struct {
	ID               int64
	Subject, Snippet string
	Participants     []string
	Count, Unread    int
	Flagged          int // messages starred
	LastDate         time.Time
	HasAttachments   bool
}

ThreadSummary is a conversation as listed. Count, Unread and Flagged cover the whole thread.

type View

type View struct {
	FolderID int64
	Unified  string
	LabelID  int64
}

View selects what a list shows: one folder, a unified view ("inbox", "starred") or one label's mail across accounts. Exactly one is set.

type WitEvent

type WitEvent struct {
	ai.Event
	RunID     int64
	Citations []Citation
	Approvals []Approval
}

WitEvent is one event of a Wit run. An interrupted event carries the approvals it recorded, in call order. An Assist run starts with a "context" event whose Citations are the mail its context shows the model.

Directories

Path Synopsis
Package ai talks to model services.
Package ai talks to model services.
Package auth holds the provider table and account auto-detection.
Package auth holds the provider table and account auto-detection.
Package conn dials and authenticates IMAP and SMTP connections.
Package conn dials and authenticates IMAP and SMTP connections.
Package coretest is a real core.Service for tests outside core: a mocked keychain, fake IMAP / SMTP servers per account, a fake model service, a fake MCP server and fixture plugins, all on this machine.
Package coretest is a real core.Service for tests outside core: a mocked keychain, fake IMAP / SMTP servers per account, a fake model service, a fake MCP server and fixture plugins, all on this machine.
Package events is a lossy in-process event bus for UI notifications.
Package events is a lossy in-process event bus for UI notifications.
Package gateway decides whether an AI tool call may run, needs approval, or is denied, and audits every decision.
Package gateway decides whether an AI tool call may run, needs approval, or is denied, and audits every decision.
internal
testllm
Package testllm is a fake model service for tests: the model-list endpoint and a scripted, streaming chat endpoint for each protocol (openai, openai-responses, anthropic).
Package testllm is a fake model service for tests: the model-list endpoint and a scripted, streaming chat endpoint for each protocol (openai, openai-responses, anthropic).
testmail
Package testmail provides in-memory IMAP and SMTP servers for tests.
Package testmail provides in-memory IMAP and SMTP servers for tests.
testoauth
Package testoauth is a fake OAuth 2.0 authorization and token server for tests, with a stand-in for the user's browser.
Package testoauth is a fake OAuth 2.0 authorization and token server for tests, with a stand-in for the user's browser.
Package mail parses MIME messages and sanitizes HTML bodies.
Package mail parses MIME messages and sanitizes HTML bodies.
Package mailsync pulls folders and messages from IMAP into the store.
Package mailsync pulls folders and messages from IMAP into the store.
Package plugins runs third-party TypeScript plugins (spec 5.3–5.5): manifest validation, the sandbox and the ctx API.
Package plugins runs third-party TypeScript plugins (spec 5.3–5.5): manifest validation, the sandbox and the ctx API.
Package send submits messages over SMTP.
Package send submits messages over SMTP.
Package skills loads SKILL.md directories (Agent Skills standard).
Package skills loads SKILL.md directories (Agent Skills standard).
Package store is Godwit's SQLite storage layer.
Package store is Godwit's SQLite storage layer.

Jump to

Keyboard shortcuts

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