agent

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Path-validation helpers for the mutating filesystem tools (write_file, edit_file, mkdir, copy_file, move_file, delete_file). Three layers of defense:

  1. Cwd confinement — paths must resolve under the session's cwd, or under one of the user-provided AllowedPaths roots.
  2. Symlink rejection — symlinked write targets are refused unless the user explicitly opts in. Stops the "symlink to /etc/passwd" trick.
  3. Deny list — yottacode's own state directories (sessions, auto/, permissions.json, permissions.local.json) and git-internal paths are off-limits regardless of approval. Self-grants and memory injection don't go through the tool surface.

Reads run through a narrower validator (ValidateReadPath) gated by a targeted deny list of credential-bearing paths (DefaultDenyReadPaths). The agent legitimately reads dotfiles, USER.md, /etc/os-release, etc., so the read deny list is targeted — well-known credential locations only — instead of a blanket cwd-confinement. Reading credentials directly into the model's context (and from there into the upstream provider's logs) is a silent exfiltration vector via prompt injection; run_bash is the escape hatch for the rare case the user really wants the contents, because run_bash always prompts.

Index

Constants

View Source
const DefaultSystemPrompt = `` /* 3739-byte string literal not displayed */

DefaultSystemPrompt is the agent identity prompt sent to the model at the start of every session. It declares yottacode's tool surface and the action discipline the model should follow.

Both the TUI (`internal/tui/run.go`) and the non-interactive runner (`internal/oneshot/oneshot.go`) consume this single constant. Historically each kept its own copy of the string and the two drifted — the TUI gained guidance about choosing between edit_file / apply_diff / write_file (and a longer rule on read_many_files) that oneshot never got. One source of truth retires that drift class; a regression test in TestDefaultSystemPrompt_NamesEveryRegisteredTool keeps the tool list honest.

Memory injection (USER.md / YOTTACODE.md / memory tools) wraps this prompt downstream — see internal/memory/memory.go SystemPrompt for the "background — do not narrate" framing that gets layered on.

Variables

This section is empty.

Functions

func DefaultDenyPaths

func DefaultDenyPaths(cwd string) []string

func DefaultDenyReadPaths

func DefaultDenyReadPaths(cwd string) []string

DefaultDenyReadPaths returns the hardcoded list of paths the agent's auto-execute read tools (read_file, read_many_files, grep) refuse to touch. Targeted at well-known credential stores; intentionally narrow so the model can still read dotfiles, /etc/os-release, USER.md, and other benign system files.

  • ~/.yottacode/.env — the agent's own provider keys. Reading this into context exfiltrates the active session's API key to the upstream provider on the next turn.
  • ~/.yottacode/auth/ — OAuth bearer + refresh tokens for the openai-auth provider. Same exfiltration risk as .env, plus the refresh token grants long-lived access. Whole directory denied so future per-provider auth files inherit the protection.
  • ~/.ssh/, ~/.gnupg/ — private key material.
  • ~/.aws/{credentials,config}, ~/.config/gcloud/ — cloud provider access.
  • ~/.netrc — HTTP basic-auth credentials.
  • ~/.config/gh/hosts.yml, ~/.config/hub — GitHub tokens.
  • ~/.docker/config.json — registry tokens.
  • ~/.kube/config — cluster credentials.
  • <cwd>/.env, <cwd>/.env.local — project secrets, the most common accidental-exfiltration target.

Power users who need the model to read one of these can bypass at the OS layer (cat the contents into a non-denied file first) or via run_bash, which prompts. Listing more paths is cheap; the cost is false-positive blocks on benign reads. Keep the list to files universally understood as secrets.

func Turn

func Turn(
	ctx context.Context,
	cfg LoopConfig,
	history *[]adapter.Message,
	events chan<- Event,
	decisions <-chan Decision,
) error

Turn drives one user-initiated round: it streams an assistant response (emitting Reasoning/Content tokens), dispatches any tool calls (with approval flow if required), feeds the results back, and loops until the assistant produces a tool-free reply or hits MaxIterations.

events is producer-only; Turn never closes it (caller owns lifecycle). decisions is consumer-only; Turn reads from it only after emitting an ApprovalNeeded event. Cancel ctx to abort cleanly.

history is mutated in place: user message is assumed already appended by the caller; Turn appends each assistant reply and tool result.

func ValidateReadPath

func ValidateReadPath(path string, deny []string) error

DefaultDenyPaths returns the hardcoded list of paths the agent's mutating filesystem tools must refuse to write to. Includes:

  • User-scope yottacode state under ~/.yottacode/ (sessions, memory/, projects/, auth/, index.sqlite, USER.md). The agent has supported pathways for the memory dirs (memory_save / memory_forget); the generic write_file / edit_file surface must not be a back door. USER.md is global preferences — the agent's project-scope view doesn't have enough signal to curate cross-project preferences.
  • Project-scope yottacode state under <cwd>/.yottacode/ (permissions.json, permissions.local.json). The permissions files are the user's policy surface — letting the model edit them via tools would let it self-grant approval. The /permissions slash command and the user's editor are the only legitimate write paths.
  • Git internals: .git/HEAD, .git/config, .git/index, .git/refs/, .git/packed-refs, .git/objects/. These define repo state; writes here should go through `git` commands, not direct filesystem manipulation. .git/hooks/ is deliberately NOT in the list — model authoring of hooks is a legitimate task.

