From f5298306b1eca77c1af9c230f35cef0d3deadcd0 Mon Sep 17 00:00:00 2001 From: ollie Date: Tue, 21 Jul 2026 12:53:51 +0200 Subject: [PATCH] agent: layered preamble scaffolding BuildRuntime now accepts optional baseLayers (variadic strings) that are prepended to the agent-resolved preamble. This enables the server to compose: system_prompt + operational_model + environment + agent_prompts. - config.Config: add SystemPrompt field (path override) - AgentCoreConfig: add OperationalModel and SystemPrompt fields - BuildRuntime: prepend baseLayers before agent prompt content - Remove debug fprintf statements from prompt resolution --- pkg/agent/core.go | 26 +++++++++++++++++++++++--- pkg/agent/prompt_resolver.go | 3 --- pkg/config/config.go | 3 +++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/pkg/agent/core.go b/pkg/agent/core.go index bb50280..5371956 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -38,7 +38,7 @@ type toolClassifier func(name string) bool // env provides additional environment variables injected into prompt resolution // subprocesses (e.g. OLLIE_SESSION_ID=xxx). // The caller is responsible for registering all servers on d before calling this. -func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []string) *Runtime { +func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []string, baseLayers ...string) *Runtime { var messages []string var allToolInfos []tools.ToolInfo @@ -65,11 +65,9 @@ func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []stri for k, v := range cfg.Hooks { hooks[k] = []string(v) } - fmt.Fprintf(os.Stderr, "[DEBUG BuildRuntime] cfg.Prompt.IsExec=%v len(Value)=%d\n", cfg.Prompt.IsExec, len(cfg.Prompt.Value)) if resolved, err := resolvePrompt(cfg.Prompt, cwd, env); err != nil { fmt.Fprintf(os.Stderr, "resolve prompt: %v\n", err) } else { - fmt.Fprintf(os.Stderr, "[DEBUG BuildRuntime] preamble len=%d\n", len(resolved)) preamble = resolved } genParams = backend.GenerationParams{ @@ -147,6 +145,21 @@ func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []stri compactionModel = cfg.CompactionModel } + // Prepend base layers (system prompt, operational model, environment) + // before the agent-specific preamble. + if len(baseLayers) > 0 { + var prefix strings.Builder + for _, layer := range baseLayers { + if layer != "" { + prefix.WriteString(layer) + prefix.WriteByte('\n') + } + } + if prefix.Len() > 0 { + preamble = prefix.String() + preamble + } + } + return &Runtime{ Dispatcher: d, Tools: allTools, @@ -251,6 +264,13 @@ type AgentCoreConfig struct { PromptEnvExtra []string // Remote is the SSH target for remote execution (empty = local). Remote string + // OperationalModel is server-injected text describing the 9P/D-Bus + // interface available to the agent. Not user-configurable. + OperationalModel string + // SystemPrompt is the base system prompt text (embedded default or + // override from agent config / session creation). If empty, BuildRuntime + // uses only the agent config's prompt commands. + SystemPrompt string } // agent is the Core implementation. It owns all agent and session state diff --git a/pkg/agent/prompt_resolver.go b/pkg/agent/prompt_resolver.go index c6ea7cc..ba8c0a8 100644 --- a/pkg/agent/prompt_resolver.go +++ b/pkg/agent/prompt_resolver.go @@ -71,16 +71,13 @@ func resolveExecPrompt(cmds []string, cwd string, env []string) (string, error) } var parts []string - fmt.Fprintf(os.Stderr, "[DEBUG resolveExecPrompt] %d entries\n", len(cmds)) for _, entry := range cmds { entry = strings.TrimSpace(entry) if entry == "" { continue } - fmt.Fprintf(os.Stderr, "[DEBUG resolveExecPrompt] entry=%q\n", entry) // Try resolving as a prompt file first. if content, ok := resolvePromptFile(entry, envMap, expand); ok { - fmt.Fprintf(os.Stderr, "[DEBUG resolveExecPrompt] -> file ok, len=%d\n", len(content)) if content != "" { parts = append(parts, content) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 9464e3e..00663db 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -85,6 +85,9 @@ type Config struct { Stop []string `json:"stop,omitempty"` Verbosity string `json:"verbosity,omitempty"` CompactionModel string `json:"compactionModel,omitempty"` + // SystemPrompt overrides the embedded system prompt with a file path. + // If set and the file exists, its content replaces the compiled-in default. + SystemPrompt string `json:"systemPrompt,omitempty"` } // Load parses a Config from r.