agent

package
v1.166.1 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2026 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Overview

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - Context Overflow Recovery * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Provides recovery strategies when API calls fail due to context window overflow * (prompt too long) or max output token limits being hit. * * Recovery strategy for context overflow: * 1. Aggressive tool result budget enforcement (halved limits) * 2. Tool result pairing cleanup * 3. Emergency history truncation (keep system + last N messages) * * Recovery strategy for max output tokens: * 1. Escalate max_tokens (double, up to provider cap) * 2. Inject continuation message * 3. Track escalation count to prevent infinite loops

* ChatCLI - File Staleness Tracker * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Tracks file state (mtime + content hash) between read and write operations * to detect when a file has been modified externally. This prevents the LLM * from silently overwriting changes made by the user or other processes. * * Inspired by openclaude's file staleness detection with mtime + hash fallback. * * Usage: * tracker.RecordRead("/path/to/file") // after successful read * stale, diff := tracker.CheckStaleness(path) // before write/patch

* ChatCLI - Microcompact for Tool Results * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Progressively compacts old tool results in the conversation history to * reduce context usage without losing critical information. * * Inspired by openclaude's microCompact.ts which selectively compacts * specific tools (FILE_READ, BASH, GREP, etc.) based on age. * * Strategy: * - Tool results from recent turns: unchanged * - Tool results 2+ turns old: truncated to head+tail preview * - Tool results 4+ turns old: replaced with one-line summary * * Only applies to read-only tool results (file reads, search, git status, etc.). * Write/exec results are preserved as they contain critical error information.

* ChatCLI - Quote Normalization * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Normalizes curly/smart quotes to straight quotes before write/patch operations. * LLMs frequently generate curly quotes (', ', ", ") which cause compilation * errors in source code. * * Inspired by openclaude's quote normalization in FileEditTool which preserves * file typography style. Here we take a simpler approach: always normalize to * straight quotes for code files, preserving curly quotes only in documentation.

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - Unified response envelope rendering * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Renders the assistant's final reply in the "sóbrio" treatment: a * bilateral titled rule opens the reply (model on the left, latency and * tokens on the right), the body sits on a two-space indent wrapped to the * live terminal width with ANSI preserved, and a single dim telemetry line * closes it. The envelope stays the single source of truth for chat, coder * and agent modes: callers supply pre-formatted labels and a body; an * optional typewriter effect plays the body progressively.

* ChatCLI - Session Workspace * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Per-session scratch directory that the agent can read/write freely without * touching the project tree. Created once per CLI startup, cleaned up on exit. * * Layout: * $TMPDIR/chatcli-agent-<random>/ * ├── scratch/ -> agent-writable: temp scripts, intermediate files * └── tool-results/ -> overflow from EnforceToolResultBudget (replaces * the old global /tmp/chatcli-tool-results/) * * The session workspace registers both subdirs with: * - pkg/coder/engine (write allowlist) * - cli/agent.SensitiveReadPaths (read allowlist) * - tool_result_budget (writes overflow here instead of the global dir) * * Env vars: * CHATCLI_AGENT_TMPDIR -> absolute path of scratch dir; exported for * child processes spawned by run_command / exec. * CHATCLI_AGENT_KEEP_TMPDIR=true -> skip cleanup (debugging).

* ChatCLI - Tool Result Budget Enforcement * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Enforces aggregate size limits on tool results before sending to the API. * Large results are persisted to disk and replaced with compact references, * preventing context window saturation. * * Inspired by openclaude's tool result budget enforcement and * MAX_TOOL_RESULTS_PER_MESSAGE_CHARS threshold.

* ChatCLI - Tool Result Pairing Validator * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Ensures every tool_use block in the conversation history has a matching * tool_result, and every tool_result references a valid tool_use. * * Pairing is POSITIONAL (block-scoped), not global: each assistant message * that carries tool_calls owns the stretch of history up to the next * assistant message, and its results are matched only inside that stretch. * This is required for providers whose tool_call IDs are NOT globally * unique — Moonshot/Kimi K3 emits deterministic per-turn IDs such as * "web_fetch:0"/"web_fetch:1" that repeat on every turn, so a global * id→result map silently pairs an old dangling call with a newer turn's * result and ships an invalid history (API 400: "tool_call_ids did not * have response messages").

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - Adaptive typewriter pacing * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * The reply card uses a typewriter effect to make the model's answer * feel alive. A naive fixed per-rune delay (2ms) works fine for a * handful of sentences but compounds badly on long replies — a 3 000 * rune reply at 2ms is 6 seconds of cursor-sweeping-across-borders, * which feels broken instead of alive. * * PaceText adapts the cadence: * * - Short replies keep the caller-requested delay (the effect * reads as animation). * - Long replies have their delay scaled DOWN so the whole * animation completes within a target budget (default 800ms). * - Replies above a hard threshold (8 000 visible runes) skip the * animation entirely — painting a giant code block one rune at a * time is never the right call. * * Three environment variables let advanced users tune the behavior * without rebuilding: * * CHATCLI_NO_TYPEWRITER=1 skip animation entirely * CHATCLI_TYPEWRITER_BUDGET_MS=N override the total budget in ms * (0 disables the budget; caller * delay is used verbatim) * CHATCLI_TYPEWRITER_DELAY_MS=N override the per-rune base delay * * The pacing is centralized here so every surface that types out * model output (chat envelope, agent RESPOSTA card, coder summary, * /command relay) converges on the same UX.

* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

Index

Constants

View Source
const (
	// DefaultMaxOutputBytes is the maximum output size before truncation.
	DefaultMaxOutputBytes int64 = 1 << 20 // 1MB

	// DefaultTerminateTimeout is how long to wait for graceful shutdown.
	DefaultTerminateTimeout = 5 * time.Second
)
View Source
const (
	LeftSingleCurly  = '\u2018' // '
	RightSingleCurly = '\u2019' // '
	LeftDoubleCurly  = '\u201C' // "
	RightDoubleCurly = '\u201D' // "
	LeftAngleDouble  = '\u00AB' // «
	RightAngleDouble = '\u00BB' // »
	PrimeSingle      = '\u2032' // ′
	PrimeDouble      = '\u2033' // ″
)

Curly/smart quote Unicode characters

View Source
const (
	// SyntheticToolResultContent is injected when a tool_use has no matching tool_result.
	SyntheticToolResultContent = "[Tool result missing — the tool execution was interrupted or failed silently. " +
		"Do NOT retry this tool call. Analyze what went wrong and try a different approach.]"

	// SyntheticToolResultErrorCode marks injected synthetic results so
	// providers surface them as errors (OpenAI-family [ERROR:...] marker,
	// Anthropic is_error) instead of apparent successes, and so downstream
	// consumers can distinguish repair synthetics from real outputs.
	SyntheticToolResultErrorCode = "missing_result"

	// OrphanToolResultContent is kept for API compatibility.
	//
	// Deprecated: orphaned tool results are REMOVED from history, never
	// replaced with this placeholder. No code references it.
	OrphanToolResultContent = "[Orphaned tool result — no matching tool call found. This result has been discarded.]"
)
View Source
const (
	ColorReset  = "\033[0m"
	ColorGreen  = "\033[32m"
	ColorLime   = "\033[92m"
	ColorCyan   = "\033[36m"
	ColorGray   = "\033[90m"
	ColorPurple = "\033[35m"
	ColorBold   = "\033[1m"
	ColorYellow = "\033[33m"
	ColorRed    = "\033[31m"
	ColorBlue   = "\033[34m"
)

ANSI Color codes exportados

Legacy path: hue constants (duplicated from cli/colors.go — the duplication is exactly what ui/kit exists to end). They remain because they are exported and load-bearing across cli, but new and migrated code styles through kit.Colorize / kit.Style with a theme.Role.

Variables

View Source
var (
	// DefaultTurnBudgetChars is the maximum aggregate size of all tool results
	// in a single conversation turn (assistant→tool_results group).
	// Tool results exceeding this are persisted to disk and replaced with previews.
	// Override via CHATCLI_TOOL_RESULT_BUDGET_CHARS.
	DefaultTurnBudgetChars = 200_000

	// DefaultPerResultMaxChars is the maximum size of a single tool result.
	// Override via CHATCLI_TOOL_RESULT_MAX_CHARS.
	DefaultPerResultMaxChars = 20_000

	// PreviewHeadChars is how much of a large result to keep as inline preview.
	PreviewHeadChars = 4_000

	// PreviewTailChars is how much of the end to keep for context.
	PreviewTailChars = 1_000
)

Budget configuration — configurable via environment variables.

View Source
var StatusPhrases = []string{
	"Thinking...",
	"Analyzing...",
	"Processing...",
	"Reasoning...",
	"Planning...",
	"Exploring...",
	"Connecting dots...",
	"Crafting solution...",
	"Reading code...",
	"Mapping structure...",
}

StatusPhrases is the original English fallback list, retained for backward-compat with code outside this package that imported the package-level slice. New code should call LocalizedStatusPhrases() so locale changes are honored at call time.

Deprecated: use LocalizedStatusPhrases().

Functions

func AuxAllowedPaths added in v1.102.0

func AuxAllowedPaths() []string

AuxAllowedPaths returns paths the engine and read validator should treat as inside the boundary, in addition to the workspace root. Includes the session workspace (if initialized).

func BuildSuggestions added in v1.66.0

func BuildSuggestions(command string) []string

BuildSuggestions returns alternative commands based on the denied command.

func ClassifyErrorCode added in v1.118.0

func ClassifyErrorCode(err error) string

ClassifyErrorCode maps a Go error into a stable, locale-independent short code suitable for telemetry, log fields, and the wire-level IsError marker. The returned string is bounded (≤32 chars) and contains only ASCII so it round-trips through any provider format.

Mapping rules:

*os.PathError      → ENOENT / EACCES / EISDIR / EEXIST / ENOSPC / ETOOMANY...
*fs.PathError      → same
syscall.Errno      → the errno name (ENOENT, ECONNREFUSED, etc.)
*exec.ExitError    → "ExitCode:<N>"
*net.OpError       → "NetworkError" (further classified by the wrapped err)
context.Canceled   → "Canceled"
context.DeadlineExceeded → "Timeout"
Anything else      → "UnknownError"