YOTTACODE.md is deliberately NOT in the deny list. It's the project-scope context file the agent reads on every turn, and keeping it fresh requires writes — same role CLAUDE.md plays for Claude Code. The approval modal still gates every write, so the user sees each change before it lands.

Bypass is not possible via flags. Power users can edit these files themselves with their editor; the model just can't via tools. ValidateReadPath returns nil if the read of path is permitted under the given deny list, or a descriptive error. Targeted at silent exfiltration of credential-bearing files via read_file / read_many_files / grep — tools whose RequiresApproval is false. The user can still read these files via run_bash, which always prompts.

func ValidateWritePath

func ValidateWritePath(path string, opts WritePathOptions) error

ValidateWritePath returns nil if a write to path is permitted under the given options, or a descriptive error. Validation order matters: deny list checked first (so even a path inside cwd can be refused), then symlink check, then containment check against cwd / allowed roots.

Types

type ApplyDiffTool

type ApplyDiffTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

func (*ApplyDiffTool) Description

func (t *ApplyDiffTool) Description() string

func (*ApplyDiffTool) Execute

func (t *ApplyDiffTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*ApplyDiffTool) Name

func (t *ApplyDiffTool) Name() string

func (*ApplyDiffTool) PreviewCall

func (t *ApplyDiffTool) PreviewCall(argsJSON string) string

func (*ApplyDiffTool) RequiresApproval

func (t *ApplyDiffTool) RequiresApproval(string) bool

func (*ApplyDiffTool) Schema

func (t *ApplyDiffTool) Schema() map[string]any

type ApprovalAuto

type ApprovalAuto struct {
	ToolName string
	Preview  string
	Source   string
}

ApprovalAuto is logged when the loop auto-approves (or auto-denies) a tool call without asking the user. Source identifies which gate fired: "permissions" (matched an allow rule), "deny-rule" (matched a deny rule, no execution), or "bypass-permissions" (--bypass-permissions flag is set and no rule matched).

type ApprovalNeeded

type ApprovalNeeded struct {
	ToolName string
	Preview  string
	ArgsJSON string
}

ApprovalNeeded is the request half of an approval round-trip. The loop blocks on the decisions channel until the consumer replies. ArgsJSON is the raw tool args so the consumer can render richer previews — e.g. the TUI shows a colored diff for edit_file by parsing old_string/new_string.

type AssistantMessage

type AssistantMessage struct{ Message adapter.Message }

AssistantMessage fires once a streamed assistant response is finalized, just before its tool_calls (if any) are dispatched. Includes the same Message that gets appended to history; useful for consumers that want to react to a complete reply (e.g., TUI message-list rendering).

type CommandSegment

type CommandSegment struct {
	Text      string // the segment, trimmed
	Separator string // "" for first segment; "&&", "||", ";", "|" thereafter
	Risk      Risk
	Reason    string // human-readable why this is flagged, "" when RiskNone
}

CommandSegment is one piece of a (possibly) compound shell command, separated from the next segment by a logical operator or pipe. The separator that *precedes* this segment is recorded so the modal can label "and then" vs "or" vs "piped to" relationships.

func SplitCommand

func SplitCommand(cmd string) []CommandSegment

SplitCommand parses a shell command into segments separated by &&, ||, ;, and pipes. Quoted metacharacters (`"foo && bar"`) and escaped ones (`\&\&`) are ignored. Command substitutions ($(...) and `...`) are NOT recursively split; the whole substitution stays as part of its enclosing segment with a "contains substitution" caution flag if the substitution is non-trivial.

The output is suitable for display in the approval modal — not for execution semantics. We're trying to surface what a tired human might miss, not reproduce a real shell parser.

type ContentToken

type ContentToken struct{ Text string }

ContentToken carries one chunk of the assistant's actual reply. Render in normal style.

type CopyFileTool

type CopyFileTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

func (*CopyFileTool) Description

func (t *CopyFileTool) Description() string

func (*CopyFileTool) Execute

func (t *CopyFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*CopyFileTool) Name

func (t *CopyFileTool) Name() string

func (*CopyFileTool) PreviewCall

func (t *CopyFileTool) PreviewCall(argsJSON string) string

func (*CopyFileTool) RequiresApproval

func (t *CopyFileTool) RequiresApproval(string) bool

func (*CopyFileTool) Schema

func (t *CopyFileTool) Schema() map[string]any

type Decision

type Decision int

Decision is the verdict the consumer sends back when the loop emits an ApprovalNeeded event. AllowAlways writes a derived rule to the project-local .yottacode/permissions.local.json via the permissions.Permissions value passed in LoopConfig.

