package panto import "testing" // memStore is a minimal custom SessionStore that mints its sessions via // NewSession (gap #1) rather than dispatching back into the vtable. type memStore struct { self *GoSessionStore created bool } func (m *memStore) Create() (*Session, error) { m.created = true return NewSession(m.self, SessionInfo{ID: "sess-1"}) } func (m *memStore) List() ([]SessionInfo, error) { return nil, nil } func (m *memStore) Resolve(id string) (*Session, bool, error) { if id != "sess-1" { return nil, false, nil } s, err := NewSession(m.self, SessionInfo{ID: id}) return s, err == nil, err } func (m *memStore) Latest() (*Session, bool, error) { return m.Resolve("sess-1") } func (m *memStore) Load(id string) (*Conversation, bool, error) { return nil, false, nil } func (m *memStore) AppendMessages(string, []PersistentMessage) error { return nil } // Gap #1: a Go store can mint and return a *Session from its own Create() // without recursing back into the vtable. func TestCustomStoreMintsSession(t *testing.T) { Init() defer Deinit() impl := &memStore{} gss, err := NewGoSessionStore(impl) if err != nil { t.Fatalf("NewGoSessionStore: %v", err) } defer gss.Close() impl.self = gss sess, err := gss.Create() if err != nil { t.Fatalf("Create: %v", err) } if sess == nil || sess.ptr == nil { t.Fatal("Create returned nil session") } if !impl.created { t.Fatal("custom Create() was not invoked") } got, ok, err := gss.Resolve("sess-1") if err != nil || !ok || got == nil { t.Fatalf("Resolve: got=%v ok=%v err=%v", got, ok, err) } } // Gap #3: rebuild a conversation containing tool_use / tool_result / thinking // via MessageBuilder, then read the blocks back losslessly. func TestMessageBuilderRoundTrip(t *testing.T) { Init() defer Deinit() conv, err := NewConversation() if err != nil { t.Fatalf("NewConversation: %v", err) } defer conv.Close() ub, err := NewMessageBuilder(RoleUser) if err != nil { t.Fatalf("NewMessageBuilder(user): %v", err) } if err := ub.AddToolResult("call-1", "result text", false); err != nil { t.Fatalf("AddToolResult: %v", err) } if err := conv.AddMessage(ub, nil); err != nil { t.Fatalf("AddMessage(user): %v", err) } ab, err := NewMessageBuilder(RoleAssistant) if err != nil { t.Fatalf("NewMessageBuilder(assistant): %v", err) } if err := ab.AddText("let me search"); err != nil { t.Fatalf("AddText: %v", err) } if err := ab.AddThinking("hmm", "sig", AnthropicMessages, "https://api", "claude"); err != nil { t.Fatalf("AddThinking: %v", err) } if err := ab.AddToolUse("call-1", "search", `{"q":1}`); err != nil { t.Fatalf("AddToolUse: %v", err) } if err := conv.AddMessage(ab, &Usage{Input: 10, Output: 5}); err != nil { t.Fatalf("AddMessage(assistant): %v", err) } if got := conv.MessageCount(); got != 2 { t.Fatalf("MessageCount = %d, want 2", got) } user := conv.Message(0).Snapshot() if user.Role != RoleUser || len(user.Content) != 1 { t.Fatalf("user message: role=%v blocks=%d", user.Role, len(user.Content)) } if tr := user.Content[0]; tr.Tag != BlockToolResult || tr.ToolID != "call-1" || tr.Text != "result text" || tr.IsError { t.Fatalf("tool_result block: %+v", tr) } asst := conv.Message(1).Snapshot() if asst.Role != RoleAssistant || len(asst.Content) != 3 { t.Fatalf("assistant message: role=%v blocks=%d", asst.Role, len(asst.Content)) } if b := asst.Content[0]; b.Tag != BlockText || b.Text != "let me search" { t.Fatalf("text block: %+v", b) } if b := asst.Content[1]; b.Tag != BlockThinking || b.Text != "hmm" { t.Fatalf("thinking block: %+v", b) } if b := asst.Content[2]; b.Tag != BlockToolUse || b.ToolID != "call-1" || b.ToolName != "search" || b.Text != `{"q":1}` { t.Fatalf("tool_use block: %+v", b) } if asst.Usage == nil || asst.Usage.Input != 10 || asst.Usage.Output != 5 { t.Fatalf("assistant usage: %+v", asst.Usage) } } func testConfig() Config { return Config{ Provider: ProviderConfig{ OpenAIChat: &OpenAIChatConfig{ APIKey: "test-key", BaseURL: "http://localhost", Model: "test-model", MaxTokens: 16, }, }, } } func TestNewAgentSetSystemPromptDoesNotCrash(t *testing.T) { Init() defer Deinit() a, err := NewAgent(testConfig()) if err != nil { t.Fatalf("NewAgent: %v", err) } defer a.Close() if err := a.SetSystemPrompt("be brief"); err != nil { t.Fatalf("SetSystemPrompt: %v", err) } if err := a.AddSystemMessage("also be accurate"); err != nil { t.Fatalf("AddSystemMessage: %v", err) } }