Documentation
¶
Overview ¶
Package turn registers the LLM-transcript Kind. Each Entry is one memory.Turn; the deterministic ID turn-<index>-<role> makes re-append idempotent and (index, role) order recoverable.
Index ¶
- Constants
- func EntryID(index int, role string) string
- func EntryToTurn(e memory.Entry) (memory.Turn, error)
- func HasOpeningTurn(turns []memory.Turn) bool
- func ReadAll(ctx context.Context, m memory.Memory, scope memory.Scope) ([]memory.Turn, error)
- func RecordInbox(ctx context.Context, m memory.Memory, scope memory.Scope, idx int, text string, ...) error
- type Appender
- type Kind
- type PlanTimelineItem
- type TimelineItem
- type VisibleMessage
Constants ¶
const ( KindName = "turn" IDPrefix = "turn-" )
const ( // VisibleRoleUser is a human message; VisibleRoleAgent is an agent reply. VisibleRoleUser = "user" VisibleRoleAgent = "agent" )
Chat-timeline roles a memory turn maps to, and the internal turn roles this mapping reasons about.
Variables ¶
This section is empty.
Functions ¶
func EntryID ¶
EntryID is the deterministic ID for a turn. The zero-padded index keeps lexicographic ID order aligned with (index, role) order.
func EntryToTurn ¶
EntryToTurn reconstructs a Turn from its Entry. Index/role come from the ID; content/usage from Entry.Content. Exported so cmd/oap/internal/memstream can decode HTTP responses without duplicating the conversion.
func HasOpeningTurn ¶
HasOpeningTurn reports whether turns contains a session's durable opening turn: index 0, role user — the runner's copy of the AgentSession's spec.prompt.inline.
The runner appends it only once its pod is running, so this is false for the whole window between a session being created and its runner starting. A mirror replaying the transcript inside that window can use it to render the opening message from the session's own spec, rather than showing a conversation that appears to have begun with the agent speaking.
Deliberately the SAME test the runner's own replay uses for hadInitialPrompt, so "the opening turn is placed" means one thing on both sides of the transcript.
func ReadAll ¶
ReadAll is a package-level convenience wrapper around NewAppender(m, scope).ReadAll. Callers that do not need the full Appender can use this directly.
func RecordInbox ¶
func RecordInbox(ctx context.Context, m memory.Memory, scope memory.Scope, idx int, text string, author identity.Subject) error
RecordInbox writes a Role:"inbox" turn at the given index, attributed to author (empty for non-human seeds). Mirrors channelsd's memory append; used by tests and channelsd pipelines that need to seed inbox turns without constructing a full Appender.
Types ¶
type Appender ¶
type Appender struct {
// contains filtered or unexported fields
}
Appender reads and writes turns for one session scope through a memory.Memory. It is the runner's transcript client and satisfies the loop's MemoryAppender structurally (ReadAll/ReadAfter/Append).
func (*Appender) Append ¶
Append writes t. Re-appending an identical (index, role) is a no-op; re-appending a different payload at the same (index, role) returns memory.ErrIndexConflict.
Conflict detection is a best-effort check-then-put: the Query/Put pair is not atomic, so a TOCTOU window exists, acceptable only because a single writer per session scope (the runner loop) means no two turn-writers race. A failed conflict-check Query is fatal — returned rather than falling through to a blind Put, which would drop both the error and the check.
type Kind ¶
type Kind struct{}
func (Kind) ContentSchema ¶
func (Kind) IndexedFields ¶
func (Kind) NewScopeHooks ¶
func (Kind) NewScopeHooks(_ memory.Scope) memory.ScopeHooks
func (Kind) WriteAuthority ¶
func (Kind) WriteAuthority() memory.WriteAuthority
WriteAuthority: the transcript is the agent session authoring its own conversation.
type PlanTimelineItem ¶
type PlanTimelineItem struct {
PlanName string
SnapshotJSON []byte
Deleted bool
CreatedAt time.Time
}
PlanTimelineItem is a plan card reconstructed from the "plans" system_notes: one per distinct plan name, positioned at first appearance, carrying the LATEST snapshot (opaque JSON — the memory layer does not import the plan types; a higher layer maps it to the channel card). Deleted is true when the name's latest op was "delete".
type TimelineItem ¶
type TimelineItem struct {
Kind string // "message" | "plan"
Message *VisibleMessage
Plan *PlanTimelineItem
}
TimelineItem is one ordered entry of the resumed conversation: a user/agent message or a plan-card snapshot. It is the superset VisibleMessages filters.
func VisibleTimeline ¶
func VisibleTimeline(turns []memory.Turn) []TimelineItem
VisibleTimeline extracts the ordered user-facing timeline — messages AND plan cards — from a session's raw memory turns; VisibleMessages is this, filtered. "plans" system_notes are grouped by name: one card at its first-appearance index, updated in place by later notes for the same name.
The subtlety it encapsulates is the inbox lifecycle. A mid-session human message is first written as a roleInbox turn (channelsd/chat); the runner's drainInbox promotes it to a roleUser turn at a new index and writes a roleInboxDone marker at the inbox turn's index, but PRESERVES the raw roleInbox turn for audit (memory is append-only). A consumed inbox turn (one with a matching roleInboxDone) therefore duplicates its promoted roleUser turn and is dropped — rendering both showed every drained message twice — while an unconsumed one (not yet drained, the runner is parked) is kept, so a just-sent message still renders before it is promoted.
Other rules: roleUser/roleInbox collapse to VisibleRoleUser and roleAssistant to VisibleRoleAgent; roleInboxDone and any other role contribute nothing; an agent reply's text is pulled from its respond_to_user tool_use block (see visibleText); a tool_result the live surfaces already showed the user renders as an agent message (see renderedLiveText); turns with no user-facing text (a tool-only assistant turn) are dropped. Input need not be sorted — turns are ordered by (index, role) here.
type VisibleMessage ¶
type VisibleMessage struct {
Role string // VisibleRoleUser | VisibleRoleAgent
Text string // user-facing text (respond_to_user text extracted)
CreatedAt time.Time // when the underlying turn was written
Via string // optional surface origin (e.g., "urn:ap:view:artifact:...")
}
VisibleMessage is one user-facing message extracted from a session's memory log — the conversation as a human should see it, with the runner's internal bookkeeping (inbox duplication, tool-only turns, markers) resolved away.
func VisibleMessages ¶
func VisibleMessages(turns []memory.Turn) []VisibleMessage
VisibleMessages extracts the ordered user-facing MESSAGES (no plan cards): VisibleTimeline filtered to messages, so the extraction rules live in one place. See VisibleTimeline.