const (
	// Deny refuses this single call and reports "denied by user" to
	// the model so it can recover.
	Deny Decision = iota
	// AllowOnce permits this single call. No persistence.
	AllowOnce
	// AllowAlways permits this call and asks the loop to derive a
	// pattern from it (via permissions.DeriveAllowRule) and append it
	// to permissions.local.json so future matching calls are silent.
	// The TUI suppresses this option for cases where derivation isn't
	// safe (compound shell commands, dangerous verbs).
	AllowAlways
)

type DeleteFileTool

type DeleteFileTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

func (*DeleteFileTool) Description

func (t *DeleteFileTool) Description() string

func (*DeleteFileTool) Execute

func (t *DeleteFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*DeleteFileTool) Name

func (t *DeleteFileTool) Name() string

func (*DeleteFileTool) PreviewCall

func (t *DeleteFileTool) PreviewCall(argsJSON string) string

func (*DeleteFileTool) RequiresApproval

func (t *DeleteFileTool) RequiresApproval(string) bool

func (*DeleteFileTool) Schema

func (t *DeleteFileTool) Schema() map[string]any

type EditFileTool

type EditFileTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

EditFileTool performs a surgical replacement inside an existing file. Strictly better than write_file for code edits: it preserves the rest of the file and refuses to apply a non-unique match unless replace_all is set, which catches stale assumptions before they corrupt code.

func (*EditFileTool) Description

func (t *EditFileTool) Description() string

func (*EditFileTool) Execute

func (t *EditFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*EditFileTool) Name

func (t *EditFileTool) Name() string

func (*EditFileTool) PreviewCall

func (t *EditFileTool) PreviewCall(argsJSON string) string

func (*EditFileTool) RequiresApproval

func (t *EditFileTool) RequiresApproval(string) bool

func (*EditFileTool) Schema

func (t *EditFileTool) Schema() map[string]any

type ErrorEvent

type ErrorEvent struct{ Err error }

ErrorEvent fires when the turn terminates because of an error (adapter failure, ctx cancel, etc.). The error is also returned by Turn.

type Event

type Event interface {
	// contains filtered or unexported methods
}

Event is the union of things the agent loop emits while a turn runs. Consumers (REPL today, TUI next, `yottacode run` after that) type-switch on the concrete value.

Channel ownership: the *caller* owns the events channel and is responsible for closing it; Turn never closes it. Use a buffered channel (~64) so the loop doesn't block when the consumer is briefly busy.

Approval flow: when a tool requires approval and policy doesn't pre-approve (a matching allow rule in permissions.json, or --bypass-permissions), the loop emits ApprovalNeeded and blocks on a receive from the decisions channel. The consumer must reply with a Decision or cancel ctx.

type Fallback

type Fallback struct {
	From   string
	To     string
	Reason string
	Policy string
}

Fallback fires when the multi-provider router falls through from one candidate to another after an early failure (an error before any tokens streamed). Carries enough metadata for the TUI to render a loud "↻ fallback: A → B (reason)" line — silent fallback is the failure mode the router design is built to avoid.

type FetchURLTool

type FetchURLTool struct{}

FetchURLTool retrieves a single URL over HTTP(S) and returns capped textual content. This is the local-network fallback for models that do not have provider-native web search.

func (*FetchURLTool) Description

func (t *FetchURLTool) Description() string

func (*FetchURLTool) Execute

func (t *FetchURLTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*FetchURLTool) Name

func (t *FetchURLTool) Name() string

func (*FetchURLTool) ParallelSafe

func (t *FetchURLTool) ParallelSafe(string) bool

func (*FetchURLTool) PreviewCall

func (t *FetchURLTool) PreviewCall(argsJSON string) string

func (*FetchURLTool) RequiresApproval

func (t *FetchURLTool) RequiresApproval(string) bool

func (*FetchURLTool) Schema

func (t *FetchURLTool) Schema() map[string]any

type GitBlameLinesTool

type GitBlameLinesTool struct{ Cwd string }

func (*GitBlameLinesTool) Description

func (t *GitBlameLinesTool) Description() string

func (*GitBlameLinesTool) Execute

func (t *GitBlameLinesTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitBlameLinesTool) Name

func (t *GitBlameLinesTool) Name() string

func (*GitBlameLinesTool) ParallelSafe

func (t *GitBlameLinesTool) ParallelSafe(string) bool

func (*GitBlameLinesTool) PreviewCall

func (t *GitBlameLinesTool) PreviewCall(argsJSON string) string

func (*GitBlameLinesTool) RequiresApproval

func (t *GitBlameLinesTool) RequiresApproval(string) bool

func (*GitBlameLinesTool) Schema

func (t *GitBlameLinesTool) Schema() map[string]any

type GitBranchStatusTool

type GitBranchStatusTool struct{ Cwd string }

func (*GitBranchStatusTool) Description

func (t *GitBranchStatusTool) Description() string

func (*GitBranchStatusTool) Execute

func (t *GitBranchStatusTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitBranchStatusTool) Name

func (t *GitBranchStatusTool) Name() string

func (*GitBranchStatusTool) ParallelSafe

func (t *GitBranchStatusTool) ParallelSafe(string) bool

func (*GitBranchStatusTool) PreviewCall

func (t *GitBranchStatusTool) PreviewCall(string) string

