workspace.go

  1// Package workspace defines the Workspace interface used by all
  2// frontends (TUI, CLI) to interact with a running workspace. Two
  3// implementations exist: one wrapping a local app.App instance and one
  4// wrapping the HTTP client SDK.
  5package workspace
  6
  7import (
  8	"context"
  9	"time"
 10
 11	tea "charm.land/bubbletea/v2"
 12	"charm.land/catwalk/pkg/catwalk"
 13	mcptools "github.com/charmbracelet/crush/internal/agent/tools/mcp"
 14	"github.com/charmbracelet/crush/internal/config"
 15	"github.com/charmbracelet/crush/internal/history"
 16	"github.com/charmbracelet/crush/internal/lsp"
 17	"github.com/charmbracelet/crush/internal/message"
 18	"github.com/charmbracelet/crush/internal/oauth"
 19	"github.com/charmbracelet/crush/internal/permission"
 20	"github.com/charmbracelet/crush/internal/session"
 21)
 22
 23// LSPClientInfo holds information about an LSP client's state. This is
 24// the frontend-facing type; implementations translate from the
 25// underlying app or proto representation.
 26type LSPClientInfo struct {
 27	Name            string
 28	State           lsp.ServerState
 29	Error           error
 30	DiagnosticCount int
 31	ConnectedAt     time.Time
 32}
 33
 34// LSPEventType represents the type of LSP event.
 35type LSPEventType string
 36
 37const (
 38	LSPEventStateChanged       LSPEventType = "state_changed"
 39	LSPEventDiagnosticsChanged LSPEventType = "diagnostics_changed"
 40)
 41
 42// LSPEvent represents an LSP event forwarded to the TUI.
 43type LSPEvent struct {
 44	Type            LSPEventType
 45	Name            string
 46	State           lsp.ServerState
 47	Error           error
 48	DiagnosticCount int
 49}
 50
 51// AgentModel holds the model information exposed to the UI.
 52type AgentModel struct {
 53	CatwalkCfg catwalk.Model
 54	ModelCfg   config.SelectedModel
 55}
 56
 57// Workspace is the main abstraction consumed by the TUI and CLI. It
 58// groups every operation a frontend needs to perform against a running
 59// workspace, regardless of whether the workspace is in-process or
 60// remote.
 61type Workspace interface {
 62	// Sessions
 63	CreateSession(ctx context.Context, title string) (session.Session, error)
 64	GetSession(ctx context.Context, sessionID string) (session.Session, error)
 65	ListSessions(ctx context.Context) ([]session.Session, error)
 66	SaveSession(ctx context.Context, sess session.Session) (session.Session, error)
 67	DeleteSession(ctx context.Context, sessionID string) error
 68	CreateAgentToolSessionID(messageID, toolCallID string) string
 69	ParseAgentToolSessionID(sessionID string) (messageID string, toolCallID string, ok bool)
 70
 71	// Messages
 72	ListMessages(ctx context.Context, sessionID string) ([]message.Message, error)
 73	ListUserMessages(ctx context.Context, sessionID string) ([]message.Message, error)
 74	ListAllUserMessages(ctx context.Context) ([]message.Message, error)
 75
 76	// Agent
 77	AgentRun(ctx context.Context, sessionID, prompt string, attachments ...message.Attachment) error
 78	AgentCancel(sessionID string)
 79	AgentIsBusy() bool
 80	AgentIsSessionBusy(sessionID string) bool
 81	AgentModel() AgentModel
 82	AgentIsReady() bool
 83	AgentQueuedPrompts(sessionID string) int
 84	AgentQueuedPromptsList(sessionID string) []string
 85	AgentClearQueue(sessionID string)
 86	AgentSummarize(ctx context.Context, sessionID string) error
 87	UpdateAgentModel(ctx context.Context) error
 88	InitCoderAgent(ctx context.Context) error
 89	GetDefaultSmallModel(providerID string) config.SelectedModel
 90
 91	// Permissions
 92	PermissionGrant(perm permission.PermissionRequest)
 93	PermissionGrantPersistent(perm permission.PermissionRequest)
 94	PermissionDeny(perm permission.PermissionRequest)
 95	PermissionSkipRequests() bool
 96	PermissionSetSkipRequests(skip bool)
 97
 98	// FileTracker
 99	FileTrackerRecordRead(ctx context.Context, sessionID, path string)
100	FileTrackerLastReadTime(ctx context.Context, sessionID, path string) time.Time
101	FileTrackerListReadFiles(ctx context.Context, sessionID string) ([]string, error)
102
103	// History
104	ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error)
105
106	// LSP
107	LSPStart(ctx context.Context, path string)
108	LSPStopAll(ctx context.Context)
109	LSPGetStates() map[string]LSPClientInfo
110	LSPGetClient(name string) (*lsp.Client, bool)
111
112	// Config (read-only data)
113	Config() *config.Config
114	WorkingDir() string
115	Resolver() config.VariableResolver
116
117	// Config mutations (proxied to server in client mode)
118	UpdatePreferredModel(scope config.Scope, modelType config.SelectedModelType, model config.SelectedModel) error
119	SetCompactMode(scope config.Scope, enabled bool) error
120	SetProviderAPIKey(scope config.Scope, providerID string, apiKey any) error
121	SetConfigField(scope config.Scope, key string, value any) error
122	RemoveConfigField(scope config.Scope, key string) error
123	ImportCopilot() (*oauth.Token, bool)
124	RefreshOAuthToken(ctx context.Context, scope config.Scope, providerID string) error
125
126	// Project lifecycle
127	ProjectNeedsInitialization() (bool, error)
128	MarkProjectInitialized() error
129	InitializePrompt() (string, error)
130
131	// MCP operations (server-side in client mode)
132	MCPGetStates() map[string]mcptools.ClientInfo
133	MCPRefreshPrompts(ctx context.Context, name string)
134	MCPRefreshResources(ctx context.Context, name string)
135	RefreshMCPTools(ctx context.Context, name string)
136	ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
137	GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
138
139	// Events
140	Subscribe(program *tea.Program)
141	Shutdown()
142}
143
144// MCPResourceContents holds the contents of an MCP resource.
145type MCPResourceContents struct {
146	URI      string `json:"uri"`
147	MIMEType string `json:"mime_type,omitempty"`
148	Text     string `json:"text,omitempty"`
149	Blob     []byte `json:"blob,omitempty"`
150}