初始化仓库

This commit is contained in:
2026-06-02 23:14:41 +08:00
commit 0bc3f02670
520 changed files with 191097 additions and 0 deletions
@@ -0,0 +1,302 @@
package engine_matrix
import (
"context"
"fmt"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/chenhg5/cc-connect/core"
)
type promptRecord struct {
sessionID string
prompt string
}
type matrixAgent struct {
mu sync.Mutex
sessions []*matrixSession
list []core.AgentSessionInfo
records []promptRecord
}
func newMatrixAgent() *matrixAgent {
return &matrixAgent{}
}
func (a *matrixAgent) Name() string { return "matrix-agent" }
func (a *matrixAgent) StartSession(_ context.Context, sessionID string) (core.AgentSession, error) {
a.mu.Lock()
defer a.mu.Unlock()
if sessionID == "" {
sessionID = fmt.Sprintf("agent-session-%d", len(a.sessions)+1)
}
session := &matrixSession{agent: a, id: sessionID, alive: true, events: make(chan core.Event, 32)}
a.sessions = append(a.sessions, session)
a.list = append(a.list, core.AgentSessionInfo{
ID: sessionID,
Summary: "release matrix session",
MessageCount: 1,
ModifiedAt: time.Now(),
})
return session, nil
}
func (a *matrixAgent) ListSessions(_ context.Context) ([]core.AgentSessionInfo, error) {
a.mu.Lock()
defer a.mu.Unlock()
return append([]core.AgentSessionInfo(nil), a.list...), nil
}
func (a *matrixAgent) Stop() error {
a.mu.Lock()
sessions := append([]*matrixSession(nil), a.sessions...)
a.mu.Unlock()
for _, session := range sessions {
_ = session.Close()
}
return nil
}
func (a *matrixAgent) addRecord(sessionID, prompt string) {
a.mu.Lock()
defer a.mu.Unlock()
a.records = append(a.records, promptRecord{sessionID: sessionID, prompt: prompt})
}
func (a *matrixAgent) waitRecords(t *testing.T, n int) []promptRecord {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
a.mu.Lock()
if len(a.records) >= n {
out := append([]promptRecord(nil), a.records...)
a.mu.Unlock()
return out
}
a.mu.Unlock()
time.Sleep(10 * time.Millisecond)
}
a.mu.Lock()
defer a.mu.Unlock()
t.Fatalf("timeout waiting for %d prompts, got %d: %#v", n, len(a.records), a.records)
return nil
}
func (a *matrixAgent) recordCount() int {
a.mu.Lock()
defer a.mu.Unlock()
return len(a.records)
}
type matrixSession struct {
mu sync.Mutex
agent *matrixAgent
id string
alive bool
events chan core.Event
counter int
}
func (s *matrixSession) Send(prompt string, _ []core.ImageAttachment, _ []core.FileAttachment) error {
s.mu.Lock()
id := s.id
s.counter++
count := s.counter
s.mu.Unlock()
s.agent.addRecord(id, prompt)
s.events <- core.Event{Type: core.EventResult, Content: "matrix response " + id + " #" + strconv.Itoa(count), Done: true}
return nil
}
func (s *matrixSession) Events() <-chan core.Event { return s.events }
func (s *matrixSession) RespondPermission(string, core.PermissionResult) error {
return nil
}
func (s *matrixSession) CurrentSessionID() string {
s.mu.Lock()
defer s.mu.Unlock()
return s.id
}
func (s *matrixSession) Alive() bool {
s.mu.Lock()
defer s.mu.Unlock()
return s.alive
}
func (s *matrixSession) Close() error {
s.mu.Lock()
defer s.mu.Unlock()
if !s.alive {
return nil
}
s.alive = false
close(s.events)
return nil
}
type matrixPlatform struct {
mu sync.Mutex
texts []string
}
func (p *matrixPlatform) Name() string { return "matrix" }
func (p *matrixPlatform) Start(core.MessageHandler) error {
return nil
}
func (p *matrixPlatform) Stop() error { return nil }
func (p *matrixPlatform) Reply(_ context.Context, replyCtx any, content string) error {
return p.Send(context.Background(), replyCtx, content)
}
func (p *matrixPlatform) Send(_ context.Context, _ any, content string) error {
p.mu.Lock()
defer p.mu.Unlock()
p.texts = append(p.texts, content)
return nil
}
func (p *matrixPlatform) clear() {
p.mu.Lock()
defer p.mu.Unlock()
p.texts = nil
}
func (p *matrixPlatform) snapshot() []string {
p.mu.Lock()
defer p.mu.Unlock()
return append([]string(nil), p.texts...)
}
func (p *matrixPlatform) waitTextContaining(t *testing.T, substr string) string {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
for _, text := range p.snapshot() {
if strings.Contains(strings.ToLower(text), strings.ToLower(substr)) {
return text
}
}
time.Sleep(10 * time.Millisecond)
}
t.Fatalf("timeout waiting for %q, got %#v", substr, p.snapshot())
return ""
}
func newMatrixEngine(t *testing.T) (*core.Engine, *matrixAgent, *matrixPlatform) {
t.Helper()
agent := newMatrixAgent()
platform := &matrixPlatform{}
engine := core.NewEngine("release-core", agent, []core.Platform{platform}, t.TempDir()+"/sessions.json", core.LangEnglish)
t.Cleanup(func() {
engine.Stop()
_ = agent.Stop()
})
return engine, agent, platform
}
func matrixMessage(content string) *core.Message {
return &core.Message{
SessionKey: "matrix:chat-1:user-1",
Platform: "matrix",
UserID: "user-1",
UserName: "Release Tester",
ChatName: "Release Room",
Content: content,
ReplyCtx: "reply-ctx",
}
}
func receive(engine *core.Engine, platform *matrixPlatform, content string) {
engine.ReceiveMessage(platform, matrixMessage(content))
}
func TestSessionLifecycleCommandsThroughReceiveMessage(t *testing.T) {
engine, agent, platform := newMatrixEngine(t)
receive(engine, platform, "first user turn")
agent.waitRecords(t, 1)
platform.waitTextContaining(t, "matrix response")
platform.clear()
receive(engine, platform, "/new release-named")
platform.waitTextContaining(t, "release-named")
platform.clear()
receive(engine, platform, "second user turn")
agent.waitRecords(t, 2)
platform.waitTextContaining(t, "matrix response")
platform.clear()
receive(engine, platform, "/list")
platform.waitTextContaining(t, "release-named")
platform.clear()
receive(engine, platform, "/name renamed-release")
platform.waitTextContaining(t, "renamed-release")
platform.clear()
receive(engine, platform, "/list")
platform.waitTextContaining(t, "renamed-release")
platform.clear()
receive(engine, platform, "/current")
platform.waitTextContaining(t, "session")
platform.clear()
receive(engine, platform, "/status")
platform.waitTextContaining(t, "user-1")
}
func TestAliasDisabledCommandAndBannedWordsThroughReceiveMessage(t *testing.T) {
engine, agent, platform := newMatrixEngine(t)
engine.AddAlias("帮助", "/whoami")
receive(engine, platform, "帮助")
platform.waitTextContaining(t, "user-1")
if got := agent.recordCount(); got != 0 {
t.Fatalf("alias to command should not reach agent, got %d prompts", got)
}
platform.clear()
engine.SetDisabledCommands([]string{"whoami"})
receive(engine, platform, "/whoami")
platform.waitTextContaining(t, "disabled")
if got := agent.recordCount(); got != 0 {
t.Fatalf("disabled command should not reach agent, got %d prompts", got)
}
platform.clear()
engine.SetBannedWords([]string{"forbidden"})
receive(engine, platform, "this contains forbidden content")
platform.waitTextContaining(t, "blocked")
if got := agent.recordCount(); got != 0 {
t.Fatalf("banned message should not reach agent, got %d prompts", got)
}
}
func TestCustomPromptCommandThroughReceiveMessage(t *testing.T) {
engine, agent, platform := newMatrixEngine(t)
engine.AddCommand("daily", "Daily summary", "Summarize release status for {{1}}", "", "", "release-test")
receive(engine, platform, "/daily beta")
records := agent.waitRecords(t, 1)
if !strings.Contains(records[0].prompt, "Summarize release status for beta") {
t.Fatalf("custom command prompt = %q", records[0].prompt)
}
platform.waitTextContaining(t, "matrix response")
}
func TestUnknownSlashCommandNotifiesThenFallsThroughToAgent(t *testing.T) {
engine, agent, platform := newMatrixEngine(t)
receive(engine, platform, "/not-a-command keep this request")
platform.waitTextContaining(t, "forwarding")
records := agent.waitRecords(t, 1)
if !strings.Contains(records[0].prompt, "/not-a-command keep this request") {
t.Fatalf("unknown slash command should fall through to agent, got prompt %q", records[0].prompt)
}
}