Callers should not parse this string back; it's an opaque sentinel for telemetry and conditional UI ("retry on Timeout, not on EACCES").

func CleanupBudgetFiles added in v1.99.0

func CleanupBudgetFiles()

CleanupBudgetFiles removes temporary budget enforcement files.

func CompactToolLabel added in v1.97.0

func CompactToolLabel(subcmd string, rawArgs string) string

CompactToolLabel builds a compact label from a subcmd + args. Examples: "Read(main.go)", "Write(pkg/handler.go)", "Exec(go test ./...)", "Patch(3 edits)"

func ContinuationMessage added in v1.99.0

func ContinuationMessage() models.Message

ContinuationMessage returns the message to inject when max_tokens is hit.

func CountPendingToolCalls added in v1.99.0

func CountPendingToolCalls(history []models.Message) int

CountPendingToolCalls returns how many tool calls in the last assistant message don't yet have a tool result. Used to detect incomplete tool execution.

func DefaultAllowedCommands added in v1.97.0

func DefaultAllowedCommands() map[string]string

DefaultAllowedCommands returns the categorized command allowlist.

func DetectCurlyQuotes added in v1.99.0

func DetectCurlyQuotes(content string) (count int, positions []int)

DetectCurlyQuotes checks if the content contains any curly/smart quotes. Returns the count and positions for diagnostics.

func EnvelopeWidth added in v1.119.2

func EnvelopeWidth() int

EnvelopeWidth returns the width to use for a response envelope on the current terminal. Delegates to kit.ContentWidth: right-edge margin so native scrollbars never clip the border, clamped to a minimum so the box never collapses on tiny terminals; no upper cap (full-screen terminals use their full width — a direct user preference).

func GenerateToolCallID added in v1.99.0

func GenerateToolCallID(turn, callIndex int) string

GenerateToolCallID creates a deterministic tool call ID for XML-parsed tool calls that don't have native IDs. Uses the turn number and call index for uniqueness.

func HasSuspiciousUnicode added in v1.99.0

func HasSuspiciousUnicode(content string) bool

HasSuspiciousUnicode checks for Unicode characters that look like ASCII but aren't. Returns true if any lookalike characters are found.

func IntegrateTaskTracking added in v1.47.0

func IntegrateTaskTracking(tracker *TaskTracker, reasoningText string, logger *zap.Logger)

IntegrateTaskTracking processa o reasoning e atualiza o plano de tarefas

func IsContextTooLongError added in v1.99.0

func IsContextTooLongError(err error) bool

IsContextTooLongError checks if an error is a context overflow error.

func IsLikelyPayloadProblem added in v1.104.0

func IsLikelyPayloadProblem(err error, historyChars int) bool

IsLikelyPayloadProblem combines strict payload-size errors with ambiguous network errors that become highly suspect when the history is large. Proxies occasionally close the TCP connection mid-POST when the body exceeds a limit instead of returning a proper 413, which surfaces as EOF / connection reset / broken pipe — indistinguishable from transient network issues unless you know the request was huge.

The historyChars threshold avoids false positives on small requests where EOF is almost certainly a genuine transient failure.

func IsPayloadTooLargeError added in v1.104.0

func IsPayloadTooLargeError(err error) bool

IsPayloadTooLargeError detects HTTP 413 responses and proxy-level body size rejections — common in corporate environments where the egress proxy or API gateway caps POST bodies at 1-5 MB, independently of the model's context window. These are recoverable by compacting history more aggressively and retrying, so we distinguish them from generic "context too long" model errors (which typically return 400).

func IsProxyWAFRejection added in v1.104.0

func IsProxyWAFRejection(err error) bool

IsProxyWAFRejection detects 403 Forbidden responses that come from a corporate proxy / WAF / gateway rather than from the LLM provider's own auth layer. LLM auth 403s say things like "permission_denied" or "invalid_api_key"; WAF 403s cite firewall / policy / size / security rules. Distinguishing them matters: auth 403 needs a token refresh, WAF 403 needs a smaller payload. Never flags a plain unqualified 403.

Also catches a second flavor: SDK-level decode failures where the upstream returned HTML (a proxy block page) instead of JSON. The AWS SDK, for example, surfaces this as "StatusCode: 403 ... deserialization failed ... invalid character '<' looking for beginning of value" — a legitimate AWS 403 would return well-formed JSON, so this pattern is an unambiguous middlebox fingerprint.

func LocalizedStatusPhrases added in v1.119.0

func LocalizedStatusPhrases() []string

LocalizedStatusPhrases returns the rotating "thinking" messages in the currently active locale. Resolved at call time (not at package init) because i18n.Init may not have run yet when var-level initializers fire — calling it eagerly would freeze the slice to the raw keys.

func MarkTaskCompleted added in v1.47.0

func MarkTaskCompleted(tracker *TaskTracker)

MarkTaskCompleted marca a tarefa atual como concluída

func MarkTaskFailed added in v1.47.0

func MarkTaskFailed(tracker *TaskTracker, errorMsg string)

MarkTaskFailed marca a tarefa atual como falhada

func MarkTaskInProgress added in v1.47.0

func MarkTaskInProgress(tracker *TaskTracker)

MarkTaskInProgress marca a tarefa atual como em andamento

func MaxToolConcurrency added in v1.118.0

func MaxToolConcurrency() int

MaxToolConcurrency returns the active concurrency budget, reading the CHATCLI_AGENT_MAX_TOOL_CONCURRENCY environment variable on every call so a /config security mutation takes effect immediately. Values <=0 fall back to the default; the upper bound is intentionally not enforced — operators who want 64 concurrent fetches can have them.

func NormalizeQuotes added in v1.99.0

func NormalizeQuotes(content, filePath string) string

NormalizeQuotes replaces curly/smart quotes with straight ASCII quotes. Only applies to code files — documentation files (.md, .txt, .rst) are unchanged.

func NormalizeQuotesAlways added in v1.99.0

func NormalizeQuotesAlways(content string) string

NormalizeQuotesAlways replaces curly quotes regardless of file type. Use this for tool call arguments where curly quotes are always wrong.

func NormalizeToolArgs added in v1.99.0

func NormalizeToolArgs(toolName, raw string) (string, bool)

NormalizeToolArgs attempts multiple recovery strategies to parse malformed JSON from LLM tool call arguments. This handles common issues like:

  • Single quotes instead of double quotes
  • Unquoted keys: {cmd: "read", file: "main.go"}
  • Plain string values that should be wrapped: "main.go" → {"file":"main.go"}
  • Completely unstructured text: "read --file main.go"

Returns the normalized JSON string and true if recovery succeeded.

func PaceText added in v1.119.3

func PaceText(text string, requested time.Duration)

PaceText prints text with an adaptive typewriter cadence. Short bodies use rune-by-rune animation at the requested delay (the effect reads as animation); long bodies switch to a chunked mode where multiple runes paint per ~10ms tick so the total animation completes within the configured budget; very long bodies skip the animation entirely.

Why two modes instead of one: a naive "scale down per-rune delay" approach degenerates below the OS scheduler's granularity (~1-2ms on Linux/macOS). With 2 000 runes and an 800ms budget the per-rune math says 400μs/rune but the actual wall-clock can balloon to 4s on CI under noise. Chunking sidesteps that: each sleep is a full 10ms, well above scheduler granularity, and we just emit more runes per tick to hit the same total time. The result is deterministically bounded by the budget regardless of how the host schedules sleeps.

ANSI escape sequences embedded in text are emitted as part of the chunk they land in — they don't trigger sleeps or count toward the printable budget so color transitions never pause the eye.

func ParallelToolsEnabled added in v1.118.0

func ParallelToolsEnabled() bool

ParallelToolsEnabled reports whether the orchestrator should partition the batch and run concurrency-safe tools in parallel. Opt-in via CHATCLI_AGENT_PARALLEL_TOOLS=true while the feature bakes in production usage. When false, every tool runs sequentially regardless of its capability flags — preserving the legacy behavior bit-for-bit.

Default ON for new installs after Fase 7 acceptance; until then the env var gates the rollout.

func RegisterAuxReadPath added in v1.102.0

func RegisterAuxReadPath(path string)

RegisterAuxReadPath adds a directory that SensitiveReadPaths.IsReadAllowed will always permit. Typically called by the session workspace.

func RegisterResultDirSetter added in v1.102.0

func RegisterResultDirSetter(fn ResultDirSetter)

RegisterResultDirSetter adds a callback that will be invoked with the session tool-results directory during InitSessionWorkspace (and with "" on Cleanup).

func SetBudgetResultDir added in v1.102.0

func SetBudgetResultDir(dir string)

SetBudgetResultDir overrides the directory where EnforceToolResultBudget persists overflow files. Pass an empty string to reset to default. This is wired by cli/agent.InitSessionWorkspace.

func SetProcessGroup added in v1.66.0

func SetProcessGroup(cmd *exec.Cmd)

SetProcessGroup configures the command to create a new process group. Must be called before cmd.Start().

func TelemetrySafe added in v1.118.0

func TelemetrySafe(err error) string

TelemetrySafe returns a short, locale-independent description suitable for log fields. Sanitizes file paths and quotation marks so the output is reliably greppable.

func TerminalWidth added in v1.119.2

func TerminalWidth() int

TerminalWidth reports the live terminal width in columns. Delegates to kit.TermWidth — the single width helper (fallback 100 when stdout is not a TTY).

func UnregisterAuxReadPath added in v1.102.0

func UnregisterAuxReadPath(path string)

UnregisterAuxReadPath removes a previously registered aux read path.

func ValidateToolResultPairing added in v1.99.0

func ValidateToolResultPairing(history []models.Message) bool

ValidateToolResultPairing checks if the history has pairing issues without repairing. Returns true if the history is valid (no repairs needed).

func VisibleLen

func VisibleLen(s string) int

VisibleLen calcula comprimento visível em colunas do terminal (sem ANSI codes). Delegates to kit.VisibleLen (lipgloss.Width) — one measurement path for wrap math and border math keeps them in agreement when content has emoji presentation sequences.

