Files
kandev/apps/backend/internal/orchestrator/messagequeue/repository.go
T
Carlos Florêncio 7893a5e33e fix: trigger workflow on agent task messages (#1555)
* fix: trigger workflow on agent task messages

* fix: harden agent task message dispatch

* fix: preserve completed task message sessions

* fix: restore review step on task message failure

* test: cover completed task message handoff

* fix: publish task message rollback events

* fix: restore task message session rollback

* fix: avoid orphaned task message rows

* docs: explain task message prompt recording

* fix: harden task message rollback cleanup

* fix: complete task message rollback cleanup

* fix: restore task message dispatch side effects

* fix: restore task state after capture failure

* fix: simplify task message rollback cleanup

* fix: preserve task message rollback snapshots

* fix: restore task message turn-start snapshots

* fix: preserve task message queue snapshots

* fix: fail closed on office task lookup

---------

Co-authored-by: root <root@kandev.ts.net>
2026-06-30 18:28:28 +01:00

72 lines
4.0 KiB
Go

package messagequeue
import "context"
// Repository abstracts persistent storage of queued messages and pending moves.
// Operations on queued messages are atomic per session.
type Repository interface {
// Insert appends a new entry at the tail of the session's FIFO queue.
// Returns ErrQueueFull if maxPerSession > 0 and the count would exceed it.
Insert(ctx context.Context, msg *QueuedMessage, maxPerSession int) error
// AppendOrInsertTail concatenates content onto the tail entry when the tail
// exists AND its QueuedBy matches the supplied queuedBy. Otherwise inserts a
// new entry. Returns the resulting entry and whether the call appended (true)
// or inserted (false). Honors maxPerSession when inserting.
AppendOrInsertTail(ctx context.Context, sessionID, taskID, content, model, queuedBy string, planMode bool, attachments []MessageAttachment, metadata map[string]interface{}, maxPerSession int) (*QueuedMessage, bool, error)
// InsertOrReplaceByCoalesceKey replaces an existing queued entry with the
// same session, queued_by, and metadata coalesce key. If no matching entry
// exists, it inserts a new tail entry when allowInsert is true. Returns
// ErrEntryNotFound when allowInsert is false and no matching entry exists.
InsertOrReplaceByCoalesceKey(ctx context.Context, msg *QueuedMessage, coalesceKey string, maxPerSession int, allowInsert bool) (*QueuedMessage, bool, error)
// ListBySession returns all entries for a session ordered by position ascending.
ListBySession(ctx context.Context, sessionID string) ([]QueuedMessage, error)
// CountBySession returns the number of entries for a session.
CountBySession(ctx context.Context, sessionID string) (int, error)
// TakeHead atomically returns and deletes the lowest-position entry for the
// session. Returns nil, nil if the queue is empty.
TakeHead(ctx context.Context, sessionID string) (*QueuedMessage, error)
// UpdateContent replaces the content/attachments of an entry. The session
// scope (`AND session_id = ?`) is mandatory so a caller can't update an
// entry by guessing its UUID across sessions. If queuedBy is non-empty the
// update only succeeds when the entry's queued_by also matches. Returns
// ErrEntryNotFound when no row matches all guards.
UpdateContent(ctx context.Context, sessionID, entryID, content string, attachments []MessageAttachment, queuedBy string) error
// DeleteByID removes a single entry. The session scope (`AND session_id = ?`)
// is mandatory so a caller can't delete an entry by guessing its UUID across
// sessions — the queue_full MCP payload deliberately discloses sibling-task
// entry IDs, so without this guard a hostile agent could prune another
// task's queue. Agent-authored entries (`queued_by="agent"`) are immutable
// from this path and return ErrEntryNotFound.
DeleteByID(ctx context.Context, sessionID, entryID string) error
// DeleteAllBySession removes every entry for a session. Returns the count of
// rows removed.
DeleteAllBySession(ctx context.Context, sessionID string) (int, error)
// TransferSession moves all entries (and any pending move) from oldSessionID
// to newSessionID. Used on workflow session switches.
TransferSession(ctx context.Context, oldSessionID, newSessionID string) error
// ReplaceSession replaces a session's queued entries and pending move with
// the supplied snapshot, preserving queued-message identity fields.
ReplaceSession(ctx context.Context, sessionID string, entries []QueuedMessage, pendingMove *PendingMove) error
// SetPendingMove upserts the deferred workflow move for a session.
SetPendingMove(ctx context.Context, sessionID string, move *PendingMove) error
// GetPendingMove returns the deferred move for a session without removing it.
// Returns nil, nil if absent.
GetPendingMove(ctx context.Context, sessionID string) (*PendingMove, error)
// TakePendingMove returns and removes the deferred move for a session.
// Returns nil, nil if absent.
TakePendingMove(ctx context.Context, sessionID string) (*PendingMove, error)
}