summaryrefslogtreecommitdiffhomepage
path: root/internal/app
diff options
context:
space:
mode:
Diffstat (limited to 'internal/app')
-rw-r--r--internal/app/app.go106
-rw-r--r--internal/app/app_new.go203
-rw-r--r--internal/app/event_adapter.go158
-rw-r--r--internal/app/interfaces.go42
-rw-r--r--internal/app/services_bridge.go250
5 files changed, 697 insertions, 62 deletions
diff --git a/internal/app/app.go b/internal/app/app.go
index 0ef89d1b9..7d0bdd4cb 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -2,7 +2,6 @@ package app
import (
"context"
- "database/sql"
"maps"
"sync"
"time"
@@ -11,12 +10,7 @@ import (
"github.com/sst/opencode/internal/config"
"github.com/sst/opencode/internal/fileutil"
- "github.com/sst/opencode/internal/history"
- "github.com/sst/opencode/internal/llm/agent"
- "github.com/sst/opencode/internal/logging"
"github.com/sst/opencode/internal/lsp"
- "github.com/sst/opencode/internal/message"
- "github.com/sst/opencode/internal/permission"
"github.com/sst/opencode/internal/session"
"github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/tui/theme"
@@ -25,15 +19,15 @@ import (
type App struct {
CurrentSession *session.Session
- Logs logging.Service
- Sessions session.Service
- Messages message.Service
- History history.Service
- Permissions permission.Service
+ Logs interface{} // TODO: Define LogService interface when needed
+ Sessions SessionService
+ Messages MessageService
+ History interface{} // TODO: Define HistoryService interface when needed
+ Permissions interface{} // TODO: Define PermissionService interface when needed
Status status.Service
Client *client.Client
- PrimaryAgent agent.Service
+ PrimaryAgent AgentService
LSPClients map[string]*lsp.Client
@@ -48,55 +42,42 @@ type App struct {
completionDialogOpen bool
}
-func New(ctx context.Context, conn *sql.DB) (*App, error) {
- err := logging.InitService(conn)
- if err != nil {
- slog.Error("Failed to initialize logging service", "error", err)
- return nil, err
- }
- err = session.InitService(conn)
- if err != nil {
- slog.Error("Failed to initialize session service", "error", err)
- return nil, err
- }
- err = message.InitService(conn)
- if err != nil {
- slog.Error("Failed to initialize message service", "error", err)
- return nil, err
- }
- err = history.InitService(conn)
- if err != nil {
- slog.Error("Failed to initialize history service", "error", err)
- return nil, err
- }
- err = permission.InitService()
- if err != nil {
- slog.Error("Failed to initialize permission service", "error", err)
- return nil, err
- }
- err = status.InitService()
+func New(ctx context.Context) (*App, error) {
+ // Initialize status service (still needed for UI notifications)
+ err := status.InitService()
if err != nil {
slog.Error("Failed to initialize status service", "error", err)
return nil, err
}
+
+ // Initialize file utilities
fileutil.Init()
- client, err := client.NewClient("http://localhost:16713")
+ // Create HTTP client
+ httpClient, err := client.NewClient("http://localhost:16713")
if err != nil {
slog.Error("Failed to create client", "error", err)
return nil, err
}
+ // Create service bridges
+ sessionBridge := NewSessionServiceBridge(httpClient)
+ messageBridge := NewMessageServiceBridge(httpClient)
+ agentBridge := NewAgentServiceBridge(httpClient)
+
app := &App{
- Client: client,
+ Client: httpClient,
CurrentSession: &session.Session{},
- Logs: logging.GetService(),
- Sessions: session.GetService(),
- Messages: message.GetService(),
- History: history.GetService(),
- Permissions: permission.GetService(),
+ Sessions: sessionBridge,
+ Messages: messageBridge,
+ PrimaryAgent: agentBridge,
Status: status.GetService(),
LSPClients: make(map[string]*lsp.Client),
+
+ // TODO: These services need API endpoints:
+ Logs: nil, // logging.GetService(),
+ History: nil, // history.GetService(),
+ Permissions: nil, // permission.GetService(),
}
// Initialize theme based on configuration
@@ -105,22 +86,23 @@ func New(ctx context.Context, conn *sql.DB) (*App, error) {
// Initialize LSP clients in the background
go app.initLSPClients(ctx)
- app.PrimaryAgent, err = agent.NewAgent(
- config.AgentPrimary,
- app.Sessions,
- app.Messages,
- agent.PrimaryAgentTools(
- app.Permissions,
- app.Sessions,
- app.Messages,
- app.History,
- app.LSPClients,
- ),
- )
- if err != nil {
- slog.Error("Failed to create primary agent", "error", err)
- return nil, err
- }
+ // TODO: Remove this once agent is fully replaced by API
+ // app.PrimaryAgent, err = agent.NewAgent(
+ // config.AgentPrimary,
+ // app.Sessions,
+ // app.Messages,
+ // agent.PrimaryAgentTools(
+ // app.Permissions,
+ // app.Sessions,
+ // app.Messages,
+ // app.History,
+ // app.LSPClients,
+ // ),
+ // )
+ // if err != nil {
+ // slog.Error("Failed to create primary agent", "error", err)
+ // return nil, err
+ // }
return app, nil
}
diff --git a/internal/app/app_new.go b/internal/app/app_new.go
new file mode 100644
index 000000000..1568b4d57
--- /dev/null
+++ b/internal/app/app_new.go
@@ -0,0 +1,203 @@
+package app
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "sync"
+
+ "log/slog"
+
+ "github.com/sst/opencode/pkg/client"
+)
+
+// AppNew is the new app structure that uses the TypeScript backend
+type AppNew struct {
+ Client *client.Client
+ CurrentSession *client.SessionInfo
+
+ // Event handling
+ eventCtx context.Context
+ eventCancel context.CancelFunc
+ eventChan <-chan any
+
+ // UI state
+ filepickerOpen bool
+ completionDialogOpen bool
+
+ // Mutex for thread-safe operations
+ mu sync.RWMutex
+}
+
+// NewApp creates a new app instance connected to the TypeScript backend
+func NewApp(ctx context.Context) (*AppNew, error) {
+ httpClient, err := client.NewClient("http://localhost:16713")
+ if err != nil {
+ slog.Error("Failed to create client", "error", err)
+ return nil, err
+ }
+
+ app := &AppNew{
+ Client: httpClient,
+ }
+
+ // Start event listener
+ if err := app.startEventListener(ctx); err != nil {
+ return nil, err
+ }
+
+ return app, nil
+}
+
+// startEventListener connects to the SSE endpoint and processes events
+func (a *AppNew) startEventListener(ctx context.Context) error {
+ a.eventCtx, a.eventCancel = context.WithCancel(ctx)
+
+ eventChan, err := a.Client.Event(a.eventCtx)
+ if err != nil {
+ return err
+ }
+
+ a.eventChan = eventChan
+
+ // Start processing events in background
+ go a.processEvents()
+
+ return nil
+}
+
+// processEvents handles incoming SSE events
+func (a *AppNew) processEvents() {
+ for event := range a.eventChan {
+ switch e := event.(type) {
+ case *client.EventStorageWrite:
+ // Handle storage write events
+ slog.Debug("Storage write event", "key", e.Key)
+ // TODO: Update local state based on storage events
+ default:
+ slog.Debug("Unknown event type", "event", e)
+ }
+ }
+}
+
+// CreateSession creates a new session via the API
+func (a *AppNew) CreateSession(ctx context.Context) error {
+ resp, err := a.Client.PostSessionCreate(ctx)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ return fmt.Errorf("failed to create session: %d", resp.StatusCode)
+ }
+
+ var session client.SessionInfo
+ if err := json.NewDecoder(resp.Body).Decode(&session); err != nil {
+ return err
+ }
+
+ a.mu.Lock()
+ a.CurrentSession = &session
+ a.mu.Unlock()
+
+ return nil
+}
+
+// SendMessage sends a message to the current session
+func (a *AppNew) SendMessage(ctx context.Context, text string) error {
+ if a.CurrentSession == nil {
+ if err := a.CreateSession(ctx); err != nil {
+ return err
+ }
+ }
+
+ a.mu.RLock()
+ sessionID := a.CurrentSession.Id
+ a.mu.RUnlock()
+
+ parts := interface{}([]map[string]interface{}{
+ {
+ "type": "text",
+ "text": text,
+ },
+ })
+
+ resp, err := a.Client.PostSessionChat(ctx, client.PostSessionChatJSONRequestBody{
+ SessionID: sessionID,
+ Parts: &parts,
+ })
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ // The response will be streamed via SSE
+ return nil
+}
+
+// GetSessions retrieves all sessions
+func (a *AppNew) GetSessions(ctx context.Context) ([]client.SessionInfo, error) {
+ resp, err := a.Client.PostSessionList(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var sessions []client.SessionInfo
+ if err := json.NewDecoder(resp.Body).Decode(&sessions); err != nil {
+ return nil, err
+ }
+
+ return sessions, nil
+}
+
+// GetMessages retrieves messages for a session
+func (a *AppNew) GetMessages(ctx context.Context, sessionID string) (interface{}, error) {
+ resp, err := a.Client.PostSessionMessages(ctx, client.PostSessionMessagesJSONRequestBody{
+ SessionID: sessionID,
+ })
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var messages interface{}
+ if err := json.NewDecoder(resp.Body).Decode(&messages); err != nil {
+ return nil, err
+ }
+
+ return messages, nil
+}
+
+// Close shuts down the app and its connections
+func (a *AppNew) Close() {
+ if a.eventCancel != nil {
+ a.eventCancel()
+ }
+}
+
+// UI state methods
+func (a *AppNew) SetFilepickerOpen(open bool) {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.filepickerOpen = open
+}
+
+func (a *AppNew) IsFilepickerOpen() bool {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return a.filepickerOpen
+}
+
+func (a *AppNew) SetCompletionDialogOpen(open bool) {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+ a.completionDialogOpen = open
+}
+
+func (a *AppNew) IsCompletionDialogOpen() bool {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return a.completionDialogOpen
+} \ No newline at end of file
diff --git a/internal/app/event_adapter.go b/internal/app/event_adapter.go
new file mode 100644
index 000000000..4772bde49
--- /dev/null
+++ b/internal/app/event_adapter.go
@@ -0,0 +1,158 @@
+package app
+
+import (
+ "encoding/json"
+ "time"
+
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/sst/opencode/internal/message"
+ "github.com/sst/opencode/pkg/client"
+)
+
+// StorageWriteMsg is sent when a storage.write event is received
+type StorageWriteMsg struct {
+ Key string
+ Content interface{}
+}
+
+// ProcessSSEEvent converts SSE events into TUI messages
+func ProcessSSEEvent(event interface{}) tea.Msg {
+ switch e := event.(type) {
+ case *client.EventStorageWrite:
+ return StorageWriteMsg{
+ Key: e.Key,
+ Content: e.Content,
+ }
+ }
+
+ // Return the raw event if we don't have a specific handler
+ return event
+}
+
+// MessageFromStorage converts storage content to internal message format
+type MessageData struct {
+ ID string `json:"id"`
+ Role string `json:"role"`
+ Parts []interface{} `json:"parts"`
+ Metadata map[string]interface{} `json:"metadata"`
+}
+
+// SessionInfoFromStorage converts storage content to session info
+type SessionInfoData struct {
+ ID string `json:"id"`
+ Title string `json:"title"`
+ ShareID *string `json:"shareID,omitempty"`
+ Tokens struct {
+ Input float32 `json:"input"`
+ Output float32 `json:"output"`
+ Reasoning float32 `json:"reasoning"`
+ } `json:"tokens"`
+}
+
+// ConvertStorageMessage converts a storage message to internal message format
+func ConvertStorageMessage(data interface{}, sessionID string) (*message.Message, error) {
+ // Convert the interface{} to JSON then back to our struct
+ jsonData, err := json.Marshal(data)
+ if err != nil {
+ return nil, err
+ }
+
+ var msgData MessageData
+ if err := json.Unmarshal(jsonData, &msgData); err != nil {
+ return nil, err
+ }
+
+ // Convert parts
+ var parts []message.ContentPart
+ for _, part := range msgData.Parts {
+ partMap, ok := part.(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ partType, ok := partMap["type"].(string)
+ if !ok {
+ continue
+ }
+
+ switch partType {
+ case "text":
+ if text, ok := partMap["text"].(string); ok {
+ parts = append(parts, message.TextContent{Text: text})
+ }
+ case "tool-invocation":
+ if toolInv, ok := partMap["toolInvocation"].(map[string]interface{}); ok {
+ // Convert tool invocation to tool call
+ toolCall := message.ToolCall{
+ ID: toolInv["toolCallId"].(string),
+ Name: toolInv["toolName"].(string),
+ Type: "function",
+ }
+
+ if args, ok := toolInv["args"]; ok {
+ argsJSON, _ := json.Marshal(args)
+ toolCall.Input = string(argsJSON)
+ }
+
+ if state, ok := toolInv["state"].(string); ok {
+ toolCall.Finished = state == "result"
+ }
+
+ parts = append(parts, toolCall)
+
+ // If there's a result, add it as a tool result
+ if result, ok := toolInv["result"]; ok && toolCall.Finished {
+ resultStr := ""
+ switch r := result.(type) {
+ case string:
+ resultStr = r
+ default:
+ resultJSON, _ := json.Marshal(r)
+ resultStr = string(resultJSON)
+ }
+
+ parts = append(parts, message.ToolResult{
+ ToolCallID: toolCall.ID,
+ Name: toolCall.Name,
+ Content: resultStr,
+ })
+ }
+ }
+ }
+ }
+
+ // Convert role
+ var role message.MessageRole
+ switch msgData.Role {
+ case "user":
+ role = message.User
+ case "assistant":
+ role = message.Assistant
+ case "system":
+ role = message.System
+ default:
+ role = message.MessageRole(msgData.Role)
+ }
+
+ // Create message
+ msg := &message.Message{
+ ID: msgData.ID,
+ Role: role,
+ SessionID: sessionID,
+ Parts: parts,
+ CreatedAt: time.Now(), // TODO: Get from metadata
+ UpdatedAt: time.Now(), // TODO: Get from metadata
+ }
+
+ // Try to get timestamps from metadata
+ if metadata, ok := msgData.Metadata["time"].(map[string]interface{}); ok {
+ if created, ok := metadata["created"].(float64); ok {
+ msg.CreatedAt = time.Unix(int64(created/1000), 0)
+ }
+ if completed, ok := metadata["completed"].(float64); ok {
+ msg.UpdatedAt = time.Unix(int64(completed/1000), 0)
+ }
+ }
+
+ return msg, nil
+} \ No newline at end of file
diff --git a/internal/app/interfaces.go b/internal/app/interfaces.go
new file mode 100644
index 000000000..2f4287078
--- /dev/null
+++ b/internal/app/interfaces.go
@@ -0,0 +1,42 @@
+package app
+
+import (
+ "context"
+ "time"
+
+ "github.com/sst/opencode/internal/message"
+ "github.com/sst/opencode/internal/pubsub"
+ "github.com/sst/opencode/internal/session"
+)
+
+// SessionService defines the interface for session operations
+type SessionService interface {
+ Create(ctx context.Context, title string) (session.Session, error)
+ Get(ctx context.Context, id string) (session.Session, error)
+ List(ctx context.Context) ([]session.Session, error)
+ Update(ctx context.Context, id, title string) error
+ Delete(ctx context.Context, id string) error
+}
+
+// MessageService defines the interface for message operations
+type MessageService interface {
+ pubsub.Subscriber[message.Message]
+
+ GetBySession(ctx context.Context, sessionID string) ([]message.Message, error)
+ List(ctx context.Context, sessionID string) ([]message.Message, error)
+ Create(ctx context.Context, sessionID string, params message.CreateMessageParams) (message.Message, error)
+ Update(ctx context.Context, msg message.Message) (message.Message, error)
+ Delete(ctx context.Context, id string) error
+ DeleteSessionMessages(ctx context.Context, sessionID string) error
+ Get(ctx context.Context, id string) (message.Message, error)
+ ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]message.Message, error)
+}
+
+// AgentService defines the interface for agent operations
+type AgentService interface {
+ Run(ctx context.Context, sessionID string, text string, attachments ...message.Attachment) (string, error)
+ Cancel(sessionID string) error
+ IsBusy() bool
+ IsSessionBusy(sessionID string) bool
+ CompactSession(ctx context.Context, sessionID string, force bool) error
+} \ No newline at end of file
diff --git a/internal/app/services_bridge.go b/internal/app/services_bridge.go
new file mode 100644
index 000000000..e2d2ab5ba
--- /dev/null
+++ b/internal/app/services_bridge.go
@@ -0,0 +1,250 @@
+package app
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/sst/opencode/internal/message"
+ "github.com/sst/opencode/internal/pubsub"
+ "github.com/sst/opencode/internal/session"
+ "github.com/sst/opencode/pkg/client"
+)
+
+// SessionServiceBridge adapts the HTTP API to the old session.Service interface
+type SessionServiceBridge struct {
+ client *client.Client
+}
+
+// NewSessionServiceBridge creates a new session service bridge
+func NewSessionServiceBridge(client *client.Client) *SessionServiceBridge {
+ return &SessionServiceBridge{client: client}
+}
+
+// Create creates a new session
+func (s *SessionServiceBridge) Create(ctx context.Context, title string) (session.Session, error) {
+ resp, err := s.client.PostSessionCreate(ctx)
+ if err != nil {
+ return session.Session{}, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ return session.Session{}, fmt.Errorf("failed to create session: %d", resp.StatusCode)
+ }
+
+ var info client.SessionInfo
+ if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
+ return session.Session{}, err
+ }
+
+ // Convert to old session type
+ return session.Session{
+ ID: info.Id,
+ Title: info.Title,
+ CreatedAt: time.Now(), // API doesn't provide this yet
+ UpdatedAt: time.Now(), // API doesn't provide this yet
+ }, nil
+}
+
+// Get retrieves a session by ID
+func (s *SessionServiceBridge) Get(ctx context.Context, id string) (session.Session, error) {
+ // TODO: API doesn't have a get by ID endpoint yet
+ // For now, list all and find the one we want
+ sessions, err := s.List(ctx)
+ if err != nil {
+ return session.Session{}, err
+ }
+
+ for _, sess := range sessions {
+ if sess.ID == id {
+ return sess, nil
+ }
+ }
+
+ return session.Session{}, fmt.Errorf("session not found: %s", id)
+}
+
+// List retrieves all sessions
+func (s *SessionServiceBridge) List(ctx context.Context) ([]session.Session, error) {
+ resp, err := s.client.PostSessionList(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var infos []client.SessionInfo
+ if err := json.NewDecoder(resp.Body).Decode(&infos); err != nil {
+ return nil, err
+ }
+
+ // Convert to old session type
+ sessions := make([]session.Session, len(infos))
+ for i, info := range infos {
+ sessions[i] = session.Session{
+ ID: info.Id,
+ Title: info.Title,
+ CreatedAt: time.Now(), // API doesn't provide this yet
+ UpdatedAt: time.Now(), // API doesn't provide this yet
+ }
+ }
+
+ return sessions, nil
+}
+
+// Update updates a session - NOT IMPLEMENTED IN API YET
+func (s *SessionServiceBridge) Update(ctx context.Context, id, title string) error {
+ // TODO: Not implemented in TypeScript API yet
+ return fmt.Errorf("session update not implemented in API")
+}
+
+// Delete deletes a session - NOT IMPLEMENTED IN API YET
+func (s *SessionServiceBridge) Delete(ctx context.Context, id string) error {
+ // TODO: Not implemented in TypeScript API yet
+ return fmt.Errorf("session delete not implemented in API")
+}
+
+// AgentServiceBridge provides a minimal agent service that sends messages to the API
+type AgentServiceBridge struct {
+ client *client.Client
+}
+
+// NewAgentServiceBridge creates a new agent service bridge
+func NewAgentServiceBridge(client *client.Client) *AgentServiceBridge {
+ return &AgentServiceBridge{client: client}
+}
+
+// Run sends a message to the chat API
+func (a *AgentServiceBridge) Run(ctx context.Context, sessionID string, text string, attachments ...message.Attachment) (string, error) {
+ // TODO: Handle attachments when API supports them
+ if len(attachments) > 0 {
+ // For now, ignore attachments
+ // return "", fmt.Errorf("attachments not supported yet")
+ }
+
+ parts := interface{}([]map[string]interface{}{
+ {
+ "type": "text",
+ "text": text,
+ },
+ })
+
+ resp, err := a.client.PostSessionChat(ctx, client.PostSessionChatJSONRequestBody{
+ SessionID: sessionID,
+ Parts: &parts,
+ })
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ // The actual response will come through SSE
+ // For now, just return success
+ return "", nil
+}
+
+// Cancel cancels the current generation - NOT IMPLEMENTED IN API YET
+func (a *AgentServiceBridge) Cancel(sessionID string) error {
+ // TODO: Not implemented in TypeScript API yet
+ return nil
+}
+
+// IsBusy checks if the agent is busy - NOT IMPLEMENTED IN API YET
+func (a *AgentServiceBridge) IsBusy() bool {
+ // TODO: Not implemented in TypeScript API yet
+ return false
+}
+
+// IsSessionBusy checks if the agent is busy for a specific session - NOT IMPLEMENTED IN API YET
+func (a *AgentServiceBridge) IsSessionBusy(sessionID string) bool {
+ // TODO: Not implemented in TypeScript API yet
+ return false
+}
+
+// CompactSession compacts a session - NOT IMPLEMENTED IN API YET
+func (a *AgentServiceBridge) CompactSession(ctx context.Context, sessionID string, force bool) error {
+ // TODO: Not implemented in TypeScript API yet
+ return fmt.Errorf("session compaction not implemented in API")
+}
+
+// MessageServiceBridge provides a minimal message service that fetches from the API
+type MessageServiceBridge struct {
+ client *client.Client
+ broker *pubsub.Broker[message.Message]
+}
+
+// NewMessageServiceBridge creates a new message service bridge
+func NewMessageServiceBridge(client *client.Client) *MessageServiceBridge {
+ return &MessageServiceBridge{
+ client: client,
+ broker: pubsub.NewBroker[message.Message](),
+ }
+}
+
+// GetBySession retrieves messages for a session
+func (m *MessageServiceBridge) GetBySession(ctx context.Context, sessionID string) ([]message.Message, error) {
+ return m.List(ctx, sessionID)
+}
+
+// List retrieves messages for a session
+func (m *MessageServiceBridge) List(ctx context.Context, sessionID string) ([]message.Message, error) {
+ resp, err := m.client.PostSessionMessages(ctx, client.PostSessionMessagesJSONRequestBody{
+ SessionID: sessionID,
+ })
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ // The API returns a different format, we'll need to adapt it
+ var rawMessages interface{}
+ if err := json.NewDecoder(resp.Body).Decode(&rawMessages); err != nil {
+ return nil, err
+ }
+
+ // TODO: Convert the API message format to our internal format
+ // For now, return empty to avoid compilation errors
+ return []message.Message{}, nil
+}
+
+// Create creates a new message - NOT NEEDED, handled by chat API
+func (m *MessageServiceBridge) Create(ctx context.Context, sessionID string, params message.CreateMessageParams) (message.Message, error) {
+ // Messages are created through the chat API
+ return message.Message{}, fmt.Errorf("use chat API to send messages")
+}
+
+// Update updates a message - NOT IMPLEMENTED IN API YET
+func (m *MessageServiceBridge) Update(ctx context.Context, msg message.Message) (message.Message, error) {
+ // TODO: Not implemented in TypeScript API yet
+ return message.Message{}, fmt.Errorf("message update not implemented in API")
+}
+
+// Delete deletes a message - NOT IMPLEMENTED IN API YET
+func (m *MessageServiceBridge) Delete(ctx context.Context, id string) error {
+ // TODO: Not implemented in TypeScript API yet
+ return fmt.Errorf("message delete not implemented in API")
+}
+
+// DeleteSessionMessages deletes all messages for a session - NOT IMPLEMENTED IN API YET
+func (m *MessageServiceBridge) DeleteSessionMessages(ctx context.Context, sessionID string) error {
+ // TODO: Not implemented in TypeScript API yet
+ return fmt.Errorf("delete session messages not implemented in API")
+}
+
+// Get retrieves a message by ID - NOT IMPLEMENTED IN API YET
+func (m *MessageServiceBridge) Get(ctx context.Context, id string) (message.Message, error) {
+ // TODO: Not implemented in TypeScript API yet
+ return message.Message{}, fmt.Errorf("get message by ID not implemented in API")
+}
+
+// ListAfter retrieves messages after a timestamp - NOT IMPLEMENTED IN API YET
+func (m *MessageServiceBridge) ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]message.Message, error) {
+ // TODO: Not implemented in TypeScript API yet
+ return []message.Message{}, fmt.Errorf("list messages after timestamp not implemented in API")
+}
+
+// Subscribe subscribes to message events
+func (m *MessageServiceBridge) Subscribe(ctx context.Context) <-chan pubsub.Event[message.Message] {
+ return m.broker.Subscribe(ctx)
+} \ No newline at end of file