From e98533ea18b4cfbdecfe0bf4cf868c03e4e7e58f Mon Sep 17 00:00:00 2001 From: Scott Nixon Date: Sun, 8 Mar 2026 20:14:24 -0700 Subject: [PATCH 1/2] Fix wizard generateToml() to use multi-line TOML strings for system_prompt The spawn wizard used single-line TOML strings for system_prompt, which broke when the prompt contained newlines. Switch to multi-line basic strings (triple-quoted) matching the pattern already used in spawnBuiltin(). Fixes RightNow-AI/openfang#463 --- crates/openfang-api/static/js/pages/agents.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/openfang-api/static/js/pages/agents.js b/crates/openfang-api/static/js/pages/agents.js index 2313ee303..581bfccbd 100644 --- a/crates/openfang-api/static/js/pages/agents.js +++ b/crates/openfang-api/static/js/pages/agents.js @@ -420,7 +420,7 @@ function agentsPage() { lines.push('', '[model]'); lines.push('provider = "' + f.provider + '"'); lines.push('model = "' + f.model + '"'); - lines.push('system_prompt = "' + f.systemPrompt.replace(/"/g, '\\"') + '"'); + lines.push('system_prompt = """\n' + f.systemPrompt.replace(/\\/g, '\\\\').replace(/"""/g, '\\"\\"\\"') + '\n"""'); if (f.profile === 'custom') { lines.push('', '[capabilities]'); if (f.caps.memory_read) lines.push('memory_read = ["*"]'); From f90478f26af35d5f91d433d6df8b912e51f183e3 Mon Sep 17 00:00:00 2001 From: Scott Nixon Date: Sun, 8 Mar 2026 20:15:54 -0700 Subject: [PATCH 2/2] Add tests for multi-line system_prompt TOML parsing Tests cover the exact TOML format produced by the dashboard wizard's generateToml() function: multi-line prompts, embedded quotes, code blocks, single-line backward compat, and custom profile with capabilities. --- crates/openfang-types/src/agent.rs | 124 +++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/crates/openfang-types/src/agent.rs b/crates/openfang-types/src/agent.rs index 3aa914163..47a6fcd55 100644 --- a/crates/openfang-types/src/agent.rs +++ b/crates/openfang-types/src/agent.rs @@ -1169,4 +1169,128 @@ provider = "openai" assert_eq!(cfg.model, "gpt-4o"); assert_eq!(cfg.provider, "openai"); } + + // ----- Multi-line system_prompt TOML tests (wizard generateToml output) ----- + + #[test] + fn test_manifest_multiline_system_prompt_toml() { + // This is the exact TOML format the dashboard wizard generateToml() now produces + let toml_str = r#" +name = "brand-guardian" +module = "builtin:chat" + +[model] +provider = "google" +model = "gemini-3-flash-preview" +system_prompt = """ +You are Brand Guardian, an expert brand strategist. + +Your Core Mission: +- Develop brand strategy including purpose, vision, mission, values +- Design complete visual identity systems +- Establish brand voice and messaging architecture + +Critical Rules: +- Establish comprehensive brand foundation before tactical implementation +- Ensure all brand elements work as a cohesive system +""" +"#; + let manifest: AgentManifest = toml::from_str(toml_str).unwrap(); + assert_eq!(manifest.name, "brand-guardian"); + assert_eq!(manifest.model.provider, "google"); + assert_eq!(manifest.model.model, "gemini-3-flash-preview"); + assert!(manifest.model.system_prompt.contains("Brand Guardian")); + assert!(manifest.model.system_prompt.contains("Critical Rules:")); + // Verify newlines are preserved + assert!(manifest.model.system_prompt.contains('\n')); + } + + #[test] + fn test_manifest_multiline_system_prompt_with_quotes() { + // System prompt containing double quotes (common in persona prompts) + let toml_str = r#" +name = "test-agent" + +[model] +provider = "groq" +model = "llama-3.3-70b-versatile" +system_prompt = """ +You are a "helpful" assistant. +When users say "hello", respond warmly. +""" +"#; + let manifest: AgentManifest = toml::from_str(toml_str).unwrap(); + assert!(manifest.model.system_prompt.contains("\"helpful\"")); + assert!(manifest.model.system_prompt.contains("\"hello\"")); + } + + #[test] + fn test_manifest_multiline_system_prompt_with_code_blocks() { + // System prompt containing markdown-style code blocks + let toml_str = r#" +name = "coder" + +[model] +provider = "deepseek" +model = "deepseek-chat" +system_prompt = """ +You are a coding assistant. + +Example output format: +```python +def hello(): + print("world") +``` + +Always use proper indentation. +""" +"#; + let manifest: AgentManifest = toml::from_str(toml_str).unwrap(); + assert!(manifest.model.system_prompt.contains("```python")); + assert!(manifest.model.system_prompt.contains("def hello()")); + } + + #[test] + fn test_manifest_single_line_system_prompt_still_works() { + // Ensure the old single-line format still parses fine + let toml_str = r#" +name = "simple" + +[model] +provider = "groq" +model = "llama-3.3-70b-versatile" +system_prompt = "You are a helpful assistant." +"#; + let manifest: AgentManifest = toml::from_str(toml_str).unwrap(); + assert_eq!(manifest.model.system_prompt, "You are a helpful assistant."); + } + + #[test] + fn test_manifest_wizard_custom_profile_with_capabilities() { + // Full wizard output when profile=custom with capabilities block + let toml_str = r#" +name = "brand-guardian" +module = "builtin:chat" + +[model] +provider = "google" +model = "gemini-3-flash-preview" +system_prompt = """ +You are Brand Guardian. +Protect brand consistency across all touchpoints. +""" + +[capabilities] +memory_read = ["*"] +memory_write = ["self.*"] +"#; + let manifest: AgentManifest = toml::from_str(toml_str).unwrap(); + assert_eq!(manifest.name, "brand-guardian"); + assert!(manifest.model.system_prompt.contains("Brand Guardian")); + assert_eq!(manifest.capabilities.memory_read, vec!["*".to_string()]); + assert_eq!( + manifest.capabilities.memory_write, + vec!["self.*".to_string()] + ); + } }