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 files through the memdir package and threads them into PromptContext.ProjectMemory / .UserProfile. The skill registry is similarly flattened by the caller into []SkillRef so sysprompt does not depend on internal/tools/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 ¶
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 + body + (optional) skills + (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 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.
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
}
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
Description string
Schema json.RawMessage // raw JSON Schema object; rendered verbatim in the <functions> block
}
DeferredToolSpec is the prompt-side view of one deferred tool. The sysprompt package interpolates these into a <functions> block in the main prompt so the model sees full schemas from turn 1 — no tool_search round trip is required to call a deferred tool.
Mirrors tools.Descriptor's three LLM-facing fields. The caller (agent.Main) flattens the deferred-tool catalog into []DeferredToolSpec so sysprompt does not depend on internal/tools or internal/toolset.
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.
// 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-7", "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)
ProjectMemory string // contents of <workdir>/EVVA.md; "" = skip.
UserProfile string // contents of <evvaHome>/USER_PROFILE.md; "" = skip.
}
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.