func WrapPlainStringForTool added in v1.99.0

func WrapPlainStringForTool(toolName, value string) string

WrapPlainStringForTool wraps a plain string value into the appropriate JSON structure for the given tool. This handles cases where the LLM returns just the value instead of a proper JSON object.

This function is conservative — it only wraps values that look like a single argument (e.g., a file path or search term). It does NOT wrap CLI-style args like "read --file main.go" because those are handled by the CLI parser.

Examples:

WrapPlainStringForTool("read_file", "main.go") → `{"file":"main.go"}`
WrapPlainStringForTool("run_command", "ls -la") → `{"cmd":"ls -la"}`
WrapPlainStringForTool("search_files", "TODO") → `{"term":"TODO"}`

Types

type BatchOptions added in v1.118.0

type BatchOptions struct {
	// MaxConcurrency caps parallelism for this batch. Zero or negative
	// falls back to MaxToolConcurrency().
	MaxConcurrency int

	// CancelSiblings, when true, cancels in-flight siblings via the
	// batch ctx when any tool returns an infra error. The legacy serial
	// loop fails fast on first error; we mirror that for the concurrent
	// path so users don't pay for trailing operations after one already
	// signals "stop".
	CancelSiblings bool

	// Logger is used for batch-level diagnostics. Nil → no-op.
	Logger *zap.Logger
}

BatchOptions configures a single RunBatch invocation.

type BudgetReport added in v1.99.0

type BudgetReport struct {
	TotalToolResults     int
	TotalOriginalChars   int64
	TotalFinalChars      int64
	ResultsTruncated     int
	ResultsPersistedDisk int
	BytesSavedToDisk     int64
}

BudgetReport describes what the budget enforcement did.

func EnforceToolResultBudget added in v1.99.0

func EnforceToolResultBudget(history []models.Message, logger *zap.Logger) ([]models.Message, *BudgetReport)

EnforceToolResultBudget scans the conversation history and truncates oversized tool results in-place. Large results are persisted to temporary files and replaced with compact previews containing a file reference.

The function works in two passes:

  1. Per-result enforcement: any single result exceeding DefaultPerResultMaxChars is truncated with a preview.
  2. Per-turn enforcement: if the aggregate of all tool results in a turn exceeds DefaultTurnBudgetChars, the largest results are progressively truncated until the turn fits within budget.

Returns the (possibly modified) history and a report.

type CommandAllowlist added in v1.97.0

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

CommandAllowlist validates commands against a categorized allowlist. In strict mode, only allowed commands can execute. In permissive mode, unknown commands fall back to the legacy denylist validator.

func NewCommandAllowlist added in v1.97.0

func NewCommandAllowlist() *CommandAllowlist

NewCommandAllowlist creates a new allowlist validator configured from environment.

func (*CommandAllowlist) GetMode added in v1.97.0

func (al *CommandAllowlist) GetMode() SecurityMode

GetMode returns the current security mode.

func (*CommandAllowlist) IsAllowed added in v1.97.0

func (al *CommandAllowlist) IsAllowed(fullCommand string) (bool, string, string)

IsAllowed checks if a command is in the allowlist. Returns (allowed, category, reason).

type CommandBlock

type CommandBlock struct {
	Description string
	Commands    []string
	Language    string
	ContextInfo CommandContextInfo
}

CommandBlock representa um bloco de comandos executáveis

type CommandContextInfo

type CommandContextInfo struct {
	SourceType    SourceType
	FileExtension string
	IsScript      bool
	ScriptType    string // shell, python, etc.
}

CommandContextInfo contém metadados sobre a origem e natureza de um comando

type CommandExecutor

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

CommandExecutor executa comandos do sistema de forma segura

func NewCommandExecutor

func NewCommandExecutor(logger *zap.Logger) *CommandExecutor

NewCommandExecutor cria uma nova instância do executor

func (*CommandExecutor) CaptureOutput

func (e *CommandExecutor) CaptureOutput(ctx context.Context, shell string, args []string) ([]byte, error)

CaptureOutput executa comando e captura apenas a saída (para uso interno)

func (*CommandExecutor) Execute

func (e *CommandExecutor) Execute(ctx context.Context, command string, interactive bool) (*ExecutionResult, error)

Execute executa um comando e retorna o resultado

type CommandOutput

type CommandOutput struct {
	CommandBlock CommandBlock
	Output       string
	ErrorMsg     string
}

CommandOutput representa o resultado da execução de um comando

type CommandValidator

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

CommandValidator validates shell-level requests before execution.

Layered strategy (cheapest to most expensive):

  1. inlineCodeAnalyzer — for invocations like `python -c <code>`, `node -e <code>`, etc., dynamically classifies the inline source. Replaces the older `\bpython[23]?\s+-c\b` regex that produced false positives on benign one-liners like `python -c "print(1)"`.
  2. dangerousPatterns — traditional regex over the full line. Catch-all for patterns that span multiple segments (curl|sh, base64|bash) or that flag a single dangerous invocation (rm -rf, mkfs, sudo, etc).
  3. extraDenyPatterns — user-supplied denylist via CHATCLI_AGENT_DENYLIST.

The shell parsing layer (ShellSegment) feeds (1). The legacy full-line regex pass in (2) is preserved verbatim for zero behavioral regression on the existing dangerous-command corpus.

func NewCommandValidator

func NewCommandValidator(logger *zap.Logger) *CommandValidator

NewCommandValidator builds a validator with default rules.

func (*CommandValidator) IsDangerous

func (v *CommandValidator) IsDangerous(cmd string) bool

IsDangerous checks whether a request is potentially harmful.

Evaluation pipeline:

  1. Inline-code analysis — for each shell segment that invokes an interpreter via -c/-e/-r, classify the inline source. RiskHigh short-circuits to dangerous immediately. RiskSafe does not make this segment dangerous (the rest of the line is still scored by the next layers).
  2. dangerousPatterns — traditional regex over the full line.
  3. extraDenyPatterns — user-supplied denylist.
  4. Sudo guard.

The "safe inline -c suppresses the false positive" property holds because the `\bpython[23]?\s+-c\b` family was removed from layer 2: the classifier is the single source of truth for that class.

func (*CommandValidator) IsLikelyInteractive

func (v *CommandValidator) IsLikelyInteractive(cmd string) bool

IsLikelyInteractive verifica se um comando provavelmente é interativo

func (*CommandValidator) ValidateCommand

func (v *CommandValidator) ValidateCommand(cmd string) error

ValidateCommand valida um comando antes da execução

type ContextManager

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

ContextManager gerencia contexto de execução para o agente

func NewContextManager

func NewContextManager(logger *zap.Logger) *ContextManager

NewContextManager cria uma nova instância do gerenciador de contexto

func (*ContextManager) CreateExecutionContext

func (cm *ContextManager) CreateExecutionContext() (context.Context, context.CancelFunc)

CreateExecutionContext cria um contexto com timeout para execução

func (*ContextManager) GetDefaultTimeout

func (cm *ContextManager) GetDefaultTimeout() time.Duration

GetDefaultTimeout retorna o timeout padrão configurado

func (*ContextManager) RequestLLMContinuation

func (cm *ContextManager) RequestLLMContinuation(
	ctx context.Context,
	llmClient interface{},
	history []models.Message,
	previousCommand string,
	output string,
	stderr string,
	userContext string,
) (string, error)

RequestLLMContinuation solicita continuação à LLM com contexto adicional

func (*ContextManager) RequestLLMWithPreExecutionContext

func (cm *ContextManager) RequestLLMWithPreExecutionContext(
	ctx context.Context,
	llmClient interface{},
	history []models.Message,
	originalCommand string,
	userContext string,
) (string, error)

RequestLLMWithPreExecutionContext solicita refinamento antes da execução

func (*ContextManager) SetDefaultTimeout

func (cm *ContextManager) SetDefaultTimeout(timeout time.Duration)

SetDefaultTimeout atualiza o timeout padrão

type ContextRecovery added in v1.99.0

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

ContextRecovery manages recovery state across a session.

func NewContextRecovery added in v1.99.0

func NewContextRecovery(config ContextRecoveryConfig, logger *zap.Logger) *ContextRecovery

NewContextRecovery creates a recovery manager.

func (*ContextRecovery) CanEscalateMaxTokens added in v1.99.0

func (cr *ContextRecovery) CanEscalateMaxTokens() bool

CanEscalateMaxTokens returns true if more escalation attempts are available.

func (*ContextRecovery) CanRecoverContextOverflow added in v1.99.0

func (cr *ContextRecovery) CanRecoverContextOverflow() bool

CanRecoverContextOverflow returns true if more recovery attempts are available.

func (*ContextRecovery) MaxTokensEscalation added in v1.99.0

func (cr *ContextRecovery) MaxTokensEscalation(currentMaxTokens, providerCap int) (int, bool)

MaxTokensEscalation computes the escalated max_tokens value. Returns (newMaxTokens, shouldEscalate).

func (*ContextRecovery) RecoverContextOverflow added in v1.99.0

func (cr *ContextRecovery) RecoverContextOverflow(history []models.Message) ([]models.Message, bool)

RecoverContextOverflow applies increasingly aggressive recovery strategies to reduce the conversation history size.

Strategy progression:

  1. First attempt: aggressive budget enforcement + pairing cleanup
  2. Second attempt: emergency truncation (keep only recent messages)
  3. Third attempt: nuclear truncation (keep only system + last 2 messages)

Returns the recovered history and true if recovery was applied.

type ContextRecoveryConfig added in v1.99.0

type ContextRecoveryConfig struct {
	// MaxRecoveryAttempts is the maximum number of context-too-long recoveries per session.
	MaxRecoveryAttempts int

	// MaxTokenEscalations is the maximum number of max_tokens escalations per session.
	MaxTokenEscalations int

	// EmergencyKeepMessages is how many recent messages to keep during emergency truncation.
	// System messages are always preserved.
	EmergencyKeepMessages int

	// AggressiveBudgetRatio reduces the tool result budget to this fraction during recovery.
	// 0.5 means half the normal budget.
	AggressiveBudgetRatio float64
}

ContextRecoveryConfig controls recovery behavior.

