Documentation
¶
Index ¶
- func ApplyBackgroundTaskSnapshots(turns []UITurn, tasks []UIBackgroundTask)
- type UIAttachment
- type UIBackgroundTask
- type UIExecutionLocation
- type UIForwardRef
- type UIMessage
- type UIMessageStreamConverter
- type UIMessageStreamEvent
- type UIMessageType
- type UIReasoningTiming
- type UIReplyRef
- type UIToolApproval
- type UIToolApprovalOption
- type UITurn
- type UIUserInput
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ApplyBackgroundTaskSnapshots ¶
func ApplyBackgroundTaskSnapshots(turns []UITurn, tasks []UIBackgroundTask)
ApplyBackgroundTaskSnapshots overlays live background-task state onto converted UI turns. This keeps persisted background_started tool results accurate after a page reload.
Types ¶
type UIAttachment ¶
type UIAttachment struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Path string `json:"path,omitempty"`
URL string `json:"url,omitempty"`
Base64 string `json:"base64,omitempty"`
Name string `json:"name,omitempty"`
ContentHash string `json:"content_hash,omitempty"`
BotID string `json:"bot_id,omitempty"`
Mime string `json:"mime,omitempty"`
Size int64 `json:"size,omitempty"`
StorageKey string `json:"storage_key,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
} // @name conversation.UIAttachment
UIAttachment is the normalized attachment shape used by the web frontend.
func UIAttachmentFromAgentAttachment ¶
func UIAttachmentFromAgentAttachment(attachment event.FileAttachment) UIAttachment
UIAttachmentFromAgentAttachment adapts a runtime file attachment to the UI shape, including the bot_id/storage_key metadata extraction the UI needs to resolve asset URLs.
func UIAttachmentsFromTurnAttachments ¶
func UIAttachmentsFromTurnAttachments(botID string, attachments []turn.Attachment) []UIAttachment
UIAttachmentsFromTurnAttachments converts turn attachments into the normalized UI shape for live runtime projections. Runtime state carries media references, not the uploaded bytes: Base64 is deliberately not copied, which would duplicate every attachment into the live backend.
type UIBackgroundTask ¶
type UIBackgroundTask struct {
TaskID string `json:"task_id"`
Status string `json:"status"`
Command string `json:"command,omitempty"`
AgentID string `json:"agent_id,omitempty"`
AgentSessionID string `json:"agent_session_id,omitempty"`
OutputFile string `json:"output_file,omitempty"`
ExitCode int32 `json:"exit_code,omitempty"`
Duration string `json:"duration,omitempty"`
OutputTail string `json:"output_tail,omitempty"`
Stream string `json:"stream,omitempty"`
Chunk string `json:"chunk,omitempty"`
Stalled bool `json:"stalled,omitempty"`
} // @name conversation.UIBackgroundTask
UIBackgroundTask is the compact background exec state sent to the Web UI.
type UIExecutionLocation ¶
type UIForwardRef ¶
type UIForwardRef struct {
MessageID string `json:"message_id,omitempty"`
FromUserID string `json:"from_user_id,omitempty"`
FromConversationID string `json:"from_conversation_id,omitempty"`
Sender string `json:"sender,omitempty"`
Date int64 `json:"date,omitempty"`
} // @name conversation.UIForwardRef
type UIMessage ¶
type UIMessage struct {
ID int `json:"id"`
Type UIMessageType `json:"type"`
Content string `json:"content,omitempty"`
Name string `json:"name,omitempty"`
Input any `json:"input,omitempty"`
Output any `json:"output,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Running *bool `json:"running,omitempty"`
Progress []any `json:"progress,omitempty"`
ElapsedTimeSeconds *float64 `json:"elapsed_time_seconds,omitempty"`
Approval *UIToolApproval `json:"approval,omitempty"`
ExecutionLocation *UIExecutionLocation `json:"execution_location,omitempty"`
// Diff is a UI-only unified diff attached to the tool call at execution
// time (edit/write tools). It never reaches the model: rows persist it
// under the diffs metadata key — on the assistant row (lifted out of
// providerMetadata at store time) or, for the deferred-approval path, on
// the tool message row — never inside the tool result.
Diff string `json:"diff,omitempty"`
UserInput *UIUserInput `json:"user_input,omitempty"`
Attachments []UIAttachment `json:"attachments,omitempty"`
Background *UIBackgroundTask `json:"background_task,omitempty"`
ReasoningTiming *UIReasoningTiming `json:"reasoning_timing,omitempty"`
Code string `json:"code,omitempty"`
// Args are the machine-readable parameters of a notice block: the string
// values of the runtime_notice event metadata (dep_id and install_task_id
// for a workspace dependency notice, for instance). The client renders
// actions from them instead of parsing Content.
Args map[string]string `json:"args,omitempty"`
} // @name conversation.UIMessage
UIMessage is the normalized assistant output block used by the web frontend.
func ConvertModelMessagesToUIAssistantMessages ¶
func ConvertModelMessagesToUIAssistantMessages(messages []turn.ModelMessage) []UIMessage
ConvertModelMessagesToUIAssistantMessages converts assistant/tool output messages into frontend-friendly UI message blocks.
func ConvertRawModelMessagesToUIAssistantMessages ¶
func ConvertRawModelMessagesToUIAssistantMessages(raw json.RawMessage) []UIMessage
ConvertRawModelMessagesToUIAssistantMessages converts terminal stream payload messages into frontend-friendly assistant UI messages.
type UIMessageStreamConverter ¶
type UIMessageStreamConverter struct {
// contains filtered or unexported fields
}
UIMessageStreamConverter converts low-level stream events into complete UI messages.
func NewUIMessageStreamConverter ¶
func NewUIMessageStreamConverter() *UIMessageStreamConverter
NewUIMessageStreamConverter creates a new UI stream converter.
func (*UIMessageStreamConverter) ConvertTerminalMessages ¶
func (c *UIMessageStreamConverter) ConvertTerminalMessages(raw json.RawMessage) []UIMessage
ConvertTerminalMessages converts the terminal snapshot into UI messages whose IDs line up with the blocks this converter emitted during the live stream. The client orders and upserts blocks by ID, and the snapshot regenerates only text/reasoning/tool blocks from raw model messages — attachments exist solely as stream events. A plain sequential renumbering would therefore shift onto the IDs of live attachment blocks and overwrite them at stream end (the generated image flashes away, then reappears after the history refresh). Instead each snapshot block reuses the ID of its live counterpart — tools are matched by tool call ID, text/reasoning positionally within their kind — and only blocks without one get fresh IDs.
func (*UIMessageStreamConverter) HandleEvent ¶
func (c *UIMessageStreamConverter) HandleEvent(event UIMessageStreamEvent) []UIMessage
HandleEvent updates converter state and returns zero or one complete UI messages.
type UIMessageStreamEvent ¶
type UIMessageStreamEvent struct {
Type string
Delta string
ToolName string
ToolCallID string
Input any
Output any
Progress any
Attachments []UIAttachment
Error string
ApprovalID string
UserInputID string
ShortID int
Status string
Code string
Metadata map[string]any
}
UIMessageStreamEvent is the generic event shape accepted by the UI stream converter. The handler layer adapts agent/channel events to this struct to avoid package cycles.
func UIStreamEventFromAgentEvent ¶
func UIStreamEventFromAgentEvent(ev event.StreamEvent) UIMessageStreamEvent
UIStreamEventFromAgentEvent adapts a runtime stream event to the UI converter's input shape. This is the ONLY adaptation point between the streaming vocabulary (internal/agent/event) and UI rendering - every delivery path (WS handler, trigger/background delivery, parity tests) must use it. It previously existed as two hand-maintained copies (handlers and flow) that had already drifted: one extracted bot_id/storage_key from attachment metadata, the other silently didn't.
type UIMessageType ¶
type UIMessageType string // @name conversation.UIMessageType
UIMessageType identifies the frontend-friendly message block type.
const ( UIMessageText UIMessageType = "text" UIMessageReasoning UIMessageType = "reasoning" UIMessageTool UIMessageType = "tool" UIMessageAttachments UIMessageType = "attachments" UIMessageError UIMessageType = "error" UIMessageCommand UIMessageType = "command" UIMessageStatus UIMessageType = "status" // UIMessageNotice is an inline runtime degradation notice (tools // unavailable, an interaction declined). Name carries the machine code, // Content the human-readable text. UIMessageNotice UIMessageType = "notice" )
type UIReasoningTiming ¶
type UIReasoningTiming struct {
DurationMS int64 `json:"duration_ms"`
} // @name conversation.UIReasoningTiming
UIReasoningTiming is the persisted server observation for one reasoning block. It is absent for legacy rows and non-streaming responses whose block boundaries were not observable.
type UIReplyRef ¶
type UIReplyRef struct {
MessageID string `json:"message_id,omitempty"`
Sender string `json:"sender,omitempty"`
Preview string `json:"preview,omitempty"`
Attachments []UIAttachment `json:"attachments,omitempty"`
} // @name conversation.UIReplyRef
type UIToolApproval ¶
type UIToolApproval struct {
ApprovalID string `json:"approval_id"`
ShortID int `json:"short_id,omitempty"`
Status string `json:"status"`
DecisionReason string `json:"decision_reason,omitempty"`
CanApprove bool `json:"can_approve,omitempty"`
// Options are the agent-provided permission options, verbatim; the client
// renders one action per option and answers with the chosen option id.
Options []UIToolApprovalOption `json:"options,omitempty"`
SelectedOptionID string `json:"selected_option_id,omitempty"`
} // @name conversation.UIToolApproval
type UIToolApprovalOption ¶
type UITurn ¶
type UITurn struct {
// RuntimeForkable means this persisted turn has a runtime fork anchor.
// Session-level runtime support and access permissions still apply.
RuntimeForkable bool `json:"runtime_forkable"`
TurnID string `json:"turn_id" validate:"required" format:"uuid"`
// TurnPosition is the immutable turn-level sequence reserved at admission.
// The frontend uses it to order turns and reconcile the settled list
// against live/optimistic turns; never derived from text or timestamps.
TurnPosition *int64 `json:"turn_position,omitempty"`
Role string `json:"role" validate:"required" enums:"user,assistant,system"`
Kind string `json:"kind,omitempty"`
Messages []UIMessage `json:"messages,omitempty"`
Text string `json:"text,omitempty"`
UserMessageKind string `json:"user_message_kind,omitempty"`
SkillActivation *turn.SkillActivation `json:"skill_activation,omitempty"`
Attachments []UIAttachment `json:"attachments,omitempty"`
Reply *UIReplyRef `json:"reply,omitempty"`
Forward *UIForwardRef `json:"forward,omitempty"`
BackgroundTask *UIBackgroundTask `json:"background_task,omitempty"`
Timestamp time.Time `json:"timestamp" validate:"required" format:"date-time"`
Platform string `json:"platform,omitempty"`
SenderDisplayName string `json:"sender_display_name,omitempty"`
SenderAvatarURL string `json:"sender_avatar_url,omitempty"`
SenderUserID string `json:"sender_user_id,omitempty"`
ExternalMessageID string `json:"external_message_id,omitempty"`
ID string `json:"id,omitempty"`
} // @name conversation.UITurn
UITurn is the normalized chat turn used by the web frontend.
func ConvertMessagesToUITurns ¶
func ConvertMessagesToUITurns(messages []messagepkg.Message) []UITurn
ConvertMessagesToUITurns converts persisted message rows into frontend-friendly turns.
func NewRequestUserTurn ¶
func NewRequestUserTurn(cmd turn.StartTurnCommand, turnID string) *UITurn
NewRequestUserTurn builds the subscriber-facing projection of the user message that fired a run, so subscribers watching a thread see the triggering message while the run is still executing — not only after the step committer lands it in history.
Two invariants keep the live bubble seamless with the settled one:
- The result is nil exactly when the turn will not persist a user message (mirrors prependTurnUserMessage in internal/agent/application): an attachment-only or discuss-shaped command produces no history turn, so projecting one would make a bubble vanish at the database handover.
- The text is the same display text the persistence layer stores (mirrors the displayText switch in service_store.go): UserVisibleText when present, else the envelope-unwrapped Query. Anything else would visibly change the bubble's content when the runtime projection hands over to the database at run end.
Reply, forward, sender, and platform fields come straight from the command, which is also the source buildInteractionMetadata persists from.
type UIUserInput ¶
type UIUserInput struct {
UserInputID string `json:"user_input_id"`
ShortID int `json:"short_id,omitempty"`
Status string `json:"status"`
Questions []userinput.UIQuestion `json:"questions,omitempty"`
Answers []userinput.UIAnswer `json:"answers,omitempty"`
CanRespond bool `json:"can_respond,omitempty"`
} // @name conversation.UIUserInput