sysprompt

package
v1.11.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package sysprompt builds the system prompt for each kind of agent evva runs. The package exposes one AgentDefinition per built-in agent (see agent_def.go) whose BuildSystemPrompt function turns a PromptContext into the full prompt string. The Main agent's prompt is composed from shared fragments (identity, environment, memory, skills, dev) plus per-agent harness/tools/planning blocks; subagents (Explore, General) own a single hand-written string mirroring ref/src/tools/AgentTool/built-in/*Agent.ts.

The package is dependency-light on purpose: it imports only stdlib. The caller (cmd/evva/main.go via agent.Main) reads memory through the memdir package and threads it into PromptContext.WorkdirMemory / .MemoryIndex. The skill registry is similarly flattened by the caller into []SkillRef so sysprompt does not depend on pkg/skill.

Tool names interpolated into prompts live in toolnames.go and are drift-checked against internal/tools/name.go by toolnames_link_test.go.

Index

Constants

This section is empty.

Variables

View Source
var (
	MainAgent = AgentDefinition{
		Name:              "evva",
		WhenToUse:         "",
		OmitMemory:        false,
		AdvertiseSkills:   true,
		BuildSystemPrompt: buildMainPrompt,
		As:                []string{"main"},
	}

	ExploreAgent = AgentDefinition{
		Name:              subagentExplore,
		WhenToUse:         exploreWhenToUse,
		OmitMemory:        true,
		AdvertiseSkills:   false,
		BuildSystemPrompt: buildExplorePrompt,
		As:                []string{"subagent"},
	}

	GeneralAgent = AgentDefinition{
		Name:              subagentGeneral,
		WhenToUse:         generalWhenToUse,
		OmitMemory:        true,
		AdvertiseSkills:   false,
		BuildSystemPrompt: buildGeneralPrompt,
		As:                []string{"subagent"},
	}

	PlanAgent = AgentDefinition{
		Name:              subagentPlan,
		WhenToUse:         planWhenToUse,
		OmitMemory:        true,
		AdvertiseSkills:   false,
		BuildSystemPrompt: buildPlanPrompt,
		As:                []string{"subagent"},
	}
)

Built-in agent registry. Phase 11 adds PlanAgent for design-phase work inside plan mode; Phase 6 may add more main-tier personas (nono, noen) as siblings.

Functions

func ComposeDiskMainPrompt

func ComposeDiskMainPrompt(body string, ctx PromptContext, def AgentDefinition) string

ComposeDiskMainPrompt assembles a system prompt for a disk-loaded main-tier persona. body is the verbatim system_prompt.md the loader captured; def carries the OmitMemory / AdvertiseSkills flags from meta.yml. The result is identity + environment + (optional) memory + tools guide + body + (optional) skills + (optional) deferred catalog + (optional) dev feedback, joined the same way the built-in evva prompt is composed.

Lives here (in the sysprompt package) so the section builders stay package-private and disk-persona composition has a single seam.

Disk personas DELIBERATELY skip the ref-ported conduct sections (doing-tasks, actions, tone, output-efficiency, system, session-specific-guidance). Those would conflict with the persona's own definition; the persona supplies its own conduct rules in body. Harness mechanics are a different matter (RP-19): how tool calling works does not vary by persona and no operator should have to hand-write it, so the per-tool-gated diskToolsGuideSection renders BEFORE body (harness facts first, personality second) and the deferred-tool catalog renders near the bottom, mirroring the main agent's section order.

Types

type AgentDefinition

