package ui import ( "github.com/kamelion/ratatoskr-go/internal/chat" "github.com/kamelion/ratatoskr-go/internal/storage" ) // Session — одна вкладка-сессия UI: независимый диалог с LLM. // // Каждая вкладка имеет собственный UserID и Address, поэтому core видит её как // отдельный чат со своим активным task (политика «одна задача на чат» работает // из коробки). Свободная сессия (BoundTask==0) — in-memory диалог с моделью // вне машины задач; task-сессия (BoundTask>0) — диалог конкретной задачи, у // которой UserID == task.ChatID (чат задачи уникален). type Session struct { UserID chat.UserID Address chat.Address Title string BoundTask int64 // 0 = свободный чат; >0 — задача, к которой привязана вкладка } // NewFreeSession создаёт свободную сессию (без задачи). userID/address должны // быть уникальными (например "ui://sess-"). Транскрипт живёт только в памяти. func NewFreeSession(userID chat.UserID, address chat.Address) *Session { return &Session{UserID: userID, Address: address, Title: "Новый чат", BoundTask: 0} } // NewTaskSession создаёт сессию, привязанную к задаче. UserID == task.ChatID, // чтобы Router/core находили активную задачу этой вкладки. address — уникальный // адрес доставки вкладки (не должен совпадать с UserID других вкладок). func NewTaskSession(address chat.Address, t storage.Task) *Session { title := t.Title if title == "" { title = "Задача #" + Itoa(t.ID) } return &Session{UserID: chat.UserID(t.ChatID), Address: address, Title: title, BoundTask: t.ID} } // SessionID — префикс идентификаторов UI-сессий (и UserID, и Address). const SessionID = "ui://sess-" // IsFreeSession — true, если вкладка не привязана к задаче. func IsFreeSession(s *Session) bool { return s != nil && s.BoundTask == 0 }