func (*GitBranchStatusTool) RequiresApproval

func (t *GitBranchStatusTool) RequiresApproval(string) bool

func (*GitBranchStatusTool) Schema

func (t *GitBranchStatusTool) Schema() map[string]any

type GitCheckpointTool

type GitCheckpointTool struct{ Cwd string }

func (*GitCheckpointTool) Description

func (t *GitCheckpointTool) Description() string

func (*GitCheckpointTool) Execute

func (t *GitCheckpointTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitCheckpointTool) Name

func (t *GitCheckpointTool) Name() string

func (*GitCheckpointTool) PreviewCall

func (t *GitCheckpointTool) PreviewCall(argsJSON string) string

func (*GitCheckpointTool) RequiresApproval

func (t *GitCheckpointTool) RequiresApproval(string) bool

func (*GitCheckpointTool) Schema

func (t *GitCheckpointTool) Schema() map[string]any

type GitCommitTool

type GitCommitTool struct{ Cwd string }

func (*GitCommitTool) Description

func (t *GitCommitTool) Description() string

func (*GitCommitTool) Execute

func (t *GitCommitTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitCommitTool) Name

func (t *GitCommitTool) Name() string

func (*GitCommitTool) PreviewCall

func (t *GitCommitTool) PreviewCall(argsJSON string) string

func (*GitCommitTool) RequiresApproval

func (t *GitCommitTool) RequiresApproval(string) bool

func (*GitCommitTool) Schema

func (t *GitCommitTool) Schema() map[string]any

type GitDiffFilesTool

type GitDiffFilesTool struct{ Cwd string }

func (*GitDiffFilesTool) Description

func (t *GitDiffFilesTool) Description() string

func (*GitDiffFilesTool) Execute

func (t *GitDiffFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitDiffFilesTool) Name

func (t *GitDiffFilesTool) Name() string

func (*GitDiffFilesTool) ParallelSafe

func (t *GitDiffFilesTool) ParallelSafe(string) bool

func (*GitDiffFilesTool) PreviewCall

func (t *GitDiffFilesTool) PreviewCall(argsJSON string) string

func (*GitDiffFilesTool) RequiresApproval

func (t *GitDiffFilesTool) RequiresApproval(string) bool

func (*GitDiffFilesTool) Schema

func (t *GitDiffFilesTool) Schema() map[string]any

type GitLogFileTool

type GitLogFileTool struct{ Cwd string }

func (*GitLogFileTool) Description

func (t *GitLogFileTool) Description() string

func (*GitLogFileTool) Execute

func (t *GitLogFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitLogFileTool) Name

func (t *GitLogFileTool) Name() string

func (*GitLogFileTool) ParallelSafe

func (t *GitLogFileTool) ParallelSafe(string) bool

func (*GitLogFileTool) PreviewCall

func (t *GitLogFileTool) PreviewCall(argsJSON string) string

func (*GitLogFileTool) RequiresApproval

func (t *GitLogFileTool) RequiresApproval(string) bool

func (*GitLogFileTool) Schema

func (t *GitLogFileTool) Schema() map[string]any

type GitMergeBaseTool

type GitMergeBaseTool struct{ Cwd string }

func (*GitMergeBaseTool) Description

func (t *GitMergeBaseTool) Description() string

func (*GitMergeBaseTool) Execute

func (t *GitMergeBaseTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitMergeBaseTool) Name

func (t *GitMergeBaseTool) Name() string

func (*GitMergeBaseTool) ParallelSafe

func (t *GitMergeBaseTool) ParallelSafe(string) bool

func (*GitMergeBaseTool) PreviewCall

func (t *GitMergeBaseTool) PreviewCall(argsJSON string) string

func (*GitMergeBaseTool) RequiresApproval

func (t *GitMergeBaseTool) RequiresApproval(string) bool

func (*GitMergeBaseTool) Schema

func (t *GitMergeBaseTool) Schema() map[string]any

type GitShowFileAtRevTool

type GitShowFileAtRevTool struct{ Cwd string }

func (*GitShowFileAtRevTool) Description

func (t *GitShowFileAtRevTool) Description() string

func (*GitShowFileAtRevTool) Execute

func (t *GitShowFileAtRevTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitShowFileAtRevTool) Name

func (t *GitShowFileAtRevTool) Name() string

func (*GitShowFileAtRevTool) ParallelSafe

func (t *GitShowFileAtRevTool) ParallelSafe(string) bool

func (*GitShowFileAtRevTool) PreviewCall

func (t *GitShowFileAtRevTool) PreviewCall(argsJSON string) string

func (*GitShowFileAtRevTool) RequiresApproval

func (t *GitShowFileAtRevTool) RequiresApproval(string) bool

func (*GitShowFileAtRevTool) Schema

func (t *GitShowFileAtRevTool) Schema() map[string]any

type GitStageFilesTool

type GitStageFilesTool struct{ Cwd string }

func (*GitStageFilesTool) Description

func (t *GitStageFilesTool) Description() string

func (*GitStageFilesTool) Execute

func (t *GitStageFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitStageFilesTool) Name