func DefaultContextRecoveryConfig added in v1.99.0

func DefaultContextRecoveryConfig() ContextRecoveryConfig

DefaultContextRecoveryConfig returns the default recovery configuration.

type EnhancedExecutionResult added in v1.66.0

type EnhancedExecutionResult struct {
	Command      string
	Output       string
	Error        string
	ExitCode     int
	Duration     time.Duration
	WasKilled    bool
	WasTruncated bool
	OriginalSize int64
	Severity     string
}

EnhancedExecutionResult extends ExecutionResult with truncation info.

type ExecuteFunc added in v1.118.0

type ExecuteFunc func(ctx context.Context, call ToolCall) (ToolResult, error)

ExecuteFunc is the callback the orchestrator invokes per tool call. It returns the structured ToolResult; the second return is an infrastructure error that aborts the batch (context.Canceled, network down, plugin binary missing) — business errors stay inside the result with IsError=true.

type ExecutionResult

type ExecutionResult struct {
	Command   string
	Output    string
	Error     string
	ExitCode  int
	Duration  time.Duration
	WasKilled bool
}

ExecutionResult contém o resultado de uma execução

type FileStalenessTracker added in v1.99.0

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

FileStalenessTracker tracks file states across read/write operations.

func NewFileStalenessTracker added in v1.99.0

func NewFileStalenessTracker() *FileStalenessTracker

NewFileStalenessTracker creates a new tracker.

func (*FileStalenessTracker) CheckStaleness added in v1.99.0

func (t *FileStalenessTracker) CheckStaleness(path string) StalenessResult

CheckStaleness checks if a file has been modified since the last recorded read. Returns a StalenessResult. If the file was never read, it's not considered stale.

func (*FileStalenessTracker) Clear added in v1.99.0

func (t *FileStalenessTracker) Clear(path string)

Clear removes the recorded state for a file (e.g., after a successful write).

func (*FileStalenessTracker) ClearAll added in v1.99.0

func (t *FileStalenessTracker) ClearAll()

ClearAll removes all tracked file states.

func (*FileStalenessTracker) RecordRead added in v1.99.0

func (t *FileStalenessTracker) RecordRead(path string) error

RecordRead records the current state of a file after a successful read. Should be called after every read tool execution.

func (*FileStalenessTracker) TrackedFiles added in v1.99.0

func (t *FileStalenessTracker) TrackedFiles() []string

TrackedFiles returns the list of currently tracked file paths.

type FileState added in v1.99.0

type FileState struct {
	Path        string    `json:"path"`
	ModTime     time.Time `json:"mod_time"`
	ContentHash string    `json:"content_hash"` // SHA-256 hex
	Size        int64     `json:"size"`
	ReadAt      time.Time `json:"read_at"`
}

FileState records the state of a file at the time it was read.

type InlineCodeRisk added in v1.118.0

type InlineCodeRisk int

InlineCodeRisk is the verdict returned by the inline-code classifier for `python -c '<code>'`, `node -e '<code>'`, and friends. The middle level `Unknown` is reserved for code we can't confidently classify (parse failure, exotic syntax) — callers should treat Unknown as "elevate to confirmation" in strict mode and "allow" in lenient mode.

const (
	// RiskSafe — inline source contains only read-only operations: stdlib
	// pretty-printing, JSON encoding, simple math. No imports of os /
	// subprocess / network libraries, no file writes, no eval/exec.
	RiskSafe InlineCodeRisk = iota
	// RiskUnknown — we couldn't determine risk (mixed signals, unknown
	// builtins, dynamic indirection). Conservative callers should treat
	// this as RiskHigh.
	RiskUnknown
	// RiskHigh — inline source uses primitives that can execute arbitrary
	// commands, write files, open network connections, or otherwise escape
	// the language sandbox.
	RiskHigh
)

func (InlineCodeRisk) String added in v1.118.0

func (r InlineCodeRisk) String() string

String renders the risk level for log/telemetry purposes.

type InlineCodeRiskAnalyzer added in v1.118.0

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

InlineCodeRiskAnalyzer classifies inline source code passed to a language interpreter via the standard exec flags. It uses substring matching (regex) rather than per-language ASTs because:

  1. Shipping a Python+JS+Perl+Ruby+PHP+Lua parser bundle would inflate the binary and create a maintenance nightmare.
  2. Inline scripts on the command line are short by definition — a regex pass over a few hundred bytes is reliably fast.
  3. The false-negative rate of "obfuscated inline malware" is bounded by what fits on one CLI line, and most real attacks cited in security literature use the same vocabulary (os.system, child_process, etc.).

The analyzer is goroutine-safe: all state is precompiled at construction time and Analyze is a pure function thereafter.

func NewInlineCodeRiskAnalyzer added in v1.118.0

func NewInlineCodeRiskAnalyzer() *InlineCodeRiskAnalyzer

NewInlineCodeRiskAnalyzer builds the default analyzer. Pass strict=true to elevate RiskUnknown to RiskHigh, useful for compliance-sensitive deployments. The environment variable CHATCLI_AGENT_INLINE_CODE_STRICT also forces strict mode.

func (*InlineCodeRiskAnalyzer) Analyze added in v1.118.0

func (a *InlineCodeRiskAnalyzer) Analyze(lang, source string) InlineCodeRisk

Analyze returns the risk level for the given inline source string. `lang` should be a normalized interpreter name (python, node, perl, ruby, php, lua); other values return RiskUnknown.

func (*InlineCodeRiskAnalyzer) IsHighRisk added in v1.118.0

func (a *InlineCodeRiskAnalyzer) IsHighRisk(lang, source string) bool

IsHighRisk is a convenience wrapper for callers that only care about the strict-elevation decision.

type MicrocompactConfig added in v1.99.0

type MicrocompactConfig struct {
	// TurnsBeforeTruncate is the age (in turns) after which tool results
	// start being truncated. Default: 2.
	TurnsBeforeTruncate int

	// TurnsBeforeSummarize is the age (in turns) after which tool results
	// are replaced with a one-line summary. Default: 4.
	TurnsBeforeSummarize int

	// TruncateHeadChars is how many chars to keep at the head during truncation.
	TruncateHeadChars int

	// TruncateTailChars is how many chars to keep at the tail during truncation.
	TruncateTailChars int

	// MinContentSize is the minimum content size (chars) to trigger compaction.
	// Small tool results are never compacted.
	MinContentSize int

	// CCR, when set, preserves every byte microcompact drops: the original
	// tool result is archived in the CCR store and the preview/summary stub
	// embeds a <<ccr:KEY>> marker the model can expand with @recall. Nil
	// keeps the legacy lossy behavior.
	CCR *compress.Layer
}

MicrocompactConfig controls progressive compaction behavior.

func DefaultMicrocompactConfig added in v1.99.0

func DefaultMicrocompactConfig() MicrocompactConfig

DefaultMicrocompactConfig returns the default configuration.

Defaults are intentionally gentle — two turns of full-fidelity tool output, four turns before falling back to a one-line summary, and generous head+tail previews. This protects multi-turn, cross-file workflows (large refactors, review sessions, long debugging arcs) where the model may need to re-consult content it read several turns ago. Users chasing maximum token frugality can tighten every knob via the CHATCLI_MICROCOMPACT_* env vars below.

type MicrocompactReport added in v1.99.0

type MicrocompactReport struct {
	Truncated  int
	Summarized int
	CharsSaved int64
}

MicrocompactReport describes what the microcompact did.

func ApplyMicrocompact added in v1.99.0

func ApplyMicrocompact(history []models.Message, currentTurn int, config MicrocompactConfig, logger *zap.Logger) ([]models.Message, *MicrocompactReport)

ApplyMicrocompact progressively compacts old tool results in the history. currentTurn is the 0-based index of the current turn in the agent loop.

The function identifies "turns" by counting assistant messages. Tool results following each assistant message belong to that turn. Results from older turns are progressively compacted.

type PairingRepairReport added in v1.99.0

type PairingRepairReport struct {
	SyntheticResultsInjected int      // tool_use blocks without matching tool_result
	OrphanResultsRemoved     int      // tool_result blocks without matching tool_use
	DuplicateToolUsePruned   int      // duplicate or empty tool_use IDs within a single assistant message
	ResultsRelocated         int      // results moved next to their tool_call past interposed messages
	MissingToolUseIDs        []string // IDs of tool calls that had no result
	OrphanToolResultIDs      []string // IDs of tool results that had no call
}

PairingRepairReport describes what the pairing validator repaired.

func EnsureToolResultPairing added in v1.99.0

func EnsureToolResultPairing(history []models.Message, logger *zap.Logger) ([]models.Message, *PairingRepairReport)

EnsureToolResultPairing validates and repairs the conversation history so that every assistant message carrying ToolCalls is immediately followed by exactly one tool result message per call — the shape every native tool API requires.

Scope rules (per assistant block, in order):

  1. Results are claimed from the stretch between the assistant message and the NEXT assistant message. A matching result that sits past interposed user/system messages is relocated to be adjacent to its call.
  2. Calls with no claimable result get a synthetic error result injected directly after the assistant message.
  3. Duplicate IDs WITHIN one assistant message are pruned (all but the first), and calls with an empty ID are pruned outright — they can never be paired, and serializing them ships an invalid empty id. The same ID reappearing in a LATER assistant message is legal — providers like Moonshot/Kimi reuse deterministic per-turn IDs.
  4. Tool result messages not claimed by any block are orphans and removed.

Returns the repaired history and a report of what was changed. If no repairs are needed, returns the original slice unchanged — the common case is allocation-free via a validation prepass, since this runs on the persistent history every agent turn.

func (*PairingRepairReport) HasRepairs added in v1.99.0

func (r *PairingRepairReport) HasRepairs() bool

HasRepairs returns true if any repairs were made.

type PartitionPolicy added in v1.118.0

type PartitionPolicy interface {
	// IsConcurrencySafe returns true when the given tool call can run
	// in parallel with other safe calls. Implementations should be
	// pure functions over the (name, args) inputs.
	IsConcurrencySafe(call ToolCall) bool
}

