fix(agent): ingest steering messages into seahorse SQLite

Steering messages were only persisted to session JSONL but not ingested
into seahorse SQLite, causing them to be missing from context assembly.

Added `ts.ingestMessage(turnCtx, al, pm)` call in the steering message
injection block alongside the existing JSONL persistence.

Test: TestSeahorseSteeringMessageIngested verifies steering messages
appear in seahorse SQLite DB after being processed.
This commit is contained in:
Liu Yuan 2026-04-03 15:18:43 +08:00
parent f77ddbeebf
commit 03e26a6d20
2 changed files with 105 additions and 0 deletions

View file

@ -866,3 +866,107 @@ func TestSeahorseAssembleSummaryNotInMessages(t *testing.T) {
t.Error("Summary content should appear in response.Summary field") t.Error("Summary content should appear in response.Summary field")
} }
} }
// TestSeahorseSteeringMessageIngested verifies that steering messages are ingested
// into seahorse SQLite, not just session JSONL.
func TestSeahorseSteeringMessageIngested(t *testing.T) {
cfg := &config.Config{
Agents: config.AgentsConfig{
Defaults: config.AgentDefaults{
Workspace: t.TempDir(),
ModelName: "test-model",
MaxTokens: 4096,
MaxToolIterations: 10,
ContextManager: "seahorse",
},
},
}
msgBus := bus.NewMessageBus()
mockProvider := &simpleMockProvider{response: "I received your message."}
al := NewAgentLoop(cfg, msgBus, mockProvider)
defaultAgent := al.registry.GetDefaultAgent()
if defaultAgent == nil {
t.Fatal("expected default agent")
}
ctx := context.Background()
sessionKey := "test-steering-ingest"
// First turn: establish conversation
_, err := al.runAgentLoop(ctx, defaultAgent, processOptions{
SessionKey: sessionKey,
Channel: "cli",
ChatID: "direct",
UserMessage: "hello",
DefaultResponse: defaultResponse,
EnableSummary: false,
SendResponse: false,
})
if err != nil {
t.Fatalf("first runAgentLoop failed: %v", err)
}
// Inject a steering message
steerErr := al.InjectSteering(providers.Message{
Role: "user",
Content: "steering message content",
})
if steerErr != nil {
t.Fatalf("InjectSteering failed: %v", steerErr)
}
// Second turn: should process steering message
_, err = al.runAgentLoop(ctx, defaultAgent, processOptions{
SessionKey: sessionKey,
Channel: "cli",
ChatID: "direct",
UserMessage: "continue",
DefaultResponse: defaultResponse,
EnableSummary: false,
SendResponse: false,
})
if err != nil {
t.Fatalf("second runAgentLoop failed: %v", err)
}
// Get the seahorse engine from context manager
seahorseCM, ok := al.contextManager.(*seahorseContextManager)
if !ok {
t.Fatal("expected seahorseContextManager")
}
// Check DB for steering message
store := seahorseCM.engine.GetRetrieval().Store()
conv, err := store.GetOrCreateConversation(ctx, sessionKey)
if err != nil {
t.Fatalf("GetOrCreateConversation: %v", err)
}
stored, err := store.GetMessages(ctx, conv.ConversationID, 20, 0)
if err != nil {
t.Fatalf("GetMessages: %v", err)
}
t.Logf("DB has %d messages:", len(stored))
for i, msg := range stored {
content := msg.Content
if len(content) > 40 {
content = content[:40] + "..."
}
t.Logf(" msg[%d]: role=%s content=%q", i, msg.Role, content)
}
// Find steering message in stored messages
foundSteering := false
for _, msg := range stored {
if msg.Content == "steering message content" {
foundSteering = true
break
}
}
if !foundSteering {
t.Error("STEERING MESSAGE NOT IN SEAHORSE DB: steering message should be ingested into SQLite")
}
}

View file

@ -1857,6 +1857,7 @@ turnLoop:
if !ts.opts.NoHistory { if !ts.opts.NoHistory {
ts.agent.Sessions.AddFullMessage(ts.sessionKey, pm) ts.agent.Sessions.AddFullMessage(ts.sessionKey, pm)
ts.recordPersistedMessage(pm) ts.recordPersistedMessage(pm)
ts.ingestMessage(turnCtx, al, pm)
} }
logger.InfoCF("agent", "Injected steering message into context", logger.InfoCF("agent", "Injected steering message into context",
map[string]any{ map[string]any{