func (t *GitStageFilesTool) Name() string

func (*GitStageFilesTool) PreviewCall

func (t *GitStageFilesTool) PreviewCall(argsJSON string) string

func (*GitStageFilesTool) RequiresApproval

func (t *GitStageFilesTool) RequiresApproval(string) bool

func (*GitStageFilesTool) Schema

func (t *GitStageFilesTool) Schema() map[string]any

type GitTool

type GitTool struct {
	Cwd string
}

GitTool is the unified entrypoint for every git command. The model passes argv-style tokens (no shell), and approval policy is decided by inspecting the first arg.

func (*GitTool) Description

func (t *GitTool) Description() string

func (*GitTool) Execute

func (t *GitTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitTool) Name

func (t *GitTool) Name() string

func (*GitTool) PreviewCall

func (t *GitTool) PreviewCall(argsJSON string) string

func (*GitTool) RequiresApproval

func (t *GitTool) RequiresApproval(argsJSON string) bool

func (*GitTool) Schema

func (t *GitTool) Schema() map[string]any

type GitUnstageFilesTool

type GitUnstageFilesTool struct{ Cwd string }

func (*GitUnstageFilesTool) Description

func (t *GitUnstageFilesTool) Description() string

func (*GitUnstageFilesTool) Execute

func (t *GitUnstageFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GitUnstageFilesTool) Name

func (t *GitUnstageFilesTool) Name() string

func (*GitUnstageFilesTool) PreviewCall

func (t *GitUnstageFilesTool) PreviewCall(argsJSON string) string

func (*GitUnstageFilesTool) RequiresApproval

func (t *GitUnstageFilesTool) RequiresApproval(string) bool

func (*GitUnstageFilesTool) Schema

func (t *GitUnstageFilesTool) Schema() map[string]any

type GlobTool

type GlobTool struct {
	Cwd string
}

GlobTool finds files matching a doublestar pattern (e.g., "**/*.go").

func (*GlobTool) Description

func (t *GlobTool) Description() string

func (*GlobTool) Execute

func (t *GlobTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GlobTool) Name

func (t *GlobTool) Name() string

func (*GlobTool) ParallelSafe

func (t *GlobTool) ParallelSafe(string) bool

func (*GlobTool) PreviewCall

func (t *GlobTool) PreviewCall(argsJSON string) string

func (*GlobTool) RequiresApproval

func (t *GlobTool) RequiresApproval(string) bool

func (*GlobTool) Schema

func (t *GlobTool) Schema() map[string]any

type GrepTool

type GrepTool struct {
	Cwd           string
	DenyReadPaths []string
}

GrepTool searches files for a pattern. Uses ripgrep if available, otherwise falls back to GNU grep. Args are passed via argv (no /bin/sh) so the model can't inject shell metacharacters. When the user supplies an explicit path, it's validated against DenyReadPaths so a targeted grep can't extract secrets line-by-line.

func (*GrepTool) Description

func (t *GrepTool) Description() string

func (*GrepTool) Execute

func (t *GrepTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*GrepTool) Name

func (t *GrepTool) Name() string

func (*GrepTool) ParallelSafe

func (t *GrepTool) ParallelSafe(string) bool

func (*GrepTool) PreviewCall

func (t *GrepTool) PreviewCall(argsJSON string) string

func (*GrepTool) RequiresApproval

func (t *GrepTool) RequiresApproval(string) bool

func (*GrepTool) Schema

func (t *GrepTool) Schema() map[string]any

type IterCap

type IterCap struct{ Max int }

IterCap fires when the loop hits MaxIterations without a final assistant reply. The turn ends after this event.

type IterationContinue

type IterationContinue struct {
	Number    int
	Reason    string
	ToolCalls int
}

IterationContinue explains why the loop is going around again after an iteration completes.

type IterationStart

type IterationStart struct {
	Number int
	Max    int
}

IterationStart fires when a new model->tools->model loop iteration begins. Number is 1-based.

type ListDirTool

type ListDirTool struct {
	Cwd string
}

ListDirTool returns the immediate children of a directory.

func (*ListDirTool) Description

func (t *ListDirTool) Description() string

func (*ListDirTool) Execute

func (t *ListDirTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*ListDirTool) Name

func (t *ListDirTool) Name() string

func (*ListDirTool) ParallelSafe

func (t *ListDirTool) ParallelSafe(string) bool

func (*ListDirTool) PreviewCall

func (t *ListDirTool) PreviewCall(argsJSON string) string

func (*ListDirTool) RequiresApproval

func (t *ListDirTool) RequiresApproval(string) bool

func (*ListDirTool) Schema

func (t *ListDirTool) Schema() map[string]any

type ListGitChangedFilesTool

type ListGitChangedFilesTool struct{ Cwd string }

func (*ListGitChangedFilesTool) Description

func (t *ListGitChangedFilesTool) Description() string

func (*ListGitChangedFilesTool) Execute

func (t *ListGitChangedFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*ListGitChangedFilesTool) Name

func (t *ListGitChangedFilesTool) Name() string

func (*ListGitChangedFilesTool) ParallelSafe

