package session import ( "ollie/agent" "os" "strings" ) func LoadAgentConfig(agentsDir, name string, open func(string) (*os.File, error)) *agent.AgentConfig { if open == nil { open = os.Open } path := agent.AgentConfigPath(agentsDir, name) f, err := open(path) if err != nil { return nil } defer f.Close() cfg, _ := agent.Load(f) return cfg } // FormatEvent converts an agent Event to bytes for appending to a chat log. // Streaming roles (assistant, reasoning) return only the content chunk; // the caller (startEventLog) is responsible for writing the [[[role]]] header. func FormatEvent(ev agent.Event) []byte { switch ev.Role { case "user": return []byte("[[[user]]]\n" + ev.Content + "\n[[[end]]]\n") case "assistant": return []byte(ev.Content) case "reasoning": return []byte(ev.Content) case "call": args := squashWhitespace(ev.Content) return []byte("[[[call:" + ev.Name + "]]]\n```\n" + args + "\n```\n[[[end]]]\n") case "tool": format := ev.OutputFormat fence := "```" if format != "" { fence += format } return []byte("[[[tool:" + ev.Name + "]]]\n" + fence + "\n" + strings.TrimRight(ev.Content, "\n") + "\n```\n[[[end]]]\n") case "error": return []byte("[[[error]]]\n" + ev.Content + "\n[[[end]]]\n") case "info": return []byte("[[[info]]]\n" + ev.Content + "\n[[[end]]]\n") case "retry": return []byte(ev.Content) case "stalled": return []byte("[[[stalled]]]\n[[[end]]]\n") case "maxsteps": return []byte("[[[maxsteps]]]\n" + ev.Content + "\n[[[end]]]\n") default: return nil } } func squashWhitespace(s string) string { return strings.Join(strings.Fields(s), " ") }