PartitionPolicy lets the caller inject capability lookups without pulling cli/plugins or cli/mcp into the agent package (cycle). The orchestrator asks: "is this call safe to parallelize?". The caller implements that by inspecting its plugin / MCP registry.

type PartitionPolicyFunc added in v1.118.0

type PartitionPolicyFunc func(call ToolCall) bool

PartitionPolicyFunc adapts a plain function into the PartitionPolicy interface for callers that don't want to define a type.

func (PartitionPolicyFunc) IsConcurrencySafe added in v1.118.0

func (f PartitionPolicyFunc) IsConcurrencySafe(call ToolCall) bool

IsConcurrencySafe satisfies PartitionPolicy.

type PathValidator added in v1.66.0

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

PathValidator validates file paths against workspace boundaries.

func NewPathValidator added in v1.66.0

func NewPathValidator(workspace string, logger *zap.Logger) *PathValidator

NewPathValidator creates a new path validator.

func (*PathValidator) DetectPathTraversal added in v1.66.0

func (pv *PathValidator) DetectPathTraversal(command string) (bool, string)

DetectPathTraversal checks if a command contains path traversal attempts.

func (*PathValidator) IsWithinWorkspace added in v1.66.0

func (pv *PathValidator) IsWithinWorkspace(targetPath string) bool

IsWithinWorkspace checks if a path is within the workspace boundary.

func (*PathValidator) SetWorkspaceBoundary added in v1.66.0

func (pv *PathValidator) SetWorkspaceBoundary(path string)

SetWorkspaceBoundary updates the workspace boundary.

func (*PathValidator) ValidateFilePaths added in v1.66.0

func (pv *PathValidator) ValidateFilePaths(command string) ValidationResult

ValidateFilePaths validates all paths in a command against safety rules.

type PatternRule added in v1.66.0

type PatternRule struct {
	Pattern     string `json:"pattern"`
	Description string `json:"description"`
	Severity    string `json:"severity"` // "critical", "high", "medium", "low"
	// contains filtered or unexported fields
}

PatternRule is a configurable deny/allow pattern.

func (*PatternRule) Compile added in v1.66.0

func (pr *PatternRule) Compile() error

Compile compiles the regex pattern.

func (*PatternRule) Matches added in v1.66.0

func (pr *PatternRule) Matches(command string) bool

Matches checks if the command matches this pattern.

type ProcessManager added in v1.66.0

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

ProcessManager handles process lifecycle management.

func NewProcessManager added in v1.66.0

func NewProcessManager(logger *zap.Logger) *ProcessManager

NewProcessManager creates a new process manager.

func (*ProcessManager) TerminateProcessTree added in v1.66.0

func (pm *ProcessManager) TerminateProcessTree(cmd *exec.Cmd, timeout time.Duration) error

TerminateProcessTree sends SIGTERM, waits for timeout, then SIGKILL. Platform-specific implementation in process_manager_unix.go / process_manager_windows.go.

func (*ProcessManager) TruncateOutput added in v1.66.0

func (pm *ProcessManager) TruncateOutput(output string, maxBytes int64) (string, bool)

TruncateOutput truncates output keeping head + tail with a marker.

type ResponseEnvelopeOptions added in v1.119.2

type ResponseEnvelopeOptions struct {
	// HeaderLeft is the visible label on the top border's left side.
	// Conventionally the icon + title (e.g. " 💬 RESPOSTA ") in color.
	HeaderLeft string

	// HeaderRight is the visible label on the top border's right side.
	// Conventionally the metrics block (e.g. " 1.4s · 312↑ 1.8k↓ ").
	// Pass an empty string to draw only the left label.
	HeaderRight string

	// FooterLeft and FooterRight compose the dim telemetry line that
	// closes the reply. Most callers leave them empty (the body's
	// terminal punctuation closes the thought).
	FooterLeft  string
	FooterRight string

	// Body is the message content to render inside the box. Typically
	// glamour-rendered markdown (ANSI escapes preserved); the envelope
	// wraps it to the resolved inner width.
	Body string

	// Color is kept for API stability; the sóbrio treatment draws dim
	// chrome and expects labels to arrive pre-colored.
	Color string

	// Typewriter enables progressive rune-by-rune painting of the body
	// for the "alive" reply feel. ANSI escapes flush instantly so
	// colors never pause the eye.
	Typewriter bool

	// TypewriterDelay overrides the per-rune delay. Zero uses the
	// default of 2ms — fast enough for long replies, slow enough to
	// register as animation. Set to a positive value to slow down or
	// to a negative value (caller-side check) to disable.
	TypewriterDelay time.Duration

	// Width pins the card width in columns. Zero asks the envelope to
	// pick EnvelopeWidth() automatically — the right choice for almost
	// every caller. Tests and special UIs (split-pane reports) can
	// override this.
	Width int
}

ResponseEnvelopeOptions configures the unified reply rendering. All label fields are PRE-FORMATTED: callers own colorization and any leading/trailing spaces they want carved out of the dash fill. Empty fields are omitted (no extra space reserved).

type ResultDirSetter added in v1.102.0

type ResultDirSetter func(dir string)

ResultDirSetter is a callback other packages (workers, plugins) can register so the session workspace can wire the overflow dir without creating import cycles.

type SafetyConfig added in v1.66.0

type SafetyConfig struct {
	Version           int           `json:"version"`
	DenyPatterns      []PatternRule `json:"deny_patterns,omitempty"`
	AllowPatterns     []PatternRule `json:"allow_patterns,omitempty"`
	WorkspaceBoundary string        `json:"workspace_boundary,omitempty"`
	AllowSudo         bool          `json:"allow_sudo"`
	MaxOutputBytes    int64         `json:"max_output_bytes,omitempty"`
}

SafetyConfig holds configurable safety rules.

func DefaultSafetyConfig added in v1.66.0

func DefaultSafetyConfig() *SafetyConfig

DefaultSafetyConfig returns sensible defaults.

func LoadSafetyConfig added in v1.66.0

func LoadSafetyConfig(globalPath, localPath string) (*SafetyConfig, error)

LoadSafetyConfig loads and merges global + local safety configs.

func MergeSafetyConfigs added in v1.66.0

func MergeSafetyConfigs(global, local *SafetyConfig) *SafetyConfig

MergeSafetyConfigs merges local into global. Local can only ADD deny patterns, not remove global ones. Local can add allow patterns for project-specific commands.

type SecurityMode added in v1.97.0

type SecurityMode string

SecurityMode determines how command validation works.

const (
	// SecurityModeStrict only allows commands in the allowlist (default).
	SecurityModeStrict SecurityMode = "strict"
	// SecurityModePermissive uses allowlist + falls back to denylist for unknown commands.
	SecurityModePermissive SecurityMode = "permissive"
)

type SensitiveReadPaths added in v1.97.0

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

SensitiveReadPaths enforces read access control in agent mode. By default, only files within the workspace are readable, with specific sensitive paths blocked even within the workspace.

func NewSensitiveReadPaths added in v1.97.0

func NewSensitiveReadPaths() *SensitiveReadPaths

NewSensitiveReadPaths creates a read path validator configured from environment.

func (*SensitiveReadPaths) IsReadAllowed added in v1.97.0

func (s *SensitiveReadPaths) IsReadAllowed(path, workspace string) (bool, string)

IsReadAllowed checks whether the given path is safe to read in agent mode. workspace is the current working directory / project root. Returns (allowed, reason).

type SessionWorkspace added in v1.102.0

type SessionWorkspace struct {
	Root           string // e.g. /tmp/chatcli-agent-abc123
	ScratchDir     string // Root/scratch  -> CHATCLI_AGENT_TMPDIR
	ToolResultsDir string // Root/tool-results
	// contains filtered or unexported fields
}

SessionWorkspace is a per-CLI-session scratch directory the agent is unconditionally allowed to read and write under.

func GetSessionWorkspace added in v1.102.0

func GetSessionWorkspace() *SessionWorkspace

GetSessionWorkspace returns the active session workspace or nil if InitSessionWorkspace was never called.

func InitSessionWorkspace added in v1.102.0

func InitSessionWorkspace(logger *zap.Logger) (*SessionWorkspace, error)

InitSessionWorkspace creates the per-session workspace and registers its subdirs with the engine + read validators. Idempotent: calling it twice returns the existing workspace.

The returned workspace should have Cleanup() called at CLI shutdown.

func (*SessionWorkspace) Cleanup added in v1.102.0

func (ws *SessionWorkspace) Cleanup()

Cleanup removes the session workspace unless CHATCLI_AGENT_KEEP_TMPDIR=true. Safe to call multiple times.

type ShellSegment added in v1.118.0

type ShellSegment struct {
	Cmd      string
	Args     []string
	Full     string
	HasPipe  bool // true if this segment is part of a pipeline
	Position int  // 0-based position in the top-level command sequence
}

ShellSegment represents one shell command — a single invocation that is not itself a pipeline or compound statement. Two `cat` commands separated by `|`, `&&`, `||`, or `;` produce two ShellSegments. Each segment is the granularity at which we apply per-command dangerous-pattern matching and inline-code classification.

The Full string is the literal text of just this segment (no operators); Cmd is the first word (the program name), and Args is everything after (including flags). Quoting is preserved in Full but stripped from Args so callers can pattern-match flag values directly.

func ParseShellSegments added in v1.118.0

func ParseShellSegments(line string) []ShellSegment

ParseShellSegments splits a shell command line into segments using a real shell parser (mvdan.cc/sh/v3/syntax). Compared to splitting by "|" or strings.Fields, this is robust against:

  • Quoted operators: `echo "a | b" | grep a` → 2 segments, not 3
  • Heredocs: `cat <<EOF\n|||\nEOF` → 1 segment
  • Escaped pipes: `printf 'a\\|b\\|c'` → 1 segment
  • Subshells: `(cd /tmp && ls)` → emits inner cmds
  • Background: `sleep 1 & echo done` → 2 segments

On parse failure the function falls back to a permissive single-segment result (the whole line as one segment). Callers must treat the segments as a best-effort decomposition, not a security boundary on their own — the dangerous-pattern matcher still runs against the full line as a belt-and-suspenders measure.

func (ShellSegment) InlineSource added in v1.118.0