func (t *ListGitChangedFilesTool) ParallelSafe(string) bool

func (*ListGitChangedFilesTool) PreviewCall

func (t *ListGitChangedFilesTool) PreviewCall(argsJSON string) string

func (*ListGitChangedFilesTool) RequiresApproval

func (t *ListGitChangedFilesTool) RequiresApproval(string) bool

func (*ListGitChangedFilesTool) Schema

func (t *ListGitChangedFilesTool) Schema() map[string]any

type ListProjectStructureTool

type ListProjectStructureTool struct {
	Cwd string
}

ListProjectStructureTool returns a bounded tree view of files and directories with sizes and last-modified timestamps. Designed as the "survey first" tool: the model can scan structure once and choose what to read with read_many_files instead of reading exploratorily.

func (*ListProjectStructureTool) Description

func (t *ListProjectStructureTool) Description() string

func (*ListProjectStructureTool) Execute

func (t *ListProjectStructureTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*ListProjectStructureTool) Name

func (t *ListProjectStructureTool) Name() string

func (*ListProjectStructureTool) ParallelSafe

func (t *ListProjectStructureTool) ParallelSafe(string) bool

func (*ListProjectStructureTool) PreviewCall

func (t *ListProjectStructureTool) PreviewCall(argsJSON string) string

func (*ListProjectStructureTool) RequiresApproval

func (t *ListProjectStructureTool) RequiresApproval(string) bool

func (*ListProjectStructureTool) Schema

func (t *ListProjectStructureTool) Schema() map[string]any

type LoopConfig

type LoopConfig struct {
	Adapter  Streamer
	Registry *Registry
	// Permissions gates every tool call against project-local rules
	// loaded from .yottacode/permissions.json (committable) and
	// .yottacode/permissions.local.json (gitignored). Optional: nil
	// disables rule matching and falls through to the tool's own
	// RequiresApproval policy.
	Permissions *permissions.Permissions
	// BypassPermissions is the renamed --yolo: skip every approval
	// prompt, run silently. DANGEROUS — model-emitted commands
	// execute without a human in the loop. Explicit `deny` rules in
	// permissions.json still refuse the call (bypass is "skip
	// prompts," not "ignore my policy"). Use only in trusted CI /
	// scripted contexts.
	BypassPermissions bool
	Cwd               string
	MaxIterations     int
}

LoopConfig is the value-typed configuration for one or more agent turns. Channels are passed separately to Turn so the same config can drive a streaming session across many turns without rewiring.

type MemoryForgetTool

type MemoryForgetTool struct {
	Cwd string
}

MemoryForgetTool deletes a memory file and regenerates the scope's MEMORY.md index. Errors cleanly when the named memory does not exist — the agent can use that signal to learn the right names.

func (*MemoryForgetTool) Description

func (t *MemoryForgetTool) Description() string

func (*MemoryForgetTool) Execute

func (t *MemoryForgetTool) Execute(_ context.Context, argsJSON string) (string, error)

func (*MemoryForgetTool) Name

func (t *MemoryForgetTool) Name() string

func (*MemoryForgetTool) ParallelSafe

func (t *MemoryForgetTool) ParallelSafe(string) bool

func (*MemoryForgetTool) PreviewCall

func (t *MemoryForgetTool) PreviewCall(argsJSON string) string

func (*MemoryForgetTool) RequiresApproval

func (t *MemoryForgetTool) RequiresApproval(string) bool

func (*MemoryForgetTool) Schema

func (t *MemoryForgetTool) Schema() map[string]any

type MemorySaveTool

type MemorySaveTool struct {
	Cwd string
}

MemorySaveTool persists a typed memory file under either the user-scope (~/.yottacode/memory/) or project-scope (~/.yottacode/projects/<slug>/memory/) directory and refreshes the MEMORY.md index for that scope. Replaces the post-turn extractor — the agent now decides in-band when something is worth remembering.

func (*MemorySaveTool) Description

func (t *MemorySaveTool) Description() string

func (*MemorySaveTool) Execute

func (t *MemorySaveTool) Execute(_ context.Context, argsJSON string) (string, error)

func (*MemorySaveTool) Name

func (t *MemorySaveTool) Name() string

func (*MemorySaveTool) ParallelSafe

func (t *MemorySaveTool) ParallelSafe(string) bool

func (*MemorySaveTool) PreviewCall

func (t *MemorySaveTool) PreviewCall(argsJSON string) string

func (*MemorySaveTool) RequiresApproval

func (t *MemorySaveTool) RequiresApproval(string) bool

func (*MemorySaveTool) Schema

func (t *MemorySaveTool) Schema() map[string]any

type MkdirTool

type MkdirTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

func (*MkdirTool) Description

func (t *MkdirTool) Description() string

func (*MkdirTool) Execute

func (t *MkdirTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*MkdirTool) Name

func (t *MkdirTool) Name() string

func (*MkdirTool) PreviewCall

func (t *MkdirTool) PreviewCall(argsJSON string) string

func (*MkdirTool) RequiresApproval

func (t *MkdirTool) RequiresApproval(string) bool

func (*MkdirTool) Schema

