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:
- Cwd confinement — paths must resolve under the session's cwd, or under one of the user-provided AllowedPaths roots.
- Symlink rejection — symlinked write targets are refused unless the user explicitly opts in. Stops the "symlink to /etc/passwd" trick.
- 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
- func CheckpointFromContext(ctx context.Context) (sessionID, cpID string)
- func DefaultDenyPaths(cwd string) []string
- func DefaultDenyReadPaths(cwd string) []string
- func IsAutoModeSafetyFloor(toolName string) bool
- func IsPlanFileWrite(name, argsJSON, planFile string) bool
- func ParentDecisions(ctx context.Context) <-chan Decision
- func ParentEvents(ctx context.Context) chan<- Event
- func PlanFilePath(slug string) (string, error)
- func PlanModeGate(tool Tool, argsJSON, planFile string) (string, bool)
- func PlansDir() (string, error)
- func SlugFromPrompt(prompt, salt string) string
- func ToolPathsToSnapshot(t Tool, cwd, argsJSON string) []string
- func Turn(ctx context.Context, cfg LoopConfig, history *[]adapter.Message, ...) error
- func ValidateReadPath(path string, deny []string) error
- func ValidateWritePath(path string, opts WritePathOptions) error
- func WithCheckpoint(ctx context.Context, sessionID, cpID string) context.Context
- func WithParentDecisions(ctx context.Context, decisions <-chan Decision) context.Context
- func WithParentEvents(ctx context.Context, events chan<- Event) context.Context
- type AgentTool
- func (t *AgentTool) AgentConfigs() []subagents.AgentConfig
- func (t *AgentTool) Description() string
- func (t *AgentTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *AgentTool) Name() string
- func (t *AgentTool) ParallelSafe(string) bool
- func (t *AgentTool) PreviewCall(argsJSON string) string
- func (t *AgentTool) RequiresApproval(string) bool
- func (t *AgentTool) Schema() map[string]any
- func (t *AgentTool) SetBackgroundDoneCallback(fn func(SubagentBackgroundDone))
- type ApplyDiffTool
- func (t *ApplyDiffTool) Description() string
- func (t *ApplyDiffTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *ApplyDiffTool) Name() string
- func (t *ApplyDiffTool) PathsToSnapshot(cwd, argsJSON string) []string
- func (t *ApplyDiffTool) PreviewCall(argsJSON string) string
- func (t *ApplyDiffTool) RequiresApproval(string) bool
- func (t *ApplyDiffTool) Schema() map[string]any
- type ApprovalAuto
- type ApprovalNeeded
- type AssistantMessage
- type AutoModeState
- type CheckpointInfo
- type CheckpointWriter
- type CommandSegment
- type ContentToken
- type CopyFileTool
- func (t *CopyFileTool) Description() string
- func (t *CopyFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *CopyFileTool) Name() string
- func (t *CopyFileTool) PathsToSnapshot(cwd, argsJSON string) []string
- func (t *CopyFileTool) PreviewCall(argsJSON string) string
- func (t *CopyFileTool) RequiresApproval(string) bool
- func (t *CopyFileTool) Schema() map[string]any
- type Decision
- type DeleteFileTool
- func (t *DeleteFileTool) Description() string
- func (t *DeleteFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *DeleteFileTool) Name() string
- func (t *DeleteFileTool) PathsToSnapshot(cwd, argsJSON string) []string
- func (t *DeleteFileTool) PreviewCall(argsJSON string) string
- func (t *DeleteFileTool) RequiresApproval(string) bool
- func (t *DeleteFileTool) Schema() map[string]any
- type EditFileTool
- func (t *EditFileTool) Description() string
- func (t *EditFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *EditFileTool) Name() string
- func (t *EditFileTool) PathsToSnapshot(cwd, argsJSON string) []string
- func (t *EditFileTool) PreviewCall(argsJSON string) string
- func (t *EditFileTool) RequiresApproval(string) bool
- func (t *EditFileTool) Schema() map[string]any
- type ErrorEvent
- type Event
- type ExitPlanModeTool
- func (t *ExitPlanModeTool) Description() string
- func (t *ExitPlanModeTool) Execute(_ context.Context, _ string) (string, error)
- func (t *ExitPlanModeTool) Name() string
- func (t *ExitPlanModeTool) PreviewCall(string) string
- func (t *ExitPlanModeTool) RequiresApproval(string) bool
- func (t *ExitPlanModeTool) Schema() map[string]any
- type Fallback
- type FetchURLTool
- func (t *FetchURLTool) Description() string
- func (t *FetchURLTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *FetchURLTool) Name() string
- func (t *FetchURLTool) ParallelSafe(string) bool
- func (t *FetchURLTool) PreviewCall(argsJSON string) string
- func (t *FetchURLTool) RequiresApproval(string) bool
- func (t *FetchURLTool) Schema() map[string]any
- type GetSubagentResultTool
- func (t *GetSubagentResultTool) Description() string
- func (t *GetSubagentResultTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GetSubagentResultTool) Name() string
- func (t *GetSubagentResultTool) ParallelSafe(string) bool
- func (t *GetSubagentResultTool) PreviewCall(argsJSON string) string
- func (t *GetSubagentResultTool) RequiresApproval(string) bool
- func (t *GetSubagentResultTool) Schema() map[string]any
- type GitBlameLinesTool
- func (t *GitBlameLinesTool) Description() string
- func (t *GitBlameLinesTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitBlameLinesTool) Name() string
- func (t *GitBlameLinesTool) ParallelSafe(string) bool
- func (t *GitBlameLinesTool) PreviewCall(argsJSON string) string
- func (t *GitBlameLinesTool) RequiresApproval(string) bool
- func (t *GitBlameLinesTool) Schema() map[string]any
- type GitBranchStatusTool
- func (t *GitBranchStatusTool) Description() string
- func (t *GitBranchStatusTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitBranchStatusTool) Name() string
- func (t *GitBranchStatusTool) ParallelSafe(string) bool
- func (t *GitBranchStatusTool) PreviewCall(string) string
- func (t *GitBranchStatusTool) RequiresApproval(string) bool
- func (t *GitBranchStatusTool) Schema() map[string]any
- type GitCheckpointTool
- func (t *GitCheckpointTool) Description() string
- func (t *GitCheckpointTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitCheckpointTool) Name() string
- func (t *GitCheckpointTool) PreviewCall(argsJSON string) string
- func (t *GitCheckpointTool) RequiresApproval(string) bool
- func (t *GitCheckpointTool) Schema() map[string]any
- type GitCommitTool
- func (t *GitCommitTool) Description() string
- func (t *GitCommitTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitCommitTool) Name() string
- func (t *GitCommitTool) PreviewCall(argsJSON string) string
- func (t *GitCommitTool) RequiresApproval(string) bool
- func (t *GitCommitTool) Schema() map[string]any
- type GitDiffFilesTool
- func (t *GitDiffFilesTool) Description() string
- func (t *GitDiffFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitDiffFilesTool) Name() string
- func (t *GitDiffFilesTool) ParallelSafe(string) bool
- func (t *GitDiffFilesTool) PreviewCall(argsJSON string) string
- func (t *GitDiffFilesTool) RequiresApproval(string) bool
- func (t *GitDiffFilesTool) Schema() map[string]any
- type GitLogFileTool
- func (t *GitLogFileTool) Description() string
- func (t *GitLogFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitLogFileTool) Name() string
- func (t *GitLogFileTool) ParallelSafe(string) bool
- func (t *GitLogFileTool) PreviewCall(argsJSON string) string
- func (t *GitLogFileTool) RequiresApproval(string) bool
- func (t *GitLogFileTool) Schema() map[string]any
- type GitMergeBaseTool
- func (t *GitMergeBaseTool) Description() string
- func (t *GitMergeBaseTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitMergeBaseTool) Name() string
- func (t *GitMergeBaseTool) ParallelSafe(string) bool
- func (t *GitMergeBaseTool) PreviewCall(argsJSON string) string
- func (t *GitMergeBaseTool) RequiresApproval(string) bool
- func (t *GitMergeBaseTool) Schema() map[string]any
- type GitShowFileAtRevTool
- func (t *GitShowFileAtRevTool) Description() string
- func (t *GitShowFileAtRevTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitShowFileAtRevTool) Name() string
- func (t *GitShowFileAtRevTool) ParallelSafe(string) bool
- func (t *GitShowFileAtRevTool) PreviewCall(argsJSON string) string
- func (t *GitShowFileAtRevTool) RequiresApproval(string) bool
- func (t *GitShowFileAtRevTool) Schema() map[string]any
- type GitStageFilesTool
- func (t *GitStageFilesTool) Description() string
- func (t *GitStageFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitStageFilesTool) Name() string
- func (t *GitStageFilesTool) PreviewCall(argsJSON string) string
- func (t *GitStageFilesTool) RequiresApproval(string) bool
- func (t *GitStageFilesTool) Schema() map[string]any
- type GitTool
- func (t *GitTool) Description() string
- func (t *GitTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitTool) Name() string
- func (t *GitTool) PreviewCall(argsJSON string) string
- func (t *GitTool) RequiresApproval(argsJSON string) bool
- func (t *GitTool) Schema() map[string]any
- type GitUnstageFilesTool
- func (t *GitUnstageFilesTool) Description() string
- func (t *GitUnstageFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GitUnstageFilesTool) Name() string
- func (t *GitUnstageFilesTool) PreviewCall(argsJSON string) string
- func (t *GitUnstageFilesTool) RequiresApproval(string) bool
- func (t *GitUnstageFilesTool) Schema() map[string]any
- type GlobTool
- func (t *GlobTool) Description() string
- func (t *GlobTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GlobTool) Name() string
- func (t *GlobTool) ParallelSafe(string) bool
- func (t *GlobTool) PreviewCall(argsJSON string) string
- func (t *GlobTool) RequiresApproval(string) bool
- func (t *GlobTool) Schema() map[string]any
- type GrepTool
- func (t *GrepTool) Description() string
- func (t *GrepTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *GrepTool) Name() string
- func (t *GrepTool) ParallelSafe(string) bool
- func (t *GrepTool) PreviewCall(argsJSON string) string
- func (t *GrepTool) RequiresApproval(string) bool
- func (t *GrepTool) Schema() map[string]any
- type IterCap
- type IterationContinue
- type IterationStart
- type ListDirTool
- func (t *ListDirTool) Description() string
- func (t *ListDirTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *ListDirTool) Name() string
- func (t *ListDirTool) ParallelSafe(string) bool
- func (t *ListDirTool) PreviewCall(argsJSON string) string
- func (t *ListDirTool) RequiresApproval(string) bool
- func (t *ListDirTool) Schema() map[string]any
- type ListGitChangedFilesTool
- func (t *ListGitChangedFilesTool) Description() string
- func (t *ListGitChangedFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *ListGitChangedFilesTool) Name() string
- func (t *ListGitChangedFilesTool) ParallelSafe(string) bool
- func (t *ListGitChangedFilesTool) PreviewCall(argsJSON string) string
- func (t *ListGitChangedFilesTool) RequiresApproval(string) bool
- func (t *ListGitChangedFilesTool) Schema() map[string]any
- type ListProjectStructureTool
- func (t *ListProjectStructureTool) Description() string
- func (t *ListProjectStructureTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *ListProjectStructureTool) Name() string
- func (t *ListProjectStructureTool) ParallelSafe(string) bool
- func (t *ListProjectStructureTool) PreviewCall(argsJSON string) string
- func (t *ListProjectStructureTool) RequiresApproval(string) bool
- func (t *ListProjectStructureTool) Schema() map[string]any
- type LoopConfig
- type MemoryForgetTool
- func (t *MemoryForgetTool) Description() string
- func (t *MemoryForgetTool) Execute(_ context.Context, argsJSON string) (string, error)
- func (t *MemoryForgetTool) Name() string
- func (t *MemoryForgetTool) ParallelSafe(string) bool
- func (t *MemoryForgetTool) PreviewCall(argsJSON string) string
- func (t *MemoryForgetTool) RequiresApproval(string) bool
- func (t *MemoryForgetTool) Schema() map[string]any
- type MemorySaveTool
- func (t *MemorySaveTool) Description() string
- func (t *MemorySaveTool) Execute(_ context.Context, argsJSON string) (string, error)
- func (t *MemorySaveTool) Name() string
- func (t *MemorySaveTool) ParallelSafe(string) bool
- func (t *MemorySaveTool) PreviewCall(argsJSON string) string
- func (t *MemorySaveTool) RequiresApproval(string) bool
- func (t *MemorySaveTool) Schema() map[string]any
- type MkdirTool
- func (t *MkdirTool) Description() string
- func (t *MkdirTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *MkdirTool) Name() string
- func (t *MkdirTool) PreviewCall(argsJSON string) string
- func (t *MkdirTool) RequiresApproval(string) bool
- func (t *MkdirTool) Schema() map[string]any
- type MoveFileTool
- func (t *MoveFileTool) Description() string
- func (t *MoveFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *MoveFileTool) Name() string
- func (t *MoveFileTool) PathsToSnapshot(cwd, argsJSON string) []string
- func (t *MoveFileTool) PreviewCall(argsJSON string) string
- func (t *MoveFileTool) RequiresApproval(string) bool
- func (t *MoveFileTool) Schema() map[string]any
- type Mutator
- type ParallelSafeTool
- type PlanEntry
- type PlanModeState
- type PlanStore
- type ProviderToolCall
- type ReadFileTool
- func (t *ReadFileTool) Description() string
- func (t *ReadFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *ReadFileTool) Name() string
- func (t *ReadFileTool) ParallelSafe(string) bool
- func (t *ReadFileTool) PreviewCall(argsJSON string) string
- func (t *ReadFileTool) RequiresApproval(string) bool
- func (t *ReadFileTool) Schema() map[string]any
- type ReadManyFilesTool
- func (t *ReadManyFilesTool) Description() string
- func (t *ReadManyFilesTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *ReadManyFilesTool) Name() string
- func (t *ReadManyFilesTool) ParallelSafe(string) bool
- func (t *ReadManyFilesTool) PreviewCall(argsJSON string) string
- func (t *ReadManyFilesTool) RequiresApproval(string) bool
- func (t *ReadManyFilesTool) Schema() map[string]any
- type ReasoningToken
- type Registry
- func (r *Registry) AsAdapterTools() []adapter.Tool
- func (r *Registry) AsAdapterToolsFiltered(filter func(name string) bool) []adapter.Tool
- func (r *Registry) Get(name string) (Tool, bool)
- func (r *Registry) Names() map[string]bool
- func (r *Registry) Register(t Tool)
- func (r *Registry) Tools() []Tool
- type Risk
- type RollbackTool
- func (t *RollbackTool) Description() string
- func (t *RollbackTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *RollbackTool) Name() string
- func (t *RollbackTool) PreviewCall(argsJSON string) string
- func (t *RollbackTool) RequiresApproval(string) bool
- func (t *RollbackTool) Schema() map[string]any
- type RunBashTool
- func (t *RunBashTool) Description() string
- func (t *RunBashTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *RunBashTool) Name() string
- func (t *RunBashTool) PreviewCall(argsJSON string) string
- func (t *RunBashTool) RequiresApproval(string) bool
- func (t *RunBashTool) Schema() map[string]any
- type RunTestsTool
- func (t *RunTestsTool) Description() string
- func (t *RunTestsTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *RunTestsTool) Name() string
- func (t *RunTestsTool) PreviewCall(argsJSON string) string
- func (t *RunTestsTool) RequiresApproval(string) bool
- func (t *RunTestsTool) Schema() map[string]any
- type StreamProgress
- type Streamer
- type SubagentBackgroundDone
- type SubagentDone
- type SubagentProgress
- type SubagentStart
- type Todo
- type TodoStatus
- type TodoUpdate
- type TodoWriteTool
- func (t *TodoWriteTool) Description() string
- func (t *TodoWriteTool) Execute(_ context.Context, argsJSON string) (string, error)
- func (t *TodoWriteTool) Name() string
- func (t *TodoWriteTool) PlanStore() *PlanStore
- func (t *TodoWriteTool) PreviewCall(argsJSON string) string
- func (t *TodoWriteTool) RequiresApproval(string) bool
- func (t *TodoWriteTool) Schema() map[string]any
- type Tool
- type ToolResult
- type ToolStart
- type TurnDone
- type TurnInterrupted
- type WriteFileTool
- func (t *WriteFileTool) Description() string
- func (t *WriteFileTool) Execute(ctx context.Context, argsJSON string) (string, error)
- func (t *WriteFileTool) Name() string
- func (t *WriteFileTool) PathsToSnapshot(cwd, argsJSON string) []string
- func (t *WriteFileTool) PreviewCall(argsJSON string) string
- func (t *WriteFileTool) RequiresApproval(string) bool
- func (t *WriteFileTool) Schema() map[string]any
- type WritePathOptions
- type YoloModeState
Constants ¶
const AgentToolName = "Agent"
AgentToolName is the schema-visible name of the subagent dispatch tool. Capital-A mirrors Claude Code's surface ("Agent"/"Task"). The name is referenced in several places (recursion guard, plan-mode gate exemption checks, TUI tool-card suppression) so it lives as a const here. Exported so consumers in internal/tui can compare against it without hardcoding the string.
const DefaultSystemPrompt = `` /* 7955-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.
const ExitPlanModeRefusalMessage = "User chose to keep planning. " +
"END THIS TURN NOW with a brief one-sentence question asking what they'd like to change about the plan. " +
"Do NOT call exit_plan_mode again in this turn. " +
"Do NOT edit the plan file in this turn. " +
"Do NOT call any other tools in this turn. " +
"Wait for the user's next message before doing anything else. " +
"After they respond with feedback, you may revise the plan file and call exit_plan_mode again on the following turn."
ExitPlanModeRefusalMessage is what the loop returns to the model when the user picks [K] / Keep planning at the plan-approval card. Phrased firmly so the model STOPS THE TURN and waits for user feedback. Without the firmness, models read "call exit_plan_mode again when ready" and immediately re-call without changing the plan — looping the approval card forever. The user pressed [K] because they have something to say; the model must yield the turn so they can say it.
const ExitPlanModeSavedForLaterMessage = "user approved the plan but is saving it for implementation later. " +
"The plan file is preserved on disk; the user will resume via /plan list when ready. " +
"END THIS TURN NOW. Do NOT call any more tools. Do NOT implement any part of the plan. " +
"Do NOT suggest next steps. Reply with a one-sentence acknowledgement and stop."
ExitPlanModeSavedForLaterMessage is what the loop returns to the model when the user picks [L] / approve and implement later. The plan is good but the user isn't starting work now — they'll resume via /plan list or --plan-resume in a future session. The message is phrased firmly to make the model END THE TURN: no more tool calls, no implementation, no "next steps" prose. The user will re-initiate when they're ready.
const MaxBackgroundSubagents = 8
MaxBackgroundSubagents caps how many background subagents may be running concurrently per session. Hit the cap → the tool rejects the call with a recoverable error message the model can adapt around. Foreground subagents are unbounded (they serialize on the parent's call stack anyway). 8 is a round number that matches what most users informally do — enough for genuine parallelism, low enough to keep API spend bounded if a model gets enthusiastic.
const PlanModeAddendum = `` /* 5572-byte string literal not displayed */
PlanModeAddendum is the per-iteration system message appended on top of DefaultSystemPrompt when LoopConfig.PlanMode is active. The single `%s` is filled with the current plan-file path. Mirrors Claude Code's plan-mode framing so the model recognizes the surface regardless of which agent it's running under.
Lives in the prompt module (not plan_mode.go) so the schema-vs-prompt regression test in prompt_test.go can assert plan-mode directives are reachable from the same package as the rest of the prompt copy.
Variables ¶
This section is empty.
Functions ¶
func CheckpointFromContext ¶ added in v0.2.0
CheckpointFromContext recovers the (sessionID, checkpointID) pair stored by WithCheckpoint. Returns empty strings when no checkpoint is bound — callers should treat that as "checkpointing disabled."
func DefaultDenyPaths ¶
func DefaultDenyReadPaths ¶
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 IsAutoModeSafetyFloor ¶ added in v0.2.0
IsAutoModeSafetyFloor returns true for tools whose approval prompt must NOT be skipped by auto mode. These are the calls that run arbitrary code (run_bash) or write permanent / hard-to-reverse history (git_commit, git_checkpoint, rollback). The user opted into auto mode to skip edit-by-edit approval friction — not to silently hand over shell access or amend git history.
To get true blanket auto-approval (including run_bash and commits), launch with --dangerously-skip-permissions; that's the user-explicit "yolo" path and is intentionally session-wide so it can't be toggled away in the middle of a run (mirroring Claude Code).
func IsPlanFileWrite ¶ added in v0.2.0
IsPlanFileWrite reports whether this tool call is one of the mutating tools (write_file / edit_file / apply_diff) targeting the resolved plan file. The loop uses this to auto-approve those writes in plan mode — they're the model's only legitimate mutation surface while planning, so prompting on every edit is friction with no value (the gate already established the target is the plan file). False when planFile is empty, when the tool isn't a write tool, or when the target path differs from planFile.
func ParentDecisions ¶ added in v0.2.0
ParentDecisions recovers the channel attached by WithParentDecisions, or nil when no parent loop is on the stack.
func ParentEvents ¶ added in v0.2.0
ParentEvents recovers the channel attached by WithParentEvents, or nil when no parent loop is on the stack (tests, oneshot paths that haven't wired it in). Always check for nil before sending.
func PlanFilePath ¶ added in v0.2.0
PlanFilePath resolves a slug to its absolute plan-file path. The returned path is what the gate compares writes against and what the system-prompt addendum tells the model to write to.
func PlanModeGate ¶ added in v0.2.0
PlanModeGate is the read/write classifier the loop consults before every tool call when plan mode is active. Returns ("", false) when the call may proceed, or (errorString, true) when the call must be refused. The errorString is what the model sees as the tool result, so it's phrased as actionable guidance — the model can switch to a read-only or plan-file alternative on the next iteration.
Allowlist:
- exit_plan_mode: the only way out of plan mode.
- todo_write: progress tracking, no side effects.
- write_file/edit_file/apply_diff: only when the target path equals planFile. Any other write target is blocked.
- any tool whose RequiresApproval returns false: the implicit "read-only" classification (read_file, grep, glob, list_*, git_log_file, fetch_url, …). New read-only tools auto-classify.
The gate runs BEFORE Permissions.Evaluate so explicit deny rules still win in plan mode (Deny > plan-mode-allow > permissions > tool-policy).
func PlansDir ¶ added in v0.2.0
PlansDir returns the directory plan files live under: $YOTTACODE_HOME/plans (when set) or ~/.yottacode/plans otherwise. Does not create the directory — write_file's MkdirAll handles that lazily on first write.
func SlugFromPrompt ¶ added in v0.2.0
SlugFromPrompt converts a free-form prompt into a stable, filesystem- safe slug suitable for a plan filename. Format:
<kebab>-<16-hex-of-sha256(salt|prompt)>
The kebab portion caps at 60 characters so the suffixed filename stays well under common filesystem limits. Empty / all-punctuation input falls back to "untitled".
The hash suffix gives every plan a unique filename even when two prompts share the same opening words (e.g. "fix bug in foo" vs "fix bug in bar" both truncated to "fix-bug-in") and avoids the slug-collision class entirely. Salt is typically the session ID, so the same prompt typed in a different session lands on a different file.
func ToolPathsToSnapshot ¶ added in v0.2.0
ToolPathsToSnapshot exposes the Mutator capability to callers in other packages (e.g. the agent loop's checkpoint hook) without forcing them to import nothing-vs-something interface assertions. Returns nil when the tool isn't a Mutator.
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 ¶
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.
func WithCheckpoint ¶ added in v0.2.0
WithCheckpoint binds a checkpoint id + session id to ctx. The TUI calls this immediately after checkpoints.Begin returns, before passing ctx into Turn. Returns ctx unchanged when either id is empty so callers don't need to special-case nil-checkpoint paths.
func WithParentDecisions ¶ added in v0.2.0
WithParentDecisions attaches the parent loop's decisions channel to ctx. Tools that don't need to forward approvals should ignore this seam — the channel is receive-only and may be nil.
func WithParentEvents ¶ added in v0.2.0
WithParentEvents attaches the parent loop's events channel to ctx so downstream Tool.Execute calls can pull it via ParentEvents(ctx). The channel is send-only; tools may push Subagent* / progress events onto it without coordinating with the loop.
Tools that don't need this seam should ignore the helper — the normal tool-result return value is still the primary output path.
Types ¶
type AgentTool ¶ added in v0.2.0
type AgentTool struct {
// Configs is the resolved set of agent definitions (builtin +
// global + project). The Execute method looks up subagent_type
// against this slice; it should remain stable across the session.
Configs []subagents.AgentConfig
// Tasks is the session-scoped task registry. Foreground runs add
// + MarkDone within a single Execute; background runs add now and
// MarkDone later from a detached goroutine.
Tasks *subagents.Registry
// Adapter is the streamer the child Turn calls into. Shared with
// the parent — adapter calls are stateless per-request and
// concurrency-safe by construction.
Adapter Streamer
// ParentRegistry is the live tool set the parent session is using.
// We clone it for the child, dropping the Agent tool itself plus
// exit_plan_mode, and intersecting with the agent's `tools:`
// allowlist when one is configured. The clone is a single-pass
// snapshot — runtime changes to the parent registry don't
// propagate into in-flight children, which keeps semantics easy
// to reason about.
ParentRegistry *Registry
// Permissions is the parent's permission ruleset; children
// inherit it unchanged. Per-config narrowing is a v2 extension.
Permissions *permissions.Permissions
// YoloMode is the process-wide yolo overlay. The pointer is
// shared so a yolo session also applies to its subagents — the
// user explicitly opted into unattended mutation, child runs
// included.
YoloMode *YoloModeState
// PlanMode is the parent's plan-mode state. Pointer-shared so a
// child run under a plan-mode parent inherits the restriction
// transitively (no writes outside the plan file). When the
// parent flips out of plan mode mid-conversation the next
// subagent run automatically sees the new state. nil is safe —
// runChild allocates a fresh inactive state in that case.
PlanMode *PlanModeState
// AutoMode is the parent's auto-mode state. Pointer-shared so a
// child inherits parent's auto-mode (mutating tools auto-allow
// except the safety floor, iteration cap multiplied 4×). nil is
// safe — runChild allocates a fresh inactive state in that case.
AutoMode *AutoModeState
// Cwd is the working directory child tools resolve relative
// paths against. Same as the parent's cwd.
Cwd string
// TranscriptDir is the directory background-task transcripts get
// persisted under. Must exist at Execute time; the caller (TUI
// or oneshot wiring) creates it via subagents.EnsureTranscriptDir
// at startup.
TranscriptDir string
// AllowBackground controls whether `run_in_background: true` is
// honored. The TUI sets this true; oneshot leaves it false so the
// non-interactive entry point returns a sensible error string the
// model can recover from rather than silently detaching work that
// nobody will see.
AllowBackground bool
// SystemPromptSuffix is appended to the agent definition's body
// when building the child's system prompt. Used to inject runtime
// context the static config can't know (currently empty; reserved
// for cwd / repo metadata if we decide to inject it).
SystemPromptSuffix string
// contains filtered or unexported fields
}
AgentTool dispatches typed-subagent invocations. One instance is registered per session — Execute spawns a child agent.Turn against a filtered registry and either blocks until the child completes (foreground) or detaches the child to a goroutine that updates the task registry on completion (background).
func (*AgentTool) AgentConfigs ¶ added in v0.2.0
func (t *AgentTool) AgentConfigs() []subagents.AgentConfig
Configs returns the resolved set of agent definitions (for the TUI's /subagents help / status rendering and for tests). The returned slice references the same memory; callers must not mutate.
func (*AgentTool) Description ¶ added in v0.2.0
func (*AgentTool) Execute ¶ added in v0.2.0
Execute is the parent-loop entry point. Parses args, validates the subagent_type, builds the child config + history, and either:
- foreground: spawns the child Turn, drains its events through the translator inline, and returns the captured final reply as the tool result string the model sees;
- background: launches the whole flow in a goroutine, returns a task-id handle immediately, and posts completion via the session-level callback when the child finishes.
func (*AgentTool) ParallelSafe ¶ added in v0.2.0
ParallelSafe returns false so two Agent calls from the same assistant message serialize. Two simultaneous subagent spawns can rate-limit the same provider key and burn the iteration budget on both — better to do them in sequence. (Background subagents are the parallelism story; foreground spawns are a one-at-a-time affair.)
func (*AgentTool) PreviewCall ¶ added in v0.2.0
func (*AgentTool) RequiresApproval ¶ added in v0.2.0
RequiresApproval is always false for the Agent tool itself — delegation is just compute. The child's own mutating tool calls still go through their normal approval flow (and v1 auto-denies any ApprovalNeeded inside the child since the child has no UI attached). The user retains control via the parent's permission rules.
func (*AgentTool) SetBackgroundDoneCallback ¶ added in v0.2.0
func (t *AgentTool) SetBackgroundDoneCallback(fn func(SubagentBackgroundDone))
SetBackgroundDoneCallback installs the session-level handler that receives a SubagentBackgroundDone event when a detached child finishes. Safe to call after registration; safe to call with nil to clear.
type ApplyDiffTool ¶
type ApplyDiffTool struct {
Cwd string
WriteOpts WritePathOptions
}
func (*ApplyDiffTool) Description ¶
func (t *ApplyDiffTool) Description() string
func (*ApplyDiffTool) Name ¶
func (t *ApplyDiffTool) Name() string
func (*ApplyDiffTool) PathsToSnapshot ¶ added in v0.2.0
func (t *ApplyDiffTool) PathsToSnapshot(cwd, argsJSON string) []string
PathsToSnapshot reports every file the diff touches so /checkpoints can restore each pre-image. Uses the same ParseDiffPaths the tool already runs during validation, so the snapshot set matches the validation set by construction.
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 ¶
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" (--dangerously-skip-permissions flag is set and no rule matched).
type ApprovalNeeded ¶
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 ¶
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 AutoModeState ¶ added in v0.2.0
AutoModeState is the per-session, runtime-mutable auto-mode flag the loop reads on every tool dispatch. When active, mutating tools that would normally hit an approval modal auto-allow with Source=auto-mode — EXCEPT for the safety floor (run_bash, git_commit, git_checkpoint, rollback), which always prompt regardless of mode.
Mutually exclusive with plan mode at the TUI layer: entering one turns the other off. The loop-level gates don't enforce this on their own — they just observe whichever flag is set.
The pointer is shared between LoopConfig and the TUI Model so a flip from /auto, Shift+Tab, or the plan-card [Y] hotkey takes effect on the next iteration with no reconstruction. atomic.Bool keeps that benign race detector-clean.
func (*AutoModeState) IsActive ¶ added in v0.2.0
func (a *AutoModeState) IsActive() bool
IsActive is a nil-safe check used by the loop.
type CheckpointInfo ¶ added in v0.2.0
type CheckpointInfo struct{ Message string }
CheckpointInfo carries a non-fatal status from the checkpoint subsystem — typically a snapshot failure for one file (permission denied, race with deletion) that should NOT abort the user's tool call. The TUI renders these dimly in the scrollback so the user knows the file won't be restorable from this checkpoint without confusing them about whether the tool itself failed.
type CheckpointWriter ¶ added in v0.2.0
CheckpointWriter is the slice of the checkpoint store the agent loop depends on. Defining it here keeps internal/checkpoint out of the agent's import surface and lets tests substitute a recording fake.
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) Name ¶
func (t *CopyFileTool) Name() string
func (*CopyFileTool) PathsToSnapshot ¶ added in v0.2.0
func (t *CopyFileTool) PathsToSnapshot(cwd, argsJSON string) []string
PathsToSnapshot reports only the destination — src is read-only here.
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 // SaveForLater is the plan-mode-specific "[L] approve and // implement later" decision. The loop refuses the tool call (so // Execute never runs) but returns a firm "end this turn" message // to the model instead of the generic denial / refinement hint. // Only meaningful for exit_plan_mode; other tools should never // receive this value, and the loop falls back to generic-denial // semantics if they do. SaveForLater )
type DeleteFileTool ¶
type DeleteFileTool struct {
Cwd string
WriteOpts WritePathOptions
}
func (*DeleteFileTool) Description ¶
func (t *DeleteFileTool) Description() string
func (*DeleteFileTool) Name ¶
func (t *DeleteFileTool) Name() string
func (*DeleteFileTool) PathsToSnapshot ¶ added in v0.2.0
func (t *DeleteFileTool) PathsToSnapshot(cwd, argsJSON string) []string
PathsToSnapshot reports the target so /checkpoints can recreate the deleted file on rewind.
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) Name ¶
func (t *EditFileTool) Name() string
func (*EditFileTool) PathsToSnapshot ¶ added in v0.2.0
func (t *EditFileTool) PathsToSnapshot(cwd, argsJSON string) []string
PathsToSnapshot reports the target file so /checkpoints can restore the pre-edit contents on rewind.
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 --dangerously-skip-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 ExitPlanModeTool ¶ added in v0.2.0
type ExitPlanModeTool struct{}
ExitPlanModeTool is the model's signal that planning is finished and the plan file is ready for user approval. Mirrors Claude Code's `ExitPlanMode` exactly: the tool takes no `plan` argument — the content is read from the plan file the model has been writing to all along. Single source of truth (the file on disk), lower token usage, and no ambiguity about whether the approval card shows the same thing as the file the model intends to execute.
The tool itself is intentionally minimal: RequiresApproval=true routes the call through the standard approval flow, the TUI reads the plan file from disk and renders a plan-specific approval card ([A]/[K] hotkeys) for `exit_plan_mode` rather than the generic preview, and on approve the TUI flips the shared PlanModeState.Active flag off before forwarding the decision. The loop's `deniedResultFor` is special-cased for this tool name so a [K] (Keep planning) returns refinement guidance to the model instead of the generic "denied by user".
Execute therefore only runs on the approve path and unconditionally returns the "approved" message. The TUI is responsible for the "file is missing/empty" guard — it auto-denies before showing the approval card.
func (*ExitPlanModeTool) Description ¶ added in v0.2.0
func (t *ExitPlanModeTool) Description() string
func (*ExitPlanModeTool) Execute ¶ added in v0.2.0
Execute is only reached on the approve path — the loop short-circuits on Deny in promptForApproval and never calls the tool. Before showing the approval card the TUI inspects the plan file and auto-denies if it's missing or empty, so by the time we're here the file existed and the user said yes. Return the "approved" string and the model continues.
func (*ExitPlanModeTool) Name ¶ added in v0.2.0
func (t *ExitPlanModeTool) Name() string
func (*ExitPlanModeTool) PreviewCall ¶ added in v0.2.0
func (t *ExitPlanModeTool) PreviewCall(string) string
func (*ExitPlanModeTool) RequiresApproval ¶ added in v0.2.0
func (t *ExitPlanModeTool) RequiresApproval(string) bool
RequiresApproval is always true: every exit_plan_mode call goes through the approval card so the user can see the proposed plan before yottacode regains write access.
func (*ExitPlanModeTool) Schema ¶ added in v0.2.0
func (t *ExitPlanModeTool) Schema() map[string]any
Schema is an empty object — exit_plan_mode takes no arguments. The model writes the plan to disk via write_file/edit_file first, then calls this tool to surface it for approval. Matches Claude Code's ExitPlanMode shape.
type Fallback ¶
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) 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 GetSubagentResultTool ¶ added in v0.2.0
type GetSubagentResultTool struct {
// Tasks is the session-scoped subagent task registry. The same
// pointer the AgentTool uses to record spawns; pointer-shared
// so we observe live-updated state.
Tasks *subagents.Registry
}
GetSubagentResultTool retrieves a previously-dispatched subagent's state and final reply from the session task registry. The intended flow:
- Parent calls Agent(...) with run_in_background:true → tool returns a task id handle immediately.
- Parent's turn ends; user keeps working; child runs to completion in a goroutine.
- Some turns later, when the user asks about the subagent's findings, the parent calls get_subagent_result(task_id=<id>) and the final reply lands as a normal tool result that flows back into the parent's adapter context.
Without this tool, background subagents are fire-and-forget: the transcript lives on disk and in the registry, but the parent's model has no programmatic way to pull a completed result back into the conversation. The pairing with run_in_background is what makes background runs actually useful.
Read-only and ParallelSafe — safe to call repeatedly, safe to call alongside other read-only tools in the same model turn.
func (*GetSubagentResultTool) Description ¶ added in v0.2.0
func (t *GetSubagentResultTool) Description() string
func (*GetSubagentResultTool) Name ¶ added in v0.2.0
func (t *GetSubagentResultTool) Name() string
func (*GetSubagentResultTool) ParallelSafe ¶ added in v0.2.0
func (t *GetSubagentResultTool) ParallelSafe(string) bool
ParallelSafe lets the model fetch several subagent results in one turn — useful for "summarize all the background investigations I started" workflows.
func (*GetSubagentResultTool) PreviewCall ¶ added in v0.2.0
func (t *GetSubagentResultTool) PreviewCall(argsJSON string) string
func (*GetSubagentResultTool) RequiresApproval ¶ added in v0.2.0
func (t *GetSubagentResultTool) RequiresApproval(string) bool
RequiresApproval is false: the tool only reads from the in-memory task registry and produces a string — no disk mutation, no network calls, no shell. Safe to auto-execute on every call.
func (*GetSubagentResultTool) Schema ¶ added in v0.2.0
func (t *GetSubagentResultTool) Schema() map[string]any
type GitBlameLinesTool ¶
type GitBlameLinesTool struct{ Cwd string }
func (*GitBlameLinesTool) Description ¶
func (t *GitBlameLinesTool) Description() string
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) 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) 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) 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) 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) 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) 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) 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) 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 (*GitTool) PreviewCall ¶
func (*GitTool) RequiresApproval ¶
type GitUnstageFilesTool ¶
type GitUnstageFilesTool struct{ Cwd string }
func (*GitUnstageFilesTool) Description ¶
func (t *GitUnstageFilesTool) Description() string
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 (*GlobTool) ParallelSafe ¶
func (*GlobTool) PreviewCall ¶
func (*GlobTool) RequiresApproval ¶
type GrepTool ¶
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 (*GrepTool) ParallelSafe ¶
func (*GrepTool) PreviewCall ¶
func (*GrepTool) RequiresApproval ¶
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 ¶
IterationContinue explains why the loop is going around again after an iteration completes.
type IterationStart ¶
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) 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) 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) 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 internal name for the user-facing
// --dangerously-skip-permissions flag (mirroring Claude Code).
// 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
// PlanMode is the shared plan-mode flag the TUI flips via /plan or
// Shift+Tab. nil disables plan mode entirely (oneshot; tests). When
// set and Active, the loop prepends a plan-mode addendum to the
// system prompt on every request and gates mutating tools through
// PlanModeGate before approval evaluation. Pointer-shared so a TUI
// flip takes effect on the next iteration with no reconfiguration.
PlanMode *PlanModeState
// AutoMode is the shared auto-mode flag the TUI flips via /auto,
// Shift+Tab, or the plan-card [Y] hotkey. When active, the loop
// auto-approves non-safety-floor tool calls (no modal) so the
// model can implement a multi-step plan without per-edit friction.
// run_bash and git mutations remain in the safety floor — see
// IsAutoModeSafetyFloor.
AutoMode *AutoModeState
// YoloMode is the unrestricted toggle — auto-approves ALL tool
// calls including the safety floor, and removes the iteration
// cap entirely. Explicit Deny rules in permissions.json still
// win. Intended for unattended long-running implementations
// where the user has decided no further oversight is needed.
// Mutually exclusive with AutoMode and PlanMode at the TUI layer.
YoloMode *YoloModeState
// Checkpoints, when non-nil, receives pre-image snapshot
// requests for every Mutator tool call. nil disables checkpoint
// capture entirely — oneshot and tests pass nil. The TUI builds
// a *checkpoint.Store and attaches it; see internal/tui/run.go.
// Implementations must be safe under concurrent SnapshotPath
// calls within a turn.
Checkpoints CheckpointWriter
}
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) 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) 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 (*MkdirTool) PreviewCall ¶
func (*MkdirTool) RequiresApproval ¶
type MoveFileTool ¶
type MoveFileTool struct {
Cwd string
WriteOpts WritePathOptions
}
func (*MoveFileTool) Description ¶
func (t *MoveFileTool) Description() string
func (*MoveFileTool) Name ¶
func (t *MoveFileTool) Name() string
func (*MoveFileTool) PathsToSnapshot ¶ added in v0.2.0
func (t *MoveFileTool) PathsToSnapshot(cwd, argsJSON string) []string
PathsToSnapshot reports both src (so we can recreate it on rewind) and dst (so we can remove the moved-to file on rewind).
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 Mutator ¶ added in v0.2.0
Mutator is the optional capability marker for tools that modify files on disk. The checkpoint subsystem queries this before tool.Execute to capture pre-images so /checkpoints can restore. PathsToSnapshot returns absolute paths the tool intends to touch, derived from argsJSON. Returning extra paths is harmless (snapshots are content-addressed and dedup); returning too few breaks restore.
Tools that mutate files via opaque side effects (e.g. run_bash) do NOT implement this — those mutations are intentionally untracked, mirroring Claude Code /rewind. Surface the limitation in user-facing docs / picker footer.
type ParallelSafeTool ¶
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 PlanEntry ¶ added in v0.2.0
PlanEntry describes one plan file on disk for the picker + CLI resume flow. Slug is the basename without the `.md` suffix (matches the format SlugFromPrompt produces).
func ListPlans ¶ added in v0.2.0
ListPlans enumerates plan files under PlansDir() and returns them sorted by modified-time descending (newest first). Missing directory returns (nil, nil) — a fresh install has no plans yet and that's not an error.
func MatchPlan ¶ added in v0.2.0
MatchPlan returns the first plan whose slug contains the query (case-insensitive substring match). Plans must already be sorted newest-first — typical usage is ListPlans → MatchPlan, so the most recent match wins on ties. Returns nil when nothing matches; the caller renders the list to help the user pick a real slug.
type PlanModeState ¶ added in v0.2.0
PlanModeState is the per-session, runtime-mutable plan-mode flag the loop reads on every tool dispatch and prompt assembly. The TUI flips `Active` from the main goroutine via /plan or Shift+Tab while the agent goroutine reads it inside executeToolCall and streamIteration — atomic.Bool keeps that benign race detector-clean.
The pointer is shared between LoopConfig and the TUI Model, so a flip in cmd_plan.go takes effect on the very next iteration with no reconstruction or message rewriting. PlanFile is set when entering plan mode (from `/plan <topic>` arg or the first user message) and kept stable for the lifetime of the active plan. Until it's set, writes to the plan file are blocked — the gate compares against PlanFile and an empty string never matches a real path.
func (*PlanModeState) IsActive ¶ added in v0.2.0
func (p *PlanModeState) IsActive() bool
IsActive is a nil-safe check used by the loop.
type PlanStore ¶ added in v0.2.0
type PlanStore struct {
// contains filtered or unexported fields
}
PlanStore is the session-scoped owner of the current todo list. The TodoWriteTool replaces the list wholesale on each call; the agent loop reads the snapshot to emit a TodoUpdate event; the TUI renders from that event; session save/load copies the slice in/out for cross-session persistence.
func NewPlanStore ¶ added in v0.2.0
func NewPlanStore() *PlanStore
type ProviderToolCall ¶
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 ¶
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) 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 ¶
func (*ReadManyFilesTool) Description ¶
func (t *ReadManyFilesTool) Description() string
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 ¶
AsAdapterTools converts the registry into the schema shape the adapter advertises to the model. Equivalent to AsAdapterToolsFiltered(nil) — every registered tool is exposed.
func (*Registry) AsAdapterToolsFiltered ¶ added in v0.2.0
AsAdapterToolsFiltered is the gated variant: when filter is non-nil and returns false for a tool name, that tool is omitted from the advertised schema. Used by the loop to hide `exit_plan_mode` outside of plan mode — without the filter the model could synthesize the call out of context and confuse the user with an approval card for a plan that doesn't exist. Pure read-side filter; the registry's own map is unchanged so Get() still resolves the tool when (legitimately) called.
func (*Registry) Names ¶ added in v0.2.0
Names returns the set of registered tool names. Useful for callers that need to validate references (e.g. subagent allowlists) without caring about the Tool values themselves.
func (*Registry) Tools ¶ added in v0.2.0
Tools returns every registered Tool. The order is non-deterministic (map iteration). Subagent registry construction uses this to clone the parent's toolset while applying an allowlist filter — see internal/agent/agent_tool.go. Read-only on the registry: callers must not mutate the returned tools.
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.
func AssessRisk ¶
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.
type RollbackTool ¶
type RollbackTool struct{ Cwd string }
func (*RollbackTool) Description ¶
func (t *RollbackTool) Description() string
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) 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) 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 SubagentBackgroundDone ¶ added in v0.2.0
type SubagentBackgroundDone struct {
TaskID string
AgentType string
Result string
Errored bool
Duration time.Duration
TokensUsed int
ToolCalls int // child's tool-call count, for inline stats rendering
}
SubagentBackgroundDone fires asynchronously when a background subagent completes after the parent turn has already ended. The TUI surfaces this as a card on the next idle redraw / via /subagents list. Oneshot rejects background invocations entirely so this event is unreachable from non-interactive contexts.
type SubagentDone ¶ added in v0.2.0
type SubagentDone struct {
TaskID string
AgentType string
Result string
Errored bool
Duration time.Duration
TokensUsed int
ToolCalls int // child's tool-call count, for inline stats rendering
}
SubagentDone fires when a foreground subagent completes. The Result is the child's final assistant message — that same string is also returned synchronously from the Agent tool's Execute, so the parent's model receives it as a normal tool result. SubagentDone is for the UI side of the picture: it lets the TUI close the "subagent running" card and oneshot print a final status line.
type SubagentProgress ¶ added in v0.2.0
SubagentProgress is the parent-visible activity stream for a running subagent — typically "Explore: read_file internal/foo.go" or "Plan: grep TODO". The child's raw ContentToken/ReasoningToken events are deliberately NOT forwarded; only high-level tool-level activity reaches the parent, which keeps the parent's UI uncluttered AND keeps the child's reasoning out of the parent's adapter context.
type SubagentStart ¶ added in v0.2.0
type SubagentStart struct {
TaskID string
AgentType string
Prompt string
Background bool
TranscriptPath string
}
SubagentStart fires when the Agent tool begins a child Turn. The parent's TUI/oneshot renders this as a short header so the user can see that delegation is happening; the child's transcript file at TranscriptPath captures everything that happens inside the child.
type Todo ¶ added in v0.2.0
type Todo struct {
Content string `json:"content"`
Status TodoStatus `json:"status"`
}
Todo is one item in the agent's working plan. Content is the human-readable description; Status is one of the three lifecycle values above. There is intentionally no stable ID — the model passes the full list every call, so identity is positional and renames are indistinguishable from delete+add (matching Claude's shape).
type TodoStatus ¶ added in v0.2.0
type TodoStatus string
TodoStatus is the lifecycle state of a single todo item. The three values mirror Claude Code's TodoWrite contract so the model has a familiar schema target.
const ( TodoPending TodoStatus = "pending" TodoInProgress TodoStatus = "in_progress" TodoCompleted TodoStatus = "completed" )
type TodoUpdate ¶ added in v0.2.0
type TodoUpdate struct{ Todos []Todo }
TodoUpdate fires after a tool implementing the planAware interface (TodoWriteTool today) finishes, carrying the new full snapshot of the working plan. The TUI renders this as a scrollback card showing the current list with status markers; oneshot prints a one-liner on stderr. The slice is a copy — consumers may retain it.
type TodoWriteTool ¶ added in v0.2.0
type TodoWriteTool struct {
Store *PlanStore
}
TodoWriteTool is the yottacode analogue of Claude Code's TodoWrite: a model-callable tool that replaces the working plan on every call, with one item allowed to be `in_progress` at a time. Rendering and persistence happen one layer out — this tool just owns the validated write into the PlanStore. The loop notices the write via the planAware interface (see loop.go) and emits a TodoUpdate event for the TUI.
func (*TodoWriteTool) Description ¶ added in v0.2.0
func (t *TodoWriteTool) Description() string
func (*TodoWriteTool) Name ¶ added in v0.2.0
func (t *TodoWriteTool) Name() string
func (*TodoWriteTool) PlanStore ¶ added in v0.2.0
func (t *TodoWriteTool) PlanStore() *PlanStore
PlanStore exposes the underlying store so the agent loop can snapshot the list after this tool runs and emit a TodoUpdate event. Satisfies the unexported planAware interface in loop.go.
func (*TodoWriteTool) PreviewCall ¶ added in v0.2.0
func (t *TodoWriteTool) PreviewCall(argsJSON string) string
func (*TodoWriteTool) RequiresApproval ¶ added in v0.2.0
func (t *TodoWriteTool) RequiresApproval(string) bool
RequiresApproval is always false: this tool has no filesystem, network, or external side effects — it's purely a visibility primitive that updates a per-session in-memory list. Matches Claude Code's TodoWrite posture exactly: real safety comes from the per-mutation prompts on edit_file, write_file, run_bash, etc., not from gating the plan itself.
func (*TodoWriteTool) Schema ¶ added in v0.2.0
func (t *TodoWriteTool) Schema() map[string]any
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 ¶
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 ¶
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 TurnInterrupted ¶ added in v0.2.0
type TurnInterrupted struct {
// PartialContent is the assistant text that streamed before the
// cancel, already appended to history. Carried in the event so the
// TUI can render a one-line snippet without re-walking history.
PartialContent string
// OrphanedCalls counts tool_use entries in the just-cancelled batch
// that received synthetic results (i.e. were never actually run or
// did not produce real output). Zero when the cancel landed mid-
// stream before any tool call started.
OrphanedCalls int
}
TurnInterrupted fires when the turn ended via user-initiated context cancellation (Enter or Esc/Ctrl+C mid-turn) rather than an error or a clean finish. By the time this event lands, the loop has already preserved history correctness: any tokens that streamed before the cancel are appended as a content-only assistant message, and any in-flight or pending tool_calls in the current batch get synthetic "interrupted by user" tool_result entries so no tool_use is left orphaned for the next request. Consumers should render this as a calm marker, not an error — the turn was cut on purpose.
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) Name ¶
func (t *WriteFileTool) Name() string
func (*WriteFileTool) PathsToSnapshot ¶ added in v0.2.0
func (t *WriteFileTool) PathsToSnapshot(cwd, argsJSON string) []string
PathsToSnapshot reports the destination path so /checkpoints can restore the pre-write contents (or remove the file if it didn't exist before this turn).
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
// PlanModeAllowedFile is the absolute path of the single plan file
// the agent is permitted to write to while plan mode is active.
// When non-empty, ValidateWritePath short-circuits to nil for an
// exact match (after symlink rejection still applies). Empty when
// plan mode is off — the regular Cwd / AllowedPaths / DenyExact
// stack is the only authority. The TUI mutates this field on the
// registered *WriteFileTool / *EditFileTool / *ApplyDiffTool when
// /plan toggles, and zeroes it on exit.
PlanModeAllowedFile string
}
WritePathOptions configures the validator for a single tool. Build it once per session in run.go and share across every mutating tool.
type YoloModeState ¶ added in v0.2.0
YoloModeState is the per-session "no questions asked" flag. When active, the loop auto-approves every tool call WITHOUT the safety floor that auto mode keeps (run_bash, git_commit, git_checkpoint, rollback all auto-allow silently), AND removes the iteration cap. Explicit Deny rules in permissions.json still win — yolo is "skip prompts," not "ignore my policy."
Mutually exclusive with AutoMode and PlanMode at the TUI layer: entering yolo turns the other two off. The loop-level gate doesn't enforce this on its own; the TUI does.
Intentionally NOT in the Shift+Tab mode cycle — you have to type /yolo to enter so it can't be reached by accident.
func (*YoloModeState) IsActive ¶ added in v0.2.0
func (y *YoloModeState) IsActive() bool
IsActive is a nil-safe check used by the loop.
Source Files
¶
- agent_tool.go
- approval.go
- auto_mode.go
- cmdsplit.go
- coding_workflow_tools.go
- edit_tool.go
- events.go
- exec_tool.go
- exit_plan_mode_tool.go
- fs_search_tools.go
- fs_tools.go
- get_subagent_result_tool.go
- git_tool.go
- git_workflow_tools.go
- local_coding_tools.go
- loop.go
- memory_tools.go
- parent_events.go
- path.go
- plan.go
- plan_mode.go
- plan_tool.go
- prompt.go
- tools.go
- web_tools.go
- writepath.go
- yolo_mode.go