源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
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
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
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user