type AgentDefinition struct {
	Name              string
	WhenToUse         string
	OmitMemory        bool
	AdvertiseSkills   bool
	BuildSystemPrompt func(ctx PromptContext) string

	// As controls where this agent appears. Values:
	//   "main"     — selectable via /profile (Phase 6 picker).
	//   "subagent" — invokable via the Agent tool's subagent_type.
	// Both can be set; for built-ins the slice is fixed (Main is main-only;
	// Explore/General are subagent-only). Disk agents declare this via
	// meta.yml's `as:` field.
	As []string

	// ActiveTools / DeferredTools name the tools this agent's profile loads.
	// Empty means "use the built-in constructor's default" (Main, Explore,
	// General supply their own lists in agent.Main/Explore/General). For
	// disk-loaded agents these come from tools.yml.
	ActiveTools   []tools.ToolName
	DeferredTools []tools.ToolName

	// Model is the optional model override declared in meta.yml. Empty
	// means "inherit from parent" (existing built-in behavior).
	Model string

	// OutputStyle is the optional output style declared in meta.yml. When
	// set it wins over the user's configured output_style while this
	// persona is active (PRD output-styles §5.5). Empty for built-ins.
	OutputStyle string

	// PromptBody is the raw persona prompt body before composition, set when
	// the definition came from disk (system_prompt.md) or a public AgentSpec.
	// Empty for built-ins whose prompt is assembled from internal fragments —
	// their prompt isn't recoverable as a single string. Lets a definition
	// round-trip back to a public AgentDefinition for inspection.
	PromptBody string

	// LongRunning marks a persona that stays live for a very long time — a swarm
	// member runs for weeks. The prompt builder then drops fragments that would
	// drift across rebuilds and bust the prompt-cache prefix: currently the
	// "- Today:" date in the environment section (gated via PromptContext.OmitDate
	// in mainProfileFromDiskAgent). Built-in evva leaves it false; internal/swarm
	// sets it at member registration (RP-5).
	LongRunning bool

	// PromptSuffix, when non-empty, is appended verbatim (after one blank
	// line) to the END of the fully composed system prompt — built-in and
	// disk-composed paths alike. It is the seam a swarm uses to attach its
	// team protocol to a persona whose prompt is assembled internally and so
	// cannot be pre-concatenated into a body string (RP-29). Living on the
	// definition (not an agent option) means every prompt re-render —
	// ReloadSkills, MCP discovery, SwitchProfile — re-reads it from the
	// registry and keeps it.
	PromptSuffix string
}

AgentDefinition is the Go-side seam for a built-in agent. Phase 2's internal/agent/loader/ will define an AgentRegistry interface that this struct satisfies; for Phase 0 it is a concrete struct since the only agents are built-ins. Disk-authored agents (Phase 2) construct an AgentDefinition where BuildSystemPrompt is a closure that returns the on-disk system_prompt.md body.

Field semantics:

  • Name wire identifier ("evva", "explore", "general-purpose"). Same string the Agent tool's subagent_type enum will accept (Phase 2 unifies these). Lowercase, hyphenated.
  • WhenToUse description shown in the Agent tool's catalog so a parent agent knows what to delegate. Empty for Main (Main is not delegated to).
  • OmitMemory subagents skip <workdir>/EVVA.md and <evvaHome>/USER_PROFILE.md. Matches ref TS omitClaudeMd: true on Explore/Plan.
  • AdvertiseSkills only the Main agent surfaces the skill catalog. Subagents don't (it would bloat their context).
  • BuildSystemPrompt assembles the prompt from ctx. Each built-in closure-captures its own per-agent text fragments.

func (AgentDefinition) IsMain

func (d AgentDefinition) IsMain() bool

IsMain reports whether this agent appears in the /profile picker (Phase 6).

func (AgentDefinition) IsSubagent

func (d AgentDefinition) IsSubagent() bool

IsSubagent reports whether this agent is invokable via the Agent tool's subagent_type parameter.

type DeferredToolSpec

type DeferredToolSpec struct {
	Name string
}

DeferredToolSpec is the prompt-side view of one deferred tool. Only the Name is included — full schemas are fetched on demand via tool_search, matching ref/ Claude Code's name-only advertisement.

type PromptContext