func (s ShellSegment) InlineSource(flagPos int) string

InlineSource returns the inline source code passed via -c / -e / -r. Assumes IsInlineCodeInvocation returned true with the given flag pos.

func (ShellSegment) IsInlineCodeInvocation added in v1.118.0

func (s ShellSegment) IsInlineCodeInvocation() (lang string, flagPos int, ok bool)

IsInlineCodeInvocation returns true when the segment looks like `interpreter -flag inline-code` for one of the languages we analyze dynamically. The expected exec flag (-c, -e, -r) is returned so callers can find the next arg = the inline source.

func (ShellSegment) IsPureStdinConsumer added in v1.118.0

func (s ShellSegment) IsPureStdinConsumer() bool

IsPureStdinConsumer returns true when the segment is a known no-side-effect transformer that is safe regardless of who is feeding it data. Used by the classifier to decide that `<anything> | jq .` should not be elevated to dangerous just because the left-hand side touches files.

type SourceType

type SourceType int

SourceType define o tipo de origem do comando

const (
	SourceTypeUserInput SourceType = iota
	SourceTypeFile
	SourceTypeCommandOutput
)

type StalenessResult added in v1.99.0

type StalenessResult struct {
	IsStale      bool
	Reason       string // human-readable reason
	OriginalHash string
	CurrentHash  string
	OriginalMod  time.Time
	CurrentMod   time.Time
	OriginalSize int64
	CurrentSize  int64
}

StalenessResult describes whether a file has changed since it was last read.

func (*StalenessResult) FormatWarning added in v1.99.0

func (r *StalenessResult) FormatWarning(path string) string

FormatWarning generates a warning message for stale files, suitable for injecting into tool results so the LLM can decide how to proceed.

type Task added in v1.47.0

type Task struct {
	ID          int
	Description string
	Status      TaskStatus
	StartedAt   time.Time
	CompletedAt time.Time
	Error       string
	Attempts    int
}

type TaskPlan added in v1.47.0

type TaskPlan struct {
	Tasks         []*Task
	CurrentTask   int
	CreatedAt     time.Time
	UpdatedAt     time.Time
	NeedsReplan   bool
	FailureCount  int
	PlanSignature string
}

type TaskSpec added in v1.118.0

type TaskSpec struct {
	Description string
	Status      TaskStatus
}

TaskSpec is the LLM-friendly view of a planned task. It is what the @todo plugin uses to talk to the tracker — a flat structure decoupled from the internal *Task type's bookkeeping fields (attempts, timestamps).

type TaskStatus added in v1.47.0

type TaskStatus string
const (
	TaskPending    TaskStatus = "pending"
	TaskInProgress TaskStatus = "in_progress"
	TaskCompleted  TaskStatus = "completed"
	TaskFailed     TaskStatus = "failed"
)

type TaskTracker added in v1.47.0

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

func NewTaskTracker added in v1.47.0

func NewTaskTracker(logger *zap.Logger) *TaskTracker

func (*TaskTracker) FormatProgress added in v1.47.0

func (t *TaskTracker) FormatProgress() string

func (*TaskTracker) GetCurrentTask added in v1.47.0

func (t *TaskTracker) GetCurrentTask() *Task

func (*TaskTracker) GetPlan added in v1.47.0

func (t *TaskTracker) GetPlan() *TaskPlan

func (*TaskTracker) MarkByID added in v1.118.0

func (t *TaskTracker) MarkByID(id int, status TaskStatus, errorMsg string) bool

MarkByID updates the status of the task with the given 1-indexed ID. Returns false when no such task exists. Used by the @todo plugin's partial-update path when the LLM wants to flip a single status without resending the whole list.

The CurrentTask cursor advances if the marked task was the current one and the new status is Completed, mirroring MarkCurrentAs.

func (*TaskTracker) MarkCurrentAs added in v1.47.0

func (t *TaskTracker) MarkCurrentAs(status TaskStatus, errorMsg string)

func (*TaskTracker) NeedsReplanning added in v1.47.0

func (t *TaskTracker) NeedsReplanning() bool

func (*TaskTracker) ParseReasoning added in v1.47.0

func (t *TaskTracker) ParseReasoning(reasoningText string) error

func (*TaskTracker) ResetPlan added in v1.47.0

func (t *TaskTracker) ResetPlan()

func (*TaskTracker) ResetPlanFromReasoning added in v1.47.0

func (t *TaskTracker) ResetPlanFromReasoning(reasoningText string, preserveCompleted bool) error

ResetPlanFromReasoning recria o plano a partir de um novo reasoning. Se preserveCompleted=true, tenta preservar como concluídas as tarefas ja concluídas do plano anterior.

func (*TaskTracker) SetTasks added in v1.118.0

func (t *TaskTracker) SetTasks(specs []TaskSpec)

SetTasks replaces the entire plan with the supplied list. Used by the @todo plugin to mirror Claude Code's TodoWrite semantics: the LLM emits the full updated list every call; the tracker reconciles without preserving prior state.

This is the only public path that lets a caller set per-task status explicitly. MarkCurrentAs is for the per-turn ReAct flow (one task at a time); SetTasks is for the LLM-driven plan overwrite.

type ToolBatch added in v1.118.0

type ToolBatch struct {
	// Concurrent is true when every call in the batch is concurrency-safe
	// and read-only-or-network-only (no shared-state mutations). The batch
	// runs through errgroup with a semaphore-bounded fan-out.
	Concurrent bool

	// Calls are the per-tool invocations in their original order. The
	// orchestrator returns results in the same order so the caller can
	// keep its existing index-keyed accounting (batchOutputBuilder etc).
	Calls []ToolCall
}

ToolBatch represents a contiguous run of tool calls that share the same execution policy (all concurrency-safe or all serial). The orchestrator partitions a turn's calls into a sequence of batches and runs each one to completion before moving on, preserving the relative order of serial steps relative to surrounding parallel batches.

func PartitionToolCalls added in v1.118.0

func PartitionToolCalls(calls []ToolCall, policy PartitionPolicy) []ToolBatch

PartitionToolCalls splits a turn's tool calls into a sequence of batches where each batch is either fully concurrent-safe or fully serial. Consecutive concurrent-safe calls are coalesced into one batch (up to MaxToolConcurrency() — larger groups are split into multiple back-to-back concurrent batches to keep memory bounded). A serial call always opens a fresh batch and any following calls stay in their own batches until the next concurrency-safe run.

The algorithm is deterministic and order-preserving: a serial call between two safe calls is NEVER folded into either neighbor's parallel batch — that would change the observable execution order.

type ToolCall added in v1.43.0

type ToolCall struct {
	Name string
	Args string
	Raw  string
}

ToolCall represents a parsed tool invocation from AI output text.

func ParseToolCalls added in v1.43.0

func ParseToolCalls(text string) ([]ToolCall, error)

ParseToolCalls extracts tool calls from AI response text.

Supported formats:

  • XML self-closing: <tool_call name="@x" args="..." />
  • XML paired: <tool_call name="@x" args="..."></tool_call>
  • <tool> alias: <tool name="@x" args="..." /> — models backed by other agent CLIs (Devin CLI, Codex CLI, Claude Code) shorten the tag
  • Attributes in any order, single or double quotes
  • Args containing '>' characters (JSON, HTML entities, etc.)
  • JSON tool calls: {"tool_call":"@coder","args":{...}}
  • Multiple tool calls in a single response

type ToolContext added in v1.118.0

type ToolContext struct {
	// FilesRead tracks the absolute paths the tool reported reading.
	// Used by the file-staleness detector to know which paths to watch.
	FilesRead []string

	// FilesWritten tracks paths the tool reported writing. Used by the
	// same staleness detector and by the engine's session workspace
	// allowlist to surface new writes to read-on-demand.
	FilesWritten []string

	// HostsContacted tracks the network hosts the tool contacted.
	// Telemetry / audit use only.
	HostsContacted []string

	// Extra is a free-form bag for forward compatibility. Plugins that
	// need to track something the orchestrator doesn't know about yet
	// can stash it here; consumers must defensively type-assert.
	Extra map[string]any
}

ToolContext is the orchestrator-owned, per-turn state that ContextMutation callbacks can edit. It's a small, intentionally minimal surface — kept here in the agent package rather than the cli package so plugins under cli/plugins/ can manipulate it without an import cycle.

Fields populated lazily; nil maps are valid and Mutate helpers handle the lazy-init.

func (*ToolContext) ApplyMutations added in v1.118.0

func (c *ToolContext) ApplyMutations(results []ToolResult)

ApplyMutations runs each callback serially in the order given. The orchestrator calls this after a batch of concurrent tools completes — the serial application is what makes it safe for tools to mutate the shared context from within a parallel batch.

func (*ToolContext) GetExtra added in v1.118.0

func (c *ToolContext) GetExtra(key string) (any, bool)

GetExtra returns the value at key, plus an ok flag distinguishing "stored as nil" from "missing entirely".

func (*ToolContext) Merge added in v1.118.0

func (c *ToolContext) Merge(other *ToolContext)

Merge copies every recorded entry from other into c, deduping. Used by the orchestrator when reconciling per-turn context into the session-scoped state.

func (*ToolContext) PutExtra added in v1.118.0

func (c *ToolContext) PutExtra(key string, value any)

PutExtra stores a value in the Extra bag, allocating the map on first use.

func (*ToolContext) RecordFileRead added in v1.118.0

func (c *ToolContext) RecordFileRead(path string)

RecordFileRead appends a path to FilesRead, deduping. Called by tools that want to surface a read to the orchestrator's tracking layer (file-staleness, undo, audit) without owning a reference to it.

func (*ToolContext) RecordFileWrite added in v1.118.0

func (c *ToolContext) RecordFileWrite(path string)

RecordFileWrite appends a path to FilesWritten, deduping.

func (*ToolContext) RecordHostContacted added in v1.118.0

func (c *ToolContext) RecordHostContacted(host string)

RecordHostContacted appends a host to HostsContacted, deduping.

type ToolResult added in v1.118.0

