client

package
v0.0.21 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package client is the gRPC-facing layer of mecatui: it dials mecated, creates sessions, opens the bidi Converse stream, and translates proto Events into the plain Go tea.Msg structs the ui consumes. It is the ONLY mecatui package that imports contracts/gen + grpc; the ui never sees a proto type. This boundary is deliberate: it keeps the Elm model rendering "pure data" and makes the whole event pipeline testable from a scripted fake (see Recver / fakeStream in tests) with no network.

Index

Constants

View Source
const (
	DreamTargetProjectMemory = "project_memory"
	DreamTargetUserModel     = "user_model"
	DreamDecisionApply       = "apply"
	DreamDecisionDismiss     = "dismiss"
)
View Source
const (
	ProposalStatusStaged     = "staged"
	ProposalStatusPromoting  = "promoting"
	ProposalStatusPromoted   = "promoted"
	ProposalStatusRejected   = "rejected"
	ProposalStatusDeferred   = "deferred_unsupported"
	ProposalStatusConflicted = "conflicted"
	ProposalStatusUndone     = "undone"
)
View Source
const ModeDefaultString = "default"

ModeDefaultString is the canonical CLI/UI spelling for default permission mode.

Variables

View Source
var (
	// ErrNoClipboardTool reports that NO backend clipboard binary exists (no
	// wl-paste / xclip / pbpaste / pngpaste / powershell). The UI surfaces an
	// actionable "install wl-clipboard / xclip" hint rather than a generic error.
	ErrNoClipboardTool = errors.New("no clipboard tool available")
	// ErrEmptyClipboard reports that a backend exists but the clipboard holds
	// neither a usable image nor any text. The UI shows a benign "clipboard is
	// empty" status (no transcript error).
	ErrEmptyClipboard = errors.New("clipboard is empty")
)
View Source
var ErrSessionInventoryRestart = errors.New("session inventory changed; restart from page one")

ErrSessionInventoryRestart means the server rejected a continuation cursor because its catalog generation changed. Callers must restart at page one rather than mixing generations.

Functions

func CreateScheduleCmd

func CreateScheduleCmd(ctx context.Context, c ScheduleLister, spec ScheduleSpec) tea.Cmd

CreateScheduleCmd creates a schedule off the update goroutine; the result arrives as a ScheduleMsg. Exported for the planned in-overlay Create form (Phase 3b); the v1 overlay does not call it.

func DecideDreamPlanCmd

func DecideDreamPlanCmd(ctx context.Context, c DreamClient, id, decision string, generation, requestID uint64) tea.Cmd

func DecideReflectionCmd

func DecideReflectionCmd(ctx context.Context, c ReflectionClient, p LearningProposal, decision, project string, generation uint64) tea.Cmd

func DeleteScheduleCmd

func DeleteScheduleCmd(ctx context.Context, c ScheduleLister, name string) tea.Cmd

DeleteScheduleCmd deletes a schedule off the update goroutine; the result arrives as a ScheduleActionMsg{Action:"deleted"}.

func DeleteSessionCmd

func DeleteSessionCmd(ctx context.Context, d SessionDeleter, id string) tea.Cmd

DeleteSessionCmd performs DeleteSession off the reducer goroutine.

func DiffLearnedSkillCmd

func DiffLearnedSkillCmd(ctx context.Context, c LearnedSkillClient, s LearnedSkill, requestID uint64) tea.Cmd

func EventToMsg

func EventToMsg(ev *mecatlv1.Event) tea.Msg

EventToMsg maps a single proto Event onto its tea.Msg. It is a total function over the documented type strings; an unknown/empty type returns nil (the reader skips nil so unknown future event kinds are ignored, not fatal). This is the single translation point between the proto schema and the ui model and is unit-tested over every type.

func FireNowCmd

func FireNowCmd(ctx context.Context, c ScheduleLister, name string) tea.Cmd

FireNowCmd forces an immediate fire off the update goroutine; the result arrives as a ScheduleActionMsg{Action:"fired"} carrying the fire id.

func GenerateDreamPlanCmd

func GenerateDreamPlanCmd(ctx context.Context, c DreamClient, target string, generation, requestID uint64) tea.Cmd

func GetLearnedSkillCmd

func GetLearnedSkillCmd(ctx context.Context, c LearnedSkillClient, s LearnedSkill, requestID uint64) tea.Cmd

func GetMcpPromptCmd

func GetMcpPromptCmd(ctx context.Context, m MCP, server, name string, args map[string]string) tea.Cmd

GetMcpPromptCmd renders one prompt by (server, name) with arguments.

func GetReflectionCmd

func GetReflectionCmd(ctx context.Context, c ReflectionClient, id, project string, generation uint64) tea.Cmd

func GetScheduleCmd

func GetScheduleCmd(ctx context.Context, c ScheduleLister, name string) tea.Cmd

GetScheduleCmd fetches a single schedule off the update goroutine; the result arrives as a ScheduleMsg.

func GetSessionTranscriptCmd

func GetSessionTranscriptCmd(ctx context.Context, loader SessionTranscripter, id string) tea.Cmd

GetSessionTranscriptCmd returns a command that loads one authoritative transcript.

func GetSoulCmd

func GetSoulCmd(ctx context.Context, c SoulFetcher) tea.Cmd

GetSoulCmd fetches the soul snapshot off the update goroutine; the result (success or error) arrives as a SoulMsg.

func GetUserModelCmd

func GetUserModelCmd(ctx context.Context, c UserModelLister) tea.Cmd

GetUserModelCmd fetches the user-model index off the update goroutine; the result (success or error) arrives as a UserModelMsg.

func GetUserModelCmdTagged

func GetUserModelCmdTagged(ctx context.Context, c UserModelLister, generation uint64) tea.Cmd

GetUserModelCmdTagged correlates an inventory response with one overlay opening.

func GetUserModelEntryCmd

func GetUserModelEntryCmd(ctx context.Context, c UserModelDetailer, key string) tea.Cmd

GetUserModelEntryCmd fetches selected-entry detail off the update goroutine.

func GetUserModelEntryCmdTagged

func GetUserModelEntryCmdTagged(ctx context.Context, c UserModelDetailer, key string, generation uint64) tea.Cmd

GetUserModelEntryCmdTagged correlates exact detail with its selected key and overlay generation so delayed responses cannot replace newer state.

func IsDreamDecisionConflict

func IsDreamDecisionConflict(err error) bool

func IsDreamPlanGone

func IsDreamPlanGone(err error) bool

func IsInvalidArgument

func IsInvalidArgument(err error) bool

