package domain // Exchange is an immutable logical conversation ending in an assistant message. type Exchange struct { parent *Exchange runID string parentRunID string messages []RenderedMessage count int } // CaptureExchange creates a new exchange without modifying its parent. func CaptureExchange(parent *Exchange, runID string, messages []RenderedMessage, output string) *Exchange { turn := make([]RenderedMessage, len(messages)+1) copyRenderedMessages(turn, messages) turn[len(messages)] = RenderedMessage{Role: RoleAssistant, Content: output} exchange := &Exchange{parent: parent, runID: runID, messages: turn, count: len(turn)} if parent != nil { exchange.parentRunID = parent.runID exchange.count += parent.count } return exchange } // RunID returns the identity of the run that produced this exchange. func (e *Exchange) RunID() string { if e == nil { return "" } return e.runID } // ParentRunID returns the immediate source run identity, if present. func (e *Exchange) ParentRunID() string { if e == nil { return "" } return e.parentRunID } // MessageCount returns the number of messages in the logical history. func (e *Exchange) MessageCount() int { if e == nil { return 0 } return e.count } // Messages returns a detached copy of the complete logical history. func (e *Exchange) Messages() []RenderedMessage { if e == nil { return nil } messages := make([]RenderedMessage, e.count) for current := e; current != nil; current = current.parent { start := current.count - len(current.messages) copyRenderedMessages(messages[start:current.count], current.messages) } return messages }