func (t *MkdirTool) Schema() map[string]any

type MoveFileTool

type MoveFileTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

func (*MoveFileTool) Description

func (t *MoveFileTool) Description() string

func (*MoveFileTool) Execute

func (t *MoveFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*MoveFileTool) Name

func (t *MoveFileTool) Name() string

func (*MoveFileTool) PreviewCall

func (t *MoveFileTool) PreviewCall(argsJSON string) string

func (*MoveFileTool) RequiresApproval

func (t *MoveFileTool) RequiresApproval(string) bool

func (*MoveFileTool) Schema

func (t *MoveFileTool) Schema() map[string]any

type ParallelSafeTool

type ParallelSafeTool interface {
	ParallelSafe(argsJSON string) bool
}

ParallelSafeTool is an optional capability marker for tools that can run concurrently with other read-only tool calls from the same assistant message. Keep this narrow and explicit: a false negative only costs some latency, but a false positive can create hard-to-debug races.

type ProviderToolCall

type ProviderToolCall struct {
	ToolName string
	Phase    string
	Detail   string
}

ProviderToolCall carries a provider-native tool lifecycle update emitted by the adapter stream itself, e.g. OpenAI/xAI web search or code interpreter.

type ReadFileTool

type ReadFileTool struct {
	Cwd           string
	DenyReadPaths []string
}

ReadFileTool lets the model fetch local file contents. Read-only, no approval. DenyReadPaths blocks a small set of credential-bearing locations (see DefaultDenyReadPaths) so prompt injection can't silently exfiltrate keys; everything else is fair game.

func (*ReadFileTool) Description

func (t *ReadFileTool) Description() string

func (*ReadFileTool) Execute

func (t *ReadFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*ReadFileTool) Name

func (t *ReadFileTool) Name() string

func (*ReadFileTool) ParallelSafe

func (t *ReadFileTool) ParallelSafe(string) bool

func (*ReadFileTool) PreviewCall

func (t *ReadFileTool) PreviewCall(argsJSON string) string

func (*ReadFileTool) RequiresApproval

func (t *ReadFileTool) RequiresApproval(string) bool

func (*ReadFileTool) Schema

func (t *ReadFileTool) Schema() map[string]any

type ReadManyFilesTool

type ReadManyFilesTool struct {
	Cwd           string
	DenyReadPaths []string
}

func (*ReadManyFilesTool) Description

func (t *ReadManyFilesTool) Description() string

func (*ReadManyFilesTool) Execute

func (t *ReadManyFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*ReadManyFilesTool) Name

func (t *ReadManyFilesTool) Name() string

func (*ReadManyFilesTool) ParallelSafe

func (t *ReadManyFilesTool) ParallelSafe(string) bool

func (*ReadManyFilesTool) PreviewCall

func (t *ReadManyFilesTool) PreviewCall(argsJSON string) string

func (*ReadManyFilesTool) RequiresApproval

func (t *ReadManyFilesTool) RequiresApproval(string) bool

func (*ReadManyFilesTool) Schema

func (t *ReadManyFilesTool) Schema() map[string]any

type ReasoningToken

type ReasoningToken struct{ Text string }

ReasoningToken carries one chunk of "thinking" output from a reasoning model (Qwen 3, DeepSeek R1). Render dimmed.

type Registry

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

Registry owns the set of tools exposed to a given agent run.

func NewRegistry

func NewRegistry() *Registry

func (*Registry) AsAdapterTools

func (r *Registry) AsAdapterTools() []adapter.Tool

AsAdapterTools converts the registry into the schema shape the adapter advertises to the model.

func (*Registry) Get

func (r *Registry) Get(name string) (Tool, bool)

func (*Registry) Register

func (r *Registry) Register(t Tool)

type Risk

type Risk int

Risk classifies how dangerous a command segment looks at a glance. Used by the approval modal to color-code parts of a compound command so users can see destructive segments without parsing the whole line.

const (
	RiskNone Risk = iota
	RiskCaution
	RiskDestructive
)

func AssessRisk

func AssessRisk(segment string) (Risk, string)

AssessRisk classifies a single segment. Returns RiskNone for boring commands; RiskCaution for things worth a glance; RiskDestructive for patterns that almost always end in tears if mistakenly approved. The reason string is human-readable for display next to the segment.

func (Risk) String

func (r Risk) String() string

type RollbackTool

type RollbackTool struct{ Cwd string }

func (*RollbackTool) Description

func (t *RollbackTool) Description() string

func (*RollbackTool) Execute

func (t *RollbackTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*RollbackTool) Name

func (t *RollbackTool) Name() string

func (*RollbackTool) PreviewCall

func (t *RollbackTool) PreviewCall(argsJSON string) string

func (*RollbackTool) RequiresApproval

func (t *RollbackTool) RequiresApproval(string) bool

func (*RollbackTool) Schema

func (t *RollbackTool) Schema() map[string]any

type RunBashTool

type RunBashTool struct {
	Cwd string
}

RunBashTool runs a shell command in cwd via /bin/sh -c. Always requires approval. There is no sandbox today; for real isolation, run yottacode itself inside a container.