type PromptContext struct {
	// Identity
	AgentName string    // e.g. "evva" — the name the main agent introduces itself as.
	Today     time.Time // anchors the model in absolute time; zero = use time.Now() at render.

	// OmitDate drops the "- Today:" line from the environment section. Long-
	// running personas (swarm members) set it so the system-prompt prefix stays
	// bit-stable across rebuilds — a drifting date would bust the prompt cache for
	// a swarm that runs for weeks. The time they need arrives in wake/run prompts,
	// never in the static system prompt (RP-5). Driven by AgentDefinition.LongRunning.
	OmitDate bool

	// OmitSkillAuthoring drops the "How to create a skill" guidance from the
	// main prompt's skills section. Long-running swarm-resident personas set
	// it (via AgentDefinition.LongRunning) for the same reason disk swarm
	// members do (RP-10-3): a member with write/bash should not be invited to
	// author SKILL.md mid-run.
	OmitSkillAuthoring bool

	// Environment
	OS       string // runtime.GOOS — "darwin", "linux", "windows".
	Shell    string // basename of $SHELL — "zsh", "bash", ...
	WorkDir  string // absolute path of the current working directory.
	EvvaHome string // ~/.evva — where skills, memory, and config live.
	Env      string // "dev" | "prod" — dev gates the feedback section.
	Model    string // canonical model id ("claude-opus-4-8", "deepseek-chat", ...). Empty = skip the model line in env block.

	// Catalogs
	Skills        []SkillRef         // advertised skill list; empty = skip the section.
	DeferredTools []DeferredToolSpec // deferred-tool catalog; rendered as a <functions> block in the main prompt. Empty = skip the section.

	// Memory (loaded by internal/memdir)
	WorkdirMemory string // contents of <workdir>/EVVA.md (user-authored); "" = skip.
	MemoryIndex   string // <appHome>/memory/MEMORY.md body (truncated, inject-ready); "" = skip.

	// EnableAutoMemory gates the typed-memory guidance + the MEMORY.md index
	// sections in the main prompt. false → both sections are suppressed so the
	// model isn't told about a memory system it can't use this session.
	EnableAutoMemory bool

	// EnableDynamicWorkflow swaps the multi-step-work protocol: true renders
	// the dynamic-workflow board guidance (wf_task_* + engine dispatch) in
	// place of the todo_write protocol, matching the tool swap the Main
	// profile performs under the same flag. Solo main agent only.
	EnableDynamicWorkflow bool

	// RepoMap is the already-rendered, token-bounded repo-map body (built by
	// internal/repomap from the LSP layer, or its glob fallback). "" → the
	// section is skipped entirely. The string arrives pre-rendered so this
	// package keeps its stdlib-only, no-IO discipline — the same one-way arrow
	// as memory. Main agent only; subagents never set it.
	RepoMap string

	// Output style (resolved by the caller via internal/outputstyle — same
	// one-way arrow as memory and skills). Empty OutputStylePrompt = the
	// default style: no overlay section, prompt byte-identical to a build
	// that never knew the feature. When set, the identity line defers to the
	// style ("according to your Output Style below…", ref's
	// getSimpleIntroSection) and an "# Output Style: <name>" section renders.
	// OutputStyleKeepCoding false additionally drops the "Doing tasks"
	// coding-doctrine section (ref's keepCodingInstructions === true check).
	// Main-tier agents only; subagents and swarm-resident (LongRunning)
	// personas never set these.
	OutputStyleName       string
	OutputStylePrompt     string
	OutputStyleKeepCoding bool
}

PromptContext is the input bundle for every AgentDefinition.BuildSystemPrompt. Build once per agent at construction time and pass by value — builders read-only. Zero values render cleanly: empty environment fields become "(unknown)" / "(unset)"; empty Skills / ProjectMemory / UserProfile suppress their respective sections.

func DetectContext

func DetectContext(agentName, evvaHome, env string) PromptContext

DetectContext returns a PromptContext with the runtime-detectable fields (Today, OS, Shell, WorkDir) populated. Caller fills AgentName, EvvaHome, Env, Skills, and the two memory fields — those live in configs.AppConfig and on the memdir.Snapshot, which the sysprompt module deliberately does not import.

type SkillRef

type SkillRef struct {
	Name        string
	Description string
}

SkillRef is the prompt-side view of a user-installed skill — just the name and description we advertise to the model. The sysprompt package deliberately does not depend on pkg/skill; the caller flattens the skill registry into this struct.

Jump to

Keyboard shortcuts

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