IsInvalidArgument reports whether err carries gRPC codes.InvalidArgument — the code the server maps a REJECTED CreateSession selector to (an unknown provider_id surfaces as server.ErrInvalidArgument → codes.InvalidArgument via toStatus; both the in-process UNIX-socket server and a remote mecated speak the same gRPC path). status.Code traverses wrapped errors, so CreateSession's "create session: %w" wrap above survives classification. The ui's connect-time fallback (issue #41) gates its zero-selection retry on this: only a genuine REJECTION of the carried selector falls back to the server default — a transient failure (unavailable, deadline) keeps the fatal path with the original error, never a dishonest "rejected" warning. Nil → false (codes.OK); a non-status error → false (codes.Unknown).

func IsLoopbackHost

func IsLoopbackHost(server string) bool

IsLoopbackHost reports whether the host part of a "host:port" (or bare host) target is loopback: an IP in 127.0.0.0/8, ::1, or the name "localhost". A target with no resolvable/parseable host is treated as NON-loopback (fail safe — we'd rather demand TLS than leak a token).

func IsProposalConflict

func IsProposalConflict(err error) bool

func ListAgentsCmd

func ListAgentsCmd(ctx context.Context, c AgentLister) tea.Cmd

ListAgentsCmd fetches the agent-definition inventory off the update goroutine; the result (success or error) arrives as an AgentsMsg.

func ListCommandsCmd

func ListCommandsCmd(ctx context.Context, c Commander, workspace string) tea.Cmd

ListCommandsCmd fetches the slash commands for workspace off the update goroutine; the result (success or error) arrives as a CommandsMsg.

func ListFiresCmd

func ListFiresCmd(ctx context.Context, c ScheduleLister, scheduleName string) tea.Cmd

ListFiresCmd fetches a schedule's fire records off the update goroutine; the result arrives as a ScheduleFiresMsg.

func ListLearnedSkillsCmd

func ListLearnedSkillsCmd(ctx context.Context, c LearnedSkillClient, project string, requestID uint64) tea.Cmd

func ListMcpPromptsCmd

func ListMcpPromptsCmd(ctx context.Context, m MCP, server string) tea.Cmd

ListMcpPromptsCmd lists prompts (server "" = all).

func ListMcpResourcesCmd

func ListMcpResourcesCmd(ctx context.Context, m MCP, server string) tea.Cmd

ListMcpResourcesCmd lists resources (server "" = all).

func ListMcpSourcesCmd

func ListMcpSourcesCmd(ctx context.Context, m MCP) tea.Cmd

ListMcpSourcesCmd lists the inventory sources (the panel snapshot).

func ListModelsCmd

func ListModelsCmd(ctx context.Context, l ModelLister, requestToken uint64) tea.Cmd

ListModelsCmd fetches the model inventory off the update goroutine; the result (success or error) arrives as a ModelsMsg carrying the required request token, so the Model can reject stale catalog results.

func ListReflectionsCmd

func ListReflectionsCmd(ctx context.Context, c ReflectionClient, status string, cursors ReflectionCursors, project string, generation uint64) tea.Cmd

func ListSchedulesCmd

func ListSchedulesCmd(ctx context.Context, c ScheduleLister) tea.Cmd

ListSchedulesCmd fetches the schedule list off the update goroutine; the result (success or error) arrives as a SchedulesMsg.

func ListSessionsCmd

func ListSessionsCmd(ctx context.Context, s SessionLister) tea.Cmd

ListSessionsCmd returns the legacy all-pages command used by non-progressive callers.

func ListSessionsPageCmd

func ListSessionsPageCmd(ctx context.Context, s SessionPager, cursor string, requestToken uint64) tea.Cmd

ListSessionsPageCmd returns a command that fetches exactly one inventory page.

func ListSkillChangesCmd

func ListSkillChangesCmd(ctx context.Context, c LearnedSkillClient, project string) tea.Cmd

func ListSkillsCmd

func ListSkillsCmd(ctx context.Context, c SkillLister) tea.Cmd

ListSkillsCmd fetches the skills inventory off the update goroutine; the result (success or error) arrives as a SkillsMsg.

func ListToolHiveGroupsCmd

func ListToolHiveGroupsCmd(ctx context.Context, m MCP) tea.Cmd

ListToolHiveGroupsCmd lists the configured ToolHive groups.

func ListWorktreesCmd

func ListWorktreesCmd(ctx context.Context, c WorktreeLister, workspace string) tea.Cmd

ListWorktreesCmd fetches the worktrees for workspace off the update goroutine; the result (success or error) arrives as a WorktreesMsg.

func LiveStreamCmd

func LiveStreamCmd(ctx context.Context, live LiveStreamer, id string) (ch chan tea.Msg, stop func())

LiveStreamCmd opens the live session event stream for session id synchronously, then runs ReadLoop on a goroutine. It returns the message channel and an idempotent teardown function.

func ModeFromString

func ModeFromString(s string) mecatlv1.PermissionMode

ModeFromString maps a CLI mode string to the proto enum. Unknown/empty maps to UNSPECIFIED (the server defaults that to DEFAULT).

func ModeString

func ModeString(m mecatlv1.PermissionMode) string

ModeString maps the proto enum to the CLI/UI spelling. Unknown/unspecified values degrade to "default", matching the server boundary.

func MutateLearnedSkillCmd

func MutateLearnedSkillCmd(ctx context.Context, c LearnedSkillClient, action string, s LearnedSkill, requestID uint64) tea.Cmd

func NextMode

func NextMode(mode string) string

NextMode returns the next mode in the TUI's cycle order.

func PauseScheduleCmd

func PauseScheduleCmd(ctx context.Context, c ScheduleLister, name string) tea.Cmd

PauseScheduleCmd pauses a schedule off the update goroutine; the result arrives as a ScheduleActionMsg{Action:"paused"}.

func PreflightSessionAdoptionCmd

func PreflightSessionAdoptionCmd(ctx context.Context, adopter SessionAdopter, sourceID string, bindings AdoptionBindings) tea.Cmd

PreflightSessionAdoptionCmd performs preflight outside the reducer.

func ReadMcpResourceCmd

func ReadMcpResourceCmd(ctx context.Context, m MCP, server, uri string) tea.Cmd

ReadMcpResourceCmd reads one resource by (server, uri).

func ReconnectLiveCmd

func ReconnectLiveCmd(ctx context.Context, live LiveStreamer, replayer SessionReplayer, id string) (ch chan tea.Msg, stop func())

ReconnectLiveCmd opens the live-feed reconnect+catch-up loop for session id: it runs reconnectLiveLoop on a goroutine, pushing tea.Msgs (LiveReconnectingMsg / LiveReconnectedMsg / the catch-up event msgs) onto a buffered (64) channel the ui drains via WaitForMsg (the SAME fan-in the live and replay streams use). The loop stops when ctx is done. Returns the channel + an idempotent teardown that cancels the loop's ctx AND JOINS the loop goroutine, so a caller that mutates state the loop reads (e.g. a test shrinking the package-level backoff vars) cannot race the loop's final backoff read after stop. Mirrors LiveStreamCmd needs no join because its ReadLoop goroutine only reads the injected stream, not package-level test knobs. The ui stores the channel + stop, tags reads with the live-reconnect generation, and re-arms the live reader on LiveReconnectedMsg.

func ReflectSessionCmd

func ReflectSessionCmd(ctx context.Context, c ReflectionClient, sessionID string, generation uint64) tea.Cmd

func RefreshResolvedModelCmd

func RefreshResolvedModelCmd(ctx context.Context, g SessionGetter, id string) tea.Cmd

RefreshResolvedModelCmd refetches the resolved model for session id off the update goroutine; the result (success or error) arrives as a ResolvedModelMsg with SessionID stamped so the reducer can correlate/drop it. It backs two paths:

  1. The footer context-meter heal: the ui fires it on a turn boundary while the meter's denominator is still unknown, and the ResolvedModelMsg arm raises the window once the server's live-first resolution heals it.
  2. The plan-approval mode+model refresh (issue #206): after a plan_approved terminal, the ui fires it to refetch the server's flipped mode (plan→default/ acceptEdits) and the execute model, so the header updates from the session snapshot.

Mode is carried alongside ResolvedModel so the reducer can update both the mode echo and the effective model in one refetch (reusing the ResolvedModelMsg arm). Capabilities is carried so the caps-heal path (/sessions continue, /effort fork, issue #348) can re-derive affordances in the same round-trip.

func RenameSessionCmd

func RenameSessionCmd(ctx context.Context, r SessionRenamer, id, title string) tea.Cmd

RenameSessionCmd performs RenameSession off the reducer goroutine.

func RestoreBackoffForTest

func RestoreBackoffForTest() func()

RestoreBackoffForTest saves the current backoff knobs and returns a restore func. Tests (including cross-package ui tests) shrink the knobs (and set jitter to 0 for deterministic timing) and defer the restore so the package-level vars are reset for the next test. This is the exported seam that lets cmd/mecatui/ui tests shrink the client's reconnect backoff without importing the unexported vars.

func ResumeScheduleCmd

func ResumeScheduleCmd(ctx context.Context, c ScheduleLister, name string) tea.Cmd

ResumeScheduleCmd resumes a schedule off the update goroutine; the result arrives as a ScheduleActionMsg{Action:"resumed"}.

func RollbackLearnedSkillCmd

func RollbackLearnedSkillCmd(ctx context.Context, c LearnedSkillClient, s LearnedSkill, requestID uint64) tea.Cmd

func SetModeCmd

func SetModeCmd(ctx context.Context, s ModeSetter, id, mode string) tea.Cmd

SetModeCmd asks the server to change a session's permission mode off the update goroutine.

func StageClipboardImage

func StageClipboardImage(mime string, data []byte, caps Capabilities) (*mecatlv1.Content, string, error)

StageClipboardImage rebuilds clipboard bytes the UI staged (proto-free, as a mime+data pair under an "[Image #N]" marker) into an inline media Content part at SUBMIT time, applying the same cap-gate + size-cap as every other path. The UI appends the returned part to its opaque media.Parts slice and the descriptor to media.Descriptors without ever naming the proto type. It is the submit-side counterpart to onClipboardPaste's staging.

func StagePathMedia

func StagePathMedia(path string, caps Capabilities) (mime string, data []byte, descriptor string, err error)

StagePathMedia reads a file the UI wants to attach via a drag-and-drop / pasted path, sniffs it, and runs it through the SAME sniff/cap/size logic as an @-mention — returning the sniffed mime, the raw bytes, and the human descriptor for a successful media (image/audio) attachment. It errors for anything that is not a media attachment: a text file (the path-paste branch wants a real media file, not an inline-text body — text falls through to literal paste), an unsupported binary, a cap-gated kind, an oversize file, or an unreadable path. The UI uses this for the pasted-image-PATH branch (it stats for a fast reject, then calls here for the read + sniff + build); on ANY error the UI falls back to inserting the path literally. net/http + proto stay here in client.

func TransientResultError

func TransientResultError(text string) bool

TransientResultError classifies a terminal ResultMsg's error TEXT as transient. A result-carried error has no gRPC status (the run completed with stop=error and an error string), so the classification is vocabulary-only. Empty text is never transient (a clean end_turn carries no error).

func TransientStreamErr

func TransientStreamErr(err error) bool

TransientStreamErr classifies a Converse stream Recv error for presentation and compatibility only. A stream error has no semantic commit fact, so the TUI never uses this signal to authorize automatic replay or queue draining. The gRPC status code is the primary signal, with vocabulary fallback for older servers.

func UndoReflectionCmd

func UndoReflectionCmd(ctx context.Context, c ReflectionClient, p LearningProposal, project string, generation uint64) tea.Cmd

func WaitForMsg

func WaitForMsg(ch <-chan tea.Msg) tea.Cmd

WaitForMsg is the canonical Bubble Tea fan-in command: it blocks on one msg from ch and returns it, so Update can re-arm it (return WaitForMsg(ch) again) to pull the next one. When ch is closed it returns StreamClosedMsg so the ui can tear down cleanly without a nil-msg storm.

Types

type ActivityReplayStatus

type ActivityReplayStatus struct {
	Available     bool
	Complete      bool
	Authoritative bool
}

ActivityReplayStatus describes the optional, non-authoritative activity replay plane independently from the snapshot-derived transcript.

type AdoptionBindings

type AdoptionBindings struct {
	Workspace       string
	EnvironmentKind string
	EnvironmentID   string
	ProviderID      string
	ModelID         string
	Profile         string
}

AdoptionBindings is the proto-free explicit execution/model binding reviewed before a legacy session is copied into a new main chat.

type AdoptionPreflight

type AdoptionPreflight struct {
	Eligible bool
	Reason   CapabilityReason
	Bindings AdoptionBindings
}

AdoptionPreflight is the server-authoritative eligibility result and resolved binding display.

type AdoptionResult

type AdoptionResult struct {
	SessionID       string
	SourceSessionID string
	Capabilities    Capabilities
	ResolvedModel   ResolvedModel
}

AdoptionResult identifies the new main session and its source/capability echoes.

type Agent

type Agent struct {
	Name           string
	Description    string
	Model          string
	PermissionMode string
	Color          string
	Tools          []string
}

Agent is one resolved agent definition (proto AgentInfo, proto-free): the def's routing name, its one-line routing description, the resolved provider model (empty = inherit parent), the raw frontmatter permission mode, the optional colour UX hint, and the effective read-only tool scope at the Subagent call site. Definitions are the routing targets for Subagent delegations — this is discovery only; activation stays the model's run-path concern.

type AgentLister

type AgentLister interface {
	ListAgents(ctx context.Context) ([]Agent, error)
}

AgentLister is the subset of *Client the ui's /agents panel needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type AgentsMsg

type AgentsMsg struct {
	Agents []Agent
	Err    error
}

AgentsMsg carries a ListAgents result for the /agents panel. Err is set on failure; the panel surfaces it rather than silently degrading, mirroring the skills panel's error handling.

type ApprovalMsg

type ApprovalMsg struct {
	AskID       string
	Verdict     string
	Tool        string
	CallID      string
	AllowAlways bool
}

ApprovalMsg is the verdict half of a permission ask (EvApproval), relayed only by the replay (log-only on the live wire). Metadata-only (gauntlet #7): tool NAME + verdict string + askID + callID + the allow-always flag. NEVER raw args.

type AssistantDeltaMsg

type AssistantDeltaMsg struct {
	Turn int32
	Text string
}

AssistantDeltaMsg is a streamed chunk of assistant markdown to append+rerender.

type Capabilities

type Capabilities struct {
	MCP           bool
	SlashCommands bool
	Memory        bool
	Skills        bool
	Teams         bool
	Agents        bool
	Bash          bool
	// Soul / UserModel report whether the server has a soul source / user-model store
	// wired. They gate the /soul and /usermodel read-only inspection panels.
	Soul      bool
	UserModel bool
	// ModelSelection is true when >=1 provider is available (ListModels would return
	// at least one model). It gates the /models picker: an old server (field absent →
	// false) hides the command, same mechanism as Soul/UserModel.
	ModelSelection bool
	// Image/Audio report whether the wired provider consumes that media kind. They
	// gate the @-mention file-attach UX: a client refuses to send a part the
	// server's provider cannot read (an old server with no field → false → degrade).
	Image bool
	Audio bool
	// Posture is the SERVER-WIDE operator posture tier ("strict"/"trusted"/"auto"/
	// "yolo"), CHROME ONLY: the ui renders a "⚠ auto"/"⚠ yolo" badge so an operator
	// sees the daemon's automation posture. NOT session state. Empty (an older server,
	// or strict/trusted) → no badge.
	Posture string
	// Worktrees is true when a WorktreeLister is wired (ListWorktrees may return a
	// non-empty list for a real git repo). It gates the /worktrees overlay — the
	// first-class operator workflow for binding a session to an EXISTING sibling
	// git worktree (issue #102). An older server, or a no-FS/cloud server with no
	// lister, yields false, so the overlay is honestly absent.
	Worktrees bool
	// Scheduling is true when a ScheduleStore is reachable on the server (the
	// ScheduleService RPCs are functional). Gates the /schedule overlay. An older
	// server (field absent → false) hides the overlay. Independent of the scheduler
	// tick loop: the overlay can create/inspect/pause/resume/fire-now on any
	// store-backed server; auto-firing on a cadence is the server's tick loop
	// (ON by default on a store-backed server, ADR 0073 — `--no-scheduler` opts out).
	Scheduling        bool
	Reflection        bool
	LearningProposals bool
	LearnedSkills     bool
	StorageHealth     bool
	StorageMigration  bool
	StorageCleanup    bool
	LegacyAdoption    bool
	// ManualDream is nil when an older server does not expose the capability object.
	// A non-nil value keeps /dream discoverable even when both targets are unavailable,
	// so the overlay can explain the target-specific reasons.
	ManualDream *ManualDreamCapabilities
	// Steer is true when the server's engine arms the mid-run steer inbox
	// (steer-while-running, issue #512): a `steer` frame on the bidi Converse stream
	// then drains at the next turn boundary and the authoritative steer / steer.outcome
	// events echo back. When false (the operator disabled it, or an older server with
	// no field → proto3 default false), the ui keeps the client-side terminal
	// merge-queue (issue #228) byte-identical — it never sends a steer frame the
	// server would only ack too_late.
	Steer bool
}

Capabilities is the proto-free mirror of mecatlv1.ServerCapabilities: which optional features the connected server has enabled. The ui renders honest affordances from it (advertise only reachable features; explain empty inventories as "not enabled" vs "enabled but empty") WITHOUT importing proto. All-false is the safe default (an older server omits the field).

type CapabilityReason

type CapabilityReason string

CapabilityReason explains why a session action is unavailable.

const (
	CapabilityReasonInspectOnlyKind        CapabilityReason = "inspect_only_kind"
	CapabilityReasonAwaitingApproval       CapabilityReason = "awaiting_approval"
	CapabilityReasonActiveElsewhere        CapabilityReason = "active_elsewhere"
	CapabilityReasonTranscriptUnavailable  CapabilityReason = "transcript_unavailable"
	CapabilityReasonEnvironmentUnavailable CapabilityReason = "environment_unavailable"
	CapabilityReasonStorageUnsupported     CapabilityReason = "storage_unsupported"
	CapabilityReasonProtectedProvenance    CapabilityReason = "protected_provenance"
	CapabilityReasonInvalidTranscript      CapabilityReason = "invalid_transcript"
	CapabilityReasonAdoptionActive         CapabilityReason = "active"
	CapabilityReasonAdoptionLeased         CapabilityReason = "leased"
	CapabilityReasonBindingUnresolved      CapabilityReason = "binding_unresolved"
	CapabilityReasonNotLegacy              CapabilityReason = "not_legacy"
	CapabilityReasonUnknown                CapabilityReason = "unknown"
)

Server-provided unavailable-action reasons.

type CleanupCandidate

type CleanupCandidate struct {
	SessionID           string
	Kind, State, Reason string
	ModifiedAt          time.Time
	EstimatedBytes      int64
}

CleanupCandidate is one content-free cleanup candidate.

type CleanupCounts

type CleanupCounts struct {
	Total                     int
	ByKind, ByState, ByReason map[string]int
}

CleanupCounts is a protected-row aggregate grouped by stable taxonomy.

type CleanupItemError

type CleanupItemError struct{ ItemHandle, ReasonCode, Message string }

CleanupItemError is one sanitized stable per-item failure.

type CleanupJob

type CleanupJob struct {
	ID, State                                  string
	Processed, Deleted, Skipped, Stale, Failed int
	Errors                                     []CleanupItemError
}

CleanupJob is a bounded maintenance-job projection.

type CleanupPlan

type CleanupPlan struct {
	ConfirmationToken, PlannedJobID              string
	Available                                    bool
	UnavailableReason, Generation, PolicyVersion string
	Eligible                                     []CleanupCandidate
	EligibleCounts, Protected                    CleanupCounts
	EstimatedBytes                               int64
}

CleanupPlan is the caller-bound dry-run projection.

type CleanupScope

type CleanupScope struct{ Kinds []string }

CleanupScope is the exact durable-kind subset requested by a management client.

type Client

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

Client is a connected mecated gRPC client: the dialled conn plus the generated service stub. Close it on shutdown.

func Dial

func Dial(cfg DialConfig) (*Client, error)

Dial connects to mecated per cfg. It uses grpc.NewClient (not the deprecated grpc.Dial), attaches a per-RPC bearer credential when a token is set, and configures transport credentials (plaintext for loopback by default, TLS/mTLS when requested). The connection is lazy; the first RPC (CreateSession) surfaces a connect error.

func (*Client) AdoptSession

func (c *Client) AdoptSession(ctx context.Context, sourceID, idempotencyKey string, bindings AdoptionBindings) (AdoptionResult, error)

AdoptSession publishes the caller/source-bound idempotent main-session copy.

func (*Client) ApplySessionCleanup

func (c *Client) ApplySessionCleanup(ctx context.Context, token string) (CleanupJob, error)

ApplySessionCleanup applies a caller-bound confirmation token.

func (*Client) ApplySessionMigration

func (c *Client) ApplySessionMigration(ctx context.Context, planID string, batchSize int32) (SessionMigrationJob, error)

ApplySessionMigration starts a durable job and processes its first bounded batch.

func (*Client) CancelSessionCleanup

func (c *Client) CancelSessionCleanup(ctx context.Context, id string) (CleanupJob, error)

CancelSessionCleanup stops future items in a cleanup job.

func (*Client) CancelSessionMigration

func (c *Client) CancelSessionMigration(ctx context.Context, jobID string) (SessionMigrationJob, error)

CancelSessionMigration prevents future items while retaining committed families.

func (*Client) Close

func (c *Client) Close() error

Close releases the underlying connection.

func (*Client) CloseSession

func (c *Client) CloseSession(ctx context.Context, id string) error

CloseSession asks the server to end (and forget) the session under id, tearing down its per-session engine + any per-session MCP manager server-side. The /models restart-now handoff calls it on the OLD session before creating the new one, so a model switch leaves no orphaned server-side session. A nil/unknown id surfaces the server's error; the caller treats a close failure best-effort (the new session is created regardless).

func (*Client) CreateSchedule

func (c *Client) CreateSchedule(ctx context.Context, spec ScheduleSpec) (Schedule, error)

CreateSchedule saves a new schedule (an upsert by name) and returns the created aggregate. It is the SINGLE proto-build point for Create (scheduleSpecToProto). Exported for the planned in-overlay Create form (Phase 3b); the v1 overlay does not call it.

func (*Client) CreateSession

func (c *Client) CreateSession(ctx context.Context, workspace string, mode mecatlv1.PermissionMode, sel ModelSelection) (string, Capabilities, ResolvedModel, error)

CreateSession allocates a server-side session against an absolute workspace and returns its id together with the server's advertised Capabilities. mode is the proto PermissionMode (see ModeFromString). sel is the optional, proto-free model selection (its zero value ⇒ no provider_id/model_id set ⇒ the server's default). This is the SINGLE proto-build point for the model selection: the ui passes a plain ModelSelection and never sees the proto request. The Capabilities are the proto-free mirror of the create response's ServerCapabilities; an older server that omits the field yields the all-false zero value (see capabilitiesFrom). The ResolvedModel is the EFFECTIVE provider+model the server resolved the session to (echoed verbatim); an older server that omits the field yields the zero value (see resolvedModelFrom), which the ui renders as no model segment.

func (*Client) CreateSessionWithCarryover

func (c *Client) CreateSessionWithCarryover(ctx context.Context, workspace string, mode mecatlv1.PermissionMode, sel ModelSelection, sourceSessionID string) (string, Capabilities, ResolvedModel, error)

CreateSessionWithCarryover is CreateSession seeded with the source session's conversation history (issue #20). sourceSessionID, when non-empty, sets source_session_id on the request; the server snapshots the source (it must be at a turn boundary) and seeds the new session's history. The server is the authority on same-vs-cross: a same-provider carryover replays verbatim, a cross-provider carryover strips the prior provider's private replay blobs. An empty sourceSessionID is byte-identical to CreateSession (no carryover). The caller owns closing the source session AFTER the new one is ready (the server snapshotted it at create time). This is the SINGLE proto-build point for the carryover selector — the ui passes plain strings and never sees the proto.

func (*Client) DecideDreamPlan

func (c *Client) DecideDreamPlan(ctx context.Context, id, decision string) (DreamReceipt, error)

func (*Client) DecideLearningProposal

func (c *Client) DecideLearningProposal(ctx context.Context, id, version, decision, reason, project string) (LearningProposal, error)

func (*Client) DeleteSchedule

func (c *Client) DeleteSchedule(ctx context.Context, name string) error

DeleteSchedule removes the schedule stored under name. Idempotent.

func (*Client) DeleteSession

func (c *Client) DeleteSession(ctx context.Context, id string) error

DeleteSession permanently removes a stored session.

func (*Client) DiffLearnedSkill

func (c *Client) DiffLearnedSkill(ctx context.Context, skill LearnedSkill) (string, error)

func (*Client) FireNow

func (c *Client) FireNow(ctx context.Context, name string) (fireID, sessionID string, err error)

FireNow forces an immediate fire of the schedule, returning the per-fire session id (fire_id == session_id on the wire).

func (*Client) ForkSession

func (c *Client) ForkSession(ctx context.Context, srcID, title, reasoningEffort string) (string, error)

ForkSession creates a peer session from the conversation-history snapshot of the session srcID (ADR 0065) and returns the bare new session id. reasoningEffort is the OPTIONAL effort override (ADR 0068): empty inherits the source's effort verbatim; provider and model ALWAYS inherit. This is the SINGLE proto-build point for the fork — the ui passes plain strings and never sees the proto request. The caller owns the follow-up GetSession refetch for the forked session's resolved model/capabilities echo (ForkSessionResponse carries only the id, no streaming).

func (*Client) GenerateDreamPlan

func (c *Client) GenerateDreamPlan(ctx context.Context, target string) (DreamPlan, error)

func (*Client) GetFire

func (c *Client) GetFire(ctx context.Context, fireID string) (ScheduleFire, error)

GetFire returns the fire record stored under fireID. Included for completeness (the overlay uses ListFires; a future per-fire drill-down would use this).

func (*Client) GetLearnedSkill

func (c *Client) GetLearnedSkill(ctx context.Context, project, id, owner, version string) (LearnedSkill, error)

func (*Client) GetLearningProposal

func (c *Client) GetLearningProposal(ctx context.Context, id, project string) (LearningProposal, error)

func (*Client) GetMCPPrompt

func (c *Client) GetMCPPrompt(ctx context.Context, server, name string, args map[string]string) (string, []MCPPromptMessage, error)

GetMCPPrompt renders one prompt by (server, name) with the given arguments.

func (*Client) GetSchedule

func (c *Client) GetSchedule(ctx context.Context, name string) (Schedule, error)

GetSchedule returns the schedule stored under name (the inspect sub-view's refresh).

func (*Client) GetSession

func (c *Client) GetSession(ctx context.Context, id string) (SessionSnapshot, error)

GetSession looks up an existing session by id and returns the server-authored session snapshot subset mecatui needs: the current permission mode and the EFFECTIVE provider+model the server has resolved it to (echoed verbatim, including the context window). This is the self-healing refetch the footer context meter uses: for a session on a LIVE-ONLY model (present in the live /models listing but not the curated catalog — e.g. an OpenRouter openai/gpt-5.5) the create-time echo can carry a 0 / curated-floor window when the async live model-list swap had not yet landed; once it has, the server's ResolvedModel resolves the real live window (live-first via the same windowResolver the engine compacts at) and GetSession returns it. A nil Session/ResolvedModel (older server) yields zero values (see snapshotFrom / resolvedModelFrom).

func (*Client) GetSessionCleanupJob

func (c *Client) GetSessionCleanupJob(ctx context.Context, id string) (CleanupJob, error)

GetSessionCleanupJob reads one caller-bound cleanup job.

func (*Client) GetSessionMigrationJob

func (c *Client) GetSessionMigrationJob(ctx context.Context, jobID string) (SessionMigrationJob, error)

GetSessionMigrationJob fetches sanitized caller-bound durable progress.

func (*Client) GetSessionTranscript

func (c *Client) GetSessionTranscript(ctx context.Context, id string) (SessionTranscript, error)

GetSessionTranscript fetches the authoritative snapshot-derived transcript.

func (*Client) GetSoul

func (c *Client) GetSoul(ctx context.Context) (Soul, error)

GetSoul fetches the resolved soul snapshot (a startup snapshot server-side).

func (*Client) GetStorageHealth

func (c *Client) GetStorageHealth(ctx context.Context) (StorageHealth, error)

GetStorageHealth fetches the authenticated aggregate status.

func (*Client) GetUserModel

func (c *Client) GetUserModel(ctx context.Context) (UserModel, error)

GetUserModel fetches the current user-model index (a LIVE read server-side).

func (*Client) GetUserModelEntry

func (c *Client) GetUserModelEntry(ctx context.Context, key string) (UserModel, error)

GetUserModelEntry fetches exact read-only detail. Older servers ignore the additive key and return Detail=nil, which the UI reports honestly.

func (*Client) List

func (c *Client) List(ctx context.Context, workspace string) ([]Worktree, error)

List implements WorktreeLister. It delegates to ListWorktrees so *Client satisfies the interface while keeping the public ListWorktrees name stable.

func (*Client) ListAgents

func (c *Client) ListAgents(ctx context.Context) ([]Agent, error)

ListAgents lists the resolved agent-definition inventory (a startup snapshot server-side).

func (*Client) ListCommands

func (c *Client) ListCommands(ctx context.Context, workspace string) ([]Command, error)

ListCommands lists the available slash commands for workspace ("" => empty).

func (*Client) ListFires

func (c *Client) ListFires(ctx context.Context, scheduleName string) ([]ScheduleFire, error)

ListFires returns the fire records for a schedule (the inspect sub-view).

func (*Client) ListLearnedSkills

func (c *Client) ListLearnedSkills(ctx context.Context, project string) ([]LearnedSkill, error)

func (*Client) ListLearnedSkillsPage

func (c *Client) ListLearnedSkillsPage(ctx context.Context, project string) ([]LearnedSkill, map[string]uint64, error)

func (*Client) ListLearningProposals

func (c *Client) ListLearningProposals(ctx context.Context, status, cursor string, limit int, project string) (LearningProposalPage, error)

func (*Client) ListMCPPrompts

func (c *Client) ListMCPPrompts(ctx context.Context, server string) ([]MCPPrompt, error)

ListMCPPrompts lists prompts, optionally filtered to one server ("" = all).

func (*Client) ListMCPResources

func (c *Client) ListMCPResources(ctx context.Context, server string) ([]MCPResource, error)

ListMCPResources lists resources, optionally filtered to one server ("" = all).

func (*Client) ListMCPSources

func (c *Client) ListMCPSources(ctx context.Context) ([]MCPSource, error)

ListMCPSources lists the inventory sources (the panel snapshot).

func (*Client) ListModels

func (c *Client) ListModels(ctx context.Context) ([]ModelInfo, []ProviderStatus, error)

ListModels fetches the selectable-model inventory across AVAILABLE providers, (provider_id, id)-sorted (the server sorts; the client preserves that order), plus (issue #262) the per-provider live-listing status.

func (*Client) ListSchedules

func (c *Client) ListSchedules(ctx context.Context) ([]Schedule, error)

ListSchedules lists all stored schedules (the /schedule overlay's initial fetch).

func (*Client) ListSessionPage

func (c *Client) ListSessionPage(ctx context.Context, cursor string) (SessionInventoryPage, error)

ListSessionPage fetches one bounded page. An ABORTED response is the public stale-cursor restart signal; the UI never needs to inspect gRPC status codes.

func (*Client) ListSessions

func (c *Client) ListSessions(ctx context.Context) ([]SessionListItem, error)

ListSessions fetches every bounded inventory page for non-interactive callers such as --resume-latest. Interactive pickers use ListSessionPage directly so page one can render before continuation work begins.

func (*Client) ListSkillChanges

func (c *Client) ListSkillChanges(ctx context.Context, project string) ([]SkillChange, error)

func (*Client) ListSkills

func (c *Client) ListSkills(ctx context.Context) ([]Skill, error)

ListSkills lists the resolved skills inventory (a startup snapshot server-side).

func (*Client) ListToolHiveGroups

func (c *Client) ListToolHiveGroups(ctx context.Context) ([]string, error)

ListToolHiveGroups lists the configured ToolHive group names.

func (*Client) ListWorktrees

func (c *Client) ListWorktrees(ctx context.Context, workspace string) ([]Worktree, error)

ListWorktrees lists the git worktrees of the repo rooted at workspace ("" => empty). It is the proto-build point for the /worktrees overlay.

func (*Client) MutateLearnedSkill

func (c *Client) MutateLearnedSkill(ctx context.Context, action string, skill LearnedSkill) (LearnedSkill, error)

func (*Client) OpenConverse

func (c *Client) OpenConverse(ctx context.Context) (*Stream, error)

OpenConverse opens a fresh bidi Converse stream and wraps it in a Stream (serialised Sends + a Recver for the reader goroutine). Each user prompt opens one stream — matching the "one run per Converse" model. The stream's lifetime is bound to ctx; cancelling ctx aborts the run.

func (*Client) PauseSchedule

func (c *Client) PauseSchedule(ctx context.Context, name string) error

PauseSchedule disables a schedule without deleting it (Enabled=false).

func (*Client) PlanSessionCleanup

func (c *Client) PlanSessionCleanup(ctx context.Context, scope CleanupScope) (CleanupPlan, error)

PlanSessionCleanup requests a read-only cleanup plan.

func (*Client) PlanSessionMigration

func (c *Client) PlanSessionMigration(ctx context.Context) (SessionMigrationPlan, error)

PlanSessionMigration fetches a read-only, caller-bound physical migration estimate.

func (*Client) PreflightSessionAdoption

func (c *Client) PreflightSessionAdoption(ctx context.Context, sourceID string, bindings AdoptionBindings) (AdoptionPreflight, error)

PreflightSessionAdoption asks the server to validate one legacy source and explicit bindings.

func (*Client) ReadMCPResource

func (c *Client) ReadMCPResource(ctx context.Context, server, uri string) ([]MCPResourceContents, error)

ReadMCPResource reads one resource by (server, uri).

func (*Client) ReflectSession

func (c *Client) ReflectSession(ctx context.Context, sessionID string) (ReflectionReceipt, error)

func (*Client) RenameSession

func (c *Client) RenameSession(ctx context.Context, id, title string) (SessionSnapshot, error)

RenameSession replaces a stored session title and returns the authoritative server snapshot, including title provenance.

func (*Client) ResumeSchedule

func (c *Client) ResumeSchedule(ctx context.Context, name string) error

ResumeSchedule re-enables a paused schedule (Enabled=true).

func (*Client) ResumeSessionMigration

func (c *Client) ResumeSessionMigration(ctx context.Context, jobID string, batchSize int32) (SessionMigrationJob, error)

ResumeSessionMigration processes another bounded batch of an existing job.

func (*Client) RollbackLearnedSkill

func (c *Client) RollbackLearnedSkill(ctx context.Context, skill LearnedSkill) (LearnedSkill, error)

func (*Client) SetMode

func (c *Client) SetMode(ctx context.Context, id, mode string) (string, error)

SetMode asks the server to change the session's permission posture and returns the updated authoritative mode. Mid-turn changes are rejected by the server; callers that want next-prompt semantics should defer and retry once idle.

func (*Client) StreamSessionEvents

func (c *Client) StreamSessionEvents(ctx context.Context, id string) (*EventStream, error)

StreamSessionEvents opens the durable-event-log replay (cloud-native Phase 3a read-back) for session id and wraps the returned server stream in an EventStream. The replay yields *mecatlv1.Event directly (NO ConverseResponse envelope), and INCLUDES the three log-only kinds (approval/user_prompt/ compaction.archive) — a transcript viewer wants the verdicts and user prompts; metadata-only by construction (gauntlet #7). An unknown id yields an EMPTY stream (absence is data) → a single StreamClosedMsg; a server with no durable EventLog returns gRPC UNIMPLEMENTED → a StreamErrMsg.

func (*Client) StreamSessionLive

func (c *Client) StreamSessionLive(ctx context.Context, id string) (*EventStream, error)

StreamSessionLive opens the LIVE per-session event stream (ADR 0075 Scenario 5): the server pushes events including the three log-only kinds (approval/user_prompt/compaction.archive) as they occur — principally fire-result delivery notes for the active session. It wraps the returned server stream in an EventStream. The SAME projection path (EventToMsg → readEventLoop) means a live delivery and a replay produce the SAME DeliveryNoteMsg. A server with no live subscription bridge returns gRPC UNIMPLEMENTED → StreamErrMsg.

func (*Client) UndoLearningPromotion

func (c *Client) UndoLearningPromotion(ctx context.Context, id, version, project string) (LearningProposal, error)

type Clipboard

type Clipboard interface {
	Read(ctx context.Context) (mime string, data []byte, err error)
	// ReadPrimary reads the PRIMARY selection (the X11/Wayland select-to-copy
	// buffer, pasted by middle-click) as text. It returns ErrNoClipboardTool when
	// no backend binary exists OR the platform has no primary selection at all
	// (macOS/Windows) — the caller is expected to fall back to the OSC52 primary
	// read (tea.ReadPrimaryClipboard) in that case — and ErrEmptyClipboard when a
	// backend exists but the primary selection is empty (both wl-paste and xclip
	// exit non-zero on an empty selection, so a subprocess error maps here too).
	ReadPrimary(ctx context.Context) (string, error)
	// Write copies data (of the given mime, e.g. "text/plain") into the OS
	// clipboard via the platform binary. It is best-effort; a missing backend or a
	// failed subprocess returns an error the caller is expected to treat as a muted
	// non-event, never a transcript error.
	Write(ctx context.Context, mime string, data []byte) error
}

Clipboard reads AND writes the OS clipboard. Read returns the clipboard's content as a (mime, data) pair: an image/* mime when the clipboard holds an image (which the UI stages as an inline media part), or a text mime (text/plain) when it holds text (which the UI inserts into the textarea). The mime tells the caller which branch to take. Write is the BEST-EFFORT shell-clipboard fallback behind the in-app text-selection copy: the UI's primary copy path is OSC52 (tea.SetClipboard), and Write mirrors the same payload into the platform clipboard binary so the copy still lands on terminals that don't honour OSC52. A Write error is non-fatal — OSC52 is the primary and the copy is considered to have succeeded if either path works — so the UI must NOT surface it loudly. A nil Clipboard on Deps cleanly disables BOTH ctrl+v read and the shell write (the same convention as a nil MCP/Cmds collaborator); the OSC52 copy still runs. The UI imports client, so the interface + its sentinels live here, not in the ui package.

func NewClipboard

func NewClipboard() Clipboard

NewClipboard wires the real backend: exec.CommandContext(...).Output(), the real PATH lookup + environment, and the 3s timeout.

type Command

type Command struct {
	Name        string
	Description string
	Builtin     bool
}

Command is one discovered slash command (proto Command, proto-free): its invocation name (without the leading "/") and a short description. Builtin marks a CLIENT-SIDE command (e.g. /clear, /help) injected by the ui rather than discovered from the server; it stays false for every server row (the zero value). The ui uses it to dispatch the row to a Model action instead of expanding it server-side, and to win name collisions with workspace commands.

type Commander

type Commander interface {
	ListCommands(ctx context.Context, workspace string) ([]Command, error)
}

Commander is the subset of *Client the ui's command palette needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type CommandsMsg

type CommandsMsg struct {
	Commands []Command
	Err      error
}

CommandsMsg carries a ListCommands success (the palette's command set). Err is set on failure; the palette degrades quietly to "no commands" rather than surfacing an error chrome, so the field is informational.

type CompactionArchiveMsg

type CompactionArchiveMsg struct {
	Replaced []ConversationMessage
}

CompactionArchiveMsg is the pre-compaction conversation (EvCompactionArchive), relayed only by the replay, so a transcript recovers the dropped turns. It is the parent's OWN conversation (gauntlet #7 — no child content).

type CompactionMsg

type CompactionMsg struct{ Text string }

CompactionMsg is a muted "history compacted" notice.

type ConnectErrMsg

type ConnectErrMsg struct{ Err error }

ConnectErrMsg reports a dial/CreateSession failure.

type ContentBlock

type ContentBlock struct {
	// Kind is the block kind (text/image/audio/resource-link/embedded-resource/
	// structured-content). UNSPECIFIED is treated as absent by the renderer.
	Kind ContentBlockKind
	// MimeType is the IANA media type of inline bytes (image/audio/blob).
	MimeType string
	// Data is the inline content bytes (image/audio/blob).
	Data []byte
	// URL is the remote reference (resource-link URI, or URL-sourced media).
	URL string
	// Text is the text payload (text, embedded-resource text, or structured-content JSON).
	Text string
	// Name is the resource-link name.
	Name string
	// Title is the resource-link title.
	Title string
	// Description is the resource-link description.
	Description string
}

ContentBlock is the plain-data mirror of mecatlv1.ContentBlock, duplicated here so the ui stays proto-free (the same discipline as Usage). Only the fields the ui renders are carried: the kind discriminant plus the per-kind payload (mime/data/url for media, text for text/embedded/structured, and the resource-link name/uri).

type ContentBlockKind

type ContentBlockKind string

ContentBlockKind discriminates a ContentBlock, mirroring the proto ContentBlock.Kind enum as a plain string so the ui keys off it without importing proto.

const (
	// ContentBlockUnspecified is the zero value; consumers treat it as absent.
	ContentBlockUnspecified ContentBlockKind = ""
	// ContentBlockText is a text block (already represented in the model-facing body).
	ContentBlockText ContentBlockKind = "text"
	// ContentBlockImage is an inline image block.
	ContentBlockImage ContentBlockKind = "image"
	// ContentBlockAudio is an inline audio block.
	ContentBlockAudio ContentBlockKind = "audio"
	// ContentBlockResourceLink is a reference to an MCP resource by URI.
	ContentBlockResourceLink ContentBlockKind = "resource_link"
	// ContentBlockEmbeddedResource is an embedded MCP resource (text or blob).
	ContentBlockEmbeddedResource ContentBlockKind = "embedded_resource"
	// ContentBlockStructuredContent is a JSON structured-content block.
	ContentBlockStructuredContent ContentBlockKind = "structured_content"
)

type ConvToolCall

type ConvToolCall struct {
	ID   string
	Name string
	Args string // raw JSON
}

ConvToolCall is the proto-free mirror of one assistant-message tool invocation (mecatlv1.ToolCall as carried by a ConversationMessage). Distinct from ToolCallMsg (an event) — see the ConversationMessage doc.

type ConvToolResult

type ConvToolResult struct {
	CallID            string
	Content           string
	IsError           bool
	Blocks            []ContentBlock
	StructuredContent string
}

ConvToolResult is the proto-free mirror of a tool-role message's result (mecatlv1.ToolResult as carried by a ConversationMessage). Distinct from ToolResultMsg (an event) — see the ConversationMessage doc.

type ConversationMessage

type ConversationMessage struct {
	Role            string
	Text            string
	ToolCalls       []ConvToolCall
	ToolResult      *ConvToolResult
	Reasoning       string
	ProviderPhase   string
	ReasoningItemID string
	Parts           []ContentBlock
}

ConversationMessage is the proto-free mirror of mecatlv1.ConversationMessage: one immutable entry in the model-visible conversation history. It mirrors the session.Message value object — Role + Text + the assistant's ToolCalls + an optional tool-role ToolResult + the opaque provider replay blobs (Reasoning / ProviderPhase / ReasoningItemID) + the user-role media Parts. The ToolCalls/ToolResult fields use the dedicated ConvToolCall/ConvToolResult structs below (NOT the event-msg types ToolCallMsg/ToolResultMsg — those are EVENTS, not message PARTS: a tool.call event is a transient status line, a ConvToolCall is the persisted assistant message part; overloading them would conflate the two lifecycles). If the Phase-3 ui only renders Role+Text, the extra fields are unused-but-cheap.

type DeliveryNoteMsg

type DeliveryNoteMsg struct {
	ScheduleName string
	FireID       string
	Text         string
	Parts        []ContentBlock
}

DeliveryNoteMsg is a fire-result delivery note (ADR 0075 Scenario 5): the fenced-untrusted harness note the scheduler delivered into the origin session. It is projected from an EvUserPrompt event whose text starts with the "[scheduled task <name> (fire <id>) ..." provenance header renderFireDelivery emits. ScheduleName + FireID are structured fields the ui renders as a distinct delivery card with a scheduled-task affordance; Text carries the full note body verbatim (the same fenced-untrusted content the engine recorded). Parts carries any non-text media that rode alongside the note (normally nil — delivery notes are text-only). The live subscription and the replay (StreamSessionEvents) path share this type via EventToMsg.

type DialConfig

type DialConfig struct {
	Server    string // host:port, e.g. 127.0.0.1:8080
	AuthToken string // optional bearer; sent as "authorization: Bearer <tok>"
	UseTLS    bool   // enable transport TLS
	TLSCAFile string // optional custom CA bundle for server verification
	Insecure  bool   // skip TLS verification (testing only; with UseTLS)
}

DialConfig is the connection-time configuration: address, optional bearer token, and TLS posture. It mirrors mecated's trust model — loopback is unauthenticated plaintext by default; non-loopback may need a token and/or TLS/mTLS.

type DreamClient

type DreamClient interface {
	GenerateDreamPlan(context.Context, string) (DreamPlan, error)
	DecideDreamPlan(context.Context, string, string) (DreamReceipt, error)
}

type DreamDecisionErrorKind

type DreamDecisionErrorKind uint8
const (
	DreamDecisionUnknown DreamDecisionErrorKind = iota
	DreamDecisionInProgress
	DreamDecisionConflict
	DreamDecisionTerminalConflict
	DreamDecisionPlanGone
)

func ClassifyDreamDecisionError

func ClassifyDreamDecisionError(err error) DreamDecisionErrorKind

type DreamMsg

type DreamMsg struct {
	Plan       *DreamPlan
	Receipt    *DreamReceipt
	Err        error
	Generation uint64
	RequestID  uint64
}

type DreamOperation

type DreamOperation struct {
	Kind                   string
	Survivor               DreamParticipant
	Sources                []DreamParticipant
	Replacement            DreamReplacement
	Reason                 string
	ExactDuplicateEligible bool
}

type DreamParticipant

type DreamParticipant struct {
	Key, Value, Description string
}

type DreamPlan

type DreamPlan struct {
	ID, Target                         string
	ExpiresAt                          time.Time
	PlannedOperationCount, SourceCount int
	Operations                         []DreamOperation
}

type DreamReceipt

type DreamReceipt struct {
	ID, Target, Disposition                       string
	Planned, Applied, Conflicted, Skipped, Failed int
}

type DreamReplacement

type DreamReplacement struct {
	Value, Description string
}

type DreamTargetCapability

type DreamTargetCapability struct {
	Generate, Decide  bool
	UnavailableReason string
}

type EventRecver

type EventRecver interface {
	Recv() (*mecatlv1.Event, error)
}

EventRecver is the minimal receive side of a server-streaming Event replay (StreamSessionEvents): it yields *mecatlv1.Event directly, with NO ConverseResponse envelope. The generated grpc.ServerStreamingClient[Event] satisfies it (its Recv returns *Event); tests supply a scripted fake. It is the Event-replay analogue of Recver (which wraps one extra ConverseResponse envelope for the bidi Converse stream).

type EventStream

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

EventStream wraps one open StreamSessionEvents replay: a receive side ONLY (read-only, no Send side, unlike the bidi Converse Stream). The generated grpc.ServerStreamingClient[mecatlv1.Event] satisfies the EventRecver in production; tests supply a scripted fake.

func NewEventStream

func NewEventStream(recv EventRecver) *EventStream

NewEventStream wraps an EventRecver in an EventStream. Pass the generated grpc.ServerStreamingClient[mecatlv1.Event] from StreamSessionEvents in production; pass a fake EventRecver in tests.

func (*EventStream) ReadLoop

func (s *EventStream) ReadLoop(ctx context.Context, out chan<- tea.Msg)

ReadLoop runs the receive loop on its OWN goroutine over the replay stream: it delegates to readEventLoop (the shared translation path), so the replay and a live Converse run project identically for the same event sequence. It pushes translated tea.Msgs onto out, then closes out when the replay ends. A clean EOF yields StreamClosedMsg; any other error yields StreamErrMsg. Run it off the Bubble Tea update goroutine; pass a cancellable context so the ui can tear it down when it leaves the transcript view.

type FakeEventStream

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

FakeEventStream is a scripted, in-memory EventRecver for replay tests. It is the exported twin of the unexported fakeEventStream in events_test.go; keep their Recv semantics byte-identical (the projection-equivalence test depends on the same drain-then-EOF/error contract).

func NewFakeEventStream

func NewFakeEventStream(script ...*mecatlv1.Event) *FakeEventStream

NewFakeEventStream returns a scripted EventRecver over the given event slice. A zero-arg call yields an empty stream (Recv returns io.EOF immediately) so a proto-free caller can build an offline replay without naming the proto type.

func (*FakeEventStream) Recv

func (f *FakeEventStream) Recv() (*mecatlv1.Event, error)

Recv implements EventRecver: it yields the next scripted event, then io.EOF (or the configured endErr) once the script is exhausted.

func (*FakeEventStream) WithEndErr

func (f *FakeEventStream) WithEndErr(err error) *FakeEventStream

WithEndErr configures the error returned once the script drains (nil ⇒ io.EOF).

type HookDecision

type HookDecision string

HookDecision is the outcome a hook fire produced, as plain data the ui colours and ranks without touching proto. Mirrors mecatlv1.HookDecision.

const (
	// HookInfo is a benign, informational hook notice (the default).
	HookInfo HookDecision = "info"
	// HookBlocked means the hook vetoed the action — the most severe notice.
	HookBlocked HookDecision = "blocked"
	// HookModified means the hook rewrote the action's payload without blocking.
	HookModified HookDecision = "modified"
	// HookAdvisory means the hook flagged content as a finding but did NOT alter
	// the call/result (advisory guardrail). Client-visible warning, model-invisible.
	HookAdvisory HookDecision = "advisory"
)

type HookMsg

type HookMsg struct {
	Text     string
	Phase    string
	Tool     string
	Decision HookDecision
}

HookMsg is an inline hook notice. Beyond the human-readable Text it carries the structured Phase (lifecycle point, e.g. "PreToolUse"), the related Tool (for per-tool phases), and the Decision (info/blocked/modified/advisory) so the ui can render it distinctly from a compaction notice and colour a blocked or advisory hook.

type LearnedSkill

type LearnedSkill struct {
	ID, Name, Version, Revision, State, OwnerAgent, Description, Body, Supersedes string
	Project, PublicationStatus, PublicationError                                  string
	Generation                                                                    uint64
	EvidenceCount                                                                 int
	Evaluations                                                                   []SkillEvaluation
	Receipts                                                                      []SkillChange
}

type LearnedSkillClient

type LearnedSkillClient interface {
	ListLearnedSkills(context.Context, string) ([]LearnedSkill, error)
	GetLearnedSkill(context.Context, string, string, string, string) (LearnedSkill, error)
	MutateLearnedSkill(context.Context, string, LearnedSkill) (LearnedSkill, error)
	RollbackLearnedSkill(context.Context, LearnedSkill) (LearnedSkill, error)
	ListSkillChanges(context.Context, string) ([]SkillChange, error)
	DiffLearnedSkill(context.Context, LearnedSkill) (string, error)
}

LearnedSkillClient is the optional lifecycle half of the existing /skills panel.

type LearnedSkillMsg

type LearnedSkillMsg struct {
	Skill              *LearnedSkill
	Project            string
	Generation         uint64
	SelectedSkillID    string
	SelectedOwnerAgent string
	SelectedVersion    string
	ExpectedRevision   string
	TargetVersion      string
	Action             string
	RequestID          uint64
	PublicationStatus  string
	PublicationError   string
	Err                error
}

type LearnedSkillsMsg

type LearnedSkillsMsg struct {
	Skills      []LearnedSkill
	Project     string
	Generations map[string]uint64
	RequestID   uint64
	Err         error
}

type LearningDecision

type LearningDecision struct {
	Kind, Actor, Reason string
	At                  time.Time
}

type LearningEvidence

type LearningEvidence struct {
	SessionID, Locator, ToolCallID, Digest, Availability, Preview string
	Ordinal                                                       int
	EventSeq                                                      int64
	Available                                                     bool
}

type LearningPromotion

type LearningPromotion struct {
	MemoryKey, PreviousVersion, ResultVersion string
	PreviousExists                            bool
}

type LearningProposal

type LearningProposal struct {
	ID, Version, Status, Kind, Key, Value, Description, Title, Body string
	Evidence                                                        []LearningEvidence
	Triggers                                                        []string
	Decisions                                                       []LearningDecision
	Promotion                                                       *LearningPromotion
	CreatedAt, UpdatedAt                                            time.Time
	ProjectScoped                                                   bool
	Project                                                         string
	PromotionAvailable                                              bool
	PromotionUnavailableReason                                      string
	LearnedSkillID                                                  string
}

type LearningProposalPage

type LearningProposalPage struct {
	Proposals          []LearningProposal
	NextCursor         string
	OperatorNextCursor string
	ProjectNextCursor  string
	OperatorDone       bool
	ProjectDone        bool
}

type LiveReconnectedMsg

type LiveReconnectedMsg struct{}

LiveReconnectedMsg marks a successful live-feed reopen: the reconnect loop re-opened StreamSessionLive after draining the durable catch-up. The ui clears the degraded footer state and re-arms the live reader (waitLiveCmd) off a FRESH live channel — the reconnect channel's job is done.

type LiveReconnectingMsg

type LiveReconnectingMsg struct {
	Attempt int
	Err     error
}

LiveReconnectingMsg marks one attempt of the live-feed reconnect+catch-up loop (issue #387): the live feed dropped (StreamClosedMsg/StreamErrMsg on the live reader) and the client is recovering it with bounded exponential backoff. It carries the 1-based Attempt index and the Err that closed the previous attempt (nil on the first attempt). The ui renders a degraded footer state from it. The loop also drains the durable catch-up (StreamSessionEvents) before each live reopen so delivery notes emitted during the gap are recovered; those catch-up events arrive as ordinary event msgs (DeliveryNoteMsg/…) on the SAME reconnect channel, NOT wrapped in this msg.

type LiveStreamer

type LiveStreamer interface {
	StreamSessionLive(ctx context.Context, id string) (*EventStream, error)
}

LiveStreamer opens the live event feed for a session.

type MCP

type MCP interface {
	ListMCPResources(ctx context.Context, server string) ([]MCPResource, error)
	ReadMCPResource(ctx context.Context, server, uri string) ([]MCPResourceContents, error)
	ListMCPPrompts(ctx context.Context, server string) ([]MCPPrompt, error)
	GetMCPPrompt(ctx context.Context, server, name string, args map[string]string) (string, []MCPPromptMessage, error)
	ListMCPSources(ctx context.Context) ([]MCPSource, error)
	ListToolHiveGroups(ctx context.Context) ([]string, error)
}

MCP is the subset of *Client the ui's MCP commands need. Splitting it out keeps the ui injectable with a fake for offline golden tests.

type MCPErrMsg

type MCPErrMsg struct {
	Op    string
	Class MCPErrorClass
	Err   error
}

MCPErrMsg is the classified failure msg shared by all MCP RPCs. Op names the action ("list resources", "read resource", …) for the ui; Class drives the distinct rendering (input vs server vs not-configured); Err is the raw error for detail.

func (MCPErrMsg) Error

func (e MCPErrMsg) Error() string

Error satisfies error so MCPErrMsg can be logged/compared directly.

type MCPErrorClass

type MCPErrorClass int

MCPErrorClass classifies an MCP RPC failure so the ui can render it differently. It is derived from the gRPC status code per the Stage C contract: InvalidArgument → input, FailedPrecondition → not configured, everything else (Internal, Unavailable, …) → a server-side fault.

const (
	// MCPErrInput is a client input error: an unknown server name or a missing
	// required field. Surfaced as "fix input". (gRPC InvalidArgument.)
	MCPErrInput MCPErrorClass = iota
	// MCPErrServer is a downstream/transport fault, or an unknown URI/prompt.
	// Surfaced as "server-side problem". (gRPC Internal / Unavailable / other.)
	MCPErrServer
	// MCPErrNotConfigured means no MCP provider is configured on mecated.
	// Surfaced as "MCP not configured". (gRPC FailedPrecondition.)
	MCPErrNotConfigured
)

func (MCPErrorClass) String

func (c MCPErrorClass) String() string

String renders the class as a short, stable label (used in the ui copy).

type MCPGroupsMsg

type MCPGroupsMsg struct {
	Groups []string
}

MCPGroupsMsg carries a ListToolHiveGroups success.

type MCPPrompt

type MCPPrompt struct {
	Server      string
	Name        string
	Title       string
	Description string
	Arguments   []MCPPromptArgument
}

MCPPrompt is one prompt template a server exposes (proto McpPrompt).

type MCPPromptArgument

type MCPPromptArgument struct {
	Name        string
	Title       string
	Description string
	Required    bool
}

MCPPromptArgument is one templated argument of a prompt (proto McpPromptArgument).

type MCPPromptGotMsg

type MCPPromptGotMsg struct {
	Server      string
	Name        string
	Description string
	Messages    []MCPPromptMessage
}

MCPPromptGotMsg carries a GetMcpPrompt success.

type MCPPromptMessage

type MCPPromptMessage struct {
	Role string
	Text string
}

MCPPromptMessage is one rendered message of a got prompt (proto McpPromptMessage).

type MCPPromptsMsg

type MCPPromptsMsg struct {
	Server  string
	Prompts []MCPPrompt
}

MCPPromptsMsg carries a ListMcpPrompts success.

type MCPResource

type MCPResource struct {
	Server      string
	URI         string
	Name        string
	Title       string
	Description string
	MimeType    string
	Size        int64
	ReadOnly    bool
}

MCPResource is one resource a server exposes (proto McpResource, proto-free).

type MCPResourceContents

type MCPResourceContents struct {
	URI      string
	MimeType string
	Text     string
	Blob     []byte
}

MCPResourceContents is one chunk of a read resource (proto McpResourceContents). Blob is the raw bytes when the resource is binary (Text empty).

type MCPResourceReadMsg

type MCPResourceReadMsg struct {
	Server   string
	URI      string
	Contents []MCPResourceContents
}

MCPResourceReadMsg carries a ReadMcpResource success.

type MCPResourcesMsg

type MCPResourcesMsg struct {
	Server    string // the requested server filter ("" = all)
	Resources []MCPResource
}

MCPResourcesMsg carries a ListMcpResources success.

type MCPServerInfo

type MCPServerInfo struct {
	Name      string
	URL       string
	Transport string
	Group     string
}

MCPServerInfo is one MCP server within a source (proto McpServerInfo).

type MCPSource

type MCPSource struct {
	Name        string
	Kind        string
	Enabled     bool
	Group       string
	Servers     []MCPServerInfo
	Diagnostics []string
}

MCPSource is one inventory source — a ToolHive group or config block — with its servers and any diagnostics (proto McpSource). NOTE: ListMcpSources reflects a startup snapshot; servers started AFTER mecated launched won't appear.

type MCPSourcesMsg

type MCPSourcesMsg struct {
	Sources []MCPSource
}

MCPSourcesMsg carries a ListMcpSources success (the panel inventory).

type ManualDreamCapabilities

type ManualDreamCapabilities struct {
	ProjectMemory DreamTargetCapability
	UserModel     DreamTargetCapability
}

type MediaResult

type MediaResult struct {
	// Parts are the proto Content parts to attach to the Prompt frame.
	Parts []*mecatlv1.Content
	// Descriptors mirror Parts 1:1: one human descriptor per media part, e.g.
	// "image/png (inline)". They feed conversation.addUserWithMedia for the 📎 lines.
	Descriptors []string
	// InlineText holds the delimited bodies of @-mentioned TEXT files (no media
	// kind sniffed), in mention order. The ui appends these to the prompt text so
	// the model sees the file content inline (Gemini-style), since text is not a
	// media part.
	InlineText []string
}

MediaResult is the outcome of expanding a prompt's @-mentions: the built media parts to send over the wire, a human-readable descriptor per part (for the transcript's "📎 …" placeholder lines), and any inlined text-file bodies (each already wrapped in a delimited block) the ui splices into the prompt text.

func ExpandMentions

func ExpandMentions(paths []string, caps Capabilities) (MediaResult, error)

ExpandMentions reads each @-mentioned path (the UI has already stat-filtered these to EXISTING REGULAR FILES; a token that is not a real file stays literal prose and never reaches here), sniffs its content type, and routes it to exactly one of the three spec outcomes:

  • image/* or audio/* → an inline media Content part (caps-gated and size-capped);
  • text/* → inlined into the prompt as a delimited block;
  • anything else → a CLEAR ERROR (an explicitly-@'d binary such as a PDF or zip is NOT inlined as raw-byte garbage; the user gets a loud refusal).

It is the SINGLE place proto Content is constructed from a file on the client side — the ui passes only resolved path strings and the proto-free Capabilities, keeping the ui free of proto + os.

It is LOUD on any failure (unreadable file, an unsupported file type, a media kind the server's provider cannot consume, an oversize part, too many parts, or too many total bytes): ANY error returns a zero result and that error, and the caller must send NOTHING (loud-reject, never silent-drop). It stays defensive — it still errors if handed a path it cannot read — but the UI stat-filter is the gate that decides attachment-vs-prose. Per-part and aggregate size caps mirror the domain (see the const block); the server re-validates regardless.

func (MediaResult) CheckAggregateCaps

func (r MediaResult) CheckAggregateCaps() error

CheckAggregateCaps re-validates the per-PROMPT media aggregate (part count and total bytes) over Parts. ExpandMentions already enforces these caps over its own mention parts, but the ui appends clipboard / pasted-path parts to the same MediaResult AFTER that check, so the COMBINED set can exceed the caps. The ui calls this once after merging so a mention-heavy + clipboard-heavy prompt loud-rejects client-side (keep input, send nothing) instead of being rejected post-send by the server. The per-FILE cap is enforced at construction (buildMediaPart), so this only re-checks the two aggregate limits.

type ModeChangedMsg

type ModeChangedMsg struct {
	SessionID string
	Requested string
	Mode      string
	Err       error
}

ModeChangedMsg carries the async result of SetModeCmd.

type ModeSetter

type ModeSetter interface {
	SetMode(ctx context.Context, id, mode string) (string, error)
}

ModeSetter changes a server-side session's permission mode.

type ModelInfo

type ModelInfo struct {
	ID           string // the opaque model_id sent on CreateSession
	ProviderID   string // the provider_id sent on CreateSession
	DisplayName  string // human label; falls back to ID server-side already
	Image        bool   // accepts image input
	Reasoning    bool   // emits reasoning
	ContextLimit int64  // total context window in tokens; 0 = unknown
}

ModelInfo is one selectable model's listing metadata — the proto-free mirror of mecatlv1.ModelInfo. The ui renders the /models picker purely from these. The (ProviderID, ID) pair is what CreateSession ultimately carries.

type ModelLister

type ModelLister interface {
	ListModels(ctx context.Context) ([]ModelInfo, []ProviderStatus, error)
}

ModelLister is the subset of *Client the ui's /models picker needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type ModelRetryMsg

type ModelRetryMsg struct{ Text string }

ModelRetryMsg is the durable, client-visible failed-step retry lifecycle notice.

type ModelSelection

type ModelSelection struct {
	ProviderID string
	ModelID    string
	// ReasoningEffort is the chosen reasoning-effort tier (ADR 0055): "" / "auto"
	// (unset — operator/provider default) or low/medium/high/xhigh/max. It is sent
	// on CreateSession.reasoning_effort and is meaningful WITHOUT a provider/model
	// (it rides the server-default provider), so an effort-only selection is NOT
	// zero (IsZero counts it).
	ReasoningEffort string
}

ModelSelection is the chosen (provider, model) the client sends on CreateSession. Proto-free; the zero value means "server default" (no provider_id/model_id set on the request).

func (ModelSelection) IsZero

func (s ModelSelection) IsZero() bool

IsZero reports whether the selection is empty (⇒ the server picks its default). An effort-only selection is NOT zero (the client must still send it).

func (ModelSelection) Matches

func (s ModelSelection) Matches(m ModelInfo) bool

Matches reports whether m is the model this selection names (by provider + id).

type ModelsMsg

type ModelsMsg struct {
	Models       []ModelInfo
	Statuses     []ProviderStatus
	RequestToken uint64
	Err          error
}

ModelsMsg carries a ListModels result for the /models picker. RequestToken is copied from the ListModelsCmd request so the Model can reject stale catalog results. Err is set on failure; the picker surfaces it rather than silently degrading. Statuses is the (possibly empty) per-provider live-listing outcome list — empty for every deployment without a surfaced live-inventory provider.

type NoProgressMsg

type NoProgressMsg struct{ Text string }

NoProgressMsg is a muted advisory notice emitted when a completed turn produced no tool call and no meaningful text and the loop is nudging the model to continue (or giving up after the budget). It is rendered as a transient status line, like CompactionMsg; it carries only the harness-authored reason Text (no model content).

type ParallelKind

type ParallelKind string

ParallelKind discriminates the parallel.* event kinds carried by a ParallelMsg, so the ui switches on a plain value rather than re-deriving it from the proto. The run-level start/end are their own kinds; the per-branch events carry the branch_* kinds (from the proto Parallel.kind discriminant).

const (
	// ParallelStart marks a Parallel fork-join run beginning (Join/BranchCount set).
	ParallelStart ParallelKind = "start"
	// ParallelBranchStart marks one branch beginning (BranchIndex/BranchLabel/Goal set).
	ParallelBranchStart ParallelKind = "branch_start"
	// ParallelBranchTool marks one branch's child tool resolving (ToolName/IsError/ToolCount set).
	ParallelBranchTool ParallelKind = "branch_tool"
	// ParallelBranchEnd marks one branch finishing (Stop/Usage/DurationMs/Failed/Workspace set).
	ParallelBranchEnd ParallelKind = "branch_end"
	// ParallelEnd marks a Parallel run finishing (Join/Winner/WinnerWorkspace/Usage/Stop set).
	ParallelEnd ParallelKind = "end"
)

type ParallelMsg

type ParallelMsg struct {
	Kind         ParallelKind
	ParentCallID string
	// Join / BranchCount are set on ParallelStart and ParallelEnd (run-level).
	Join        string
	BranchCount int
	// BranchIndex is the stable per-branch key, set on every branch_* kind.
	BranchIndex int
	// ChildID is the branch's child SESSION id ("parallel-<callID>-<i>") — the
	// CancelChild handle, set on branch_start/branch_end. Carried explicitly so the
	// ui never derives the (server-internal) id grammar; empty from an older server.
	ChildID string
	// BranchLabel / Goal are set on ParallelBranchStart.
	BranchLabel string
	Goal        string
	// RoutedCategory/RoutedModel are the OPT-IN semantic model router's bare metadata
	// (a category label + a model id) for a routed branch, set on branch_start only,
	// empty when no router classified the branch (ADR 0031 / ADR 0034) — never branch
	// content, so gauntlet #7 holds.
	RoutedCategory string
	RoutedModel    string
	// RoutingReason names WHY the opt-in router did NOT classify this branch
	// (branch_start only; issue #397 / ADR 0083): empty on a routed hit, otherwise a
	// bounded harness/composition gate or classifier-miss string. Bare metadata —
	// never branch content — so gauntlet #7 holds.
	RoutingReason string
	// Model is the concrete model id the branch ACTUALLY ran on (branch_start only),
	// regardless of how it was chosen (issue #112 / ADR 0035). When routed, Model ==
	// RoutedModel. Bare metadata, never branch content, so gauntlet #7 holds.
	Model string
	// ToolName / IsError / ToolCount carry per-branch tool activity (branch_tool;
	// ToolCount is also final on branch_end).
	ToolName string
	IsError  bool
	// InnerKind / Text / Detail are the ADR-0079 bounded previews, set on a
	// branch_tool event per the branch's forwarded inner event kind (tool.call /
	// tool.result / message.delta), exactly as on SubagentMsg; empty from an older
	// server. The server clamp-scrubs every preview; the ui caps again on render.
	InnerKind string
	Text      string
	Detail    string
	ToolCount int
	// Failed / Workspace are set on ParallelBranchEnd (Workspace is the branch's fork root).
	Failed    bool
	Workspace string
	// Stop is the branch terminal (branch_end) or the run-level stop (end).
	Stop string
	// Usage is the branch's cumulative usage (branch_end) or the run total (end).
	Usage Usage
	// DurationMs is the branch's wall-clock duration (branch_end).
	DurationMs int64
	// Winner / WinnerWorkspace are set on ParallelEnd: the real winning branch index
	// (-1 for join=all / none-succeeded) and its preserved fork root.
	Winner          int
	WinnerWorkspace string
}

ParallelMsg is the BOUNDED projection of a Parallel fork-join run, as plain data the ui renders in the ctrl+a Parallel tab. Unlike the FLAT SubagentMsg, a Parallel run is a GROUP: N branches of ONE call (keyed by ParentCallID) sharing a join strategy + a single winner + preserved per-branch fork paths. It carries ids, a goal label, child tool names/counts, usage, stop, duration, the join strategy, the winner index, the fork-root PATHS (handles already in the result text, not branch content) — plus the BOUNDED content previews the server clamp-scrubs per ADR 0079 (InnerKind/Text/Detail on a branch_tool event), bounded, scrubbed, and client-only (never entering the parent conversation — gauntlet #7). ParentCallID is the group key.

type PermissionAskMsg

type PermissionAskMsg struct {
	AskID  string
	Tool   string
	Args   string // raw JSON
	Reason string
}

PermissionAskMsg opens the approval modal; AskID is the exact correlation key echoed back in ResumeApproval — never inferred from the tool name.

type PermissionRetractMsg

type PermissionRetractMsg struct {
	AskID string
}

PermissionRetractMsg withdraws a previously surfaced permission ask: the owning subagent was cancelled while parked on it, so there is nothing left to approve. The ui keeps a FIFO queue of surfaced asks behind the visible modal (concurrent subagents can surface asks concurrently): a retract matching the VISIBLE ask dismisses the modal and advances the queue; a retract matching a QUEUED ask removes it in place; an unknown/stale id is ignored (idempotent).

type ProviderRouteMsg

type ProviderRouteMsg struct{ Text string }

ProviderRouteMsg is a muted advisory notice emitted once per turn when the serving provider reports which DOWNSTREAM inference provider routed the request (issue #480). Text carries the downstream slug verbatim (e.g. "anthropic", "google-vertex"); today only the openrouter entry produces it. It is rendered as a transient status line (like NoProgressMsg); it is ABSENT on a cache hit (OpenRouter strips the metadata) — the footer simply doesn't move.

type ProviderStatus

type ProviderStatus struct {
	ProviderID string
	State      string
	Hint       string
	// DefaultModelAutoSelected is true ONLY when this row's provider is the
	// DEFAULT provider AND the server AUTO-selected its default model (a
	// first-listed heal/probe pick, issue #262 review finding 7) — never true
	// when an operator configured --model/--default-model. Named to match the
	// wire field (default_model_auto_selected) and the server-side accessor
	// (providerRegistry.DefaultModelAutoSelected) verbatim.
	DefaultModelAutoSelected bool
	// ModelCount is the count of models this surfaced provider's last
	// successful live listing returned. 0 on empty/unreachable/unrecorded.
	// Named to match the wire field (model_count) verbatim.
	ModelCount int32
	// AvailableNotDefault is true ONLY when this intent-driven provider is
	// registered, reachable (State == "ok"), AND is NOT the active default
	// provider. Named to match the wire field (available_not_default) verbatim,
	// per the DefaultModelAutoSelected naming discipline.
	AvailableNotDefault bool
}

ProviderStatus is one operator-actionable provider's last live-listing outcome (ToolHive or openai-codex), the proto-free mirror of mecatlv1.ProviderStatus. State is "ok" | "unreachable" | "unauthorized" | "empty"; Hint is a short human remediation string, empty for "ok".

type ReasoningDeltaMsg

type ReasoningDeltaMsg struct {
	Turn int32
	Text string
}

ReasoningDeltaMsg is a streamed chunk of the model's human-readable reasoning summary for a turn. Display-only and clearly subordinate to the assistant text; the ui renders it collapsed by default.

type RecoverNoticeMsg

type RecoverNoticeMsg struct{ Text string }

RecoverNoticeMsg is an advisory notice emitted at run start when a session that failed on a PERMANENT provider error is recovered for re-entry. It is rendered as a transient status/warning (the run's first event overwrites it); it does NOT block the run. It carries only the harness-authored advisory Text (no model content).

type Recver

type Recver interface {
	Recv() (*mecatlv1.ConverseResponse, error)
}

Recver is the minimal receive side of a Converse stream: exactly what the reader goroutine needs. The generated grpc.BidiStreamingClient satisfies it, and tests supply a scripted fake — so the whole event pipeline runs offline, with no gRPC and no network. (The send side is the Sender interface below.)

type ReflectionClient

type ReflectionClient interface {
	ListLearningProposals(context.Context, string, string, int, string) (LearningProposalPage, error)
	GetLearningProposal(context.Context, string, string) (LearningProposal, error)
	DecideLearningProposal(context.Context, string, string, string, string, string) (LearningProposal, error)
	UndoLearningPromotion(context.Context, string, string, string) (LearningProposal, error)
	ReflectSession(context.Context, string) (ReflectionReceipt, error)
}

type ReflectionCursors

type ReflectionCursors struct {
	Operator     string
	Project      string
	OperatorDone bool
	ProjectDone  bool
}

type ReflectionMsg

type ReflectionMsg struct {
	Proposal   *LearningProposal
	Receipt    *ReflectionReceipt
	Err        error
	RequestID  string
	Generation uint64
}

type ReflectionReceipt

type ReflectionReceipt struct {
	ID, Disposition                      string
	Queued, Staged, Promoted, Conflicted int
	Abstained                            bool
}

ReflectionReceipt is the bounded result of explicit reflection.

type ReflectionsMsg

type ReflectionsMsg struct {
	Page       LearningProposalPage
	Err        error
	Generation uint64
}

type ResolvedModel

type ResolvedModel struct {
	ProviderID    string
	ModelID       string
	ContextWindow int64
	// ReasoningEffort is the EFFECTIVE reasoning-effort tier this session resolved
	// to (ADR 0055), "" when unset (provider default). The ui shows it in the model
	// footer segment (only when non-empty). Server-owned + echoed verbatim — never
	// recomputed by the client.
	ReasoningEffort string
}

ResolvedModel is the proto-free mirror of mecatlv1.ResolvedModel: the EFFECTIVE provider+model THIS session resolved to (server-owned, echoed verbatim), plus its context window. The ui shows the effective model in its header from turn zero WITHOUT importing proto. The zero value (empty ids) is the safe default for an older server that omits the field — the header then shows no model segment. The human display NAME is resolved by the ui from its ListModels inventory keyed on (ProviderID, ModelID); no display name is carried on the wire.

type ResolvedModelMsg

type ResolvedModelMsg struct {
	SessionID string
	Resolved  ResolvedModel
	Mode      string
	State     string
	Workspace string
	CreatedAt int64
	// Title is the session's stored title from the snapshot (self-heal channel for
	// the window title). See the struct doc.
	Title string
	// Capabilities is the server's feature-advertisement snapshot. See the struct doc.
	Capabilities Capabilities
	Err          error
}

ResolvedModelMsg carries the result of a GetSession refetch (the footer context-meter heal, issue #66, the plan-approval mode+model refresh, issue #206, and the caps-heal path for /sessions continue + /effort fork, issue #348). SessionID is STAMPED on every result — success AND error — so the reducer can drop a result that landed AFTER a /models switch rebound the ui to a new session (a stale window must never clobber the new session's denominator). Err set ⇒ the refetch failed; the reducer keeps the current denominator (benign — the heal simply retries on the next turn boundary).

Mode carries the server-confirmed permission posture from the session snapshot. When non-empty (the plan-approval refresh path) the reducer applies it to the header mode echo; the footer-heal path may leave it empty (the mode is unchanged).

Title carries the session's stored title from the same GetSession refetch — the self-heal channel for the terminal window title on the carryover/fork/adopt paths where the server already set a title this client never saw (the on-sent set-once in submitPrompt only seeds from a prompt the user typed HERE). The reducer adopts it only when the local sessionTitle is still empty (set-once).

Capabilities carries the server's feature-advertisement snapshot from the Session proto (issue #348). It arrives on the SAME GetSession refetch so the caps-heal path (/sessions continue, /effort fork) can re-derive affordances in one round-trip. A zero value means an older server (field absent) — the reducer keeps the current caps untouched (fail-conservative).

type ResultMsg

type ResultMsg struct {
	Stop  string
	Text  string
	Error string
	Usage Usage
	// Transient is a presentation/backward-compatibility classification only. New
	// servers derive it from RetryDisposition; only an absent typed disposition may
	// use the legacy error-text vocabulary. It never authorizes exact replay.
	Transient bool
	// Permanent controls permanent-error presentation. A present typed disposition
	// takes precedence over the legacy proto Permanent bit.
	Permanent               bool
	RetryDisposition        RetryDisposition
	RetryDispositionPresent bool
	StreamProgress          StreamProgress
	StreamProgressPresent   bool
}

ResultMsg is the terminal event: stop reason, final text, error, usage.

func (ResultMsg) FailedStepRetryEligible

func (r ResultMsg) FailedStepRetryEligible() bool

FailedStepRetryEligible reports whether this terminal result proves that replaying the failed model step is safe. Legacy/transient presentation signals are deliberately ignored: failed-step retry requires both typed facts from a new server.

type ResumeSelection

type ResumeSelection struct {
	Row        SessionListItem
	Transcript SessionTranscript
	Snapshot   SessionSnapshot
}

ResumeSelection is a statically validated startup adoption. Row carries the server-authored capability/metadata projection; Transcript is the authoritative snapshot-derived conversation. Resolving it performs no run-entry work.

type RetentionPolicy

type RetentionPolicy struct {
	MainMaxAge, ChildMaxAge, ScheduledMaxAge       time.Duration
	MainMaxCount, ChildMaxCount, ScheduledMaxCount int
	SweepCadence                                   time.Duration
}

RetentionPolicy is the proto-free effective policy shown by the UI.

type RetryDisposition

type RetryDisposition uint8

RetryDisposition is the proto-free causal classification of a failed model step. Presence is carried separately on ResultMsg so explicit unknown is distinguishable from an older server that omitted the field.

const (

	// RetryDispositionUnknown means the server cannot safely classify the failure.
	RetryDispositionUnknown RetryDisposition
	// RetryDispositionRetryable means the failure cause permits replay.
	RetryDispositionRetryable
	// RetryDispositionPermanent means replaying the same step cannot succeed.
	RetryDispositionPermanent
)

type Schedule

type Schedule struct {
	Spec  ScheduleSpec
	State ScheduleState
}

Schedule is the aggregate value object: the immutable Spec plus the durable State. Mirrors mecatlv1.Schedule.

type ScheduleActionMsg

type ScheduleActionMsg struct {
	Name   string
	Action string
	FireID string
	Err    error
}

ScheduleActionMsg carries the outcome of a pause/resume/delete/fire-now action. Action is "paused"/"resumed"/"deleted"/"fired"; FireID is set only for "fired" (the per-fire session id, pollable via GetFire). Err is set on failure.

type ScheduleFire

type ScheduleFire struct {
	ID           string
	ScheduleName string
	SessionID    string
	FiredAt      time.Time
	Stop         string
	Err          string
	// StartedAt is when the fire's run began (RecordFireStart). Zero on a
	// terminal-only fire. An in-flight fire has Stop empty + StartedAt set. #386.
	StartedAt time.Time
	// ProgressAt is the last observed progress instant for the fire. #386.
	ProgressAt time.Time
	// Deadline is the fire's wall-clock deadline (RecordFireStart). #386.
	Deadline time.Time
}

ScheduleFire is one fire record: the outcome of a single Claim→run→RecordFire cycle. Mirrors mecatlv1.ScheduleFire.

type ScheduleFiresMsg

type ScheduleFiresMsg struct {
	Fires []ScheduleFire
	Err   error
}

ScheduleFiresMsg carries a ListFires result for the /schedule overlay's inspect sub-view. Err is set on failure.

type ScheduleLister

type ScheduleLister interface {
	ListSchedules(ctx context.Context) ([]Schedule, error)
	GetSchedule(ctx context.Context, name string) (Schedule, error)
	CreateSchedule(ctx context.Context, spec ScheduleSpec) (Schedule, error)
	DeleteSchedule(ctx context.Context, name string) error
	FireNow(ctx context.Context, name string) (fireID, sessionID string, err error)
	PauseSchedule(ctx context.Context, name string) error
	ResumeSchedule(ctx context.Context, name string) error
	ListFires(ctx context.Context, scheduleName string) ([]ScheduleFire, error)
}

ScheduleLister is the subset of *Client the ui's /schedule overlay needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type ScheduleMsg

type ScheduleMsg struct {
	Schedule Schedule
	Err      error
}

ScheduleMsg carries a Get/Create result for the /schedule overlay. Err is set on failure.

type ScheduleSelector

type ScheduleSelector struct {
	ProviderID string
	ModelID    string
}

ScheduleSelector is the provider+model pair a schedule's fires run on. Both empty means "use the deployment default". Mirrors mecatlv1.ScheduleProviderSelector.

type ScheduleSpec

type ScheduleSpec struct {
	Name      string
	Prompt    string
	Trigger   ScheduleTrigger
	Selector  ScheduleSelector
	Profile   string
	Workspace string
	Mode      string
	Mutating  bool
	MaxFires  int32
	Misfire   string // canonical: "fire_once_now" (default) | "skip"
	Singleton bool
	Timezone  string
	CreatedAt time.Time
	// FireTimeout is the per-fire wall-clock deadline (issue #386). Zero means
	// "use the deployment default" (which may itself be zero for "no explicit
	// deadline"). Mirrors mecatlv1.ScheduleSpec.fire_timeout.
	FireTimeout time.Duration
}

ScheduleSpec is the immutable definition of a schedule — the "what to run and when" half. Mirrors mecatlv1.ScheduleSpec. Parts (the OPTIONAL multimodal extension) is OMITTED for v1: the /schedule overlay does not author multimodal prompts, and a future phase that does will add the field here then.

type ScheduleState

type ScheduleState struct {
	NextFireAt        time.Time
	LastFireAt        time.Time
	FireCount         int32
	Enabled           bool
	LastFireSessionID string
	// LastFireStartedAt is when the current fire's run began (RecordFireStart),
	// the in-flight liveness marker. Zero means the run has not started. #386.
	LastFireStartedAt time.Time
	// LastFireProgressAt is the last observed progress instant for the current
	// fire. Zero means no progress observed. #386.
	LastFireProgressAt time.Time
	// FireDeadline is the current fire's wall-clock deadline (RecordFireStart).
	// Zero means no explicit deadline. #386.
	FireDeadline time.Time
}

ScheduleState is the durable FIRING state of a schedule — the mutable half that advances as the schedule fires. Mirrors mecatlv1.ScheduleState.

type ScheduleTrigger

type ScheduleTrigger struct {
	Cron    string
	OneShot time.Time // zero = not set
}

ScheduleTrigger is the sum type for a schedule's firing trigger: a cron expression OR a one-shot wall-clock instant. Exactly one is set (a zero OneShot means "not set"). Mirrors mecatlv1.TriggerSpec.

type SchedulesMsg

type SchedulesMsg struct {
	Schedules []Schedule
	Err       error
}

SchedulesMsg carries a ListSchedules result for the /schedule overlay. Err is set on failure; the overlay surfaces it rather than silently degrading.

type Sender

type Sender interface {
	Send(*mecatlv1.ConverseRequest) error
}

Sender is the send side of the Converse stream. Separated from Recver so the reader goroutine holds only what it reads and the ui-side send helpers hold only what they send. The generated bidi client satisfies both.

type SessionAdoptedMsg

type SessionAdoptedMsg struct {
	SourceID   string
	Result     AdoptionResult
	Snapshot   SessionSnapshot
	Transcript SessionTranscript
	Err        error
}

SessionAdoptedMsg carries adoption plus authoritative target refetches.

type SessionAdopter

type SessionAdopter interface {
	PreflightSessionAdoption(ctx context.Context, sourceID string, bindings AdoptionBindings) (AdoptionPreflight, error)
	AdoptSession(ctx context.Context, sourceID, idempotencyKey string, bindings AdoptionBindings) (AdoptionResult, error)
}

SessionAdopter is the proto-free legacy-adoption client seam used by the UI.

type SessionAdoptionPreflightMsg

type SessionAdoptionPreflightMsg struct {
	SourceID  string
	Bindings  AdoptionBindings
	Preflight AdoptionPreflight
	Err       error
}

SessionAdoptionPreflightMsg carries one source-correlated server preflight.

type SessionCleaner

type SessionCleaner interface {
	PlanSessionCleanup(context.Context, CleanupScope) (CleanupPlan, error)
	ApplySessionCleanup(context.Context, string) (CleanupJob, error)
	CancelSessionCleanup(context.Context, string) (CleanupJob, error)
	GetSessionCleanupJob(context.Context, string) (CleanupJob, error)
}

SessionCleaner is the injectable, capability-gated destructive cleanup seam.

type SessionDeletedMsg

type SessionDeletedMsg struct {
	SessionID string
	Err       error
}

SessionDeletedMsg carries a correlated DeleteSession result.

type SessionDeleter

type SessionDeleter interface {
	DeleteSession(ctx context.Context, id string) error
}

SessionDeleter permanently deletes one stored session.

type SessionGetter

type SessionGetter interface {
	GetSession(ctx context.Context, id string) (SessionSnapshot, error)
}

SessionGetter is the narrow subset of *Client that RefreshResolvedModelCmd needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client (and the ui's wider SessionCreator, via the sessionAdapter) satisfies it.

type SessionInitMsg

type SessionInitMsg struct{ Seq int64 }

SessionInitMsg marks the run stream as live (proto type "session.init").

type SessionInventoryActionReasons

type SessionInventoryActionReasons struct {
	PublicChat     CapabilityReason
	Inspect        CapabilityReason
	CopyID         CapabilityReason
	ViewTranscript CapabilityReason
	Fork           CapabilityReason
	Rename         CapabilityReason
	Delete         CapabilityReason
}

SessionInventoryActionReasons carries the server reason for each disabled action.

type SessionInventoryCapabilities

type SessionInventoryCapabilities struct {
	PublicChat              bool
	Inspect                 bool
	AuthoritativeTranscript bool
	ActivityReplay          bool
	CopyID                  bool
	ViewTranscript          bool
	Fork                    bool
	Rename                  bool
	Delete                  bool
}

SessionInventoryCapabilities declares the actions permitted for an inventory row.

type SessionInventoryPage

type SessionInventoryPage struct {
	Sessions   []SessionListItem
	NextCursor string
	TotalCount int
}

SessionInventoryPage is one proto-free bounded inventory page.

type SessionInventoryPageMsg

type SessionInventoryPageMsg struct {
	Page         SessionInventoryPage
	Cursor       string
	RequestToken uint64
	Err          error
}

SessionInventoryPageMsg carries one progressive page result to Bubble Tea.

type SessionKind

type SessionKind string

SessionKind classifies a stored session for inventory display.

const (
	SessionKindMain           SessionKind = "main"
	SessionKindScheduled      SessionKind = "scheduled"
	SessionKindSubagent       SessionKind = "subagent"
	SessionKindParallelBranch SessionKind = "parallel_branch"
	SessionKindTeamMember     SessionKind = "team_member"
	SessionKindUnknown        SessionKind = "unknown"
)

Server-provided session kind values.

type SessionListItem

type SessionListItem struct {
	ID              string
	ModifiedAt      int64
	State           string
	Turns           int32
	ModelID         string
	CreatedAt       int64
	Title           string
	TitleProvenance string
	Workspace       string
	Kind            SessionKind
	Relationship    SessionRelationship
	Capabilities    SessionInventoryCapabilities
	Reasons         SessionInventoryActionReasons
	ReasonCode      CapabilityReason
}

SessionListItem is one server-authored stored-session inventory row. ID remains the only value sent back to APIs; Kind, Relationship, capabilities and reason code are display/action metadata and are never inferred from ID spelling.

type SessionLister

type SessionLister interface {
	ListSessions(ctx context.Context) ([]SessionListItem, error)
}

SessionLister lists all stored sessions for non-interactive selection.

type SessionManager

type SessionManager interface {
	SessionRenamer
	SessionDeleter
}

SessionManager is the mutable stored-session inventory surface.

type SessionMigrationItemError

type SessionMigrationItemError struct {
	ItemHandle string
	ReasonCode string
	Message    string
}

SessionMigrationItemError is a stable, sanitized item failure.

type SessionMigrationJob

type SessionMigrationJob struct {
	ID               string
	State            string
	V1Families       int64
	V2Families       int64
	InvalidFamilies  int64
	SkippedFamilies  int64
	CurrentBytes     int64
	ReclaimableBytes int64
	TemporaryBytes   int64
	Processed        int64
	Migrated         int64
	Failed           int64
	Errors           []SessionMigrationItemError
}

SessionMigrationJob is resumable bounded progress for a physical migration.

type SessionMigrationPlan

type SessionMigrationPlan struct {
	ID                string
	Available         bool
	UnavailableReason string
	V1Families        int64
	V2Families        int64
	InvalidFamilies   int64
	SkippedFamilies   int64
	CurrentBytes      int64
	ReclaimableBytes  int64
	TemporaryBytes    int64
}

SessionMigrationPlan is the proto-free read-only optimization estimate.

type SessionMigrator

type SessionMigrator interface {
	PlanSessionMigration(context.Context) (SessionMigrationPlan, error)
	ApplySessionMigration(context.Context, string, int32) (SessionMigrationJob, error)
	ResumeSessionMigration(context.Context, string, int32) (SessionMigrationJob, error)
	CancelSessionMigration(context.Context, string) (SessionMigrationJob, error)
	GetSessionMigrationJob(context.Context, string) (SessionMigrationJob, error)
}

SessionMigrator is the injectable, capability-gated physical optimization seam.

type SessionPager

type SessionPager interface {
	ListSessionPage(ctx context.Context, cursor string) (SessionInventoryPage, error)
}

SessionPager fetches one bounded stored-session inventory page.

type SessionReadyMsg

type SessionReadyMsg struct {
	SessionID    string
	Capabilities Capabilities
	// ResolvedModel is the EFFECTIVE provider+model the session resolved to (server-
	// owned, echoed verbatim). The ui shows it in the header from turn zero; an older
	// server yields the zero value (no model segment).
	ResolvedModel ResolvedModel
	// Mode is the server-confirmed permission posture; empty means older create path /
	// default.
	Mode string
}

SessionReadyMsg carries the session id AND the server's capabilities from the async CreateSession. Capabilities drives the ui's honest discoverability affordances; an older server yields the all-false zero value.

type SessionRelationship

type SessionRelationship struct {
	ParentSessionID string
	CallID          string
	BranchIndex     *int32
	ScheduleName    string
	OriginSessionID string
	TeamID          string
	MemberName      string
}

SessionRelationship identifies the parent, schedule, or team context of a session.

type SessionRenamedMsg

type SessionRenamedMsg struct {
	SessionID       string
	Title           string
	TitleProvenance string
	Err             error
}

SessionRenamedMsg carries a correlated RenameSession result without exposing protobuf types to the UI.

type SessionRenamer

type SessionRenamer interface {
	RenameSession(ctx context.Context, id, title string) (SessionSnapshot, error)
}

SessionRenamer renames one stored session.

type SessionReplayer

type SessionReplayer interface {
	StreamSessionEvents(ctx context.Context, id string) (*EventStream, error)
}

SessionReplayer remains the optional activity-replay seam used by the live delivery catch-up path. It is not authoritative conversation context.

type SessionSnapshot

type SessionSnapshot struct {
	Mode            string
	State           string
	Workspace       string
	CreatedAt       int64
	ResolvedModel   ResolvedModel
	Title           string
	TitleProvenance string
	// Capabilities is the server's feature-advertisement snapshot from the Session
	// proto (the SAME value CreateSessionResponse carries). A client that re-hydrates
	// a persisted session on adopt (continue, /effort fork) reads this to re-derive
	// its affordances. An older server (nil field) yields the zero value, which the
	// consumer treats as "keep current caps" (fail-conservative).
	Capabilities Capabilities
}

SessionSnapshot is the proto-free subset of a server session snapshot mecatui needs.

type SessionTranscript

type SessionTranscript struct {
	SessionID    string
	Messages     []ConversationMessage
	Complete     bool
	Activity     ActivityReplayStatus
	Kind         SessionKind
	Relationship SessionRelationship
}

SessionTranscript is the proto-free, human-displayable conversation projection.

type SessionTranscriptMsg

type SessionTranscriptMsg struct {
	SessionID  string
	Transcript SessionTranscript
	Err        error
}

SessionTranscriptMsg carries one correlated transcript load result.

type SessionTranscripter

type SessionTranscripter interface {
	GetSessionTranscript(ctx context.Context, id string) (SessionTranscript, error)
}

SessionTranscripter is the authoritative snapshot-derived conversation seam used for both continuation and non-destructive inspection.

type SessionsListedMsg

type SessionsListedMsg struct {
	Sessions []SessionListItem
	Err      error
}

SessionsListedMsg carries one session inventory listing result.

type Skill

type Skill struct {
	Name          string
	Description   string
	AgentOwned    bool
	OwnerAgent    string
	ActiveVersion string
}

Skill is one discovered skill (proto SkillInfo, proto-free): its activation name and a short one-line description. Metadata only — discovery carries no body; activation is a run-path concern handled server-side by the Skill tool.

type SkillChange

type SkillChange struct {
	ID, Operation, FromState, ToState, Verdict string
	InspectAvailable, UndoAvailable            bool
}

type SkillChangesMsg

type SkillChangesMsg struct {
	Changes []SkillChange
	Err     error
}

type SkillDiffMsg

type SkillDiffMsg struct {
	Diff      string
	Project   string
	SkillID   string
	Version   string
	RequestID uint64
	Err       error
}

type SkillEvaluation

type SkillEvaluation struct {
	Verdict                     string
	FixtureIDs                  []string
	Baseline, Treatment, Reason string
}

type SkillLister

type SkillLister interface {
	ListSkills(ctx context.Context) ([]Skill, error)
}

SkillLister is the subset of *Client the ui's /skills panel needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type SkillsMsg

type SkillsMsg struct {
	Skills []Skill
	Err    error
}

SkillsMsg carries a ListSkills result for the /skills panel. Err is set on failure; the panel surfaces it rather than silently degrading, mirroring the MCP panel's error handling.

type Soul

type Soul struct {
	Content    string
	SizeBytes  int64
	SHA256     string
	Present    bool
	Provenance SoulProvenance
	Trusted    bool
	Drifted    bool
}

Soul is the resolved soul snapshot (proto SoulInfo, proto-free): the persona content plus its provenance/trust/drift metadata. The content is the bytes that reach the prompt; the ui shows it read-only (it never edits the soul).

type SoulFetcher

type SoulFetcher interface {
	GetSoul(ctx context.Context) (Soul, error)
}

SoulFetcher is the subset of *Client the ui's /soul panel needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type SoulMsg

type SoulMsg struct {
	Soul Soul
	Err  error
}

SoulMsg carries a GetSoul result for the /soul panel. Err is set on failure; the panel surfaces it rather than silently degrading.

type SoulProvenance

type SoulProvenance int

SoulProvenance is the proto-free mirror of mecatlv1.SoulProvenance: where the selected (or dropped) soul originated. The ui renders a trust label from it WITHOUT importing proto.

const (
	// SoulProvenanceNone means no soul was selected.
	SoulProvenanceNone SoulProvenance = iota
	// SoulProvenanceUser means the user-scoped soul (always trusted).
	SoulProvenanceUser
	// SoulProvenanceProject means the project-scoped soul (trusted only with
	// --trust-project).
	SoulProvenanceProject
)

type SteerEchoMsg

type SteerEchoMsg struct {
	Text      string
	MessageID string
}

SteerEchoMsg is the run's steer-inbox DRAIN echo (proto type "steer"): the COMMITTED operator steer just recorded into the conversation as an ordinary user continuation. CLIENT-VISIBLE and AUTHORITATIVE — the engine is the sole authority on which pending bundle drained, so the ui renders THIS text, byte-identical to the recorded user message replayed to the model (recorded == streamed == model-view). MessageID echoes the client-minted id of the Steer frame that drained (empty for an id-less sender), so the ui matches the echo to the frame it sent.

type SteerOutcome

type SteerOutcome string

SteerOutcome is the proto-free, closed-enum result of a steer / steer_cancel frame (steer-while-running, issue #512) — the mirror of the proto SteerOutcome (and the engine's agent.SteerOutcome). The server is AUTHORITATIVE: the client cannot observe the exact drain moment across stream latency, so it renders the outcome the server reports rather than guessing which version won a race.

const (
	// SteerAccepted reports the steer parked in the empty single slot; it drains
	// at the next turn boundary.
	SteerAccepted SteerOutcome = "accepted"
	// SteerAppended reports the steer found the pending slot OCCUPIED and was
	// MERGED into it (append is the default): the pending bundle's text grew by a
	// blank-line separator + text, and it still drains as ONE bundle.
	// Distinguished from SteerAccepted (a NEW pending bundle) so the ui can render
	// "merged onto pending" honestly. Replacing the pending bundle is explicit
	// steer_cancel-then-resend.
	SteerAppended SteerOutcome = "appended"
	// SteerRetracted reports a steer_cancel found a pending steer and retracted it.
	SteerRetracted SteerOutcome = "retracted"
	// SteerNonePending reports a steer_cancel found the slot EMPTY (nothing to
	// retract).
	SteerNonePending SteerOutcome = "none_pending"
	// SteerTooLate reports the steer arrived after the run went terminal; it is
	// never parked. When Promoted is set on the msg, the server auto-promoted the
	// text to a fresh follow-up run (never silently dropped).
	SteerTooLate SteerOutcome = "too_late"
)

type SteerOutcomeMsg

type SteerOutcomeMsg struct {
	Outcome   SteerOutcome
	Text      string
	Promoted  bool
	MessageID string
}

SteerOutcomeMsg is the AUTHORITATIVE ack for a steer / steer_cancel frame the ui sent on the Converse stream (proto type "steer.outcome"). One per frame, sequenced in send order. Text echoes the steer text the outcome is about (empty for steer_cancel); Promoted is true only for a too_late steer the server promoted to a fresh follow-up run. MessageID echoes the client-minted id of the Steer / SteerCancel frame this ack answers — the ui correlates by ID and ignores stale acks (text is not a safe key).

type StorageHealth

type StorageHealth struct {
	Available                                                         bool
	UnavailableReason                                                 string
	CurrentBytes                                                      int64
	CurrentBytesAvailable                                             bool
	ReclaimableBytes                                                  int64
	ReclaimableBytesAvailable                                         bool
	SessionCount, FileCount                                           int64
	V1Count, V2Count                                                  int64
	MainCount, ChildCount, ScheduledCount, UnknownCount, CorruptCount int64
	Policy                                                            RetentionPolicy
	LastSweep, NextSweep                                              time.Time
	LastSweepAvailable, NextSweepAvailable                            bool
	ActiveJob, LastFailure                                            string
}

StorageHealth is a proto-free aggregate management view. It deliberately contains no session identity, owner, path, or content fields.

type StorageHealthFetcher

type StorageHealthFetcher interface {
	GetStorageHealth(context.Context) (StorageHealth, error)
}

StorageHealthFetcher is the injectable UI seam.

type Stream

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

Stream wraps one open Converse run: a receive side, a send side, and a mutex that serialises Sends. gRPC permits concurrent Send and Recv from different goroutines but NOT concurrent Sends; the reader goroutine only Recvs, while approve/cancel commands Send — so a single send-mutex is sufficient and keeps the control frames ordered.

func NewStream

func NewStream(recv Recver, send Sender) *Stream

NewStream binds a receive and send side into a Stream. Pass the same grpc.BidiStreamingClient for both in production; pass a fake Recver (and a no-op or recording Sender) in tests.

func (*Stream) ApprovalResolved

func (s *Stream) ApprovalResolved(askID string) bool

ApprovalResolved reports whether askID was already resolved on this stream.

func (*Stream) MarkApprovalResolved

func (s *Stream) MarkApprovalResolved(askID string)

MarkApprovalResolved records an ask id as resolved for this stream. It is called before the deferred send command runs, so a re-delivered ask cannot reopen a just-closed UI surface in that scheduling window.

func (*Stream) ReadLoop

func (s *Stream) ReadLoop(ctx context.Context, out chan<- tea.Msg)

ReadLoop runs the receive loop on its OWN goroutine over the live Converse stream: it drains Recv and pushes translated tea.Msgs onto out, then closes out when the stream ends. It MUST run off the Bubble Tea update goroutine (it does no rendering and touches no model state) — glamour and the model are driven only from Update via the drained channel. A clean EOF yields StreamClosedMsg; any other error yields StreamErrMsg; both then close the channel so WaitForMsg stops re-arming. It delegates to readEventLoop (the shared translation path) after stripping the ConverseResponse envelope via GetEvent, so the live stream and the replay feed project identically.

Every send selects on ctx.Done() as well as out, so the goroutine can never wedge if the ui drops the channel (e.g. endRun finalised the run and stopped draining). Pass the run's context — cancelling it unblocks and exits the reader. This makes the no-leak property structural, not just a reasoned invariant.

Note ordering: the terminal "result" event arrives as a ResultMsg BEFORE the server closes the stream, so the ui finalises on ResultMsg and treats a later StreamClosedMsg as a no-op.

func (*Stream) SendApproval

func (s *Stream) SendApproval(askID string, v Verdict) error

SendApproval resolves a paused permission.ask. askID is the exact value from the PermissionAskMsg. It sets BOTH the legacy allow bool (so an older server that ignores the verdict enum still gets the right allow/deny) AND the verdict enum (so a newer server can learn the always-allow rule); the server's mapper prefers the verdict and falls back to the bool for UNSPECIFIED.

func (*Stream) SendCancel

func (s *Stream) SendCancel() error

SendCancel aborts the in-flight run; the loop ends with a result whose stop is "cancelled". The ui keeps the stream open until that terminal result arrives.

func (*Stream) SendCancelChild

func (s *Stream) SendCancelChild(childID string) error

SendCancelChild cancels ONE child (a subagent) of the in-flight run, addressed by its child session id — the SubagentMsg ChildID, verbatim. The run itself keeps streaming; the child ends with a "cancelled by user" terminal and stays resumable. The server ignores an unknown/already-finished id (the finished-as-you-pressed race is benign).

func (*Stream) SendPrompt

func (s *Stream) SendPrompt(sessionID, text string, parts []*mecatlv1.Content) error

SendPrompt sends the mandatory first frame. It MUST be the first Send on a fresh stream (the server rejects a non-prompt first frame). parts carries the non-text media (image/audio) built by ExpandMentions; nil for a text-only prompt. The server enforces the cross-field "text or parts non-empty" rule and re-validates every part (session.ValidateMediaParts), so a media-only prompt (empty text, non-nil parts) is legal here.

func (*Stream) SendRetryStart

func (s *Stream) SendRetryStart(sessionID string) error

SendRetryStart sends the mandatory first frame for a failed-step retry. It contains no prompt text: the server reuses persisted conversation/tool state while resolving live instruction and system-prompt sources for the new model attempt.

func (*Stream) SendSteer

func (s *Stream) SendSteer(text, messageID string) error

SendSteer sends a mid-run operator steer frame on the bidi Converse stream (steer-while-running, issue #512). The server routes it to the live run's single-slot inbox; the AUTHORITATIVE outcome (accepted / appended / too_late+promoted) arrives on the SAME stream as a SteerOutcomeMsg — never assumed client-side, since the client cannot observe the exact drain moment across stream latency. The ui only calls this when Capabilities.Steer is true (a disabled/old server falls back to the client-side merge-queue instead). messageID is the client-minted correlation key the server echoes verbatim on the ack and the drain echo; empty degrades to text-order matching.

func (*Stream) SendSteerCancel

func (s *Stream) SendSteerCancel(messageID string) error

SendSteerCancel retracts the run's PENDING (un-drained) steer, if any. The authoritative outcome (retracted / none_pending) arrives as a SteerOutcomeMsg. messageID echoes the id of the Steer frame it cancels (the ui only ever has ONE bundle outstanding, so the id is a scoping hint, not a selector).

type StreamClosedMsg

type StreamClosedMsg struct{}

StreamClosedMsg reports a clean stream close (io.EOF) without a result event (e.g. server closed early). Normal completion arrives as ResultMsg first.

type StreamErrMsg

type StreamErrMsg struct {
	Err error
	// Transient is legacy display metadata from TransientStreamErr. A transport
	// failure has no typed semantic commit fact and never authorizes TUI replay.
	Transient bool
}

StreamErrMsg reports a non-EOF Recv error on the Converse stream.

type StreamProgress

type StreamProgress uint8

StreamProgress is the proto-free semantic commit boundary of a model stream.

const (

	// StreamProgressUnknown means no safe commit-boundary claim is available.
	StreamProgressUnknown StreamProgress
	// StreamProgressPrecommit means no model output became visible or committed.
	StreamProgressPrecommit
	// StreamProgressVisible means model output became externally visible.
	StreamProgressVisible
	// StreamProgressComplete means the model stream completed.
	StreamProgressComplete
)

type SubagentKind

type SubagentKind string

SubagentKind discriminates the three subagent.* event kinds carried by a SubagentMsg, so the ui switches on a plain value rather than re-deriving it.

const (
	// SubagentStart marks a Subagent tool run beginning (Goal set).
	SubagentStart SubagentKind = "start"
	// SubagentTool marks a child tool call resolving (ToolName/IsError/ToolCount set).
	SubagentTool SubagentKind = "tool"
	// SubagentEnd marks a Subagent tool run finishing (ToolCount/Usage/Stop/DurationMs set).
	SubagentEnd SubagentKind = "end"
)

type SubagentMsg

type SubagentMsg struct {
	Kind           SubagentKind
	ParentCallID   string
	ChildID        string
	Goal           string
	Background     bool
	RoutedCategory string
	RoutedModel    string
	// RoutingReason names WHY the opt-in router did NOT classify this delegation
	// (subagent.start only; issue #397 / ADR 0083): empty on a routed hit, otherwise a
	// bounded harness/composition gate or classifier-miss string. Bare metadata —
	// never child content — so gauntlet #7 holds.
	RoutingReason string
	// Model is the concrete model id the child ACTUALLY ran on (subagent.start only),
	// regardless of how it was chosen — inherited default, agent-def pin, per-call
	// override, or the opt-in router (issue #112 / ADR 0035). When routed, Model ==
	// RoutedModel. Bare metadata, never child content, so gauntlet #7 holds.
	Model    string
	ToolName string
	IsError  bool
	// InnerKind / Text / Detail are the ADR-0079 bounded previews, set on a
	// subagent.tool event per the child's forwarded inner event kind:
	// InnerKind is "tool.call" (Detail = the bounded args preview), "tool.result"
	// (Detail = the bounded result preview), or "message.delta" (Text = the capped
	// message text); empty on start/end and from an older server (the ui then renders
	// a bare chip, exactly the pre-ADR-0079 shape). The server clamp-scrubs every
	// preview (control bytes out, ≤200 runes); the ui caps again on render. ToolCount
	// (tools started) and Usage (provider-reported) are CUMULATIVE totals stamped on
	// EVERY subagent.tool event regardless of InnerKind — always current, so the ui
	// assigns them unconditionally, never sums, and never reads 0-after-positive. A
	// turn.end InnerKind advances Usage mid-run with no Text/Detail.
	InnerKind  string
	Text       string
	Detail     string
	ToolCount  int
	Usage      Usage
	Stop       string
	DurationMs int64
	// Cause is the child run's FAILURE DETAIL on subagent.end when Stop is "error"
	// (empty otherwise): the harness/provider error the server recorded on the
	// terminal result (issue #319). It is METADATA about how the delegation failed —
	// a transport/loop error string, never child-authored output — so gauntlet #7
	// holds. Server-clamped; an older server yields "".
	Cause string
}

SubagentMsg is the BOUNDED projection of a Subagent tool's child run. It carries ids, a goal label, child tool names/counts, usage, stop, and duration — plus the BOUNDED content previews the server clamp-scrubs per ADR 0079 (InnerKind/Text/ Detail on a subagent.tool event), so the ui renders a subagent's activity under its Subagent card while the previews stay bounded, scrubbed, and client-only (never entering the parent conversation — gauntlet #7). ParentCallID attributes the msg to the originating Subagent tool block. Background marks a detached-delivery (background: true) child; the server sets it on subagent.start only, and an older server yields false (no marker). RoutedCategory/RoutedModel are the OPT-IN semantic model router's bare metadata (a category label + a model id) on subagent.start, empty when no router classified the delegation (ADR 0031) — never child content, so gauntlet #7 holds.

type TeamFinding

type TeamFinding struct {
	Member string
	Body   string
}

TeamFinding is one entry in the team's shared findings ledger, as plain data the ctrl+a agents findings view renders. Mirrors mecatlv1.TeamFinding; carries only the recording member's name and a bounded body preview, never the raw finding.

type TeamKind

type TeamKind string

TeamKind discriminates the three team.* event kinds carried by a TeamMsg, so the ui switches on a plain value rather than re-deriving it from the proto.

const (
	// TeamStart marks a Team run beginning (Roster set).
	TeamStart TeamKind = "start"
	// TeamMember marks one forwarded member-session event (Member/InnerKind set,
	// plus the subset of Text/ToolName/Detail/IsError/Usage relevant to InnerKind).
	TeamMember TeamKind = "member"
	// TeamEnd marks a Team run finishing (Rounds/Stop/Usage set).
	TeamEnd TeamKind = "end"
	// TeamTasks marks a snapshot of the team's shared task list (Tasks set). It maps
	// 1:1 from the first-class team.tasks proto Event.Type — a team-wide event with no
	// Member — so the ui switches on a clean discriminant.
	TeamTasks TeamKind = "tasks"
	// TeamFindings marks a snapshot of the team's shared findings ledger (Findings
	// set). It maps 1:1 from the first-class team.findings proto Event.Type — a
	// team-wide event with no Member — mirroring TeamTasks.
	TeamFindings TeamKind = "findings"
)

type TeamMemberDisposition

type TeamMemberDisposition struct {
	Name        string
	Stopped     bool
	Reason      string
	ErrorRounds int
}

TeamMemberDisposition is one member's TERMINAL disposition, set on a TeamEnd msg. Mirrors mecatlv1.TeamMemberDisposition; closed-enum supervisor verdicts only, never member content. Stopped distinguishes a non-resumable/budget-exhausted member from a clean one; Reason refines a stop ("error"/"cancelled"/"budget"; empty when not stopped). ErrorRounds counts the member's run-level failures, which a bounded retry can leave behind on a member that FINISHED (issue #318) — so it is the only signal that a not-stopped member's run was not clean.

type TeamMemberSpec

type TeamMemberSpec struct {
	Name     string
	Role     string
	Mutating bool
	Lead     bool
	// RoutedCategory/RoutedModel are the OPT-IN semantic model router's bare metadata
	// (a category label + a model id) for a routed member, empty when no router
	// classified the member (ADR 0031 / ADR 0034) — never member content, so gauntlet
	// #7 holds.
	RoutedCategory string
	RoutedModel    string
	// RoutingReason names WHY the opt-in router did NOT classify this member
	// (team.start roster only; issue #397 / ADR 0083): empty on a routed hit,
	// otherwise a bounded harness/composition gate string. Bare metadata — never
	// member content — so gauntlet #7 holds.
	RoutingReason string
	// Model is the concrete model id the member's engine ACTUALLY runs on (team.start
	// roster only), regardless of how it was chosen (issue #112 / ADR 0035). When routed,
	// Model == RoutedModel. Bare metadata, never member content, so gauntlet #7 holds.
	Model string
}

TeamMemberSpec is one roster entry forwarded on team.start, as plain data. Mirrors mecatlv1.TeamMemberSpec; carries only member metadata, never content.

type TeamMsg

type TeamMsg struct {
	Kind         TeamKind
	ParentCallID string
	TeamID       string
	// Roster is set on TeamStart.
	Roster []TeamMemberSpec
	// Member / InnerKind and the per-event content are set on TeamMember.
	Member string
	// MemberSessionID is the member's child SESSION id ("team-<teamID>-<member>") —
	// the CancelChild handle, set on TeamMember msgs. Carried explicitly so the ui
	// never derives the (server-internal) id grammar; empty from an older server.
	MemberSessionID string
	InnerKind       string
	Text            string
	ToolName        string
	Detail          string
	IsError         bool
	// Rounds / Stop are set on TeamEnd.
	Rounds int
	Stop   string
	// Usage is a member's per-event usage (TeamMember turn.end/result) or, on
	// TeamEnd, the summed team total.
	Usage Usage
	// ContextUsed / ContextWindow are the per-member context-meter numerator
	// (current context occupancy — the most recent turn's input tokens) and
	// denominator (the member engine's context window), set on TeamMember turn.end;
	// 0 when unknown. They drive the band bar on each member lane in the ctrl+a
	// agents overlay.
	ContextUsed   int64
	ContextWindow int64
	// Tasks is the team's shared task-list snapshot, set on a TeamTasks msg (the
	// first-class team.tasks event) and on TeamEnd. It feeds the ctrl+a agents task
	// sub-view.
	Tasks []TeamTask
	// Findings is the team's shared findings-ledger snapshot, set on a TeamFindings
	// msg (the first-class team.findings event) and on TeamEnd. It feeds the ctrl+a
	// agents findings view.
	Findings []TeamFinding
	// Dispositions is the per-member terminal disposition snapshot, set on a TeamEnd
	// msg. It lets the overlay render a stopped member distinctly from a clean "done".
	Dispositions []TeamMemberDisposition
	// Cause is the member run's per-round FAILURE DETAIL on a TeamMember result
	// (InnerKind "result") when that round ended StopError (empty otherwise): the
	// harness/provider error the server recorded on the terminal result (issue #331,
	// mirroring SubagentMsg.Cause). METADATA about how the round failed — a
	// transport/loop error string, never member-authored output — so gauntlet #7
	// holds. Server-clamped; an older server yields "". Last non-empty value wins
	// (a retried member's failed rounds each surface their own cause).
	Cause string
}

TeamMsg is the BOUNDED projection of an in-process team's run, as plain data the ui renders on the Team tool card. Unlike the metadata-only SubagentMsg, a team.member event carries BOUNDED member CONTENT (Text / a capped Detail preview) — the team is meant to be watched. It is still bounded and redacted server-side, and never enters the parent conversation. ParentCallID attributes the msg to the originating Team tool block.

type TeamTask

type TeamTask struct {
	ID          string
	Description string
	State       string
	Assignee    string
	Deps        []string
}

TeamTask is one entry in the team's shared task list, as plain data the ctrl+a agents task sub-view renders. Mirrors mecatlv1.TeamTask; carries only task metadata, never member content. Deps are the task ids this task waits on.

type ToolCallMsg

type ToolCallMsg struct {
	ID   string
	Name string
	Args string // raw JSON
}

ToolCallMsg announces a tool invocation (status: running until its result).

type ToolProgressMsg

type ToolProgressMsg struct {
	Text string
}

ToolProgressMsg is a transient, human-readable progress line from a long-running tool (proto type "tool.progress"). It carries no call id and no content — only advisory Text. The ui shows it as a transient status line while a tool runs and clears it on the next tool.result (or turn boundary); it is never persisted and never enters the conversation transcript.

type ToolResultMsg

type ToolResultMsg struct {
	CallID            string
	Content           string
	IsError           bool
	Blocks            []ContentBlock
	StructuredContent string
}

ToolResultMsg resolves the matching ToolCallMsg by CallID. Content is the legacy model-facing string result body; Blocks carries the typed content blocks (when the server relayed them — resource links, images, etc.) so the ui can render user- audience artifacts distinctly from/below the model-facing text. Both may be set: the blocks are rendered IN ADDITION to Content (the model-facing text body), not as a replacement. StructuredContent is the JSON-stringified structured payload mirror.

type TurnEndMsg

type TurnEndMsg struct {
	Turn       int32
	Usage      Usage
	DurationMs int64
}

TurnEndMsg closes a turn's model exchange, carrying that turn's token usage and the elapsed model-call time. DurationMs is 0 when the server had no clock.

type TurnStartMsg

type TurnStartMsg struct{ Turn int32 }

TurnStartMsg opens a new assistant turn; the spinner starts here.

type Usage

type Usage struct {
	InputTokens      int64
	OutputTokens     int64
	CacheReadTokens  int64
	CacheWriteTokens int64
	ReasoningTokens  int64
}

Usage is the token accounting carried by ResultMsg (and usage-bearing events). Duplicated as a plain struct so ui stays proto-free.

type UserModel

type UserModel struct {
	Entries   []UserModelEntry
	SizeBytes int64
	SHA256    string
	Detail    *UserModelDetail
}

UserModel is the user-model index snapshot: the current entries plus aggregate size + hash, so the panel can show an at-a-glance footprint.

type UserModelDetail

type UserModelDetail struct {
	Current          UserModelRevision
	History          []UserModelRevision
	HistoryAvailable bool
}

UserModelDetail is an exact current value plus bounded lifecycle history.

type UserModelDetailMsg

type UserModelDetailMsg struct {
	UserModel  UserModel
	Err        error
	RequestKey string
	Generation uint64
}

UserModelDetailMsg is a distinct correlated exact-entry response.

type UserModelDetailer

type UserModelDetailer interface {
	GetUserModelEntry(ctx context.Context, key string) (UserModel, error)
}

UserModelDetailer fetches exact read-only detail for one selected key.

type UserModelEntry

type UserModelEntry struct {
	Key         string
	Description string
}

UserModelEntry is one user-model fact's listing metadata (proto UserModelEntry, proto-free): its key + one-line description. The value is omitted — discovery is metadata only.

type UserModelLister

type UserModelLister interface {
	GetUserModel(ctx context.Context) (UserModel, error)
}

UserModelLister is the subset of *Client the ui's /usermodel panel needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it.

type UserModelMsg

type UserModelMsg struct {
	UserModel  UserModel
	Err        error
	Generation uint64
}

UserModelMsg carries a GetUserModel result for the /usermodel panel. Err is set on failure; the panel surfaces it rather than silently degrading.

type UserModelRevision

type UserModelRevision struct {
	Key, Value, Description, Version, Status, Writer, Origin string
	SourceSessionID, SourceProposalID                        string
	UpdatedAt                                                time.Time
}

UserModelRevision is one proto-free lifecycle revision for read-only display.

type UserPromptMsg

type UserPromptMsg struct {
	Text  string
	Parts []ContentBlock
}

UserPromptMsg is the recorded user message (EvUserPrompt), relayed only by the replay. Text carries the flattened prompt body (or a harness-authored continuation/notice); Parts carries any non-text media (image/audio) that rode alongside it, projected to the plain ContentBlock type (image/audio only). A delivery-patterned user_prompt (the fire-result delivery channel, ADR 0075) maps to DeliveryNoteMsg instead — see deliverNoteFrom.

type Verdict

type Verdict int

Verdict is the client-local three-way resolution of a permission.ask. It keeps the proto ApprovalVerdict enum out of the ui package (which never imports contracts/gen): the ui chooses a Verdict, SendApproval translates it. The zero value is VerdictAllowOnce (the safe, transient allow).

const (
	// VerdictAllowOnce permits this single call only (no rule learned).
	VerdictAllowOnce Verdict = iota
	// VerdictAllowAlways permits this call AND learns a session-scoped rule so the
	// same exact command is not re-asked for the rest of the session.
	VerdictAllowAlways
	// VerdictDeny denies this call.
	VerdictDeny
)

type Worktree

type Worktree struct {
	Path   string
	Branch string
	Head   string
	Bare   bool
}

Worktree is one discovered git worktree (proto Worktree, proto-free): the absolute working-tree path (the value handed to CreateSession to bind a session there), the checked-out branch (empty for detached HEAD), the commit SHA the worktree is at, and whether it is bare. The ui's /worktrees overlay lists these and, on select, creates a NEW session rooted at Path.

type WorktreeLister

type WorktreeLister interface {
	List(ctx context.Context, workspace string) ([]Worktree, error)
}

WorktreeLister is the subset of *Client the ui's /worktrees overlay needs. Splitting it out keeps the ui injectable with a fake for offline tests; *Client satisfies it. The method name List matches server.WorktreeLister.List (house style for Config seam interfaces, matching CommandLister.List).

type WorktreesMsg

type WorktreesMsg struct {
	Worktrees []Worktree
	Err       error
}

WorktreesMsg carries a ListWorktrees success (the overlay's worktree list). Err is set on failure; the overlay renders it as an error line (distinct from the empty-list "no worktrees found" path) so the user can see the discovery fault.

Jump to

Keyboard shortcuts

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