type ToolResult struct {
	// Output is the human/model-readable content. For Anthropic this maps
	// to the tool_result `content` field; for OpenAI it goes into the
	// `tool` message body.
	Output string

	// IsError signals that the tool encountered a business-level failure
	// (the command exited non-zero, the URL returned 4xx, the file was
	// not found). When true, provider adapters set the relevant
	// is_error / [ERROR:<code>] marker so the model knows it's a failure
	// without parsing the body.
	IsError bool

	// ErrorCode is the stable, locale-independent classification. Empty
	// when IsError is false. Examples: "ENOENT", "EACCES", "Timeout",
	// "Canceled", "ExitCode:2", "NetworkError", "UnknownError". Filled
	// by ClassifyError (Fase 5.1) when the plugin returns a Go error;
	// plugins that hand-craft a business error must set this themselves
	// so the dashboard / log fields stay stable.
	ErrorCode string

	// NewMessages lets a tool emit additional conversation entries
	// alongside its result — typically system-role hints
	// ("output truncated to first 5000 chars") or assistant-role
	// auto-suggestions. The orchestrator appends them to history in
	// the order returned; ordering across concurrent tools is undefined
	// (so concurrent tools should not rely on NewMessages for ordering).
	NewMessages []models.Message

	// ContextMutation is an optional callback applied serially by the
	// orchestrator after the entire batch completes. Used to register
	// side effects that need to survive across turns: "I just wrote
	// /tmp/foo, add it to the allowlist", "I just read main.go, mark
	// it as recently touched". Returning nil means "no mutation".
	ContextMutation func(ctx *ToolContext)

	// MCPMeta carries provider-agnostic structured metadata that some
	// adapters can attach to the wire result. Anthropic's tool_result
	// supports a `_meta` field; other providers ignore it. Use sparingly
	// — most use cases are better served by NewMessages or telemetry.
	MCPMeta map[string]any

	// Duration is the wall-clock time the tool took to run, recorded by
	// the orchestrator and exposed here so plugins can include it in
	// telemetry/logs without re-measuring.
	Duration time.Duration
}

ToolResult is the structured outcome of a tool invocation, decoupled from the wire format used to ship results back to the LLM. Each provider adapter (claudeai, openai, googleai, …) translates this into its own representation in cli/llm/<provider>/tool_result_adapter.go; the orchestrator never branches on provider type.

Compared to the legacy `(string, error)` return shape, ToolResult lets a plugin:

  • Distinguish business errors from infrastructure errors (IsError vs the returned `error`). Business errors stay inside the conversation as tool_result with is_error=true; infrastructure errors abort the batch.
  • Tag the error class with a stable code (ENOENT, Timeout, …) so the model can reason about retryability without parsing English.
  • Emit additional conversational messages alongside the result (e.g. a warning that the output was truncated, a hint that a different tool would be more efficient).
  • Mutate the orchestrator's per-turn context — record that a file was read, register a new allowed path, push an undo handle. The mutator runs serially after the batch completes so concurrent tools don't race on the same context object.
  • Pass MCP-style structured metadata through to providers that understand it (Anthropic mcp_meta) without leaking provider specifics into plugin code.

func RunBatch added in v1.118.0

func RunBatch(ctx context.Context, batch ToolBatch, exec ExecuteFunc, opts BatchOptions) ([]ToolResult, error)

RunBatch executes the calls in batch with the given policy. The returned slice is index-aligned with batch.Calls — entry i corresponds to call i, preserving order even when the batch ran in parallel. An error is returned only for infrastructure failures that aborted the batch; per- tool business errors live inside ToolResult.IsError.

Concurrency semantics:

  • Concurrent batch: errgroup with semaphore-bounded fan-out. Each goroutine populates its slot; the main goroutine waits for Wait(). CancelSiblings=true means the first infra error cancels the batch ctx so the others observe context.Canceled and bail.
  • Serial batch: linear for-loop with the same ctx (no errgroup overhead). Errors cause an early return preserving prior results.

The function never panics — a panic in the callback is recovered and surfaced as an error result.

func WrapLegacyOutput added in v1.118.0

func WrapLegacyOutput(output string, err error) ToolResult

WrapLegacyOutput builds a ToolResult from the legacy (string, error) shape used by every plugin that hasn't migrated to ExecuteStructured. The error is mapped through ClassifyErrorCode to fill ErrorCode.

This is the bridge that lets the orchestrator drive every plugin — legacy or new — through the same ToolResult-shaped pipeline.

func WrapStructuredResult added in v1.118.0

func WrapStructuredResult(sr structuredCarrier, infraErr error) ToolResult

WrapStructuredResult elevates a plugins.StructuredResult into the agent-level ToolResult shape, filling ErrorCode from the infrastructure error when the plugin didn't set it itself. The Duration field is set by the orchestrator after this call; callers do not need to fill it.

type UIRenderer

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

UIRenderer gerencia a renderização da interface do modo agente

func NewUIRenderer

func NewUIRenderer(logger *zap.Logger) *UIRenderer

NewUIRenderer cria uma nova instância do renderizador de UI; o estilo é detectado a partir do ambiente. Para testes ou para forçar um estilo, use NewUIRendererWithStyle.

func NewUIRendererWithStyle added in v1.119.0

func NewUIRendererWithStyle(logger *zap.Logger, style UIStyle) *UIRenderer

NewUIRendererWithStyle constrói um renderer com estilo explícito. Usado por testes e por callers que precisam forçar um estilo independentemente do ambiente (ex: relatórios offline).

func (*UIRenderer) ClearScreen

func (r *UIRenderer) ClearScreen()

ClearScreen limpa a tela (se permitido)

func (*UIRenderer) Colorize

func (r *UIRenderer) Colorize(text string, color string) string

Colorize aplica cores ANSI (exportada com C maiúsculo). O resultado passa por theme.Recolor para que os códigos básicos legados (ColorCyan, ColorYellow, …) adotem a hue do tema ativo sob o profile de cor atual — toda a UI re-tematiza sem alterar os call sites, e a saída fica limpa (sem ANSI) quando não há terminal colorido (pipe/CI).

func (*UIRenderer) CompactAssistantText added in v1.118.1

func (r *UIRenderer) CompactAssistantText(text string)

CompactAssistantText renders the assistant's free-form text in the compact timeline. Distinct from CompactLine: the text uses the terminal's default foreground (ColorReset) so the assistant's actual answer stands out from the surrounding gray tool prose. Without this, in coder-compact mode the answer was visually indistinguishable from "Read(main.go)" lines and tool result excerpts — all the same ColorGray weight. Multi-line answers are wrapped, preserving the timeline indentation and dropping empty lines at the edges.

func (*UIRenderer) CompactBatchSummary added in v1.97.0

func (r *UIRenderer) CompactBatchSummary(successCount, total int, hasError bool)

CompactBatchSummary renders a one-line batch summary.

✓ 4/4 ações concluídas
✗ 2/4 ações concluídas (com erros)

func (*UIRenderer) CompactError added in v1.97.0

func (r *UIRenderer) CompactError(msg string)

CompactError renders an error inline.

✗ BLOCKED  Ação negada pelo usuário

func (*UIRenderer) CompactLine added in v1.97.0

func (r *UIRenderer) CompactLine(icon, label, text string, color string)

CompactLine renders a single inline status line (no card/box). Used for reasoning, explanations, errors, summaries in compact mode.

● PLANO  Ler main.go, modificar handler, atualizar testes
✗ ERRO   Arquivo não encontrado
✓ OK     2 arquivos modificados

func (*UIRenderer) CompactMultiLine added in v1.97.0

func (r *UIRenderer) CompactMultiLine(icon, label, text string, color string, maxLines int)

CompactMultiLine renders a compact block: icon + label on first line, then indented content lines (max N lines). For reasoning/plan display.

● PLANO
  1. Ler main.go
  2. Modificar handleRequest
  3. Atualizar testes

func (*UIRenderer) CompactToolDone added in v1.97.0

func (r *UIRenderer) CompactToolDone(toolLabel string, duration string, isError bool)

CompactToolDone renders a completed tool call in compact format:

✓ Read(main.go) 1.2s

Tool label in cyan to match CompactToolStart; the green check + cyan duration already mark this as a result so the label color reinforces the tool identity rather than competing with the success signal.

func (*UIRenderer) CompactToolStart added in v1.97.0

func (r *UIRenderer) CompactToolStart(toolLabel string)

CompactToolStart renders a tool call start in compact format:

↻ Read(main.go)

Tool label in cyan (was gray) so the tool identity stands out from the surrounding plain-gray prose. Without this, a "↻ Read(main.go)" looked identical to a free-text note in the timeline.

func (*UIRenderer) EchoUserInput added in v1.118.1

func (r *UIRenderer) EchoUserInput(text string)

EchoUserInput re-prints a line the user just submitted at the coder- mode interactive prompt, in green with a ❯ marker. The kernel echo during line-editing is uncolored, and once the line is committed it scrolls into history alongside gray tool lines and gray reasoning summaries — making it hard to tell at a glance where the user's instruction was. This persistent echo gives the user's directives a distinct visual lane.

func (*UIRenderer) IsCompact added in v1.119.0

func (r *UIRenderer) IsCompact() bool

IsCompact reports whether tool calls render as one-line ↻/✓ entries.

func (*UIRenderer) IsFull added in v1.119.0

func (r *UIRenderer) IsFull() bool

IsFull reports whether tool calls render as full bordered cards.

func (*UIRenderer) IsMinimal added in v1.119.0

func (r *UIRenderer) IsMinimal() bool

IsMinimal reports whether tool calls render as boxed-but-truncated cards.

func (*UIRenderer) PrintHeader

func (r *UIRenderer) PrintHeader()

PrintHeader imprime o cabeçalho do modo agente: uma régua titulada responsiva (hierarquia título-bold + traços dim) no lugar das duas linhas de ━ com 58 colunas fixas.

func (*UIRenderer) PrintLastResult

func (r *UIRenderer) PrintLastResult(outputs []*CommandOutput, lastIdx int)

PrintLastResult imprime o último resultado

func (*UIRenderer) PrintMenu

func (r *UIRenderer) PrintMenu()

