Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

181 lines
5.6 KiB
Go

package storesqlite
import (
"context"
"database/sql"
"errors"
"fmt"
"log/slog"
"strings"
)
func (s *Store) ListSessionMessages(
ctx context.Context,
input ListSessionMessagesInput,
) (MessagePage, bool, error) {
if s == nil || s.db == nil {
return MessagePage{}, false, errors.New("workspace database is not initialized")
}
workspaceID := strings.TrimSpace(input.WorkspaceID)
agentSessionID := strings.TrimSpace(input.AgentSessionID)
if workspaceID == "" || agentSessionID == "" {
return MessagePage{}, false, nil
}
if _, ok, err := s.GetSession(ctx, workspaceID, agentSessionID); err != nil || !ok {
return MessagePage{}, ok, err
}
queryLimit := input.Limit
if queryLimit > 0 {
queryLimit++
}
turnID := strings.TrimSpace(input.TurnID)
messageID := strings.TrimSpace(input.MessageID)
where := []string{
"workspace_id = ?",
"agent_session_id = ?",
"deleted_at_unix_ms = 0",
`(
COALESCE(TRIM(turn_id), '') = ''
OR NOT EXISTS (
SELECT 1 FROM workspace_agent_turn_history history
WHERE history.workspace_id = workspace_agent_messages.workspace_id
AND history.agent_session_id = workspace_agent_messages.agent_session_id
AND history.turn_id = workspace_agent_messages.turn_id
AND history.history_state = 'retracted'
)
)`,
}
args := []any{workspaceID, agentSessionID}
if messageID != "" {
where = append(where, "message_id = ?")
args = append(args, messageID)
}
if turnID != "" {
where = append(where, "turn_id = ?")
args = append(args, turnID)
}
order := input.Order
if order == "" {
order = MessageOrderAsc
}
var rows *sql.Rows
var err error
switch order {
case MessageOrderDesc:
if input.BeforeVersion > 0 {
whereWithCursor := append(append([]string{}, where...), "version < ?")
argsWithCursor := append(append([]any{}, args...), input.BeforeVersion, queryLimit)
rows, err = s.db.QueryContext(ctx, `
SELECT id, agent_session_id, message_id, version, turn_id, role, kind, status,
semantics_json, payload_json, occurred_at_unix_ms, started_at_unix_ms, completed_at_unix_ms,
created_at_unix_ms, updated_at_unix_ms
FROM workspace_agent_messages
WHERE `+strings.Join(whereWithCursor, " AND ")+`
ORDER BY version DESC, id DESC
LIMIT ?
`, argsWithCursor...)
} else {
argsWithLimit := append(append([]any{}, args...), queryLimit)
rows, err = s.db.QueryContext(ctx, `
SELECT id, agent_session_id, message_id, version, turn_id, role, kind, status,
semantics_json, payload_json, occurred_at_unix_ms, started_at_unix_ms, completed_at_unix_ms,
created_at_unix_ms, updated_at_unix_ms
FROM workspace_agent_messages
WHERE `+strings.Join(where, " AND ")+`
ORDER BY version DESC, id DESC
LIMIT ?
`, argsWithLimit...)
}
case MessageOrderAsc:
whereWithCursor := append(append([]string{}, where...), "version > ?")
argsWithCursor := append(append([]any{}, args...), input.AfterVersion, queryLimit)
rows, err = s.db.QueryContext(ctx, `
SELECT id, agent_session_id, message_id, version, turn_id, role, kind, status,
semantics_json, payload_json, occurred_at_unix_ms, started_at_unix_ms, completed_at_unix_ms,
created_at_unix_ms, updated_at_unix_ms
FROM workspace_agent_messages
WHERE `+strings.Join(whereWithCursor, " AND ")+`
ORDER BY version ASC, id ASC
LIMIT ?
`, argsWithCursor...)
default:
return MessagePage{}, false, fmt.Errorf("unsupported workspace agent message order: %s", order)
}
if err != nil {
slog.Warn("workspace agent messages query failed",
"event", "workspace.agent_session.messages.sqlite.query_failed",
"workspace_id", workspaceID,
"agent_session_id", agentSessionID,
"after_version", input.AfterVersion,
"before_version", input.BeforeVersion,
"order", order,
"limit", input.Limit,
"query_limit", queryLimit,
"error", err,
)
return MessagePage{}, false, fmt.Errorf("list workspace agent messages: %w", err)
}
defer rows.Close()
messages := make([]Message, 0)
for rows.Next() {
message, err := scanAgentMessage(rows)
if err != nil {
slog.Warn("workspace agent message row scan failed",
"event", "workspace.agent_session.messages.sqlite.scan_failed",
"workspace_id", workspaceID,
"agent_session_id", agentSessionID,
"after_version", input.AfterVersion,
"before_version", input.BeforeVersion,
"order", order,
"limit", input.Limit,
"query_limit", queryLimit,
"scanned_message_count", len(messages),
"error", err,
)
return MessagePage{}, false, err
}
messages = append(messages, message)
}
if err := rows.Err(); err != nil {
slog.Warn("workspace agent messages row iteration failed",
"event", "workspace.agent_session.messages.sqlite.iterate_failed",
"workspace_id", workspaceID,
"agent_session_id", agentSessionID,
"after_version", input.AfterVersion,
"before_version", input.BeforeVersion,
"order", order,
"limit", input.Limit,
"query_limit", queryLimit,
"scanned_message_count", len(messages),
"error", err,
)
return MessagePage{}, false, fmt.Errorf("iterate workspace agent messages: %w", err)
}
if err := rows.Close(); err != nil {
return MessagePage{}, false,
fmt.Errorf("close workspace agent message rows: %w", err)
}
hasMore := false
if input.Limit > 0 && len(messages) > input.Limit {
hasMore = true
messages = messages[:input.Limit]
}
latestVersion := input.AfterVersion
if order == MessageOrderDesc {
latestVersion = 0
}
for _, message := range messages {
if message.Version > latestVersion {
latestVersion = message.Version
}
}
return MessagePage{
AgentSessionID: agentSessionID,
Messages: messages,
LatestVersion: latestVersion,
HasMore: hasMore,
}, true, nil
}