func (*RunBashTool) Description

func (t *RunBashTool) Description() string

func (*RunBashTool) Execute

func (t *RunBashTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*RunBashTool) Name

func (t *RunBashTool) Name() string

func (*RunBashTool) PreviewCall

func (t *RunBashTool) PreviewCall(argsJSON string) string

func (*RunBashTool) RequiresApproval

func (t *RunBashTool) RequiresApproval(string) bool

func (*RunBashTool) Schema

func (t *RunBashTool) Schema() map[string]any

type RunTestsTool

type RunTestsTool struct{ Cwd string }

func (*RunTestsTool) Description

func (t *RunTestsTool) Description() string

func (*RunTestsTool) Execute

func (t *RunTestsTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*RunTestsTool) Name

func (t *RunTestsTool) Name() string

func (*RunTestsTool) PreviewCall

func (t *RunTestsTool) PreviewCall(argsJSON string) string

func (*RunTestsTool) RequiresApproval

func (t *RunTestsTool) RequiresApproval(string) bool

func (*RunTestsTool) Schema

func (t *RunTestsTool) Schema() map[string]any

type StreamProgress

type StreamProgress struct{}

StreamProgress is a heartbeat for adapter-side stream activity that has no visible text — currently emitted by the OpenAI Responses adapter on each `function_call_arguments.delta`. The TUI uses this to keep the live "tok/s" indicator moving on turns that produce only a tool call (no reasoning summary, no body text) so the row doesn't sit at "0.0 tok/s" the whole time.

type Streamer

type Streamer interface {
	ChatStream(ctx context.Context, messages []adapter.Message, tools []adapter.Tool) <-chan adapter.StreamEvent
}

Streamer is the slice of the adapter the loop actually depends on. Defining it here (instead of pulling in the concrete *adapter.Adapter) lets tests substitute a scripted implementation without standing up an HTTP server.

type Tool

type Tool interface {
	Name() string
	Description() string
	Schema() map[string]any
	RequiresApproval(argsJSON string) bool
	PreviewCall(argsJSON string) string
	Execute(ctx context.Context, argsJSON string) (string, error)
}

Tool is one capability the agent can invoke. Execute receives the raw JSON arguments the model emitted; tools parse them internally so the registry stays schema-agnostic.

RequiresApproval takes the argsJSON because some tools (e.g. the unified git tool) decide policy based on the specific subcommand: `git status` auto-executes, `git push --force` prompts. Tools that don't care about args may ignore the parameter.

type ToolResult

type ToolResult struct {
	ToolName string
	Output   string
	Errored  bool
}

ToolResult fires after the tool finishes. Output is the string the model will see; Errored signals whether it was a tool-level error vs. success.

type ToolStart

type ToolStart struct {
	ToolName string
	Preview  string
	ArgsJSON string
}

ToolStart fires immediately before a tool's Execute is called (after any approval flow has resolved). The consumer can render this as a status line. ArgsJSON carries the raw tool-call arguments so consumers can do structured rendering (e.g., the TUI's edit_file diff card) without parsing the human-friendly Preview string.

type TurnDone

type TurnDone struct{}

TurnDone fires when the turn completes cleanly (no more tool calls, no error, not at iter cap). The consumer can re-enable input.

type WriteFileTool

type WriteFileTool struct {
	Cwd       string
	WriteOpts WritePathOptions
}

WriteFileTool creates or overwrites a file. Always needs approval; the WriteOpts validator pre-rejects out-of-cwd, symlinked, or deny-listed paths *before* the approval modal opens, so the model can't trick a distracted user into approving a misleading path.

func (*WriteFileTool) Description

func (t *WriteFileTool) Description() string

func (*WriteFileTool) Execute

func (t *WriteFileTool) Execute(ctx context.Context, argsJSON string) (string, error)

func (*WriteFileTool) Name

func (t *WriteFileTool) Name() string

func (*WriteFileTool) PreviewCall

func (t *WriteFileTool) PreviewCall(argsJSON string) string

func (*WriteFileTool) RequiresApproval

func (t *WriteFileTool) RequiresApproval(string) bool

func (*WriteFileTool) Schema

func (t *WriteFileTool) Schema() map[string]any

type WritePathOptions

type WritePathOptions struct {
	// Cwd is the primary allowed root. Required.
	Cwd string

	// AllowedPaths is the list of additional roots a user has opted into
	// via --allow-paths or YOTTACODE_ALLOW_PATHS. Each entry is treated
	// as an absolute root the model is allowed to write under.
	AllowedPaths []string

	// DenyExact is a list of absolute paths (or path prefixes) the model
	// must never write to. Populated from DefaultDenyPaths(cwd) at
	// registration time. Always wins, even if a path otherwise matches
	// Cwd or AllowedPaths.
	DenyExact []string

	// AllowSymlinks lets the validator follow symlinks on write paths.
	// Default false — symlinks are a known exfil vector.
	AllowSymlinks bool
}

WritePathOptions configures the validator for a single tool. Build it once per session in run.go and share across every mutating tool.

Jump to

Keyboard shortcuts

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