PrintMenu renders the agent-mode action menu in three vertical columns grouped by intent (Execution / Edit & Context / View). The previous single-column layout produced a 12-row wall of `[1..N]: …` entries that scrolled past the user's tool output every turn — the columnar layout fits the same information in 6 rows while still keeping the key + description pairing readable.

Columns are built with lipgloss.JoinHorizontal so a long description in any column does not push the other columns out of alignment.

func (*UIRenderer) PrintPlanCompact

func (r *UIRenderer) PrintPlanCompact(blocks []CommandBlock, outputs []*CommandOutput)

PrintPlanCompact imprime plano em formato compacto

func (*UIRenderer) PrintPlanFull

func (r *UIRenderer) PrintPlanFull(blocks []CommandBlock, outputs []*CommandOutput, validator *CommandValidator)

PrintPlanFull imprime plano em formato completo

func (*UIRenderer) PrintPrompt

func (r *UIRenderer) PrintPrompt() string

PrintPrompt imprime o prompt de entrada

func (*UIRenderer) RenderAssistantResponseTimelineEvent added in v1.119.1

func (r *UIRenderer) RenderAssistantResponseTimelineEvent(icon, title, content, color string)

RenderAssistantResponseTimelineEvent draws the same card as RenderTimelineEvent but types the body progressively so the final assistant message feels alive instead of a single paste. Reserved for the model's "RESPOSTA/RESUMO" card — tool calls and reasoning still use the instant path because typing those would slow the agent loop down.

The body is glamour-rendered markdown, so it wraps with wrapStructured (not wrapText): replies that embed YAML/JSON/code keep their indentation instead of collapsing flush-left.

func (*UIRenderer) RenderBatchHeader added in v1.45.0

func (r *UIRenderer) RenderBatchHeader(totalActions int)

RenderBatchHeader exibe um cabeçalho indicando o início de um lote — uma régua titulada responsiva no lugar do sanduíche de ═ em tela cheia.

func (*UIRenderer) RenderBatchSummary added in v1.45.0

func (r *UIRenderer) RenderBatchSummary(successCount, total int, hasError bool)

RenderBatchSummary exibe o resultado final do lote

func (*UIRenderer) RenderMarkdownTimelineEvent added in v1.43.7

func (r *UIRenderer) RenderMarkdownTimelineEvent(icon, title, renderedMarkdownANSI, color string)

RenderMarkdownTimelineEvent renderiza markdown (já convertido para ANSI fora) dentro do card. Usa wrapStructured (não o wrapText de RenderTimelineEvent) para que YAML/JSON/código embutidos no markdown preservem a indentação dentro do card — o mesmo fix aplicado ao envelope de chat.

func (*UIRenderer) RenderModeBanner added in v1.119.0

func (r *UIRenderer) RenderModeBanner(icon, title string, color string, fields [][2]string)

RenderModeBanner draws the entry-banner used by /coder and /agent. Layout:

╭── 🛠  CODER MODE ─────────────────────────────╮
│  Objective  · <query>                         │
│  Workspace  · <wd>                            │
│  Policy     · read-only por padrão · …        │
╰───────────────────────────────────────────────╯

Fields is a slice of (label, value) pairs so callers can pass any mode-specific metadata without growing the function signature. The label is dimmed (gray) and the value rendered in the default foreground so the eye lands on the value first.

func (*UIRenderer) RenderResponseEnvelope added in v1.119.2

func (r *UIRenderer) RenderResponseEnvelope(opts ResponseEnvelopeOptions)

RenderResponseEnvelope paints the assistant's reply: titled rule with the bilateral labels, indented body (wrapStructured preserves the indentation of glamour-rendered YAML/JSON/code), dim telemetry footer.

func (*UIRenderer) RenderStreamBoxEnd added in v1.47.8

func (r *UIRenderer) RenderStreamBoxEnd(color string)

RenderStreamBoxEnd closes the streamed section with a blank separator (the sóbrio treatment has no frame to mirror).

func (*UIRenderer) RenderStreamBoxStart added in v1.47.8

func (r *UIRenderer) RenderStreamBoxStart(icon, title, color string)

streamBoxHeaderWidth captures the visible width of the header drawn by RenderStreamBoxStart so RenderStreamBoxEnd can produce a footer of the same length instead of stretching to the terminal edge. It is package- level because the start/end pair runs on the same goroutine inside a tool-execution loop; concurrent streaming boxes are not supported in this renderer, so a sync.Mutex would be ceremonial overhead. RenderStreamBoxStart opens a streamed section in the sóbrio treatment: a colored bold title line; the streamed output indents beneath it via StreamOutput. No frame to close, so RenderStreamBoxEnd only restores breathing room.

func (*UIRenderer) RenderThinking added in v1.40.0

func (r *UIRenderer) RenderThinking(thought string)

RenderThinking exibe o pensamento da IA

func (*UIRenderer) RenderTimelineEvent added in v1.40.0

func (r *UIRenderer) RenderTimelineEvent(icon, title, content, color string)

RenderTimelineEvent desenha um "card" estilizado com:

  • cabeçalho `╭── icon title ─────╮` que se estende até a largura do conteúdo,
  • bordas laterais `│ … │` em cada linha,
  • rodapé `╰──────────────────╯` do TAMANHO DO CONTEÚDO (não da tela).

Antes, o rodapé ia até a borda direita do terminal, dando uma sensação de "vazamento" quando o conteúdo era pequeno. A nova versão calcula a largura alvo como o maior entre (a) maior linha de conteúdo + padding e (b) largura do header — limitada pelo terminal. Lipgloss faz o cálculo de largura visível corretamente (ANSI-aware) via lipgloss.Width.

func (*UIRenderer) RenderToolCall added in v1.40.0

func (r *UIRenderer) RenderToolCall(toolName, rawArgs string)

RenderToolCall exibe a chamada da ferramenta de forma limpa (escondendo Base64 e sujeira HTML)

func (*UIRenderer) RenderToolCallMinimal added in v1.52.0

func (r *UIRenderer) RenderToolCallMinimal(toolName, rawArgs string, current, total int)

RenderToolCallMinimal exibe a chamada de ferramenta em modo compacto

func (*UIRenderer) RenderToolCallWithProgress added in v1.45.0

func (r *UIRenderer) RenderToolCallWithProgress(toolName, rawArgs string, current, total int)

RenderToolCallWithProgress exibe a chamada da ferramenta em formato de CARD (Box), limpando barras invertidas visuais e mostrando o progresso.

func (*UIRenderer) RenderToolResult added in v1.40.0

func (r *UIRenderer) RenderToolResult(output string, isError bool)

RenderToolResult exibe o resultado da execução. O glifo do título vem do vocabulário canônico (✓/✗, 1 célula, sem propriedade Emoji) e herda a cor da borda do card — verde/vermelho continuam carregando o estado.

func (*UIRenderer) RenderToolResultMinimal added in v1.52.0

func (r *UIRenderer) RenderToolResultMinimal(output string, isError bool)

RenderToolResultMinimal exibe o resultado em modo compacto

func (*UIRenderer) SetSkipClearOnNextDraw

func (r *UIRenderer) SetSkipClearOnNextDraw(skip bool)

SetSkipClearOnNextDraw define se o próximo clear deve ser pulado

func (*UIRenderer) ShowInPager

func (r *UIRenderer) ShowInPager(text string) error

ShowInPager abre texto em pager (less/more)

func (*UIRenderer) StreamOutput added in v1.47.8

func (r *UIRenderer) StreamOutput(line string)

StreamOutput emite uma linha de output streamado sob o título da seção (sóbrio: indentação em vez de barra lateral).

func (*UIRenderer) Style added in v1.119.0

func (r *UIRenderer) Style() UIStyle

Style returns the resolved UI style for this renderer.

type UIStyle added in v1.119.0

type UIStyle int

UIStyle selects how the renderer dispatches tool-call output across the timeline. The same enum drives both /coder and /agent paths; the env var CHATCLI_CODER_UI feeds it (legacy name kept on purpose — it now controls both modes, see DefaultUIStyleFromEnv).

const (
	// UIStyleFull renders every tool call inside a bordered card,
	// every reasoning block in its own panel. Best for supervised
	// /agent runs where the user reviews each action.
	UIStyleFull UIStyle = iota
	// UIStyleCompact renders one-line tool calls (↻/✓) so a long
	// /coder session with dozens of tool invocations stays scannable.
	UIStyleCompact
	// UIStyleMinimal renders boxed tool calls with truncated reasoning.
	// Sits between Full and Compact: lighter than Full, more context
	// than Compact.
	UIStyleMinimal
)

func DefaultUIStyleFromEnv added in v1.119.0

func DefaultUIStyleFromEnv() UIStyle

DefaultUIStyleFromEnv reads CHATCLI_CODER_UI and maps it to a UIStyle. Unset / "full" / "false" / "0" → Full. "compact" → Compact. "minimal" / "min" / "true" / "1" → Minimal. Legacy: "compact" used to imply Minimal in some call sites — kept as Compact here because that matches the explicit user intent of typing "compact".

func (UIStyle) String added in v1.119.0

func (s UIStyle) String() string

type ValidationResult added in v1.66.0

type ValidationResult struct {
	Allowed     bool
	Reason      string
	Severity    string
	Suggestions []string
	MatchedRule string
}

ValidationResult is the detailed result of command validation.

Directories

Path Synopsis
* ChatCLI - AskUser request/answer types and parsing.
* ChatCLI - AskUser request/answer types and parsing.
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* Package park: durable snapshots for the agent ReAct loop.
* Package park: durable snapshots for the agent ReAct loop.
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
convergence
* ChatCLI - Convergence: per-scorer circuit breaker.
* ChatCLI - Convergence: per-scorer circuit breaker.
lessonq
* ChatCLI - Lesson Queue: idempotency key derivation.
* ChatCLI - Lesson Queue: idempotency key derivation.
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Command Line Interface for LLM interaction * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0
* ChatCLI - Builtin agent model/effort metadata * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Shared base struct providing Model() and Effort() for built-in workers.
* ChatCLI - Builtin agent model/effort metadata * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Shared base struct providing Model() and Effort() for built-in workers.

Jump to

Keyboard shortcuts

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