agent-orchestrator/backend/internal/domain/session.go

73 lines
2.9 KiB
Go

package domain
import "time"
// These ID types are distinct string types so they can't be swapped at a call
// site by accident.
type (
// SessionID identifies a session.
SessionID string
// ProjectID identifies a project.
ProjectID string
// IssueID identifies a tracker issue.
IssueID string
)
// SessionKind distinguishes a worker session from an orchestrator session.
type SessionKind string
// Session kinds.
const (
KindWorker SessionKind = "worker"
KindOrchestrator SessionKind = "orchestrator"
)
// SessionMetadata is the typed, off-canonical metadata for a session: the
// operational handles and seed inputs the Session Manager and reaper need but
// that are NOT part of the canonical lifecycle. The set of fields is fixed here
// (no free-form keys), so what a session can carry is a compile-time fact, and
// it is folded into the sessions row off the CDC path.
//
// Empty fields mean "unset": the LCM merges metadata without overwriting a
// stored value with an empty one, so a partial write (spawn setting only the
// runtime handle) does not clobber a value set earlier (the branch at creation).
type SessionMetadata struct {
Branch string `json:"branch,omitempty"`
WorkspacePath string `json:"workspacePath,omitempty"`
RuntimeHandleID string `json:"runtimeHandleId,omitempty"`
RuntimeName string `json:"runtimeName,omitempty"`
AgentSessionID string `json:"agentSessionId,omitempty"`
Prompt string `json:"prompt,omitempty"`
}
// IsZero reports whether no metadata field is set.
func (m SessionMetadata) IsZero() bool { return m == SessionMetadata{} }
// SessionRecord is the PERSISTENCE shape: identity, canonical lifecycle, and
// metadata — everything the store holds, and nothing derived. The store reads
// and writes records; it never produces the derived display status.
//
// Metadata is json:"-" on purpose: it lives off the canonical path, so it must
// never ride along in the change_log / snapshot payloads. Enforcing that at the
// type level means no caller has to remember to scrub it before marshalling.
type SessionRecord struct {
ID SessionID `json:"id"`
ProjectID ProjectID `json:"projectId"`
IssueID IssueID `json:"issueId,omitempty"`
Kind SessionKind `json:"kind"`
Lifecycle CanonicalSessionLifecycle `json:"lifecycle"`
Metadata SessionMetadata `json:"-"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// Session is the read-model returned across the API boundary (to controllers,
// then the frontend): a SessionRecord plus the DERIVED display Status. The
// Session Manager is the single producer of Status — it builds a Session from a
// stored SessionRecord by calling DeriveLegacyStatus, so the store and API
// never recompute (or accidentally persist) it.
type Session struct {
SessionRecord
Status SessionStatus `json:"status"`
}