feat(artifactvault): close same-tx staging, recover-apply, and GET zero-write gaps

Approve/commit now write FAV staging in the domain transaction, recover-apply materializes scratch collections, and unpublished or GET paths cannot mutate the registry.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-14 12:52:28 +00:00
co-authored by Cursor
parent ba8e872b59
commit ad9b67d599
19 changed files with 838 additions and 25 deletions
+46
View File
@@ -332,3 +332,49 @@ func (m *ContextManager) GetChatTurn(ctx context.Context, sessionID, clientTurnI
cp := *rec
return &cp, nil
}
// GetChatTurnByAssistantMessageID 按会话 + 助手消息定位回合;用于 copy_only 显式 pin。
func (m *ContextManager) GetChatTurnByAssistantMessageID(ctx context.Context, sessionID string, assistantMessageID int64) (*ChatTurnRecord, error) {
if sessionID == "" || assistantMessageID <= 0 {
return nil, ErrChatTurnNotFound
}
if m.pool != nil {
var rec ChatTurnRecord
var completedAt *time.Time
var leaseExpiresAt *time.Time
err := m.pool.QueryRow(ctx, `
SELECT id, session_id, client_turn_id,
COALESCE(user_id, 0), COALESCE(project_id, 0),
COALESCE(user_message_id, 0), COALESCE(assistant_message_id, 0),
request_body_sha256, status,
COALESCE(first_request_id, ''), COALESCE(last_request_id, ''),
attempt_count, created_at, updated_at, completed_at, lease_expires_at
FROM chat_turns
WHERE session_id = $1 AND assistant_message_id = $2
ORDER BY id DESC
LIMIT 1
`, sessionID, assistantMessageID).Scan(
&rec.ID, &rec.SessionID, &rec.ClientTurnID,
&rec.UserID, &rec.ProjectID,
&rec.UserMessageID, &rec.AssistantMessageID,
&rec.RequestBodySHA256, &rec.Status,
&rec.FirstRequestID, &rec.LastRequestID,
&rec.AttemptCount, &rec.CreatedAt, &rec.UpdatedAt, &completedAt, &leaseExpiresAt,
)
if err != nil {
return nil, ErrChatTurnNotFound
}
rec.CompletedAt = completedAt
rec.LeaseExpiresAt = leaseExpiresAt
return &rec, nil
}
memTurnsMu.Lock()
defer memTurnsMu.Unlock()
for _, rec := range memTurns {
if rec != nil && rec.SessionID == sessionID && rec.AssistantMessageID == assistantMessageID {
cp := *rec
return &cp, nil
}
}
return nil, ErrChatTurnNotFound
}