codingagent

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 71 Imported by: 0

Documentation

Overview

Model picker auth-filter scope toggle.

AuthenticatedProviders + ReachableProviders are the single source of truth for "which providers can the user actually talk to right now" vs "which providers does coding.BuildModel even know how to wire".

Used by interactive `/model` overlay to default the picker scope to {auth ∩ reachable} and clamp the all-scope view to {reachable} (the other ~19 catalog providers would mis-route through BuildModel's OpenAI-compatible default branch and silently fail at first token).

The scoped set is computed here at picker-open time rather than carried on SettingsManager.

Index

Constants

View Source
const (
	UpdateStageCheck    = "check"
	UpdateStageVerify   = "verify"
	UpdateStageDownload = "download"
)

Self-update stages reported to UpdateSelf's step callback.

View Source
const AppName = "wopr"

AppName is the binary/CLI name.

View Source
const BugReportCustomEntryType = "wopr.bug-report"

BugReportCustomEntryType is the session custom-entry type recorded after a report is written.

View Source
const (

	// BugReportIssueURL is the WOPR issue form that /bug links to.
	BugReportIssueURL = "https://github.com/alexrudloff/wopr/issues/new"
)
View Source
const CONFIG_DIR_NAME = "." + AppName

CONFIG_DIR_NAME is the per-project config directory name.

View Source
const CurrentSessionVersion = 3

CurrentSessionVersion is the session file format version. Bumping this requires a migration step in session_resume.go.

View Source
const DefaultThinkingLevel = "medium"

DefaultThinkingLevel is the fallback when settings.json does not define defaultThinkingLevel. Thinking-level initialization consumes it.

View Source
const DefaultWarCouncilBuildTimeout = 15 * time.Minute

DefaultWarCouncilBuildTimeout is how long a member may take to build a candidate by default.

View Source
const DefaultWarCouncilTimeout = 35 * time.Minute

DefaultWarCouncilTimeout is how long the council waits for its members by default: room for a slow local model's sized budget and its answer.

View Source
const ENV_AGENT_DIR = "WOPR_CODING_AGENT_DIR"

ENV_AGENT_DIR overrides the config directory.

View Source
const ENV_SESSION_DIR = "WOPR_CODING_AGENT_SESSION_DIR"

ENV_SESSION_DIR overrides the session storage directory.

View Source
const InstallScriptURL = "https://raw.githubusercontent.com/" + ReleaseRepo + "/main/install.sh"

InstallScriptURL is the installer for macOS and Linux.

View Source
const ProjectMCPFileName = ".mcp.json"

ProjectMCPFileName is the project-root MCP server file other agents share (Claude Code, Cursor). wopr reads it only for a trusted project.

View Source
const ReleaseRepo = "alexrudloff/wopr"

ReleaseRepo is the GitHub repository that publishes wopr releases.

View Source
const ReleaseSigningPublicKey = "NARUZGcG7SoXSjtVV3MyFZOtvv8wM6j60305trWYHRM="

ReleaseSigningPublicKey is the base64-encoded Ed25519 public key that signs every release's SHA256SUMS (the SHA256SUMS.sig asset). `wopr update` refuses any release it cannot verify against this key.

It is empty until the maintainer generates the release key pair; see docs/project/RELEASING.md ("One-time signing key setup"). While it is empty, self-update fails closed with "release signing key not configured".

View Source
const ReleasesURL = "https://github.com/" + ReleaseRepo + "/releases"

ReleasesURL is the human-facing page listing every wopr release.

View Source
const (

	// SidebarWidth is the sidebar's column count.
	SidebarWidth = 42
)

Fullscreen layout geometry.

View Source
const Tagline = "SHALL WE PLAY A GAME?"

Tagline is wopr's tagline, the question WOPR asks in WarGames.

Variables

View Source
var CacheWarmingModes = []CacheWarmingMode{"off", "streaming", "idle"}

CacheWarmingModes lists the modes in display order.

View Source
var ErrInteractiveCrashed = errors.New("interactive mode crashed")

ErrInteractiveCrashed reports that Run recovered a crash it already printed and recorded; the caller exits 1 without printing it again.

View Source
var ErrUnknownSlashCommand = errors.New("unknown slash command")

ErrUnknownSlashCommand signals that a slash command was not found. The caller sends the original text to the model as a regular message.

View Source
var SupportedImageMIMEs = []string{
	"image/png",
	"image/jpeg",
	"image/webp",
	"image/gif",
}

SupportedImageMIMEs lists the formats we accept from the clipboard. Order = preference.

Functions

func AgentDir

func AgentDir() string

AgentDir returns the writable agent directory: $WOPR_CODING_AGENT_DIR (with ~ expanded), else DefaultAgentDir.

func ArchiveBaseName

func ArchiveBaseName(version, goos, goarch string) string

ArchiveBaseName is the release archive name without extension, which is also the directory the archive unpacks to.

func AuthenticatedProviders

func AuthenticatedProviders(agentDir string) map[string]bool

AuthenticatedProviders returns the subset of ReachableProviders for which credentials are detectable in the environment / auth.json / model registry, without probing the network.

  • github-copilot: OAuth credential present and not expired
  • openai/openrouter/groq: <ID_UPPER>_API_KEY env var set OR registry has any non-empty APIKey entry for the provider
  • ollama: OLLAMA_HOST env set (cheap heuristic; we do NOT probe localhost:11434: picking ollama with no daemon running is a user error caught at switch time)

agentDir is the wopr config dir (typically ~/.wopr/agent). auth.json and models.json are read from there.

func BugReportArchiveFileName

func BugReportArchiveFileName(id string) string

BugReportArchiveFileName names a report archive after WOPR's command name.

func BugReportIssueLink(metadata BugReportMetadata, archiveName string) string

BugReportIssueLink returns a prefilled WOPR bug-form URL. It carries only the form's own fields: a title from the first line of the description (bounded), the WOPR version, the platform, and the archive name to attach. It never carries session content, settings, or paths.

func CanonicalizePath

func CanonicalizePath(path string) string

CanonicalizePath resolves a path to its canonical filesystem form, following symlinks. If resolution fails (for example because the path does not exist yet), it falls back to the raw path.

func ClearCrashLog

func ClearCrashLog(path string)

ClearCrashLog removes the crash log; a failure leaves the records for the next report.

func CompareVersions

func CompareVersions(a, b string) int

CompareVersions compares strict semantic versions. Returns -1 if a<b, 1 if a>b, and 0 when equal or when either side is malformed. Callers parsing release metadata must reject malformed remote versions before comparison; the zero fallback here keeps development/CI local versions non-disruptive.

func ConfigRoot

func ConfigRoot() string

ConfigRoot returns the wopr configuration root directory.

Resolution order:

  1. $WOPR_HOME if set and non-empty
  2. $XDG_CONFIG_HOME/wopr if XDG_CONFIG_HOME is set
  3. ~/.wopr (default)

All wopr state: agent dir, auth.json, sessions, agents, subagent output - lives under this root.

func ContainerRemediation

func ContainerRemediation(exePath string) string

ContainerRemediation is the exact authenticated pull/redeploy instruction for an OCI image deployment. wopr performs no transport and knows no deployment topology, so a deployment that owns a specific redeploy operation supplies it verbatim through WOPR_REDEPLOY_INSTRUCTION. wopr still owns the surrounding facts (artifact identity, executable, and the guarantee that the running image is never rewritten); only the operation comes from the deployment. Without that metadata wopr falls back to the generic image pull, which is correct for a plain OCI/container install but not for an orchestrator-managed deployment.

func ConvertToPng

func ConvertToPng(base64Data, mimeType string) *tui.ConvertedImage

ConvertToPng prepares an image for the Kitty graphics protocol, which requires PNG (f=100), so a non-PNG base64 image is converted. PNG input is returned unchanged; nil means the conversion failed.

func CrashLogPath

func CrashLogPath(agentDir string) string

CrashLogPath returns the crash log location for an agent directory.

func CreateShareTrailingEntries

func CreateShareTrailingEntries(state ShareState, parentID *string, timestamp string) []any

CreateShareTrailingEntries returns the trailing wopr.share entry carrying the system prompt and tool schemas of an exported transcript.

func CurrentSystemMessage

func CurrentSystemMessage(messages []agent.AgentMessage) *ai.SystemMessage

CurrentSystemMessage replays every projected system message into the current prompt and tool state.

func DefaultAgentDir

func DefaultAgentDir() string

DefaultAgentDir returns the default <ConfigRoot>/agent directory.

func DefaultSkillsDir

func DefaultSkillsDir() string

DefaultSkillsDir returns the user agent's skills directory.

func ExpandPromptTemplate

func ExpandPromptTemplate(line string, templates []PromptTemplate) (string, bool)

ExpandPromptTemplate consumes a `/name [args]` line. If `name` matches a loaded template, returns the expanded body with arg substitution applied and ok=true. Otherwise returns ("", false). Lines without a leading `/` always return ("", false).

func ExpandSkillCommand

func ExpandSkillCommand(text string, skills []*SkillDef) (string, bool)

ExpandSkillCommand expands a "/skill:name [args]" string to the skill XML block. Returns (expanded, true) on success, ("", false) if the text doesn't start with "/skill:" or the named skill isn't in skills.

The XML block format:

<skill name="..." location="...">
References are relative to <dir>.

<body>
</skill>

Args (if any) are appended after the block separated by two newlines.

func ExpandTildePath

func ExpandTildePath(path string) string

ExpandTildePath expands a leading ~ in a filesystem path.

func ExportSessionToJsonl

func ExportSessionToJsonl(session *Session, outputPath string, createTrailingEntries TrailingEntries) (string, error)

ExportSessionToJsonl writes the session's current branch (and optional trailing entries) to outputPath, resolved against the process working directory, or to session-<ISO timestamp>.jsonl there when outputPath is empty. It returns the resolved path.

func ExtensionForImageMIME

func ExtensionForImageMIME(mime string) string

ExtensionForImageMIME returns the canonical file extension for a supported image MIME, or "" if unknown.

func FormatCacheWarmingStatus

func FormatCacheWarmingStatus(status CacheWarmingStatus, now int64) string

FormatCacheWarmingStatus is the one-line status /session shows. now is a Unix millisecond time.

func FormatCacheWarmingUsage

func FormatCacheWarmingUsage(entry UsageEntry) string

FormatCacheWarmingUsage is the one-line transcript text for persisted cache-warming usage.

func FormatChangelogForChat

func FormatChangelogForChat(entries []ChangelogEntry) string

FormatChangelogForChat takes parsed entries and produces the markdown block /changelog appends to the chat:

  • reverse the entries (so they read oldest-at-top, newest-at-bottom within the inline block),
  • join their content with two newlines,
  • wrap in a bold "What's New" header and horizontal-rule "borders" (wopr's markdown renderer paints `---` as a separator line).

Empty input returns the "No changelog entries found." fallback so a user with a missing/empty CHANGELOG sees a sensible message instead of an empty bordered block.

func FormatNoAPIKeyFoundMessage

func FormatNoAPIKeyFoundMessage(provider string) string

func FormatNoModelSelectedMessage

func FormatNoModelSelectedMessage() string

func FormatNoModelsAvailableMessage

func FormatNoModelsAvailableMessage() string

func GenerateEntryID

func GenerateEntryID() (string, error)

GenerateEntryID returns a random hexadecimal entry ID. The error is always nil; the signature is kept for callers outside this package.

func GenerateSessionID

func GenerateSessionID() (string, error)

GenerateSessionID returns a time-ordered UUIDv7 for a new session. The error is always nil; the signature is kept for callers outside this package.

func GetCacheWarmingDelayMs

func GetCacheWarmingDelayMs(ttlMs int64) (int64, bool)

GetCacheWarmingDelayMs refreshes at 90% of the TTL while preserving at least ten seconds of margin. The bool is false when the TTL is too short.

func GetProjectTrustParentPath

func GetProjectTrustParentPath(cwd string) string

GetProjectTrustParentPath returns the parent trust key, or "" at the root.

func GetProjectTrustPath

func GetProjectTrustPath(cwd string) string

GetProjectTrustPath returns the canonical trust key for a cwd.

func GetPromptCacheTtlMs

func GetPromptCacheTtlMs(model *ai.Model, options ai.StreamOptions) (int64, bool)

GetPromptCacheTtlMs is the lifetime of the prompt cache entry a request writes, from the model's promptCache tier for the retention the request used. The bool is false when the model has no lifetime for that tier.

The request's CacheRetention decides, else WOPR_CACHE_RETENTION, as the providers do.

func HasTrustRequiringProjectResources

func HasTrustRequiringProjectResources(cwd string) bool

HasTrustRequiringProjectResources reports whether cwd has project-local inputs governed by project trust. Project config entries are checked only in cwd; .agents/skills is checked from cwd through every ancestor. The user's ~/.agents/skills is global input and never requires project trust.

func ImmutableBinaryRemediation

func ImmutableBinaryRemediation(exePath string) string

ImmutableBinaryRemediation is the exact, non-looping instruction for an immutable release. wopr does not replace it in place; the owning release is re-pulled.

func IsContextOverflow

func IsContextOverflow(msg *agent.AssistantMessage, contextWindow int) bool

IsContextOverflow reports whether an assistant message represents a context overflow (ai.IsContextOverflow).

func IsRecoverableLength

func IsRecoverableLength(msg *agent.AssistantMessage, desiredMaxOutput int) bool

IsRecoverableLength reports whether a provider stopped for length before reaching the model's original output limit. The limit must be the model value before request-time context clamping (ai.IsRecoverableLength).

func IsReplayable

func IsReplayable(model *ai.Model, options ai.StreamOptions) bool

IsReplayable reports whether replaying the request with a one-token output cap leaves its cache entry untouched. Anthropic's budget-based thinking derives budget_tokens from max_tokens, and Anthropic keys the message cache on that budget, so only adaptive thinking replays safely.

func IsRetryableError

func IsRetryableError(msg *agent.AssistantMessage, contextWindow int) bool

IsRetryableError reports whether an assistant message is a transient provider or transport error worth retrying. A context overflow is not retryable: compaction handles it.

func IsWSL

func IsWSL(getenv func(string) string, readFile func(string) ([]byte, error)) bool

IsWSL reports whether the process runs under Windows Subsystem for Linux, where Windows executables are reachable through interop: WSL_DISTRO_NAME or WSLENV, else a /proc/version that names Microsoft or WSL. getenv and readFile are injected so tests never read the host.

func KeybindingsFile

func KeybindingsFile(agentDir string) string

func LoadModelSources added in v0.2.2

func LoadModelSources(agentDir string)

LoadModelSources hands the cached models.dev copy and provider lists to the model database. It reads only local files.

func NormalizePromptContent

func NormalizePromptContent(content []ai.UserContentBlock, autoResize bool, model *ai.Model) []ai.UserContentBlock

NormalizePromptContent applies the selected model's profile once, before new images enter history. Prompt failures become text hints, unlike tool results.

func OpenExternalEditor

func OpenExternalEditor(ctx context.Context, initial string, configuredEditor string) (string, error)

OpenExternalEditor writes initial into a tempfile, runs the user's editor on it (stdio inherited), and returns the file contents on success. On editor non-zero exit OR read error, returns initial unchanged plus a non-nil error.

The tempfile is always removed before this function returns.

Caller is responsible for:

  • restoring cooked-mode terminal before calling (so the editor's stdin/stdout/stderr inherit a usable TTY);
  • re-entering raw mode and triggering a full re-render after.

func ParsePromptArgs

func ParsePromptArgs(s string) []string

ParsePromptArgs splits an argument string into tokens, respecting single- and double-quoted runs. Whitespace outside quotes is the separator.

func ProjectConfigDir

func ProjectConfigDir(cwd string) string

ProjectConfigDir returns the workspace-local wopr configuration root.

func ProviderLoginHelp

func ProviderLoginHelp() string

ProviderLoginHelp returns the hint shown when no provider is set up.

func RFC3339NowNano

func RFC3339NowNano() string

RFC3339NowNano returns the current UTC time as a nanosecond-precision RFC3339 string: the timestamp format used in every session entry's header. Centralised here so coding/ doesn't have to duplicate the formatting choice.

func RawStdoutWriter

func RawStdoutWriter() io.Writer

RawStdoutWriter returns an `io.Writer` that writes to the pre-takeover stdout. Convenient for passing into helpers like `printToolsForMessages` that expect a Writer interface.

func ReachableProviders

func ReachableProviders() map[string]bool

ReachableProviders returns the set of provider IDs that coding.BuildModel has an explicit `case` for. Anything else falls through BuildModel's default branch which constructs an OpenAI-compatible client: fine for OpenAI-shaped APIs but a silent mis-route for Anthropic/Bedrock/Vertex/etc. that would error at first request.

SINGLE SOURCE OF TRUTH. Keep this set aligned with coding/model.go::BuildModel so provider additions touch one map entry here.

func ReadClipboardImageContext

func ReadClipboardImageContext(parent context.Context) ([]byte, string, error)

ReadClipboardImageContext reads a PNG/JPEG/WebP/GIF from the system clipboard while parent remains active. It returns (nil, "", nil) when the clipboard holds no image. Ctrl+V passes its renderer lifetime so teardown cancels command probes and joins the operation instead of leaving terminal I/O behind.

func RedactBugReportJSON

func RedactBugReportJSON(value any) (any, error)

RedactBugReportJSON copies a JSON-encodable value, replacing the value of every sensitive key with "<redacted>" and redacting URLs in strings.

func RedactBugReportURL

func RedactBugReportURL(value string) string

RedactBugReportURL strips credentials and secret-looking query parameters from an absolute URL, including a URL nested after a scheme prefix such as "git:https://...". Other strings are returned unchanged.

func RefreshModelCatalogs

func RefreshModelCatalogs(ctx context.Context, registry *ModelRegistry) error

RefreshModelCatalogs reloads models.json for the interactive model selectors.

func RefreshModelSources added in v0.2.2

func RefreshModelSources(ctx context.Context, agentDir string, auth *ai.AuthStorage)

RefreshModelSources refreshes what's older than a day: models.dev, and the model list of every provider with a stored or environment credential. It never refreshes a sign-in: a provider whose token has expired waits for the next run that signs in anyway.

func RenderSessionMarkdown

func RenderSessionMarkdown(title string, messages []ai.Message) string

RenderSessionMarkdown renders messages as a readable Markdown transcript. Thinking is omitted; tool calls and results are fenced verbatim.

func RenderTreeASCII

func RenderTreeASCII(root *SessionTreeNode) string

RenderTreeASCII renders a SessionTreeNode as an ASCII tree. Exported wrapper so the coding package can produce /tree output without duplicating the renderer.

func ReportDiagnostics

func ReportDiagnostics(diagnostics []AgentSessionRuntimeDiagnostic)

ReportDiagnostics writes diagnostics to stderr for non-interactive modes: "Error: " in red, "Warning: " in yellow, and info dimmed, colored only when stderr is a terminal.

func RestoreStdout

func RestoreStdout()

RestoreStdout undoes a `TakeOverStdout`. Closes the pipe writer (signalling the copy goroutine to drain + exit), waits for the goroutine, then restores `os.Stdout`. Safe to call without a matching takeover (no-op).

func SaveClipboardImageToTempFile

func SaveClipboardImageToTempFile(bytes []byte, mime string) (string, error)

SaveClipboardImageToTempFile writes the bytes to $TMPDIR/wopr-clipboard-<nanos>.<ext> and returns the absolute path. The caller is responsible for cleanup; for a paste-into-editor flow we leave the file around so the model can read it back.

func SelectStartupSession

func SelectStartupSession(
	currentLoader func() ([]SessionInfo, error),
	allLoader func() ([]SessionInfo, error),
	opts StartupUIOptions,
) (path string, selected bool, err error)

SelectStartupSession shows the session picker before cwd-bound runtime services exist: this folder's sessions, or every folder's when this one has none, with tab switching between them. It returns selected=false on cancellation.

func SelfUpdateFallback

func SelfUpdateFallback() string

SelfUpdateFallback is the manual path shown when self-update cannot run.

func SerializeSessionBranch

func SerializeSessionBranch(header SessionHeader, branch []SessionEntry, now time.Time, createTrailingEntries TrailingEntries) (string, error)

SerializeSessionBranch writes a fresh session header, the current branch with each entry's parentId chained to the previous entry, and any trailing entries, one JSON object per line.

func SessionEntryToContextMessages

func SessionEntryToContextMessages(entry SessionEntry) []agent.AgentMessage

SessionEntryToContextMessages projects one entry into context messages before context edits. State-only entries project to nothing.

func ShowStartupSelector

func ShowStartupSelector(title string, options []string, opts StartupUIOptions) (index int, selected bool, err error)

ShowStartupSelector displays a small pre-runtime choice list. It returns selected=false when the user cancels.

func SkillDiagnostics

func SkillDiagnostics(skill *SkillDef) []string

SkillDiagnostics validates a skill against the Agent Skills metadata rules. A missing description prevents model discovery; other findings are warnings.

func StoreLiveModels added in v0.2.2

func StoreLiveModels(agentDir, provider string, models []ai.LiveModel)

StoreLiveModels records a provider's freshly read model list: in the model database now and in the models store for later runs.

func SubstitutePromptArgs

func SubstitutePromptArgs(content string, args []string) string

SubstitutePromptArgs returns content with `$1`, `${1:-default}`, `${@:N:L}`, `${@:-default}`, `$ARGUMENTS`, `$@` expanded against args. A `:-default` value is used when the target arg is missing or empty. Substitution runs in a single pass, so argument values are NOT re-scanned for `$N`/`$@`/`$ARGUMENTS` patterns.

func SystemMessageFromAgentMessage

func SystemMessageFromAgentMessage(message agent.AgentMessage) (ai.SystemMessage, bool)

SystemMessageFromAgentMessage reads a projected system-role message.

func TakeOverStdout

func TakeOverStdout() error

TakeOverStdout redirects subsequent `os.Stdout` writes to `os.Stderr`. Idempotent: a second call before `RestoreStdout` is a no-op. The saved original stdout is reachable via `WriteRawStdout` for explicit framed-output callers.

func UnsupportedRemediation

func UnsupportedRemediation(exePath string) string

UnsupportedRemediation is the concrete reinstall/download instruction for a read-only, Windows, or unknown-provenance installation. It never repeats the failed self-update command.

func WriteBugReportArchive

func WriteBugReportArchive(bundle BugReportBundle, path string) (err error)

WriteBugReportArchive writes the bundle as a deflated zip at path. The file is created exclusively so an existing file is never overwritten.

Types

type AgentSessionRuntimeDiagnostic

type AgentSessionRuntimeDiagnostic struct {
	Type    string
	Message string
}

AgentSessionRuntimeDiagnostic is one startup diagnostic. Type is "info", "warning", or "error".

func CollectSettingsDiagnostics

func CollectSettingsDiagnostics(settingsManager *SettingsManager) []AgentSessionRuntimeDiagnostic

CollectSettingsDiagnostics drains the manager's settings errors as warnings naming the settings file, or the scope when no file backs the error.

func DeduplicateDiagnostics

func DeduplicateDiagnostics(diagnostics []AgentSessionRuntimeDiagnostic) []AgentSessionRuntimeDiagnostic

DeduplicateDiagnostics removes duplicate type/message diagnostics while preserving their first occurrence. Startup and runtime settings managers can report the same file error.

type BashExecutionEntry

type BashExecutionEntry struct {
	SessionEntryBase
	Role               string `json:"-"` // always "bashExecution"; set on read
	Command            string `json:"-"`
	Output             string `json:"-"`
	ExitCode           *int   `json:"-"`
	Cancelled          bool   `json:"-"`
	Truncated          bool   `json:"-"`
	FullOutputPath     string `json:"-"`
	ExcludeFromContext bool   `json:"-"`
}

BashExecutionEntry persists a `!cmd` invocation.

Wire shape:

{
  "type": "message",          // outer SessionMessageEntry discriminator
  "id": "...",
  "parentId": "...",
  "timestamp": "...",
  "message": {
    "role": "bashExecution",  // inner discriminator
    "command": "ls",
    "output": "...",
    "exitCode": 0,
    "cancelled": false,
    "truncated": false,
    "fullOutputPath": "...",
    "timestamp": 1234567890,
    "excludeFromContext": false
  }
}

In-memory we synthesize `Base.Type = "bash_execution"` after parsing so all existing exhaustive switches continue to work; this is a pure internal label, not on the wire.

`ExcludeFromContext` is set when the user invoked `!!cmd`: the entry persists for transcript purposes but is filtered out of agent hand-off so the LLM doesn't see it.

func (BashExecutionEntry) MarshalJSON

func (b BashExecutionEntry) MarshalJSON() ([]byte, error)

MarshalJSON serializes to the wire shape (type:"message" + nested).

func (*BashExecutionEntry) UnmarshalJSON

func (b *BashExecutionEntry) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the wire shape (type:"message" with an inner role:"bashExecution" message).

type BashExecutionMessage

type BashExecutionMessage struct {
	Role               string `json:"role"` // "bashExecution"
	Command            string `json:"command"`
	Output             string `json:"output"`
	ExitCode           *int   `json:"exitCode"`
	Cancelled          bool   `json:"cancelled"`
	Truncated          bool   `json:"truncated"`
	FullOutputPath     string `json:"fullOutputPath,omitempty"`
	Timestamp          int64  `json:"timestamp,omitempty"`
	ExcludeFromContext bool   `json:"excludeFromContext,omitempty"`
}

BashExecutionMessage is the inner `message` payload of bash entries.

type BinaryUpdate

type BinaryUpdate struct {
	CurrentVersion string
	LatestVersion  string
	Command        string // the command that applies it: "/upgrade"
}

BinaryUpdate describes an available newer release.

func CheckForBinaryUpdate

func CheckForBinaryUpdate(ctx context.Context, source ReleaseSource, currentVersion string) *BinaryUpdate

CheckForBinaryUpdate reports a newer published release, or nil when up to date, offline, or GitHub is unreachable. It never surfaces an error: a startup update check is best-effort and must not disrupt the session. It costs one request to the releases/latest redirect.

type BranchSummaryConfig

type BranchSummaryConfig struct {
	ReserveTokens int
	SkipPrompt    bool
}

BranchSummaryConfig is the resolved branch summary settings.

type BranchSummaryEntry

type BranchSummaryEntry struct {
	SessionEntryBase
	FromID   string    `json:"fromId"`
	Summary  string    `json:"summary"`
	Details  any       `json:"details,omitempty"`
	FromHook bool      `json:"fromHook,omitempty"`
	Usage    *ai.Usage `json:"usage,omitempty"`
}

type BranchSummarySettings

type BranchSummarySettings struct {
	ReserveTokens *int  `json:"reserveTokens,omitempty"`
	SkipPrompt    *bool `json:"skipPrompt,omitempty"`
}

BranchSummarySettings is settings.branchSummary.

type BugReportAssistantDiagnostic

type BugReportAssistantDiagnostic struct {
	EntryID       string                          `json:"entryId"`
	Timestamp     string                          `json:"timestamp"`
	Provider      string                          `json:"provider"`
	Model         string                          `json:"model"`
	API           string                          `json:"api"`
	StopReason    string                          `json:"stopReason"`
	RawStopReason string                          `json:"rawStopReason,omitempty"`
	ErrorMessage  string                          `json:"errorMessage,omitempty"`
	Diagnostics   []ai.AssistantMessageDiagnostic `json:"diagnostics"`
}

BugReportAssistantDiagnostic is one failed or diagnosed assistant turn.

type BugReportAuthStatus

type BugReportAuthStatus struct {
	Configured bool   `json:"configured"`
	Source     string `json:"source,omitempty"`
	Label      string `json:"label,omitempty"`
}

BugReportAuthStatus is a provider's auth status in a report.

type BugReportBundle

type BugReportBundle struct {
	Metadata     BugReportMetadata
	Diagnostics  BugReportDiagnostics
	SessionJSONL *string
	Summary      *string
}

BugReportBundle is everything one report archive contains.

type BugReportDiagnostics

type BugReportDiagnostics struct {
	SchemaVersion         int                            `json:"schemaVersion"`
	SessionID             string                         `json:"sessionId"`
	EntryCount            int                            `json:"entryCount"`
	AssistantMessageCount int                            `json:"assistantMessageCount"`
	Assistant             []BugReportAssistantDiagnostic `json:"assistant"`
	Crashes               []CrashRecord                  `json:"crashes"`
}

BugReportDiagnostics is diagnostics.json.

func CollectBugReportDiagnostics

func CollectBugReportDiagnostics(sessionID string, entries []SessionEntry, crashes []CrashRecord) BugReportDiagnostics

CollectBugReportDiagnostics collects failed assistant turns without conversation content, plus the crash log records without their notified flag.

type BugReportEnvironment

type BugReportEnvironment struct {
	Version   string               `json:"version"`
	UserAgent string               `json:"userAgent"`
	Runtime   string               `json:"runtime"`
	Platform  string               `json:"platform"`
	Arch      string               `json:"arch"`
	OSRelease string               `json:"osRelease"`
	OSVersion string               `json:"osVersion"`
	Shell     *string              `json:"shell"`
	Terminal  BugReportTerminalEnv `json:"terminal"`
	// EnvironmentVariables lists WOPR_* names only; values never leave the
	// machine.
	EnvironmentVariables []string `json:"environmentVariables"`
}

BugReportEnvironment describes the running WOPR process without secrets.

type BugReportFile

type BugReportFile struct {
	Name string
	Data []byte
}

BugReportFile is one archive member.

func BugReportFiles

func BugReportFiles(bundle BugReportBundle) ([]BugReportFile, error)

BugReportFiles lists the archive members in order.

type BugReportInputs

type BugReportInputs struct {
	Version         string
	SessionID       string
	CWD             string
	MessageCount    int
	Model           *ai.Model
	Provider        *BugReportProvider
	ThinkingLevel   string
	GlobalSettings  Settings
	ProjectSettings Settings
	Entries         []SessionEntry
	Branch          []SessionEntry
	Header          SessionHeader
}

BugReportInputs is the process state a report describes. The interactive host fills it; tests construct it directly.

type BugReportMetadata

type BugReportMetadata struct {
	SchemaVersion int                  `json:"schemaVersion"`
	ID            string               `json:"id"`
	CreatedAt     string               `json:"createdAt"`
	Hint          *string              `json:"hint"`
	Environment   BugReportEnvironment `json:"environment"`
	Session       BugReportSessionInfo `json:"session"`
	Model         *BugReportModel      `json:"model"`
	Provider      *BugReportProvider   `json:"provider"`
	ThinkingLevel string               `json:"thinkingLevel"`
	Settings      BugReportSettings    `json:"settings"`
}

BugReportMetadata is report.json.

func CollectBugReportMetadata

func CollectBugReportMetadata(inputs BugReportInputs, options BugReportOptions, summaryIncluded bool, now time.Time) (BugReportMetadata, error)

CollectBugReportMetadata builds report.json.

type BugReportModel

type BugReportModel struct {
	Provider         string              `json:"provider"`
	ID               string              `json:"id"`
	Name             string              `json:"name"`
	API              string              `json:"api"`
	BaseURL          string              `json:"baseUrl"`
	Reasoning        bool                `json:"reasoning"`
	Input            []string            `json:"input"`
	ContextWindow    int                 `json:"contextWindow"`
	MaxTokens        int                 `json:"maxTokens"`
	SamplingParams   any                 `json:"samplingParams"`
	Compat           any                 `json:"compat"`
	ThinkingLevelMap ai.ThinkingLevelMap `json:"thinkingLevelMap"`
	HeaderNames      []string            `json:"headerNames"`
}

BugReportModel describes the current model without credentials.

type BugReportOptions

type BugReportOptions struct {
	Hint           string
	IncludeSession bool
}

BugReportOptions are the user's consent choices.

type BugReportProvider

type BugReportProvider struct {
	ID                  string              `json:"id"`
	Name                string              `json:"name"`
	BaseURL             *string             `json:"baseUrl"`
	HeaderNames         []string            `json:"headerNames"`
	AuthStatus          BugReportAuthStatus `json:"authStatus"`
	UsingOAuth          bool                `json:"usingOAuth"`
	RegisteredAtRuntime bool                `json:"registeredAtRuntime"`
}

BugReportProvider describes the current model's provider without credentials.

type BugReportSessionEntryData

type BugReportSessionEntryData struct {
	ID              string  `json:"id"`
	CreatedAt       string  `json:"createdAt"`
	Hint            *string `json:"hint"`
	SessionIncluded bool    `json:"sessionIncluded"`
	SummaryIncluded bool    `json:"summaryIncluded"`
	Delivery        string  `json:"delivery"`
	Path            string  `json:"path,omitempty"`
}

BugReportSessionEntryData is the custom entry /bug records in the session. Delivery is always "zip".

type BugReportSessionInfo

type BugReportSessionInfo struct {
	ID              string `json:"id"`
	Included        bool   `json:"included"`
	SummaryIncluded bool   `json:"summaryIncluded"`
	MessageCount    int    `json:"messageCount"`
	CWD             string `json:"cwd,omitempty"`
}

BugReportSessionInfo records what the report shares about the session.

type BugReportSettings

type BugReportSettings struct {
	Global  any `json:"global"`
	Project any `json:"project"`
}

BugReportSettings holds the redacted global and project settings.

type BugReportTerminalEnv

type BugReportTerminalEnv struct {
	Term           *string `json:"term"`
	Program        *string `json:"program"`
	ProgramVersion *string `json:"programVersion"`
	Colorterm      *string `json:"colorterm"`
	Tmux           bool    `json:"tmux"`
	SSH            bool    `json:"ssh"`
	CI             bool    `json:"ci"`
}

BugReportTerminalEnv records terminal identification variables.

type BuiltinSlashCommand

type BuiltinSlashCommand struct {
	Name        string
	Aliases     []string
	Description string
	// ArgumentHint is shown after the command in autocomplete (e.g.
	// "<provider/model>").
	ArgumentHint string
	// Hidden commands dispatch normally but are omitted from autocomplete
	// and the docs (e.g. /debug).
	Hidden bool
	// contains filtered or unexported fields
}

BuiltinSlashCommand is one built-in slash command.

func BuiltinSlashCommands

func BuiltinSlashCommands() []BuiltinSlashCommand

BuiltinSlashCommands returns the built-in slash commands: the single source for dispatch, autocomplete, and the docs check.

type CacheWarmRequest

type CacheWarmRequest struct {
	Model   *ai.Model
	Context ai.TranscriptContext
	Options ai.StreamOptions
}

CacheWarmRequest is the request whose prompt cache entry should be kept warm, exactly as it was sent.

type CacheWarmer

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

CacheWarmer keeps one prompt cache entry alive by re-sending its request with a one-token output cap before the entry expires. Start replaces any previous run; warm requests never extend the fixed safety windows.

Each refresh is an owned task: a timer whose callback the warmer tracks, cancelled through the run's context and drained by Wait.

func NewCacheWarmer

func NewCacheWarmer(stream agent.StreamFn, sessionManager CacheWarmerSessionManager, getMode func() CacheWarmingMode) *CacheWarmer

NewCacheWarmer creates a warmer.

func (*CacheWarmer) Cancel

func (w *CacheWarmer) Cancel()

Cancel stops warming.

func (*CacheWarmer) Close

func (w *CacheWarmer) Close()

Close cancels warming for good: later Start calls are ignored and, once Close returns, no refresh reports to OnWarmed, even one still persisting its usage entry. Close waits for a report already in progress, after cancelling that report's context.

func (*CacheWarmer) OnAgentSettled

func (w *CacheWarmer) OnAgentSettled()

OnAgentSettled moves the run to its idle phase, or stops it in streaming mode.

func (*CacheWarmer) OnModeChanged

func (w *CacheWarmer) OnModeChanged()

OnModeChanged reconciles an active run after the persisted mode changes.

func (*CacheWarmer) SetOnWarmed

func (w *CacheWarmer) SetOnWarmed(onWarmed func(context.Context, UsageEntry))

SetOnWarmed sets the callback that receives the persisted usage entry after each successful refresh. Its context is cancelled when Close starts; a callback blocked on delivery must return once it is, because Close waits for it. The callback must not call Close.

func (*CacheWarmer) Start

func (w *CacheWarmer) Start(request CacheWarmRequest, isCurrent func() bool)

Start keeps the prompt cache entry written by request warm while isCurrent holds.

func (*CacheWarmer) Status

func (w *CacheWarmer) Status() CacheWarmingStatus

Status reports the current warming state and the policy inputs that produced it.

func (*CacheWarmer) Wait

func (w *CacheWarmer) Wait()

Wait blocks until every scheduled or in-flight refresh has finished, then releases the closed warmer's provider and Session references. Call it after Close.

type CacheWarmerSessionManager

type CacheWarmerSessionManager interface {
	AppendUsage(kind, provider, model string, usage ai.Usage, note string) (UsageEntry, error)
	GetBranch() []SessionEntry
}

CacheWarmerSessionManager is the Session surface the warmer uses.

type CacheWarmingAction

type CacheWarmingAction string

CacheWarmingAction is the warmer's warm-or-stop decision.

const (
	CacheWarmingActionWarm CacheWarmingAction = "warm"
	CacheWarmingActionStop CacheWarmingAction = "stop"
)

type CacheWarmingDecision

type CacheWarmingDecision struct {
	// Phase is "streaming" while the agent run that sent the request is still
	// active, then "idle".
	Phase string `json:"phase"`
	// WarmCost is the price of this refresh: a cache read of the prompt plus
	// one output token.
	WarmCost float64 `json:"warmCost"`
	// MissCost is the extra price of the next real request if the entry is lost.
	MissCost float64 `json:"missCost"`
	// ContinuationProbability estimates the chance that a real request
	// arrives before the entry expires.
	ContinuationProbability float64 `json:"continuationProbability"`
	// ExpectedSavings is ContinuationProbability*MissCost - WarmCost.
	ExpectedSavings float64 `json:"expectedSavings"`
	// EconomicsAvailable is false when the prompt size or prices are unknown.
	EconomicsAvailable bool `json:"economicsAvailable"`
	// Action is "warm" when ExpectedSavings is at least $0.05.
	Action CacheWarmingAction `json:"action"`
}

CacheWarmingDecision holds the inputs and outcome of one warm-or-stop decision, as shown by /session.

type CacheWarmingMode

type CacheWarmingMode string

CacheWarmingMode selects when prompt caches are kept warm.

type CacheWarmingStatus

type CacheWarmingStatus struct {
	// State is "inactive", "scheduled" (a refresh timer is armed), or
	// "refreshing" (a warm request is in flight).
	State string `json:"state"`
	// Reason says why nothing is scheduled.
	Reason string `json:"reason,omitempty"`
	// NextWarmAt is the Unix millisecond time of the next decision, or 0.
	NextWarmAt int64 `json:"nextWarmAt,omitempty"`
	// Decision is the pending decision, or the decision that stopped warming.
	Decision *CacheWarmingDecision `json:"decision,omitempty"`
}

CacheWarmingStatus is the warmer state /session reports.

type ChangelogEntry

type ChangelogEntry struct {
	Major   int
	Minor   int
	Patch   int
	Content string // includes the `## [x.y.z]` header line itself, trimmed
}

ChangelogEntry is one parsed `## [x.y.z]` block.

func GetNewEntries

func GetNewEntries(entries []ChangelogEntry, sinceVersion string) []ChangelogEntry

GetNewEntries returns the subset of entries whose version is strictly newer than sinceVersion (a "x.y.z" string).

func ParseChangelog

func ParseChangelog(content string) []ChangelogEntry

ParseChangelog walks the markdown content of a CHANGELOG.md and returns one ChangelogEntry per `## [x.y.z]`-style header. Order is the order found in the file (typical convention: newest first). Malformed entries are silently skipped.

type ChatViewport

type ChatViewport struct {
	Root       tui.Component
	Transcript *tui.ScrollView
}

ChatViewport is the fullscreen layout root and its transcript scroll view.

func CreateChatViewport

func CreateChatViewport(options ChatViewportOptions) ChatViewport

CreateChatViewport builds the fullscreen layout: a main column with the transcript scrolling above a fixed input dock, inset by a gutter on each side, and an optional sidebar on the right on wide terminals.

type ChatViewportOptions

type ChatViewportOptions struct {
	Document        tui.Component
	PendingMessages tui.Component
	Status          tui.Component
	Editor          tui.Component
	Footer          tui.Component
	WidgetsAbove    tui.Component
	WidgetsBelow    tui.Component
	Sidebar         tui.Component
	// SidebarVisible decides the sidebar's visibility at a terminal width;
	// nil shows it on terminals wider than 120 columns.
	SidebarVisible      func(width int) bool
	Scrollbar           string
	ScrollbarTrackStyle func(text string) string
	ScrollbarThumbStyle func(text string) string
}

ChatViewportOptions are the components of the fullscreen layout. WidgetsAbove, WidgetsBelow and Sidebar are optional (nil omits the slot); Scrollbar "" takes the "auto" default; nil styles take the ScrollView defaults.

type CompactionConfig

type CompactionConfig struct {
	Enabled          bool
	ReserveTokens    int
	KeepRecentTokens int
	MaxContextTokens int
}

CompactionConfig is the resolved compaction settings (compaction imports codingagent, so its own type cannot be used here). Zero token settings scale with the model's window (compaction.CompactionSettings.ForModel).

type CompactionEntry

type CompactionEntry struct {
	SessionEntryBase
	Summary          string    `json:"summary"`
	FirstKeptEntryID string    `json:"firstKeptEntryId"`
	TokensBefore     int       `json:"tokensBefore"`
	Details          any       `json:"details,omitempty"`
	Usage            *ai.Usage `json:"usage,omitempty"`
	FromHook         bool      `json:"fromHook"`
	// SystemMessage is the complete prompt and tool state at this compaction
	// boundary. It is absent when the projected context has no system state.
	SystemMessage json.RawMessage `json:"systemMessage,omitempty"`
}

CompactionEntry field order is the on-disk key order; keep it stable.

type CompactionModelOverride

type CompactionModelOverride struct {
	ReserveTokens    *int `json:"reserveTokens,omitempty"`
	KeepRecentTokens *int `json:"keepRecentTokens,omitempty"`
}

CompactionModelOverride holds per-model compaction token settings.

type CompactionSettingsJSON

type CompactionSettingsJSON struct {
	Enabled          *bool `json:"enabled,omitempty"`
	ReserveTokens    *int  `json:"reserveTokens,omitempty"`
	KeepRecentTokens *int  `json:"keepRecentTokens,omitempty"`
	// MaxContextTokens compacts once the context passes it, even on a larger
	// window. Default 200000; 0 lets the window alone decide.
	MaxContextTokens *int `json:"maxContextTokens,omitempty"`
	// ModelOverrides maps exact "provider/modelId" keys to token settings
	// that take precedence for that model.
	ModelOverrides map[string]CompactionModelOverride `json:"modelOverrides,omitempty"`
}

CompactionSettingsJSON is settings.compaction.

type ContextEditEntry

type ContextEditEntry struct {
	SessionEntryBase
	TargetID    string                  `json:"targetId"`
	Replacement *ContextEditReplacement `json:"replacement"`
}

ContextEditEntry is an append-only change to one earlier entry's contribution to model context. A nil Replacement serializes as null and omits the target from model context; a replacement changes only the target's content. The target entry itself is never rewritten.

type ContextEditReplacement

type ContextEditReplacement struct {
	Content json.RawMessage `json:"content"`
}

ContextEditReplacement is the non-null replacement of a context_edit entry. Content is the JSON string or content-block array that replaces the target's content (ContextEditableContent).

type ContextFile

type ContextFile struct {
	Path    string
	Content string
}

ContextFile holds a loaded project context file (AGENTS.override.md, AGENTS.md, or CLAUDE.md).

func LoadProjectContextFiles

func LoadProjectContextFiles(cwd, agentDir string) []ContextFile

LoadProjectContextFiles discovers and loads project context files from the agent config directory and every ancestor directory from cwd up to the filesystem root.

Load order:

  1. Agent config dir (e.g. ~/.wopr/): global context
  2. Ancestor directories from root down to cwd: project context

Within each directory, AGENTS.override.md replaces that directory's regular AGENTS/CLAUDE candidate while ancestor layering remains intact. Duplicate paths are skipped by their loaded path. In a nested linked worktree, the main checkout's same-named context file is skipped when the worktree root supplies its own copy.

type CrashInput

type CrashInput struct {
	Kind        string
	Message     string
	Stack       string
	SessionFile string
	CWD         string
	Version     string
}

CrashInput describes a crash to record.

type CrashRecord

type CrashRecord struct {
	Timestamp   string  `json:"timestamp"`
	Version     string  `json:"version"`
	Kind        string  `json:"kind"`
	Message     string  `json:"message"`
	Stack       *string `json:"stack"`
	SessionFile *string `json:"sessionFile"`
	CWD         string  `json:"cwd"`
	Notified    bool    `json:"notified,omitempty"`
}

CrashRecord is one crashes.json entry. Kind is "uncaught_exception" or "fatal_error".

func ReadCrashLog

func ReadCrashLog(path string) []CrashRecord

ReadCrashLog returns the valid records, or none when the file is missing or unreadable. A record needs a string timestamp and message.

func RecordCrash

func RecordCrash(crash CrashInput, path string, now time.Time) (CrashRecord, bool)

RecordCrash appends a crash, keeping the newest five. It is best effort for callers that are already crashing: it reports ok=false when nothing was written.

func TakeUnnotifiedCrash

func TakeUnnotifiedCrash(path string, now time.Time) (CrashRecord, bool)

TakeUnnotifiedCrash returns the newest crash from the last seven days that has not been announced, and marks every record announced.

type CustomEntry

type CustomEntry struct {
	SessionEntryBase
	CustomType string `json:"customType"`
	Data       any    `json:"data,omitempty"`
}

type CustomMessageEntry

type CustomMessageEntry struct {
	SessionEntryBase
	CustomType string `json:"customType"`
	Content    any    `json:"content"` // string | []ContentBlock
	Display    bool   `json:"display"`
	Details    any    `json:"details,omitempty"`
}

type DiagnosticsSettings

type DiagnosticsSettings struct {
	// Enabled defaults to true.
	Enabled *bool `json:"enabled,omitempty"`
	// TimeoutMs bounds one checker run (default 4000).
	TimeoutMs int `json:"timeoutMs,omitempty"`
	// Commands overrides the checker per language (go, typescript,
	// javascript, python, shell) with {file} and {dir} placeholders; "off"
	// turns a language off.
	Commands map[string]string `json:"commands,omitempty"`
}

DiagnosticsSettings configures post-edit diagnostics.

type ImageSettings

type ImageSettings struct {
	// AutoResize fits images to the width. Default: true.
	AutoResize *bool `json:"autoResize,omitempty"`
	// BlockImages hides images in the transcript. Default: false.
	BlockImages *bool `json:"blockImages,omitempty"`
}

ImageSettings is settings.images.

type InteractiveMode

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

InteractiveMode runs the full interactive TUI session.

func NewInteractiveMode

func NewInteractiveMode(opts InteractiveOptions) *InteractiveMode

NewInteractiveMode creates an interactive session.

func (*InteractiveMode) RestartRequested

func (m *InteractiveMode) RestartRequested() (string, bool)

RestartRequested reports whether the user chose to restart into an upgraded binary, and the session file to reopen ("" when unsaved).

func (*InteractiveMode) Run

func (m *InteractiveMode) Run(ctx context.Context) (err error)

Run renders the initial editor and footer even with quiet startup, then blocks in the interactive loop until the user exits.

type InteractiveOptions

type InteractiveOptions struct {
	CWD      string
	AgentDir string
	Model    *ai.Model
	// SettingsManager holds the settings, including this run's flag
	// overrides. Wired by main.go via coding.Services.
	SettingsManager *SettingsManager
	// SystemPrompt overrides the assembled system prompt. If empty,
	// callers should build one via prompts.BuildDefaultPrompt and pass
	// it in.
	SystemPrompt string
	// AllowedTools restricts which tool names may execute. nil means no
	// restriction; a non-nil empty map blocks every tool. Set from the
	// `tools:` allowlist on agent .md frontmatter.
	AllowedTools map[string]struct{}
	// ActiveBuiltinTools, when non-nil, restricts which built-in coding
	// tools are active (caller tools unaffected). nil = all
	// built-in tools. CLI default [read, bash, edit, write]; grep/find/ls
	// inactive unless requested via --tools.
	ActiveBuiltinTools map[string]struct{}
	// ExcludedTools is a denylist of tool names made non-callable, gating
	// built-in and caller tools alike.
	ExcludedTools map[string]struct{}
	// NoBuiltinTools hides built-in tools while leaving custom tools.
	NoBuiltinTools bool
	// PromptPaths lists additional prompt-template directories.
	// Directories are searched after the default <agentDir>/prompts/ and
	// <cwd>/.wopr/prompts/ (last-wins by name, same as project scope).
	PromptPaths []string
	// SessionDir overrides the on-disk session directory used by /new,
	// /resume, and other interactive session-management flows.
	SessionDir string
	// SessionID specifies an exact session ID for new sessions.
	// Set by --session-id. When non-empty and no
	// existing session matches, Create uses this ID instead of
	// generating a random one.
	SessionID string
	// ThemePaths lists additional theme files/directories to load.
	ThemePaths []string
	// NoPromptTemplates disables prompt template discovery.
	NoPromptTemplates bool
	// NoThemes disables custom theme discovery from disk.
	NoThemes bool

	// SessionHandle, when non-nil, is the pre-constructed agent +
	// on-disk session that InteractiveMode wraps for the TUI.
	// Constructed by the caller via coding.NewSession from the public
	// SDK package; main.go does the bridging between coding.* and
	// internal/codingagent.*. When nil, Run() returns an error: the
	// SDK is the only supported construction path post-Chunk F.
	//
	// Mutually exclusive with ResumePath: if SessionHandle is set,
	// the caller is responsible for having loaded the session via
	// coding.NewSession's ResumePath field.
	SessionHandle InteractiveSessionHandle

	// ContextUsage returns the live Session projection estimate for footer updates.
	ContextUsage func() (tokens *int, contextWindow int)

	// ModelBuilder constructs an *ai.Model from a "<provider>/<id>"
	// spec. Wired by main.go to coding.BuildModel: the public SDK
	// can't be imported from internal/codingagent (cycle), so the
	// caller injects the constructor. nil disables /model switching
	// (handler falls back to print-only).
	ModelBuilder func(spec string) (*ai.Model, error)

	// RequestAuthRuntime is the composed checkAuth/getAuth surface used by
	// warning-only auth checks. Wired by main.go from the same credential and
	// provider configuration used for model requests.
	RequestAuthRuntime *RequestAuthRuntime

	// ModelRegistry is the model registry for auth-aware operations
	// (Refresh, GetAvailable, HasConfiguredAuth). Wired by main.go.
	// nil is safe; post-login refresh is skipped.
	ModelRegistry *ModelRegistry

	// ResumePath, when non-empty, is loaded as the active session at
	// startup. The agent's message history is rebuilt from
	// session.BuildContext() and the transcript is replayed inline so
	// the user sees prior conversation.
	ResumePath string
	// InitialMessage, when non-empty, is auto-submitted to the agent
	// immediately after the TUI initialises. Sourced by main.go from
	// positional args + piped stdin so users can `wopr "hello"`,
	// `wopr < file.txt`, or pipe + run.
	InitialMessage string
	InitialImages  []ai.ImageContent
	// InitialMessages are the positional messages after the first. Each is
	// sent as its own prompt once the previous one settles.
	InitialMessages []string

	// AppVersion is wopr's release version. It records the last changelog
	// version seen and gates the startup "what's new" notification.
	AppVersion string

	// BinaryUpdateChecker, if set, is invoked asynchronously at startup and
	// returns a newer-release notice for the wopr binary itself, or nil. The
	// notice names the command that applies it (/upgrade).
	BinaryUpdateChecker func() *BinaryUpdate

	// ReleaseSource is where /upgrade reads releases; nil disables /upgrade.
	ReleaseSource func() ReleaseSource

	// Llama is the built-in llama.cpp provider: /llama manages its router
	// models, /login configures it, and startup refreshes its catalog.
	Llama *llama.Host
	// OfflineMode skips startup network catalog refreshes, like WOPR_OFFLINE.
	OfflineMode bool

	// ResourceSourceInfoProvider returns source metadata for currently known
	// prompts/skills/themes, keyed by absolute path. Used to enrich
	// verbose startup output and /reload diagnostics with package origin.
	ResourceSourceInfoProvider func() map[string]ResourceSourceInfo
	// ReloadResourceProvider recomputes settings-backed prompt/theme/skill/
	// context-file inputs on /reload instead of reusing startup snapshots.
	ReloadResourceProvider func() ReloadResourceSnapshot

	// Verbose forces verbose startup output (overrides quietStartup).
	Verbose bool

	// Skills is the loaded set of skill definitions, used to expand
	// /skill:name commands in user prompts.
	Skills []*SkillDef
	// RebuildSystemPrompt reconstructs the base system prompt + structured
	// prompt options after /reload from the current skills/context files.
	RebuildSystemPrompt func(skills []*SkillDef, contextFiles []ContextFile) string
	// SkillPaths are the source paths from which Skills were loaded.
	// Used by /reload to re-discover skills from disk. If empty, skills
	// are not reloaded (only the initial set from startup is used).
	SkillPaths []string
	// NoSkills disables skill loading and reload.
	NoSkills bool

	// ContextFiles lists the loaded AGENTS.md/CLAUDE.md project context files.
	// Shown in the startup banner when verbose/non-quiet.
	ContextFiles []ContextFile

	// ThinkingLevel is the initial thinking level override from --thinking.
	// Empty means use default (settings or "medium").
	ThinkingLevel string

	// NoModelWarning, when non-empty, is rendered as a yellow warning
	// line immediately before the welcome banner, e.g. "Warning: No models
	// available. ..." when the user starts without `--model` and has no
	// detectable credentials.
	NoModelWarning string
	// RunSetup opens setup at launch (wopr setup). Setup also opens on its
	// own on a first run.
	RunSetup bool
	// ModelFromFlag reports a model named on the command line: an explicit
	// choice, so a first run doesn't open setup.
	ModelFromFlag bool
	// StartupDiagnostics are shown in the chat after the welcome banner.
	StartupDiagnostics []AgentSessionRuntimeDiagnostic

	// StartupMark, when non-nil, is called at key phases during Run()
	// for startup timing. See cmd/wopr/startup_trace.go.
	StartupMark func(label string)
}

InteractiveOptions configures the interactive mode.

type InteractiveSessionHandle

type InteractiveSessionHandle interface {
	// Agent returns the underlying agent loop.
	Agent() *agent.Agent
	// Inner returns the on-disk session. The local-package type
	// reference (*Session) is the same as *icodingagent.Session
	// from coding/'s point of view.
	Inner() *Session
	// Events returns the agent's streaming event channel. Used by
	// processAgentEvents to drive live UI updates.
	Events() <-chan agent.AgentEvent
	// SetModel swaps the active LLM model mid-session and persists a
	// model_change audit entry.
	SetModel(*ai.Model, ...ModelMutationOptions) error
	// SetThinkingLevel applies and records reasoning without changing defaults unless Persist is set.
	SetThinkingLevel(ai.ThinkingLevel, ...ModelMutationOptions) error
	// StreamModel starts a mode-independent model operation through the
	// Session-owned runtime.
	StreamModel(context.Context, *ai.Model, ai.Context, ai.StreamOptions) *ai.AssistantMessageEventStream
	// AbortCompaction cancels an in-flight Compact() call. Safe to
	// call when no compaction is running (no-op).
	AbortCompaction()
	// CheckPromptCompaction runs prompt()'s compaction check before a new
	// user message starts a run.
	CheckPromptCompaction(ctx context.Context) error
	// RunAgentPrompt runs one agent run to settlement: start seeds it, then
	// automatic retry, overflow and length recovery, threshold compaction,
	// and queued input continue it until nothing asks for another run or ctx
	// is cancelled. The Events consumer must acknowledge the barriers it
	// waits on (see eventBarrier).
	RunAgentPrompt(ctx context.Context, start func(context.Context) ([]agent.AgentMessage, error)) ([]agent.AgentMessage, error)
	// AbortRetry cancels a pending automatic-retry delay without aborting
	// the run.
	AbortRetry()
	// AbortBranchSummary cancels an in-flight branch summarization. Safe
	// to call when no summarization is running (no-op).
	AbortBranchSummary()
	// ReplaceInner swaps the underlying on-disk session. Used by /resume
	// to point the SessionHandle at a newly loaded session so tree
	// navigation and compaction operate on the correct entries.
	ReplaceInner(sess *Session)
	// NavigateTree forks the session to targetID with optional branch
	// summarization. Returns NavigateTreeResult.
	NavigateTreeHandle(ctx context.Context, targetID string, summarize bool, customInstructions string) (NavigateTreeResult, error)
	// CacheWarmingStatus reports the Session's cache warmer, or nil when it
	// has none.
	CacheWarmingStatus() *CacheWarmingStatus
	// SetCacheWarmingMode persists the mode and reconciles active warming.
	SetCacheWarmingMode(CacheWarmingMode) error
	// OnAgentSettled tells the cache warmer that an agent run settled.
	OnAgentSettled()
}

InteractiveSessionHandle is the contract InteractiveMode uses to access a pre-constructed coding session. *coding.Session in the public SDK package satisfies this: its Agent() and Inner() methods have the matching signatures.

We use an interface (rather than importing coding/.Session directly) to break the import cycle: coding/ already imports internal/codingagent for its on-disk Session type, so internal/codingagent cannot import coding/.

main.go (which imports both packages) does the bridging:

sess, _ := coding.NewSession(svcs, opts)
m := codingagent.NewInteractiveMode(codingagent.InteractiveOptions{
    SessionHandle: sess,  // *coding.Session implements this interface
    ...
})

type KeyID

type KeyID = string

type KeybindingDefinition

type KeybindingDefinition struct {
	DefaultKeys []KeyID
	Description string
}

type KeybindingsManager

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

func DefaultKeybindingsManager

func DefaultKeybindingsManager() *KeybindingsManager

func NewKeybindingsManager

func NewKeybindingsManager(agentDir string) *KeybindingsManager

func (*KeybindingsManager) DisplayFor

func (km *KeybindingsManager) DisplayFor(action string) string

DisplayFor returns a human-readable key hint for the first binding of the given action (e.g. "Alt+Up"). Returns the raw KeyID if no display mapping exists.

func (*KeybindingsManager) Get

func (km *KeybindingsManager) Get(action string) []KeyID

func (*KeybindingsManager) HotkeyLines

func (km *KeybindingsManager) HotkeyLines() []string

func (*KeybindingsManager) KeyText

func (km *KeybindingsManager) KeyText(action string) string

KeyText returns the un-capitalized display text for every key bound to action, joined by "/". Used for the startup keybinding hints.

func (*KeybindingsManager) Matches

func (km *KeybindingsManager) Matches(input, action string) bool

func (*KeybindingsManager) MatchesEditorHistory

func (km *KeybindingsManager) MatchesEditorHistory(input string) bool

MatchesEditorHistory reports whether input matches an explicit tui.editor.historyPrevious or historyNext binding. The focused editor gives those precedence over app actions, so a user can bind ctrl+p to history although it cycles models by default.

func (*KeybindingsManager) Reload

func (km *KeybindingsManager) Reload() error

func (*KeybindingsManager) Resolve

func (km *KeybindingsManager) Resolve(input string) string

type LabelEntry

type LabelEntry struct {
	SessionEntryBase
	TargetID string  `json:"targetId"`
	Label    *string `json:"label"`
}

LabelEntry is the "user-renamed this branch" marker. Carries nil Label to delete a previously-set label.

type LoadPromptTemplatesResult

type LoadPromptTemplatesResult struct {
	Templates   []PromptTemplate
	Diagnostics []ResourceDiagnostic
}

LoadPromptTemplatesResult carries loaded commands and file-read or YAML warnings.

func LoadPromptTemplates

func LoadPromptTemplates(cwd, agentDir string, extraPaths ...string) LoadPromptTemplatesResult

LoadPromptTemplates loads user, project, and explicit prompt paths.

type MCPController

type MCPController interface {
	// MCPCommand runs one /mcp subcommand and returns markdown.
	MCPCommand(args string) (string, error)
}

MCPController is implemented by a session handle that owns MCP servers. The /mcp command uses it when present.

type MarkdownMessageType

type MarkdownMessageType string

MarkdownMessageType identifies the transcript message being transformed for display. Transformers never change model context or persisted message data.

const (
	MarkdownMessageAssistant MarkdownMessageType = "assistant"
	// MarkdownMessageAssistantThinking marks the reasoning/thinking trace of an
	// assistant turn; the built-in Mermaid transformer skips it.
	MarkdownMessageAssistantThinking MarkdownMessageType = "assistant-thinking"
)

type MarkdownSettings

type MarkdownSettings struct {
	// Mermaid is "streaming" (the default), "final", or "off".
	Mermaid string `json:"mermaid,omitempty"`
}

MarkdownSettings holds markdown rendering settings.

type MarkdownTransformContext

type MarkdownTransformContext struct {
	MessageType    MarkdownMessageType
	IsStreaming    bool
	AvailableWidth int
}

MarkdownTransformContext describes the message a display-only Markdown rewrite is rewriting.

type MessageEntry

type MessageEntry struct {
	SessionEntryBase
	Message agent.AgentMessage `json:"message"`
}

type ModelChangeEntry

type ModelChangeEntry struct {
	SessionEntryBase
	Provider string `json:"provider"`
	ModelID  string `json:"modelId"`
}

type ModelEntry

type ModelEntry struct {
	ProviderID       string
	ModelID          string
	APIKey           string
	BaseURL          string
	DisplayName      string
	API              string            // "openai-completions" | "anthropic-messages" | etc.
	Headers          map[string]string // resolved request headers from provider/model configuration
	ModelHeaders     map[string]string // intrinsic public Model.headers from the generated catalog
	AuthHeader       bool              // put API key in Authorization header (for custom providers)
	Compat           *ai.ModelCompat   // compat flags for OpenAI-compatible providers
	Reasoning        bool              // whether the model supports reasoning/thinking
	ThinkingLevelMap ai.ThinkingLevelMap
	SamplingParams   map[string]any
	InputLimits      *ai.ModelInputLimits
	Input            []string // ["text"] or ["text","image"]
	ContextWindow    int      // default: ai.DefaultContextWindow
	MaxTokens        int      // default: 16384
	InputCost        float64
	OutputCost       float64
	CacheReadCost    float64
	CacheWriteCost   float64
	CostTiers        []ai.CostTier
	// PriceUnknown marks a model nothing gave a price for: zero costs are
	// unknown, not free.
	PriceUnknown bool
	PromptCache  ai.ModelPromptCache
	Env          map[string]string // provider-scoped env overrides (auth.json env)
	Insecure     bool              // skip TLS verification (self-signed/internal-CA on-prem endpoints)
}

ModelEntry holds the resolved configuration for a single provider+model. Produced by ModelRegistry.Resolve from the models.json schema.

type ModelMutationOptions

type ModelMutationOptions struct {
	Persist bool
}

ModelMutationOptions controls whether a Session mutation also saves a global default.

type ModelRegistry

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

ModelRegistry resolves model configurations from models.json and environment.

func NewModelRegistry

func NewModelRegistry(agentDir string) *ModelRegistry

NewModelRegistry creates a registry, loading from <agent-dir>/models.json if present.

func (*ModelRegistry) BugReportProviderInfo

func (r *ModelRegistry) BugReportProviderInfo(providerID string) *BugReportProvider

BugReportProviderInfo describes providerID from models.json and runtime registrations (llama.cpp). It returns nil when the registry is nil.

func (*ModelRegistry) GetAll

func (r *ModelRegistry) GetAll() []ModelEntry

GetAll returns explicit models and configured overlays for exact generated identities; dynamic providers take precedence over models.json.

func (*ModelRegistry) GetAvailable

func (r *ModelRegistry) GetAvailable() []ModelEntry

GetAvailable returns all model entries from dynamic (runtime-registered, e.g. llama.cpp) and models.json providers that have configured auth (API key or OAuth credentials). It filters a single model list merged from built-ins, models.json custom overlays, and runtime overlays -- not just runtime-registered providers. A provider name registered dynamically takes precedence over a models.json entry of the same name, matching Resolve()'s lookup order.

func (*ModelRegistry) GetProviderAuthStatus

func (r *ModelRegistry) GetProviderAuthStatus(providerID string) ai.AuthStatus

func (*ModelRegistry) GetProviderDisplayName

func (r *ModelRegistry) GetProviderDisplayName(providerID string) string

func (*ModelRegistry) HasAnyKey

func (r *ModelRegistry) HasAnyKey(providerID string) bool

HasAnyKey reports whether the registry has at least one entry for the given provider with a non-empty (post-resolve) APIKey, or the environment configures its auth.

func (*ModelRegistry) HasCatalogModel

func (r *ModelRegistry) HasCatalogModel(providerID, modelID string) bool

HasCatalogModel reports whether provider composition retains one generated identity.

func (*ModelRegistry) HasConfiguredAuth

func (r *ModelRegistry) HasConfiguredAuth(providerID string) bool

HasConfiguredAuth reports whether the provider has either an API key (from env, models.json, or dynamic registration) or stored OAuth credentials.

func (*ModelRegistry) HasModelDefinition

func (r *ModelRegistry) HasModelDefinition(providerID, modelID string) bool

HasModelDefinition reports whether provider composition exposes providerID/modelID through an explicit models list.

func (*ModelRegistry) LoadError

func (r *ModelRegistry) LoadError() string

LoadError returns any error from parsing models.json.

func (*ModelRegistry) Refresh

func (r *ModelRegistry) Refresh()

Refresh reloads models.json from disk and preserves dynamic provider registrations.

func (*ModelRegistry) RemoveRuntimeAPIKey

func (r *ModelRegistry) RemoveRuntimeAPIKey(providerID string)

RemoveRuntimeAPIKey drops providerID's runtime key.

func (*ModelRegistry) Resolve

func (r *ModelRegistry) Resolve(providerID, modelID string) (ModelEntry, bool)

Resolve returns a ModelEntry for the given provider+model combination. Resolution order:

  1. Custom model definition in models.json (provider → models[])
  2. Model override in models.json (provider → modelOverrides[modelID])
  3. Provider-level defaults from models.json (baseUrl, apiKey, compat, headers)
  4. Environment variables (PROVIDER_API_KEY)

func (*ModelRegistry) ResolveCatalogModel

func (r *ModelRegistry) ResolveCatalogModel(providerID, modelID string, generated *ai.KnownModel) ModelEntry

ResolveCatalogModel layers provider configuration and a matching model override onto one generated model.

func (*ModelRegistry) RuntimeAPIKey

func (r *ModelRegistry) RuntimeAPIKey(providerID string) (string, bool)

RuntimeAPIKey returns the non-persistent API key set for providerID.

func (*ModelRegistry) RuntimeModels

func (r *ModelRegistry) RuntimeModels() []RuntimeModel

RuntimeModels returns the composed model identities in order: built-in providers first, then models.json providers in file order, then runtime-registered providers. Within a provider, the catalog models come first and models.json or runtime definitions replace a catalog model of the same id or follow in definition order. A runtime provider that declares models replaces the catalog.

func (*ModelRegistry) SetAuthStorage

func (r *ModelRegistry) SetAuthStorage(auth *ai.AuthStorage)

SetAuthStorage wires the credential store for auth-aware filtering. Must be called before GetAvailable or HasConfiguredAuth produce meaningful results.

func (*ModelRegistry) SetChangeListener

func (r *ModelRegistry) SetChangeListener(notify func()) func()

SetChangeListener replaces the callback invoked after a committed registry change and returns a draining detach function.

func (*ModelRegistry) SetProvider

func (r *ModelRegistry) SetProvider(name string, configMap llama.ProviderConfig)

SetProvider replaces a runtime-registered provider in one committed change, so fields absent from config are cleared rather than kept. The built-in llama.cpp provider republishes its resolved auth and catalog this way.

func (*ModelRegistry) SetRuntimeAPIKey

func (r *ModelRegistry) SetRuntimeAPIKey(providerID, apiKey string)

SetRuntimeAPIKey installs a non-persistent API key for providerID. It acts as a stored api_key credential that is never written to auth.json.

type NavigateTreeResult struct {
	EditorText string
	Cancelled  bool
	Aborted    bool
}

NavigateTreeResult is the outcome of a tree navigation. It mirrors coding.NavigateTreeResult, which this package cannot import.

type ParsedSkillBlockFromText

type ParsedSkillBlockFromText struct {
	Name        string
	Location    string
	Content     string
	UserMessage string // trailing user text after </skill>\n\n, or ""
}

ParsedSkillBlockFromText holds the parsed skill invocation data extracted from a user message text.

func ParseSkillBlock

func ParseSkillBlock(text string) *ParsedSkillBlockFromText

ParseSkillBlock attempts to parse a skill XML block from message text. Returns nil if the text doesn't match the skill block format.

type ProcessedCLIArgs

type ProcessedCLIArgs struct {
	Text   string
	Images []ai.ImageContent
}

ProcessedCLIArgs is the result of processing CLI @file arguments: text placeholders plus image attachments.

func ProcessCLIFileArguments

func ProcessCLIFileArguments(fileArgs []string, cwd string) (ProcessedCLIArgs, error)

ProcessCLIFileArguments expands CLI @file arguments into the initial-message payload. Text files are wrapped as <file name="/abs/path">\ncontent\n</file>\n. Image files are attached as ai.ImageContent blocks and represented in the text stream as either an empty <file> tag (no resize needed) or a dimension note when resized.

type ProjectTrustOption

type ProjectTrustOption struct {
	Label     string
	Trusted   bool
	Updates   []ProjectTrustUpdate
	SavedPath string
}

ProjectTrustOption is one row in a project-trust selector.

func GetProjectTrustOptions

func GetProjectTrustOptions(cwd string, includeSessionOnly bool) []ProjectTrustOption

GetProjectTrustOptions builds the ordered selector choices.

type ProjectTrustStore

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

ProjectTrustStore reads and writes <agentDir>/trust.json.

func NewProjectTrustStore

func NewProjectTrustStore(agentDir string) *ProjectTrustStore

NewProjectTrustStore opens the store rooted at agentDir.

func (*ProjectTrustStore) Get

func (s *ProjectTrustStore) Get(cwd string) (*bool, error)

Get returns the nearest decision for cwd, or nil when none applies.

func (*ProjectTrustStore) GetEntry

func (s *ProjectTrustStore) GetEntry(cwd string) (entry *ProjectTrustStoreEntry, err error)

GetEntry returns the nearest stored decision, walking through parent paths.

func (*ProjectTrustStore) Set

func (s *ProjectTrustStore) Set(cwd string, decision *bool) error

Set sets or clears the canonical decision for cwd.

func (*ProjectTrustStore) SetMany

func (s *ProjectTrustStore) SetMany(updates []ProjectTrustUpdate) error

SetMany applies one locked read-modify-write batch. A nil decision deletes that key; unrelated null entries already present in the store are preserved.

type ProjectTrustStoreEntry

type ProjectTrustStoreEntry struct {
	Path     string
	Decision bool
}

ProjectTrustStoreEntry is the nearest stored decision for a cwd.

type ProjectTrustUpdate

type ProjectTrustUpdate struct {
	Path     string
	Decision *bool
}

ProjectTrustUpdate sets (true/false) or clears (nil) a path's decision.

type ProjectedSessionEntry

type ProjectedSessionEntry struct {
	SourceEntry SessionEntry
	Messages    []agent.AgentMessage
}

ProjectedSessionEntry pairs a raw append-only entry with the model-visible messages it contributes after context edits. Messages is empty for state-only entries and omissions.

type PromptTemplate

type PromptTemplate struct {
	Name         string // basename without .md
	Description  string // from frontmatter, or first non-empty body line truncated to 60
	ArgumentHint string // from frontmatter `argument-hint`, optional
	Content      string // body (without frontmatter)
	FilePath     string // absolute path on disk
	Scope        string // "user" | "project" | "external"
}

PromptTemplate is one loaded `.md` file.

type ProviderRetryConfig

type ProviderRetryConfig struct {
	TimeoutMs       int
	MaxRetries      int
	MaxRetryDelayMs int
}

ProviderRetryConfig holds resolved provider/SDK retry settings.

type ProviderRetrySettings

type ProviderRetrySettings struct {
	TimeoutMs  *int `json:"timeoutMs,omitempty"`
	MaxRetries *int `json:"maxRetries,omitempty"`
	// MaxRetryDelayMs caps each retry delay; an explicit 0 removes the cap.
	// Default: 60000.
	MaxRetryDelayMs *int `json:"maxRetryDelayMs,omitempty"`
}

ProviderRetrySettings is settings.retry.provider: provider request retries and deadlines.

type ReleaseArchive

type ReleaseArchive struct {
	Version string
	Name    string // wopr-<version>-<goos>-<goarch>.tar.gz
	Dir     string // top-level directory inside the archive
	URL     string
	SHA256  string
}

ReleaseArchive is a platform archive whose SHA-256 came from a verified SHA256SUMS.

type ReleaseSource

type ReleaseSource struct {
	// BaseURL is the GitHub web origin (https://github.com). Tests point it
	// at an httptest TLS server.
	BaseURL string
	// Repo is owner/name.
	Repo string
	// PublicKey is the base64 Ed25519 key that must sign SHA256SUMS. Empty
	// fails closed.
	PublicKey string
	Client    *http.Client
	// GOOS and GOARCH select the archive; empty means the running platform.
	GOOS, GOARCH string
}

ReleaseSource reads wopr releases from GitHub: the releases/latest redirect names the version, and each release carries SHA256SUMS, its Ed25519 signature SHA256SUMS.sig, and one archive per platform.

func DefaultReleaseSource

func DefaultReleaseSource(client *http.Client) ReleaseSource

DefaultReleaseSource reads the published wopr releases with the embedded release signing key.

func (ReleaseSource) InstallArchive

func (s ReleaseSource) InstallArchive(ctx context.Context, archive *ReleaseArchive, exePath string) error

InstallArchive downloads archive, verifies its SHA-256 against the signed SHA256SUMS entry, extracts the wopr binary, and atomically replaces exePath. Unix only: a running .exe cannot be replaced in place.

func (ReleaseSource) LatestVersion

func (s ReleaseSource) LatestVersion(ctx context.Context) (string, error)

LatestVersion resolves the newest published release without the GitHub API.

func (ReleaseSource) ResolveArchive

func (s ReleaseSource) ResolveArchive(ctx context.Context, version string) (*ReleaseArchive, error)

ResolveArchive downloads SHA256SUMS and SHA256SUMS.sig for version, verifies the signature against the release key, and returns this platform's archive with its expected digest. Nothing is trusted before the signature verifies.

type ReloadResourceSnapshot

type ReloadResourceSnapshot struct {
	PromptPaths        []string
	ThemePaths         []string
	SkillPaths         []string
	ContextFiles       []ContextFile
	ResourceSourceInfo map[string]ResourceSourceInfo
}

ReloadResourceSnapshot is the recomputed settings/resource view used by /reload: prompt/theme/skill/context inputs are re-resolved from current settings.

type RequestAuthRuntime

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

RequestAuthRuntime resolves provider credentials for request auth.

func NewRequestAuthRuntime

func NewRequestAuthRuntime(ctx context.Context, options RequestAuthRuntimeOptions) (*RequestAuthRuntime, error)

NewRequestAuthRuntime composes built-in providers with models.json.

func (*RequestAuthRuntime) CheckAuth

func (r *RequestAuthRuntime) CheckAuth(ctx context.Context, providerID string) (*ai.AuthCheck, error)

CheckAuth reports configured auth for a provider without refreshing OAuth.

func (*RequestAuthRuntime) GetAuth

func (r *RequestAuthRuntime) GetAuth(ctx context.Context, providerID string, overrides ai.AuthResolutionOverrides) (*ai.AuthResult, error)

GetAuth resolves request auth for a provider, refreshing and persisting OAuth credentials that are about to expire.

func (*RequestAuthRuntime) GetError

func (r *RequestAuthRuntime) GetError() string

GetError reports models.json, composition, and availability errors.

func (*RequestAuthRuntime) GetModelAuth

func (r *RequestAuthRuntime) GetModelAuth(ctx context.Context, model RuntimeModel, overrides ai.AuthResolutionOverrides) (*ai.AuthResult, error)

GetModelAuth resolves request auth for a model: provider auth plus the model's intrinsic and configured headers.

func (*RequestAuthRuntime) GetModels

func (r *RequestAuthRuntime) GetModels(providerID string) []RuntimeModel

GetModels returns every provider's models, or one provider's when providerID is non-empty.

func (*RequestAuthRuntime) GetProvider

func (r *RequestAuthRuntime) GetProvider(providerID string) *RuntimeProvider

GetProvider returns a composed provider by exact id, or nil.

func (*RequestAuthRuntime) GetProviders

func (r *RequestAuthRuntime) GetProviders() []*RuntimeProvider

GetProviders returns composed providers in registration order.

func (*RequestAuthRuntime) HasConfiguredAuth

func (r *RequestAuthRuntime) HasConfiguredAuth(providerID string) bool

HasConfiguredAuth reports the provider's availability from the last refresh.

func (*RequestAuthRuntime) ListCredentials

func (r *RequestAuthRuntime) ListCredentials(ctx context.Context) ([]ai.CredentialInfo, error)

ListCredentials lists stored credential metadata.

func (*RequestAuthRuntime) Refresh

func (r *RequestAuthRuntime) Refresh(ctx context.Context)

Refresh recomputes configured-provider availability. An availability failure keeps the previous availability and is reported through GetError.

type RequestAuthRuntimeOptions

type RequestAuthRuntimeOptions struct {
	Credentials ai.CredentialStore
	// AgentDir holds models.json; empty means no models.json.
	AgentDir string
	// RefreshOnCreate computes configured-provider availability at creation.
	RefreshOnCreate bool
	// AuthContext overrides the process environment and filesystem.
	AuthContext *ai.AuthContext
}

RequestAuthRuntimeOptions configures a RequestAuthRuntime.

type ResourceCollision

type ResourceCollision struct {
	ResourceType string
	Name         string
	WinnerPath   string
	LoserPath    string
}

ResourceCollision records two prompt templates with the same name.

type ResourceDiagnostic

type ResourceDiagnostic struct {
	// Type is "warning" or "collision".
	Type      string
	Message   string
	Path      string
	Collision *ResourceCollision
}

ResourceDiagnostic is a warning or collision found while loading resources.

type ResourceSourceInfo

type ResourceSourceInfo struct {
	Path         string
	ResourceType string // "skills" | "prompts" | "themes"
	Enabled      bool
	Scope        string // "user" | "project"
	Origin       string // "package" | "top-level"
	Source       string // package source string or "local"
	BaseDir      string // package root for package-relative shortening
}

ResourceSourceInfo tracks where a loaded resource came from, to annotate startup context and collision diagnostics with package origin + scope.

func (ResourceSourceInfo) DisplayName

func (i ResourceSourceInfo) DisplayName() string

DisplayName derives the logical resource name used for collision grouping.

type RetryConfig

type RetryConfig struct {
	Enabled     bool
	MaxRetries  int
	BaseDelayMs int
	MaxDelayMs  int
}

RetryConfig holds resolved retry settings with defaults applied.

type RetrySettingsJSON

type RetrySettingsJSON struct {
	Enabled         *bool                  `json:"enabled,omitempty"`
	MaxRetries      *int                   `json:"maxRetries,omitempty"`
	BaseDelayMs     *int                   `json:"baseDelayMs,omitempty"`
	MaxAgentDelayMs *int                   `json:"maxAgentDelayMs,omitempty"`
	Provider        *ProviderRetrySettings `json:"provider,omitempty"`
}

RetrySettingsJSON is settings.retry. Defaults: enabled, maxRetries 3, baseDelayMs 2000, maxAgentDelayMs 60000.

type RouterController

type RouterController interface {
	// RouterCommand runs one /router subcommand and returns markdown.
	RouterCommand(args string) (string, error)
}

RouterController is implemented by a session handle that owns a model router. The /router command uses it when present.

type RuntimeModel

type RuntimeModel struct {
	Provider string
	ID       string
	Name     string
	// Headers are the model's intrinsic catalog headers.
	Headers ai.ProviderHeaders
}

RuntimeModel is the model identity request-auth resolution needs.

type RuntimeProvider

type RuntimeProvider struct {
	ID   string
	Auth ai.ProviderAuth
	// contains filtered or unexported fields
}

RuntimeProvider is a composed provider: its id and auth methods.

type SelfUpdateActionResult

type SelfUpdateActionResult struct {
	Done    bool
	Action  string
	Message string
	Cause   error
}

SelfUpdateActionResult is the outcome of attempting one tier's update. The caller surfaces Action and Message verbatim; Done marks a successful mutation. Cause carries the originating error so the caller can distinguish a benign "up to date" from a real failure.

func ApplySelfUpdateTier

func ApplySelfUpdateTier(applyStandalone func(exePath string) error) SelfUpdateActionResult

ApplySelfUpdateTier resolves exactly one installation tier and applies its update, surfacing any failure from the started tier without falling through. The standalone download/replace path is supplied via applyStandalone so the cmd layer keeps ownership of the HTTP client, release, and version compare.

type SelfUpdateProvenance

type SelfUpdateProvenance struct {
	Tier    SelfUpdateTier
	ExePath string
}

SelfUpdateProvenance is the resolved ownership of the running executable.

func ResolveSelfUpdateTier

func ResolveSelfUpdateTier() (*SelfUpdateProvenance, error)

ResolveSelfUpdateTier classifies the running executable before any mutation. It never mutates state.

Resolution order: explicit deployment override (WOPR_INSTALL_TIER), then Windows (never replaced in place), then a writable standalone binary.

type SelfUpdateRefused

type SelfUpdateRefused struct{ Message string }

SelfUpdateRefused reports an installation wopr does not replace in place; the message is the remediation to show verbatim.

func (*SelfUpdateRefused) Error

func (e *SelfUpdateRefused) Error() string

type SelfUpdateResult

type SelfUpdateResult struct {
	Latest string
	// UpToDate means nothing was installed because current is the latest.
	UpToDate bool
	// ExePath is the replaced executable when an update was installed.
	ExePath string
}

SelfUpdateResult is the outcome of UpdateSelf.

func UpdateSelf

func UpdateSelf(ctx context.Context, source ReleaseSource, current string, force bool, step func(stage, version string)) (SelfUpdateResult, error)

UpdateSelf resolves the latest release and, when it is newer than current (or force is set), verifies its signed SHA256SUMS and replaces the running standalone binary. step, when set, is called as each stage starts. An installation wopr must not replace returns *SelfUpdateRefused.

type SelfUpdateTier

type SelfUpdateTier string

SelfUpdateTier selects one owner before mutation.

const (
	// TierStandalone is a writable standalone binary that wopr replaces in
	// place from a verified GitHub release.
	TierStandalone SelfUpdateTier = "standalone"
	// TierImmutableBinary is a deployment-declared immutable release. Its owning
	// release is re-pulled; wopr does not replace it in place.
	TierImmutableBinary SelfUpdateTier = "immutable-binary"
	// TierContainer is an OCI image deployment. The
	// authenticated pull/redeploy path is reported; wopr does not rewrite a
	// running image.
	TierContainer SelfUpdateTier = "container"
	// TierUnsupported covers read-only and Windows installations. wopr refuses
	// mutation and reports the executable path plus concrete remediation.
	TierUnsupported SelfUpdateTier = "unsupported"
)

type Session

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

Session manages a single JSONL session file. All mutations append entries; no entry is ever rewritten or deleted (the parentId/leafID dance is how branches and undo work: abandoned entries simply become orphaned subtrees in the same file).

func NewSession

func NewSession(id, cwd string) *Session

NewSession creates a new in-memory session with an ISO millisecond header timestamp (not yet persisted).

func (*Session) Accounting

func (s *Session) Accounting() SessionAccounting

func (*Session) AppendBashExecution

func (s *Session) AppendBashExecution(command, output string, exitCode *int, cancelled, truncated bool, fullOutputPath string, excludeFromContext bool) (string, error)

AppendBashExecution persists a `!cmd` invocation. Returns the new entry id (the new leaf). The caller has already chosen excludeFromContext (true for `!!cmd`).

func (*Session) AppendBranchSummary

func (s *Session) AppendBranchSummary(parentID *string, summary string, details any, fromHook bool, usage *ai.Usage) (string, error)

AppendBranchSummary starts a new branch at parentID with a summary of the abandoned path. parentID is the explicit parent: nil means the entry is a root-level node (no parent). fromId records the leaf being abandoned ("root" when there is none). The new entry becomes the leaf.

func (*Session) AppendCompaction

func (s *Session) AppendCompaction(summary, firstKeptEntryID string, tokensBefore int, details any, fromHook bool, usage *ai.Usage) (string, error)

AppendCompaction records a compaction event as a new leaf. An empty firstKeptEntryID means none: the entry stores its own ID, so the compaction retains no preceding entries. The entry also records the current projected system state, when there is one, stamped with the entry time.

func (*Session) AppendContextEdit

func (s *Session) AppendContextEdit(targetID string, replacement *ContextEditReplacement) (string, error)

AppendContextEdit appends a branch-local edit to an earlier model-visible entry and returns the new entry ID. A nil replacement omits the target. The target must be on the active branch and be a user, assistant, or tool-result message entry, or a custom_message entry. String replacements for assistant and tool-result targets are stored as one text block because those roles require content arrays.

func (*Session) AppendCustomEntry

func (s *Session) AppendCustomEntry(customType string, data any) error

AppendCustomEntry persists state as a "custom" entry under the current leaf, reading the leaf and appending in one step so a concurrent message never forks the branch.

func (*Session) AppendCustomMessage

func (s *Session) AppendCustomMessage(customType string, content any, display bool, details any) (string, error)

AppendCustomMessage persists a custom message as a "custom_message" entry and returns the new entry id.

func (*Session) AppendEntry

func (s *Session) AppendEntry(entry any) error

AppendEntry appends an entry to the session (memory + file). The caller must have already filled in `id`, `parentId`, `timestamp` fields on the entry struct: but if `parentId` is nil-ptr AND session has a current leafID, we wire it up automatically so callers can skip the boilerplate.

Returns an error if marshalling, parsing, or file write fails.

func (*Session) AppendLabelChange

func (s *Session) AppendLabelChange(targetID string, label *string) error

AppendLabelChange writes a label entry for targetID. label=nil clears any existing label.

func (*Session) AppendMessage

func (s *Session) AppendMessage(msg agent.AgentMessage) (string, error)

AppendMessage persists one AgentMessage, generating a fresh entry id and timestamp and linking it to the current leaf.

func (*Session) AppendModelSwitch

func (s *Session) AppendModelSwitch(provider, modelID, _ string) error

AppendModelSwitch persists a `model_change` audit entry recording that the user switched models mid-session. The display name is not part of the model_change schema; the status line resolves it from the registry on resume.

func (*Session) AppendThinkingLevelChange

func (s *Session) AppendThinkingLevelChange(level string) error

AppendThinkingLevelChange persists a thinking_level_change audit entry. Called when the user cycles the thinking level via Shift+Tab.

func (*Session) AppendUsage

func (s *Session) AppendUsage(kind, provider, model string, usage ai.Usage, note string) (UsageEntry, error)

AppendUsage appends model-attributed usage that does not participate in LLM context and returns the appended entry.

func (*Session) Branch

func (s *Session) Branch(leafID string) []SessionEntry

Branch returns the path-to-root chain (in append order: root first, leaf last) ending at leafID. Returns nil if leafID isn't an entry.

func (*Session) BuildContext

func (s *Session) BuildContext(leafID *string) []agent.AgentMessage

BuildContext returns the model-visible message list for the path ending at leafID, or at the current leaf when leafID is nil. It is the Messages field of the canonical session projection, so latest compaction, retained entries, and context_edit entries on that path all apply. An unset current leaf (SetLeafID(nil)) yields no messages.

func (*Session) BuildSessionProjection

func (s *Session) BuildSessionProjection() SessionProjection

BuildSessionProjection returns the provenance-preserving projection of the current branch.

func (*Session) CWD

func (s *Session) CWD() string

func (*Session) Entries

func (s *Session) Entries() []SessionEntry

Entries returns a copy of all session entries (in append order).

func (*Session) EntryByID

func (s *Session) EntryByID(id string) (SessionEntry, bool)

EntryByID looks up a session entry by its hex ID. Returns false if the ID isn't present. Used by the chat layer to render markers (e.g. branch-summary chip on fork) without exporting the byID map.

func (*Session) FooterUsageTotals

func (s *Session) FooterUsageTotals() footerUsageTotals

FooterUsageTotals returns the footer's all-entry usage totals without building the per-model breakdown.

func (*Session) Fork

func (s *Session) Fork(forkFromID string) error

Fork moves the leaf pointer to forkFromID so the next AppendEntry becomes a sibling branch off that point. Existing entries are not touched: abandoned tail simply becomes an orphan subtree.

Use SetLeafID(nil) for the "fork before first entry" case (root-reset).

func (*Session) GetBranch

func (s *Session) GetBranch() []SessionEntry

GetBranch returns the root-to-leaf path of the current leaf.

func (*Session) GetSessionName

func (s *Session) GetSessionName() string

GetSessionName returns the latest user-defined session name (set via `/name`), or empty string when none has been set. It walks entries in reverse, returns the trimmed name from the most recent `session_info` entry; an empty trimmed name explicitly clears the name (later session_info entries with empty `name` field shadow earlier non-empty ones).

func (*Session) Header

func (s *Session) Header() SessionHeader

func (*Session) ID

func (s *Session) ID() string

func (*Session) LatestCompactionTimestampMs

func (s *Session) LatestCompactionTimestampMs() (int64, bool)

LatestCompactionTimestampMs returns the epoch-millisecond timestamp of the most recent compaction entry on the active branch (the path from the current leaf root-ward), and true when such an entry exists. Compaction checks use it to skip a stale pre-compaction usage reading that would otherwise re-trigger compaction on the first prompt after a resume.

func (*Session) LeafID

func (s *Session) LeafID() *string

LeafID returns the ID of the current leaf entry (the parent of the next AppendEntry). nil = "no entries yet, next append is a root".

func (*Session) ParentSession

func (s *Session) ParentSession() string

func (*Session) Path

func (s *Session) Path() string

func (*Session) SetLeafID

func (s *Session) SetLeafID(id *string) error

SetLeafID moves the leaf pointer. Pass nil to reset to "before first entry" (next append becomes a new root). Returns an error if the supplied id isn't an existing entry.

func (*Session) SetPath

func (s *Session) SetPath(p string)

func (*Session) TakeTrimmed added in v0.2.2

func (s *Session) TakeTrimmed() int

TakeTrimmed returns how many entries loading trimmed to the line limit, once: later calls return 0, so the caller warns once per session.

func (*Session) Tree

func (s *Session) Tree() *SessionTreeNode

Tree builds a SessionTreeNode rooted at the (synthetic) root. Children sorted by timestamp (oldest first). When multiple entries have parentId=nil we group them under a synthetic root with empty Entry.

func (*Session) UndecodableCount

func (s *Session) UndecodableCount() int

UndecodableCount returns how many distinct message entries failed to decode. Such entries are omitted from BuildContext, so a non-zero count means the reconstructed conversation is missing turns. Only entries already visited by messageFor are counted, so callers should read it after a full walk such as BuildContext.

type SessionAccounting

type SessionAccounting struct {
	UserMessages      int
	AssistantMessages int
	ToolCalls         int
	ToolResults       int
	TotalMessages     int
	Tokens            SessionTokenStats
	UsageBreakdown    []SessionUsageBreakdown
	CacheWaste        SessionCacheWaste
	// LatestCacheHitRate is the cache-read share (percent) of the last
	// assistant message's prompt, nil when that prompt had no tokens.
	LatestCacheHitRate *float64
}

SessionAccounting is the immutable all-entry accounting maintained with a Session.

type SessionCacheWaste

type SessionCacheWaste struct {
	MissedTokens int
	MissedCost   float64
	MissCount    int
}

SessionCacheWaste reports prompt tokens that a preceding request made cacheable but a later request did not read from cache.

type SessionEntry

type SessionEntry struct {
	Base SessionEntryBase
	// contains filtered or unexported fields
}

SessionEntry is the union of all entry types. We keep the raw JSON alongside parsed-base fields so unknown fields survive round-trips (forward-compat: future versions may add fields we don't know about; we must not silently drop them).

func BugReportBranch

func BugReportBranch(session *Session) []SessionEntry

BugReportBranch returns the entries on the current branch.

func NewSessionEntry

func NewSessionEntry(raw json.RawMessage, base SessionEntryBase) SessionEntry

NewSessionEntry constructs a SessionEntry from raw JSON and a pre-decoded base. Used primarily by tests and external packages that need to fabricate entries.

func (SessionEntry) AsMessage

func (e SessionEntry) AsMessage() (MessageEntry, bool)

AsMessage decodes the entry as a MessageEntry. Returns (zero, false) if the entry isn't type=message.

func (SessionEntry) MarshalJSON

func (e SessionEntry) MarshalJSON() ([]byte, error)

func (SessionEntry) Raw

func (e SessionEntry) Raw() json.RawMessage

Raw returns the entry's on-disk JSON as immutable bytes.

type SessionEntryBase

type SessionEntryBase struct {
	Type      string  `json:"type"`
	ID        string  `json:"id"`
	ParentID  *string `json:"parentId"`
	Timestamp string  `json:"timestamp"`
}

SessionEntryBase is the common prefix on every non-header entry. `ParentID` is `*string` (not "") so we can distinguish "root entry" (parentId: null) from "missing field". The wire format requires `"parentId": null` literally for roots.

type SessionHeader

type SessionHeader struct {
	Type      string `json:"type"` // always "session"
	Version   int    `json:"version,omitempty"`
	ID        string `json:"id"`
	Timestamp string `json:"timestamp"`
	CWD       string `json:"cwd"`
	// ParentSession holds the absolute path of the SOURCE jsonl on a
	// clone (separate JSONL with linear path-to-leaf snapshot). Empty
	// on plain Create.
	ParentSession string `json:"parentSession,omitempty"`
}

type SessionInfo

type SessionInfo struct {
	Path            string
	ID              string
	CWD             string
	Name            string // user-set name (via /name): empty when unset
	ParentSession   string // path of source jsonl when this session is a clone
	Created         time.Time
	Modified        time.Time
	MessageCount    int
	FirstMessage    string // first user message text: for picker preview
	AllMessagesText string // concatenated text: for fuzzy search
}

SessionInfo is the picker-display summary of a session JSONL. Returned by ListSessions; consumed by /resume picker (TUI overlay follow-up) and by --continue startup flag.

type SessionInfoEntry

type SessionInfoEntry struct {
	SessionEntryBase
	Name string `json:"name,omitempty"`
}

SessionInfoEntry stores a session-level name (set via `/name`). Persisted as type=session_info so it survives across resumes.

type SessionManager

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

SessionManager owns the on-disk sessions directory for a given cwd and tracks the active session.

func NewSessionManager

func NewSessionManager(cwd string) *SessionManager

NewSessionManager creates a SessionManager for the given working dir.

func NewSessionManagerWithDir

func NewSessionManagerWithDir(cwd, dir string) *SessionManager

NewSessionManagerWithDir overrides the default session directory (tests use this; production callers pass the result of defaultSessionDir).

func (*SessionManager) CWD

func (sm *SessionManager) CWD() string

CWD returns the working directory.

func (*SessionManager) Clone

func (sm *SessionManager) Clone(source *Session, leafID string) (*Session, error)

Clone snapshots the path-to-leafID from `source` and writes it as a brand-new JSONL with a fresh session ID. The new file's header has `parentSession` pointing at the source path. The cloned session becomes the manager's current session.

The cloned file contains a LINEAR chain (no branches): all entries off the chosen leaf path are dropped. This is what makes /clone useful: it produces a self-contained "best path" sub-session.

func (*SessionManager) Create

func (sm *SessionManager) Create(id, parentSessionPath string) (*Session, error)

Create creates a new session file on disk. parentSessionPath is non-empty only when this Create was invoked by Clone: it goes into the new file's header as `parentSession`.

func (*SessionManager) Current

func (sm *SessionManager) Current() *Session

Current returns the active session.

func (*SessionManager) DeleteSession

func (sm *SessionManager) DeleteSession(path string) error

DeleteSession removes a session file from disk. Returns error if the path doesn't exist or can't be removed.

func (*SessionManager) FindByID

func (sm *SessionManager) FindByID(id string) string

FindByID locates a session jsonl whose header `id` matches the given id (anywhere in the manager's session dir). Returns "" when no match. Used by `--session <id>`.

func (*SessionManager) FindMostRecent

func (sm *SessionManager) FindMostRecent() string

FindMostRecent returns the path to the most-recently-modified valid session jsonl in this manager's directory, or "" when none exist.

func (*SessionManager) FindMostRecentForContinue

func (sm *SessionManager) FindMostRecentForContinue() string

FindMostRecentForContinue selects the session to resume for the `--continue` flag. When this manager's directory is the cwd-encoded default it holds only this cwd's sessions, so the newest wins outright. When it is a custom `--session-dir` that may be shared across projects, the result is filtered to the newest session whose header cwd refers to this cwd so `--continue` never resumes another project's session.

func (*SessionManager) ForkFromFile

func (sm *SessionManager) ForkFromFile(sourcePath string) (*Session, error)

ForkFromFile copies every non-header entry from sourcePath into a brand-new JSONL with a fresh session ID whose header records `parentSession` = the absolute source path. Unlike Clone (which keeps only the linear path to a chosen leaf), this preserves the full entry tree; `wopr --fork` uses it. The new file is written to disk immediately and becomes the manager's current session.

func (*SessionManager) ForkToNewSession

func (sm *SessionManager) ForkToNewSession(source *Session, userMsgEntryID string) (*Session, string, error)

ForkToNewSession creates a NEW session file branched at the PARENT of userMsgEntryID (so the selected user message itself is excluded) and switches sm's current session to it. It returns the new session and the selected message's text, which the caller prefills into the editor for modification. The fork branches from the selected entry's parent, or is a fresh session carrying `parentSession` when the selected message is the root.

func (*SessionManager) ListAllSessions

func (sm *SessionManager) ListAllSessions() ([]SessionInfo, error)

ListAllSessions returns every valid session JSONL under <agentDir>/sessions/*/*.jsonl, newest modified first, for the session selector's "All" scope.

func (*SessionManager) ListCurrentSessions

func (sm *SessionManager) ListCurrentSessions() ([]SessionInfo, error)

ListCurrentSessions returns the selector's Current Folder scope. A custom session directory may contain sessions from several projects, so it filters by header cwd; the default encoded directory is already cwd-scoped.

func (*SessionManager) ListSessions

func (sm *SessionManager) ListSessions() ([]SessionInfo, error)

ListSessions returns every valid jsonl in this manager's session directory, newest mtime first. Files that fail header parsing are silently skipped (corrupted sessions shouldn't crash the picker).

func (*SessionManager) Load

func (sm *SessionManager) Load(path string) (*Session, error)

Load reads a session JSONL into memory and makes it the current session.

func (*SessionManager) RenameSession

func (sm *SessionManager) RenameSession(path, newName string) error

RenameSession updates the session name by appending a session_info entry.

func (*SessionManager) SessionDir

func (sm *SessionManager) SessionDir() string

SessionDir returns the session directory.

type SessionProjection

type SessionProjection struct {
	Entries  []ProjectedSessionEntry
	Messages []agent.AgentMessage
}

SessionProjection is the provenance-preserving, compaction-aware model context of one branch.

func BuildSessionProjection

func BuildSessionProjection(pathEntries []SessionEntry) SessionProjection

BuildSessionProjection projects a root-to-leaf entry path, with the path's final entry as the leaf. Compaction preparation uses it on SessionManager.getBranch() output.

type SessionTokenStats

type SessionTokenStats struct {
	Input      int
	Output     int
	CacheRead  int
	CacheWrite int
	Total      int
	Cost       float64 // USD
	// CostUnknown marks a session that used a model nothing gave a price
	// for: Cost leaves that usage out.
	CostUnknown bool
}

SessionTokenStats totals a session's token usage.

type SessionTreeNode

type SessionTreeNode struct {
	Entry    SessionEntry
	Children []*SessionTreeNode
	Label    string
	// LabelTimestamp is the on-disk timestamp of the LabelEntry that
	// set Label, in the ISO-8601-ish wire format. Empty when
	// no label is set. Used by the /tree picker to render `hh:mm`
	// next to the label when the user toggles label timestamps on.
	LabelTimestamp string
}

SessionTreeNode is a defensive copy of the session's branch structure for the /tree overlay (follow-up).

type SessionUsageBreakdown

type SessionUsageBreakdown struct {
	Key    string
	Cost   float64
	Tokens int
}

SessionUsageBreakdown attributes billed usage to a model or to host-side summaries.

type Settings

type Settings struct {
	DefaultProvider      string `json:"defaultProvider,omitempty"`
	DefaultModel         string `json:"defaultModel,omitempty"`
	DefaultThinkingLevel string `json:"defaultThinkingLevel,omitempty"`
	// Routing is the orchestrator's last choice, restored at the next start
	// while routing is on: a mode name (the router picks the orchestrator
	// under it) or "pinned" (the default model orchestrates).
	Routing string `json:"routing,omitempty"`
	// Subagents is the subagents' last choice, restored at the next start:
	// a mode name (routed under it), a "provider/model" spec, or "same"
	// (the orchestrator's model).
	Subagents string `json:"subagents,omitempty"`
	// ModelThinkingLevels holds per-model default thinking levels keyed by
	// "provider/modelId".
	ModelThinkingLevels map[string]string `json:"modelThinkingLevels,omitempty"`
	// Transport is "sse", "websocket", "websocket-cached", or "auto" (the
	// default).
	Transport string `json:"transport,omitempty"`
	// SteeringMode and FollowUpMode dispatch queued messages "all" at once or
	// "one-at-a-time" (the default).
	SteeringMode string `json:"steeringMode,omitempty"`
	FollowUpMode string `json:"followUpMode,omitempty"`

	Theme string `json:"theme,omitempty"`
	// FullscreenExitOutput is "clear" (the default), "transcript", or
	// "resume-hint".
	FullscreenExitOutput string `json:"fullscreenExitOutput,omitempty"`
	// FullscreenScrollbar is "auto" (the default), "always", or "hidden".
	FullscreenScrollbar string `json:"fullscreenScrollbar,omitempty"`
	// FullscreenSidebar is "" (auto: shown when the terminal is wide enough),
	// "show", or "hide"; ctrl+x b toggles it and saves the choice.
	FullscreenSidebar string `json:"fullscreenSidebar,omitempty"`
	// FullscreenCopyOnSelect copies a completed selection. Default: true.
	FullscreenCopyOnSelect *bool `json:"fullscreenCopyOnSelect,omitempty"`
	// FullscreenScrollSpeed is the lines one wheel step scrolls (1-10,
	// default 3).
	FullscreenScrollSpeed *int `json:"fullscreenScrollSpeed,omitempty"`

	Compaction    *CompactionSettingsJSON `json:"compaction,omitempty"`
	BranchSummary *BranchSummarySettings  `json:"branchSummary,omitempty"`
	Retry         *RetrySettingsJSON      `json:"retry,omitempty"`

	// HideThinkingBlock shows thinking blocks as stubs; Ctrl+T toggles it.
	HideThinkingBlock *bool  `json:"hideThinkingBlock,omitempty"`
	ShellPath         string `json:"shellPath,omitempty"`
	// CommandPrefix is prepended to every bash tool command as its own
	// first line (e.g. "set -e").
	CommandPrefix     string `json:"shellCommandPrefix,omitempty"`
	QuietStartup      *bool  `json:"quietStartup,omitempty"`
	CollapseChangelog *bool  `json:"collapseChangelog,omitempty"`
	// ShowCacheMissNotices adds transcript notices for significant prompt
	// cache misses. Default: false.
	ShowCacheMissNotices *bool `json:"showCacheMissNotices,omitempty"`
	// EnableAttributionHeaders sends wopr's app-attribution headers to
	// OpenRouter, NVIDIA, and Cloudflare. Default: true.
	EnableAttributionHeaders *bool `json:"enableAttributionHeaders,omitempty"`

	Skills  []string `json:"skills,omitempty"`
	Prompts []string `json:"prompts,omitempty"`
	Themes  []string `json:"themes,omitempty"`
	// CleanupTempFiles deletes the temp files sessions create (default on).
	CleanupTempFiles *bool `json:"cleanupTempFiles,omitempty"`
	// WarCouncil configures Global Thermonuclear War's council.
	WarCouncil *WarCouncilSettings `json:"warCouncil,omitempty"`
	// EnableSkillCommands lists /skill:name commands in autocomplete.
	// Default: true.
	EnableSkillCommands *bool `json:"enableSkillCommands,omitempty"`

	Terminal *TerminalSettings `json:"terminal,omitempty"`
	Images   *ImageSettings    `json:"images,omitempty"`

	// DefaultTools is the initial built-in tool selection; nil means the
	// default read, bash, edit and write.
	DefaultTools []string `json:"defaultTools,omitempty"`
	// DoubleEscapeAction is what double-Esc on an empty editor does: "fork",
	// "tree" (the default), or "none".
	DoubleEscapeAction string `json:"doubleEscapeAction,omitempty"`
	// TreeFilterMode is the filter /tree opens with.
	TreeFilterMode string `json:"treeFilterMode,omitempty"`
	// Hashline selects hashline anchors on read and edit: "auto" (the
	// default: on for models on free router tiers), "on", or "off". A router
	// model's own "hashline" flag wins.
	Hashline string `json:"hashline,omitempty"`
	// Diagnostics configures the checker edit and write run on the file they
	// changed.
	Diagnostics *DiagnosticsSettings `json:"diagnostics,omitempty"`
	// BashCompaction drops known noise (git hints, passing test lines) from
	// bash output before the model sees it. Default: true.
	BashCompaction *bool `json:"bashCompaction,omitempty"`
	// DefaultProjectTrust applies when no saved decision resolves project
	// trust: "ask" (the default), "always", or "never". Global only.
	DefaultProjectTrust string                   `json:"defaultProjectTrust,omitempty"`
	ThinkingBudgets     *ThinkingBudgetsSettings `json:"thinkingBudgets,omitempty"`
	// EditorPaddingX (0-3, default 0), OutputPad (0-1, default 1), and
	// AutocompleteMaxVisible (3-20, default 5) size the prompt and transcript.
	EditorPaddingX         *int   `json:"editorPaddingX,omitempty"`
	OutputPad              *int   `json:"outputPad,omitempty"`
	AutocompleteMaxVisible *int   `json:"autocompleteMaxVisible,omitempty"`
	ExternalEditor         string `json:"externalEditor,omitempty"`
	// ShowHardwareCursor shows the terminal's own cursor. Default: false, or
	// WOPR_HARDWARE_CURSOR=1.
	ShowHardwareCursor *bool             `json:"showHardwareCursor,omitempty"`
	Markdown           *MarkdownSettings `json:"markdown,omitempty"`
	SessionDir         string            `json:"sessionDir,omitempty"`
	// HTTPIdleTimeoutMs keeps the authored value: a number, a numeric string,
	// or "disabled". GetHttpIdleTimeoutMs parses it.
	HTTPIdleTimeoutMs json.RawMessage `json:"httpIdleTimeoutMs,omitempty"`
	// CacheWarming is read from global settings only, because each refresh
	// costs money.
	CacheWarming CacheWarmingMode `json:"cacheWarming,omitempty"`
	// LastChangelogVersion is the version whose changelog the user last saw.
	LastChangelogVersion string `json:"lastChangelogVersion,omitempty"`
}

Settings is one settings layer (global, project, or the merge of both) in the settings.json shape. Pointer fields tell an explicit false or zero from an unset key; the getters apply the defaults.

func (Settings) GetAutocompleteMaxVisible

func (s Settings) GetAutocompleteMaxVisible() int

GetAutocompleteMaxVisible returns the autocomplete rows. Default: 5.

func (Settings) GetBashCompaction

func (s Settings) GetBashCompaction() bool

GetBashCompaction reports whether bash output is compacted. Default: true.

func (Settings) GetBlockImages

func (s Settings) GetBlockImages() bool

GetBlockImages returns whether the transcript hides images.

func (Settings) GetBranchSummarySettings

func (s Settings) GetBranchSummarySettings() BranchSummaryConfig

GetBranchSummarySettings returns branch summary settings with defaults.

func (Settings) GetCacheWarmingMode

func (s Settings) GetCacheWarmingMode() CacheWarmingMode

GetCacheWarmingMode returns the cache-warming mode, or "streaming" when it is unset or invalid.

func (Settings) GetCleanupTempFiles added in v0.1.5

func (s Settings) GetCleanupTempFiles() bool

GetCleanupTempFiles reports whether sessions' temp files are cleaned up.

func (Settings) GetCollapseChangelog

func (s Settings) GetCollapseChangelog() bool

func (Settings) GetCommandPrefix

func (s Settings) GetCommandPrefix() string

func (Settings) GetDiagnostics

func (s Settings) GetDiagnostics() (bool, time.Duration, map[string]string)

GetDiagnostics reports whether post-edit diagnostics run, their timeout, and the per-language command overrides.

func (Settings) GetDoubleEscapeAction

func (s Settings) GetDoubleEscapeAction() string

GetDoubleEscapeAction returns the double-Esc action. Default: "tree".

func (Settings) GetEditorPaddingX

func (s Settings) GetEditorPaddingX() int

GetEditorPaddingX returns horizontal editor padding. Default: 0.

func (Settings) GetEnableSkillCommands

func (s Settings) GetEnableSkillCommands() bool

GetEnableSkillCommands reports whether /skill:name commands autocomplete.

func (Settings) GetFollowUpMode

func (s Settings) GetFollowUpMode() string

func (Settings) GetFullscreenCopyOnSelect

func (s Settings) GetFullscreenCopyOnSelect() bool

func (Settings) GetFullscreenExitOutput

func (s Settings) GetFullscreenExitOutput() string

GetFullscreenExitOutput returns "clear" (the default), "transcript", or "resume-hint".

func (Settings) GetFullscreenScrollSpeed added in v0.2.1

func (s Settings) GetFullscreenScrollSpeed() int

GetFullscreenScrollbar returns "always", "hidden", or "auto" (the default). GetFullscreenScrollSpeed returns the lines one wheel step scrolls.

func (Settings) GetFullscreenScrollbar

func (s Settings) GetFullscreenScrollbar() string

func (Settings) GetFullscreenSidebar

func (s Settings) GetFullscreenSidebar() string

GetFullscreenSidebar returns "show", "hide", or "" (auto, the default).

func (Settings) GetHashline

func (s Settings) GetHashline() string

GetHashline returns "on", "off", or "auto" (the default, also used for an unknown value).

func (Settings) GetHideThinkingBlock

func (s Settings) GetHideThinkingBlock() bool

func (Settings) GetHttpIdleTimeoutMs

func (s Settings) GetHttpIdleTimeoutMs() (int, error)

GetHttpIdleTimeoutMs returns the HTTP header/body idle timeout in milliseconds. Default: 300000 (5 minutes). Zero disables the timeout. An unparseable value is an error.

func (Settings) GetImageAutoResize

func (s Settings) GetImageAutoResize() bool

GetImageAutoResize returns whether images resize to fit. Default: true.

func (Settings) GetImageWidthCells

func (s Settings) GetImageWidthCells() int

GetImageWidthCells returns the max image width in columns. Default: 60.

func (Settings) GetMermaidRenderingMode

func (s Settings) GetMermaidRenderingMode() string

GetMermaidRenderingMode returns "off", "final", or "streaming" (the default).

func (Settings) GetModelCompactionSettings

func (s Settings) GetModelCompactionSettings(provider, modelID string) CompactionConfig

GetModelCompactionSettings returns compaction settings for one model. Each token setting resolves through compaction.modelOverrides["provider/modelID"], then the ordinary setting, then the built-in default. Empty provider and modelID select no override.

func (Settings) GetModelThinkingLevel

func (s Settings) GetModelThinkingLevel(provider, modelID string) string

GetModelThinkingLevel returns the per-model default thinking level, or "".

func (Settings) GetOutputPad

func (s Settings) GetOutputPad() int

GetOutputPad returns horizontal output padding, 0 or 1. Default: 1.

func (Settings) GetProviderRequestTimeoutMs

func (s Settings) GetProviderRequestTimeoutMs() (int, error)

GetProviderRequestTimeoutMs returns the per-request stream/read timeout: retry.provider.timeoutMs when set, otherwise httpIdleTimeoutMs.

func (Settings) GetProviderRetrySettings

func (s Settings) GetProviderRetrySettings() ProviderRetryConfig

GetProviderRetrySettings returns provider retry settings with defaults applied. timeoutMs maps onto the net/http idle deadline (GetProviderRequestTimeoutMs); maxRetries and maxRetryDelayMs drive the provider retry transport (ai.ConfigureProviderRetry).

func (Settings) GetQuietStartup

func (s Settings) GetQuietStartup() bool

func (Settings) GetRetrySettings

func (s Settings) GetRetrySettings() RetryConfig

GetRetrySettings returns retry settings with defaults applied.

func (Settings) GetShellPath

func (s Settings) GetShellPath() (string, error)

GetShellPath satisfies tools.SettingsView so the bash tool can resolve the user's preferred shell without importing internal/codingagent (which would create a cycle).

func (Settings) GetShowCacheMissNotices

func (s Settings) GetShowCacheMissNotices() bool

func (Settings) GetShowHardwareCursor

func (s Settings) GetShowHardwareCursor() bool

GetShowHardwareCursor returns whether the hardware cursor is shown.

func (Settings) GetShowImages

func (s Settings) GetShowImages() bool

GetShowImages returns whether images render. Default: true.

func (Settings) GetShowTerminalProgress

func (s Settings) GetShowTerminalProgress() bool

GetShowTerminalProgress returns whether OSC 9;4 progress is shown.

func (Settings) GetSteeringMode

func (s Settings) GetSteeringMode() string

GetSteeringMode and GetFollowUpMode return the queue dispatch modes. Default: "one-at-a-time".

func (Settings) GetTerminalCapabilityOverrides

func (s Settings) GetTerminalCapabilityOverrides() tui.CapabilityOverrides

GetTerminalCapabilityOverrides maps the terminal settings onto capability overrides: terminal.images "kitty" or "iterm2" selects that protocol and false disables images; a boolean terminal.trueColor or terminal.hyperlinks overrides detection. "auto" and any other value leave detection alone.

func (Settings) GetWarCouncil added in v0.1.5

func (s Settings) GetWarCouncil() (excluded []string, timeout time.Duration)

GetWarCouncil returns the council settings with the default time limit.

func (Settings) GetWarCouncilBuildTimeout added in v0.1.5

func (s Settings) GetWarCouncilBuildTimeout() time.Duration

GetWarCouncilBuildTimeout returns how long a member may take to build a candidate.

func (*Settings) UnmarshalJSON

func (s *Settings) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a settings file. A value whose JSON type does not match its key is dropped and the rest of the file still loads. The older object form of "skills" and retry.maxDelayMs are still read.

type SettingsError

type SettingsError struct {
	Scope string
	// Path is the settings file behind the error.
	Path  string
	Error error
}

SettingsError is a settings load or write diagnostic.

type SettingsManager

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

SettingsManager loads and merges settings: global, then project, then this run's overrides.

func NewSettingsManager

func NewSettingsManager(cwd, agentDir string) *SettingsManager

NewSettingsManager creates a SettingsManager for the given directories.

func NewSettingsManagerWithProjectTrust

func NewSettingsManagerWithProjectTrust(cwd, agentDir string, projectTrusted bool) *SettingsManager

NewSettingsManagerWithProjectTrust creates a settings manager that reads project settings only when projectTrusted is true.

func (*SettingsManager) AgentDir

func (sm *SettingsManager) AgentDir() string

AgentDir returns the global settings directory backing this manager.

func (*SettingsManager) ApplyOverrides

func (sm *SettingsManager) ApplyOverrides(overrides Settings)

ApplyOverrides lays run-only settings (command-line flags) over the merged settings. They are never saved.

func (*SettingsManager) CWD

func (sm *SettingsManager) CWD() string

CWD returns the project directory backing this manager.

func (*SettingsManager) DrainErrors

func (sm *SettingsManager) DrainErrors() []SettingsError

DrainErrors returns accumulated load/write diagnostics and clears them.

func (*SettingsManager) Get

func (sm *SettingsManager) Get() Settings

Get returns the merged settings. The manager replaces its merged value rather than changing it, so the result stays valid; callers change settings through UpdateGlobal, never through what its fields point to.

func (*SettingsManager) GetBlockImages

func (sm *SettingsManager) GetBlockImages() bool

func (*SettingsManager) GetBranchSummarySettings

func (sm *SettingsManager) GetBranchSummarySettings() BranchSummaryConfig

func (*SettingsManager) GetCacheWarmingMode

func (sm *SettingsManager) GetCacheWarmingMode() CacheWarmingMode

GetCacheWarmingMode returns the global cache-warming mode, or "streaming" when it is unset or invalid. Project settings are ignored because each refresh costs money.

func (*SettingsManager) GetCompactionSettings

func (sm *SettingsManager) GetCompactionSettings() CompactionConfig

GetCompactionSettings returns compaction settings without a model override.

func (*SettingsManager) GetDefaultProjectTrust

func (sm *SettingsManager) GetDefaultProjectTrust() string

GetDefaultProjectTrust returns the fallback project-trust mode from global settings only: "ask" (the default), "always", or "never".

func (*SettingsManager) GetDefaultThinkingLevel

func (sm *SettingsManager) GetDefaultThinkingLevel() string

func (*SettingsManager) GetEnableAttributionHeaders

func (sm *SettingsManager) GetEnableAttributionHeaders() bool

GetEnableAttributionHeaders returns whether provider requests carry wopr's app-attribution headers. Default: true.

func (*SettingsManager) GetFollowUpMode

func (sm *SettingsManager) GetFollowUpMode() string

func (*SettingsManager) GetGlobalSettings

func (sm *SettingsManager) GetGlobalSettings() Settings

GetGlobalSettings returns the persisted global settings layer.

func (*SettingsManager) GetHashline

func (sm *SettingsManager) GetHashline() string

func (*SettingsManager) GetHttpIdleTimeoutMs

func (sm *SettingsManager) GetHttpIdleTimeoutMs() (int, error)

func (*SettingsManager) GetImageAutoResize

func (sm *SettingsManager) GetImageAutoResize() bool

func (*SettingsManager) GetModelCompactionSettings

func (sm *SettingsManager) GetModelCompactionSettings(provider, modelID string) CompactionConfig

func (*SettingsManager) GetModelThinkingLevel

func (sm *SettingsManager) GetModelThinkingLevel(provider, modelID string) string

func (*SettingsManager) GetProjectSettings

func (sm *SettingsManager) GetProjectSettings() Settings

GetProjectSettings returns the persisted project settings layer.

func (*SettingsManager) GetProviderRequestTimeoutMs

func (sm *SettingsManager) GetProviderRequestTimeoutMs() (int, error)

func (*SettingsManager) GetProviderRetrySettings

func (sm *SettingsManager) GetProviderRetrySettings() ProviderRetryConfig

func (*SettingsManager) GetRetrySettings

func (sm *SettingsManager) GetRetrySettings() RetryConfig

func (*SettingsManager) GetSessionDir

func (sm *SettingsManager) GetSessionDir() (string, error)

GetSessionDir returns the configured session directory, expanding ~ forms and converting a file:// URL. An invalid file URL is an error.

func (*SettingsManager) GetSteeringMode

func (sm *SettingsManager) GetSteeringMode() string

func (*SettingsManager) GetThinkingBudgets

func (sm *SettingsManager) GetThinkingBudgets() *ThinkingBudgetsSettings

func (*SettingsManager) IsProjectTrusted

func (sm *SettingsManager) IsProjectTrusted() bool

IsProjectTrusted reports whether project settings are readable and writable.

func (*SettingsManager) Load

func (sm *SettingsManager) Load()

Load reads settings from disk. Errors recorded earlier stay until DrainErrors.

func (*SettingsManager) ProjectMCPFile

func (sm *SettingsManager) ProjectMCPFile() json.RawMessage

ProjectMCPFile returns the project's .mcp.json content, or nil when the project is untrusted or has none.

func (*SettingsManager) RawSection

func (sm *SettingsManager) RawSection(key string) (global, project json.RawMessage)

RawSection returns one top-level settings key as authored in the global file and, when the project is trusted, the project file. Feature packages that own their section's shape (mcpServers, web) parse it themselves. The files are read on each call, so an edit applies on the next call.

func (*SettingsManager) Reload

func (sm *SettingsManager) Reload()

Reload re-reads settings from disk.

func (*SettingsManager) RemoveModelThinkingLevel

func (sm *SettingsManager) RemoveModelThinkingLevel(provider, modelID string) error

func (*SettingsManager) SetCacheWarmingMode

func (sm *SettingsManager) SetCacheWarmingMode(mode CacheWarmingMode) error

SetCacheWarmingMode saves the cache-warming mode to global settings.

func (*SettingsManager) SetDefaultModelAndProvider

func (sm *SettingsManager) SetDefaultModelAndProvider(provider, model string) error

func (*SettingsManager) SetDefaultThinkingLevel

func (sm *SettingsManager) SetDefaultThinkingLevel(level string) error

func (*SettingsManager) SetFollowUpMode

func (sm *SettingsManager) SetFollowUpMode(mode string) error

func (*SettingsManager) SetLastChangelogVersion

func (sm *SettingsManager) SetLastChangelogVersion(version string) error

SetLastChangelogVersion records that the user has seen changelog entries up to and including version.

func (*SettingsManager) SetModelThinkingLevel

func (sm *SettingsManager) SetModelThinkingLevel(provider, modelID, level string) error

func (*SettingsManager) SetProjectPromptTemplatePaths

func (sm *SettingsManager) SetProjectPromptTemplatePaths(paths []string) error

func (*SettingsManager) SetProjectSkillPaths

func (sm *SettingsManager) SetProjectSkillPaths(paths []string) error

func (*SettingsManager) SetProjectThemePaths

func (sm *SettingsManager) SetProjectThemePaths(paths []string) error

func (*SettingsManager) SetProjectTrusted

func (sm *SettingsManager) SetProjectTrusted(trusted bool)

SetProjectTrusted changes project-settings access and reloads both layers.

func (*SettingsManager) SetPromptTemplatePaths

func (sm *SettingsManager) SetPromptTemplatePaths(paths []string) error

func (*SettingsManager) SetSkillPaths

func (sm *SettingsManager) SetSkillPaths(paths []string) error

func (*SettingsManager) SetSteeringMode

func (sm *SettingsManager) SetSteeringMode(mode string) error

func (*SettingsManager) SetTheme

func (sm *SettingsManager) SetTheme(theme string) error

func (*SettingsManager) SetThemePaths

func (sm *SettingsManager) SetThemePaths(paths []string) error

func (*SettingsManager) UpdateGlobal

func (sm *SettingsManager) UpdateGlobal(fn func(*Settings)) error

UpdateGlobal applies fn to the global settings, saves the change to <agentDir>/settings.json, and refreshes the merged view.

func (*SettingsManager) UpdateProject

func (sm *SettingsManager) UpdateProject(fn func(*Settings)) error

UpdateProject applies fn to the project settings, saves the change to <cwd>/.wopr/settings.json, and refreshes the merged view.

type ShareState

type ShareState struct {
	SystemPrompt string
	Tools        []ShareTool
}

ShareState is the agent state the wopr.share entry records: the system prompt and the active tool schemas.

func NewShareState

func NewShareState(systemPrompt string, tools []agent.AgentTool) ShareState

NewShareState captures the system prompt and the active agent tools.

type ShareTool

type ShareTool struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Parameters  any    `json:"parameters,omitempty"`
}

ShareTool is one tool schema recorded in the wopr.share entry.

type SkillDef

type SkillDef struct {
	Name                   string
	Description            string
	DisableModelInvocation bool
	// Body is the markdown content (stripped of frontmatter).
	Body string
	// Path is the source SKILL.md file.
	Path string
	// Dir is the skill's containing directory: used so callers can
	// resolve referenced sibling files.
	Dir string
}

SkillDef is a parsed skill: a directory under `<config>/skills/<name>/` containing a `SKILL.md` file with frontmatter (name, description) and markdown body.

wopr loads these on `--skill <name>` and appends the body to the system prompt under a `## Skills` heading.

func DeduplicateSkills

func DeduplicateSkills(defs []*SkillDef) []*SkillDef

DeduplicateSkills keeps the first definition for each public name, once Resource paths are ordered by precedence.

func LoadSkill

func LoadSkill(skillsDir, name string) (*SkillDef, error)

LoadSkill loads a skill by name from `<skillsDir>/<name>/SKILL.md`.

Lookup order (user before project):

  1. <wopr-config>/skills/<name>/SKILL.md
  2. <skillsDir>/<name>/SKILL.md (caller-provided fallback root)

Most callers pass DefaultAgentDir()/skills as skillsDir so the wopr agent tree is searched first; we keep the parameter so SDK consumers can point at a vendored skills tree without env juggling.

func LoadSkillPath

func LoadSkillPath(path string) (*SkillDef, error)

LoadSkillPath loads a skill from a SKILL.md file path or a skill directory.

func LoadSkillsFromPath

func LoadSkillsFromPath(path string) ([]*SkillDef, error)

LoadSkillsFromPath loads one or more skills from a path. Supported forms:

  • /path/to/skill/SKILL.md
  • /path/to/skill-dir/ (contains SKILL.md)
  • /path/to/skills-root/ (contains child dirs with SKILL.md)

type SlashContext

type SlashContext struct {
	// Args is the text after the command name, trimmed.
	Args string

	Append     func(string) // markdown output
	AppendText func(string) // plain output
	Clear      func()
	Quit       func()
	// Reset clears the agent's messages for /new.
	Reset func()

	ModelName        func() string
	LastAssistant    func() string
	CopyClipboard    func(text string) error
	CurrentSession   func() *Session
	SetSessionName   func(name string) error
	ForkToNewSession func(userMsgEntryID string) error
	CloneCurrent     func() (newPath string, err error)
	ListSessions     func() ([]SessionInfo, error)
	RenderTree       func() string
}

SlashContext is what a slash command sees without the interactive UI: the arguments, where output goes, and the session operations the text versions use. Session.DispatchSlash fills it.

type SlashRegistry

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

SlashRegistry resolves slash command names and aliases.

func NewSlashRegistry

func NewSlashRegistry() *SlashRegistry

NewSlashRegistry returns the registry of built-in commands.

func (*SlashRegistry) Dispatch

func (r *SlashRegistry) Dispatch(sc *SlashContext, line string) error

Dispatch runs line without the interactive UI: commands with a text version run it, the rest say they need the UI. It returns ErrUnknownSlashCommand for an unknown command and the command's error otherwise; it does not report errors through sc.

func (*SlashRegistry) Resolve

func (r *SlashRegistry) Resolve(name string) (string, bool)

Resolve returns the command's canonical name, following aliases.

type StartupUIOptions

type StartupUIOptions struct {
	AgentDir   string
	Settings   Settings
	ThemePaths []string
}

type StatusLine

type StatusLine struct {
	tui.BaseComponent
	// contains filtered or unexported fields
}

StatusLine holds the session state the prompt panel and sidebar show (Snapshot) and renders it as a footer under the transcript printed on exit:

Line 1: ~/<pwd> (<branch>) • <session-name>
Line 2: ↑<in> ↓<out> $<cost> <context%>/<window> (auto)     <model> • <thinking>

Color coding on the context-% column:

<=70%  default (no color)
>70%   yellow (warning)
>90%   red (error)

A third line shows the keyed statuses (routing, savings) when set.

func NewStatusLine

func NewStatusLine(model *ai.Model, timings *agent.Recorder) *StatusLine

NewStatusLine creates a StatusLine bound to model. timings may be nil; cost/elapsed columns are then suppressed.

func (*StatusLine) Flash

func (s *StatusLine) Flash(msg string)

Flash forwards a brief notice to the interactive mode's status sink.

func (*StatusLine) OnBranchChange

func (s *StatusLine) OnBranchChange(fn func()) func()

OnBranchChange subscribes to git-branch updates. Returns an unsubscribe.

func (*StatusLine) Render

func (s *StatusLine) Render(width int) []string

Render returns the footer lines (normally 2 plus keyed statuses).

func (*StatusLine) ResetContextUsage

func (s *StatusLine) ResetContextUsage()

ResetContextUsage clears the context-window column before a transcript rebuild re-reads it from the branch.

func (*StatusLine) SetAutoCompactEnabled

func (s *StatusLine) SetAutoCompactEnabled(enabled bool)

SetAutoCompactEnabled updates the "(auto)" indicator.

func (*StatusLine) SetContextUsage

func (s *StatusLine) SetContextUsage(tokens *int, contextWindow int)

SetContextUsage stores the Session projection estimate outside the render path. Nil tokens indicate unknown usage after compaction until a valid response.

func (*StatusLine) SetCwd

func (s *StatusLine) SetCwd(cwd string)

SetCwd sets the working directory shown in the footer and resolves the git branch. Call once at init.

func (*StatusLine) SetKeyedStatus

func (s *StatusLine) SetKeyedStatus(key, text string)

SetKeyedStatus sets (or clears) a keyed status entry in the footer's keyed-status line. Pass empty text to remove the key.

func (*StatusLine) SetModel

func (s *StatusLine) SetModel(m *ai.Model)

SetModel rebinds the model. The subscription marker derives from the active model, so a rebind re-evaluates it.

func (*StatusLine) SetName

func (s *StatusLine) SetName(name string)

SetName updates the session name shown in the footer.

func (*StatusLine) SetProviderCount

func (s *StatusLine) SetProviderCount(n int)

SetProviderCount updates the number of authenticated+reachable providers. When >1, the footer shows "(provider) model" instead of just "model".

func (*StatusLine) SetStatusHook

func (s *StatusLine) SetStatusHook(fn func(string))

func (*StatusLine) SetSubscriptionResolver

func (s *StatusLine) SetSubscriptionResolver(resolve func(*ai.Model) bool)

SetSubscriptionResolver installs the "(sub)" decision used by SetModel.

func (*StatusLine) SetThinkingLevel

func (s *StatusLine) SetThinkingLevel(level string)

SetThinkingLevel updates the thinking level display.

func (*StatusLine) SetTurnContextUsage

func (s *StatusLine) SetTurnContextUsage(u *ai.Usage)

SetTurnContextUsage records the latest usage for the context-window column. Token and cost totals come from the usage totals source instead.

func (*StatusLine) SetUsageTotalsSource

func (s *StatusLine) SetUsageTotalsSource(source func() footerUsageTotals)

SetUsageTotalsSource installs the session usage totals read on each render.

func (*StatusLine) SetUsingSubscription

func (s *StatusLine) SetUsingSubscription(v bool)

SetUsingSubscription updates the OAuth subscription indicator. When true, cost display shows "(sub)".

func (*StatusLine) SetWorking

func (s *StatusLine) SetWorking(b bool)

SetWorking flips the spinner column on/off.

func (*StatusLine) Snapshot

func (s *StatusLine) Snapshot() footerData

Snapshot returns the footer's current data, including the session usage totals, for views that present it differently (the prompt panel and the sidebar).

type StdinBuffer

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

StdinBuffer accumulates partial escape sequences across reads so the input loop only sees complete chunks. Bracketed pastes are re-emitted as a single framed payload because dispatchKey/classifyKey currently consume the ESC[200~...ESC[201~ form directly. The zero value uses the default timeouts.

func NewStdinBuffer

func NewStdinBuffer(options StdinBufferOptions) *StdinBuffer

NewStdinBuffer returns a StdinBuffer with options applied.

func (*StdinBuffer) Flush

func (b *StdinBuffer) Flush() []string

Flush emits any incomplete remainder as a single chunk. It is the timeout-based fallback path; the input loop owns the timer.

func (*StdinBuffer) FlushTimeout

func (b *StdinBuffer) FlushTimeout() time.Duration

FlushTimeout is how long the buffered remainder waits for more input before Flush emits it: the escape timeout for a lone ESC, the sequence timeout for anything else.

func (*StdinBuffer) HasPendingFlush

func (b *StdinBuffer) HasPendingFlush() bool

func (*StdinBuffer) ProcessString

func (b *StdinBuffer) ProcessString(s string) []string

ProcessString feeds s and returns any complete keystroke chunks.

func (*StdinBuffer) ProcessTerminalBytes

func (b *StdinBuffer) ProcessTerminalBytes(data []byte) []string

ProcessTerminalBytes feeds one terminal read. It decodes UTF-8 across reads so a character split between reads is held until it is complete and a lone high byte is never rewritten as a Meta key; the decoded text then goes through ProcessString.

type StdinBufferOptions

type StdinBufferOptions struct {
	// Timeout is how long an incomplete escape sequence waits for more input.
	Timeout time.Duration
	// EscapeTimeout is how long a lone ESC waits before it is the Escape key.
	EscapeTimeout time.Duration
}

StdinBufferOptions configures a StdinBuffer. A zero field keeps the default.

type TerminalSettings

type TerminalSettings struct {
	// ShowImages renders images when the terminal supports them. Default: true.
	ShowImages *bool `json:"showImages,omitempty"`
	// ImageWidthCells is the rendered image width. Default: 60.
	ImageWidthCells      int   `json:"imageWidthCells,omitempty"`
	ShowTerminalProgress *bool `json:"showTerminalProgress,omitempty"`
	// Hyperlinks, Images, and TrueColor keep the authored value; see
	// GetTerminalCapabilityOverrides.
	Hyperlinks json.RawMessage `json:"hyperlinks,omitempty"`
	Images     json.RawMessage `json:"images,omitempty"`
	TrueColor  json.RawMessage `json:"trueColor,omitempty"`
}

TerminalSettings is settings.terminal.

type ThinkingBudgetsSettings

type ThinkingBudgetsSettings struct {
	Minimal *int `json:"minimal,omitempty"`
	Low     *int `json:"low,omitempty"`
	Medium  *int `json:"medium,omitempty"`
	High    *int `json:"high,omitempty"`
}

ThinkingBudgetsSettings holds custom token budgets for each thinking level.

type ThinkingLevelEntry

type ThinkingLevelEntry struct {
	SessionEntryBase
	ThinkingLevel string `json:"thinkingLevel"`
}

ThinkingLevelEntry records a user-initiated thinking level change (Shift+Tab). Persisted as type="thinking_level_change" so the session tree can display [thinking: level].

type ThinkingSelectorComponent

type ThinkingSelectorComponent struct {
	*tui.Container
	// contains filtered or unexported fields
}

ThinkingSelectorComponent renders the /thinking selector: a search input over the available levels, the current level marked with a check, the saved default annotated, and app.thinking.save to save the highlighted level as the default.

func NewThinkingSelectorComponent

func NewThinkingSelectorComponent(
	currentLevel string,
	availableLevels []string,
	onSelect func(level string),
	onCancel func(),
	onSelectAsDefault func(level string),
	defaultThinkingLevel string,
) *ThinkingSelectorComponent

NewThinkingSelectorComponent builds the selector; defaultThinkingLevel annotates that level's description.

func (*ThinkingSelectorComponent) GetSelectList

func (s *ThinkingSelectorComponent) GetSelectList() *tui.FilterableList

GetSelectList returns the level list.

func (*ThinkingSelectorComponent) HandleInput

func (s *ThinkingSelectorComponent) HandleInput(data string)

HandleInput routes a key: the save binding saves the highlighted level as the default, navigation keys drive the list, and everything else edits the search query.

type TierError

type TierError struct {
	Remediation string
}

TierError reports that tier resolution refused the installation. It carries a concrete, non-looping remediation so the caller surfaces it verbatim.

func (*TierError) Error

func (e *TierError) Error() string

type TrailingEntries

type TrailingEntries func(parentID *string, timestamp string) []any

TrailingEntries builds export-only entries appended after the branch. It receives the last branch entry ID (nil for an empty branch) and the export timestamp.

type UndoChange added in v0.1.5

type UndoChange struct {
	Path string
	// Tool is the tool that made the change: write, edit, or apply_patch.
	Tool string
	// Deleted reports that the change created the file, so undoing it
	// removed the file.
	Deleted bool
	// ChangedSince reports that the file changed after the tool's change
	// (a shell command or the user); Saved is where that version was kept.
	ChangedSince bool
	Saved        string
}

UndoChange is one file change /undo reverted.

type UsageEntry

type UsageEntry struct {
	SessionEntryBase
	// Kind is an arbitrary usage category, such as "cache_warm".
	Kind     string   `json:"kind"`
	Provider string   `json:"provider"`
	Model    string   `json:"model"`
	Usage    ai.Usage `json:"usage"`
	// Note is an optional human-readable qualifier for usage notices.
	Note string `json:"note,omitempty"`
}

UsageEntry is model-attributed usage that does not enter LLM context, such as a cache-warming refresh.

type WarCouncilSettings added in v0.1.5

type WarCouncilSettings struct {
	Excluded       []string `json:"excluded,omitempty"`
	TimeoutSeconds int      `json:"timeoutSeconds,omitempty"`
	// BuildTimeoutSeconds bounds a member building a candidate change.
	BuildTimeoutSeconds int `json:"buildTimeoutSeconds,omitempty"`
}

WarCouncilSettings are the models the war council leaves out, so a model set up later joins it, and how long a member may take.

Source Files

Directories

Path Synopsis
Package askuser is the ask_user tool: the model asks the user one multiple-choice question, with free text allowed, when a decision is theirs.
Package askuser is the ask_user tool: the model asks the user one multiple-choice question, with free text allowed, when a decision is theirs.
Branch summarization for tree navigation.
Branch summarization for tree navigation.
Package efficiency implements four mechanisms that cut the tokens each request carries: Action Fusion, ObservationPack, the Evidence-Preserving Reducer, and Online Context Compact.
Package efficiency implements four mechanisms that cut the tokens each request carries: Action Fusion, ObservationPack, the Evidence-Preserving Reducer, and Online Context Compact.
Package export provides HTML session export from embedded template files.
Package export provides HTML session export from embedded template files.
Package frontmatter parses YAML frontmatter blocks from markdown files.
Package frontmatter parses YAML frontmatter blocks from markdown files.
Package goal is the session goal: an objective wopr keeps working toward across turns, continuing automatically until an independent audit accepts the orchestrator's completion claim or a cap pauses it.
Package goal is the session goal: an objective wopr keeps working toward across turns, continuing automatically until an independent audit accepts the orchestrator's completion claim or a cap pauses it.
Package llama is the built-in llama.cpp integration: the router HTTP/SSE client, Hugging Face search and download, the dynamic provider, the manager UI, and the /llama command.
Package llama is the built-in llama.cpp integration: the router HTTP/SSE client, Hugging Face search and download, the dynamic provider, the manager UI, and the /llama command.
Package mcp is wopr's Model Context Protocol client.
Package mcp is wopr's Model Context Protocol client.
Package prompts builds the default coding-agent system prompt.
Package prompts builds the default coding-agent system prompt.
Package pruning removes stale material from the model context without a model call (duplicate tool calls, inputs of failed calls, reads a later change made stale) and gives the model a compress tool for spans it has finished with.
Package pruning removes stale material from the model context without a model call (duplicate tool calls, inputs of failed calls, reads a later change made stale) and gives the model a compress tool for spans it has finished with.
Package queue is the session's persistent work queue: what the user asked for, what is running where, and what finished.
Package queue is the session's persistent work queue: what the user asked for, what is running where, and what finished.
Package releasetest serves a fake GitHub release of wopr for self-update tests: the releases/latest redirect, a signed SHA256SUMS, and this platform's archive, with each asset download redirected to an object store the way github.com does.
Package releasetest serves a fake GitHub release of wopr for self-update tests: the releases/latest redirect, a signed SHA256SUMS, and this platform's archive, with each asset download redirected to an object store the way github.com does.
Package router chooses a model per prompt.
Package router chooses a model per prompt.
Package sessionblob keeps session images out of the session JSONL.
Package sessionblob keeps session images out of the session JSONL.
Package shellread decides whether a shell command only reads: a pipeline of allowlisted programs, with chaining, redirection, substitution, and flags that write or run other programs rejected.
Package shellread decides whether a shell command only reads: a pipeline of allowlisted programs, with chaining, redirection, substitution, and flags that write or run other programs rejected.
Package subagent runs delegated tasks: a brief goes to a child agent with fresh context, read-only tools, and budgets; its answer comes back in a fixed format whose quotes wopr verifies before the orchestrator sees them.
Package subagent runs delegated tasks: a brief goes to a child agent with fresh context, read-only tools, and budgets; its answer comes back in a fixed format whose quotes wopr verifies before the orchestrator sees them.
Package tempfiles finds the temp files a wopr session creates and deletes them once nothing needs them.
Package tempfiles finds the temp files a wopr session creates and deletes them once nothing needs them.
tools/bash_executor.go: user bash execution (!cmd, RPC bash, SDK ExecuteBash via ExecuteBashWithOperations).
tools/bash_executor.go: user bash execution (!cmd, RPC bash, SDK ExecuteBash via ExecuteBashWithOperations).
Package web implements the web_fetch and web_search tools: a URL fetched to markdown or text, with private networks blocked by default, and a web search through a configured provider (Brave, Tavily or SearXNG), else DuckDuckGo's results page.
Package web implements the web_fetch and web_search tools: a URL fetched to markdown or text, with private networks blocked by default, and a web search through a configured provider (Brave, Tavily or SearXNG), else DuckDuckGo's results page.

Jump to

Keyboard shortcuts

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