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	UpdateSessionModels(ctx context.Context, sessionID string, models map[config.SelectedModelType]config.SelectedModel) error
 69	CreateAgentToolSessionID(messageID, toolCallID string) string
 70	ParseAgentToolSessionID(sessionID string) (messageID string, toolCallID string, ok bool)
 71
 72	// Messages
 73	ListMessages(ctx context.Context, sessionID string) ([]message.Message, error)
 74	ListUserMessages(ctx context.Context, sessionID string) ([]message.Message, error)
 75	ListAllUserMessages(ctx context.Context) ([]message.Message, error)
 76
 77	// Agent
 78	AgentRun(ctx context.Context, sessionID, prompt string, attachments ...message.Attachment) error
 79	AgentCancel(sessionID string)
 80	AgentIsBusy() bool
 81	AgentIsSessionBusy(sessionID string) bool
 82	AgentModel() AgentModel
 83	AgentIsReady() bool
 84	AgentQueuedPrompts(sessionID string) int
 85	AgentQueuedPromptsList(sessionID string) []string
 86	AgentClearQueue(sessionID string)
 87	AgentSummarize(ctx context.Context, sessionID string) error
 88	UpdateAgentModel(ctx context.Context) error
 89	InitCoderAgent(ctx context.Context) error
 90	GetDefaultSmallModel(providerID string) config.SelectedModel
 91
 92	// Permissions
 93	PermissionGrant(perm permission.PermissionRequest)
 94	PermissionGrantPersistent(perm permission.PermissionRequest)
 95	PermissionDeny(perm permission.PermissionRequest)
 96	PermissionSkipRequests() bool
 97	PermissionSetSkipRequests(skip bool)
 98
 99	// FileTracker
100	FileTrackerRecordRead(ctx context.Context, sessionID, path string)
101	FileTrackerLastReadTime(ctx context.Context, sessionID, path string) time.Time
102	FileTrackerListReadFiles(ctx context.Context, sessionID string) ([]string, error)
103
104	// History
105	ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error)
106
107	// LSP
108	LSPStart(ctx context.Context, path string)
109	LSPStopAll(ctx context.Context)
110	LSPGetStates() map[string]LSPClientInfo
111	LSPGetDiagnosticCounts(name string) lsp.DiagnosticCounts
112
113	// Config (read-only data)
114	Config() *config.Config
115	WorkingDir() string
116	Resolver() config.VariableResolver
117
118	// Config mutations (proxied to server in client mode)
119	UpdatePreferredModel(scope config.Scope, modelType config.SelectedModelType, model config.SelectedModel) error
120	SetCompactMode(scope config.Scope, enabled bool) error
121	SetProviderAPIKey(scope config.Scope, providerID string, apiKey any) error
122	SetConfigField(scope config.Scope, key string, value any) error
123	RemoveConfigField(scope config.Scope, key string) error
124	ImportCopilot() (*oauth.Token, bool)
125	RefreshOAuthToken(ctx context.Context, scope config.Scope, providerID string) error
126
127	// Project lifecycle
128	ProjectNeedsInitialization() (bool, error)
129	MarkProjectInitialized() error
130	InitializePrompt() (string, error)
131
132	// MCP operations (server-side in client mode)
133	MCPGetStates() map[string]mcptools.ClientInfo
134	MCPRefreshPrompts(ctx context.Context, name string)
135	MCPRefreshResources(ctx context.Context, name string)
136	RefreshMCPTools(ctx context.Context, name string)
137	ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
138	GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
139	EnableDockerMCP(ctx context.Context) error
140	DisableDockerMCP() error
141
142	// Events
143	Subscribe(program *tea.Program)
144	Shutdown()
145}
146
147// MCPResourceContents holds the contents of an MCP resource.
148type MCPResourceContents struct {
149	URI      string `json:"uri"`
150	MIMEType string `json:"mime_type,omitempty"`
151	Text     string `json:"text,omitempty"`
152	Blob     []byte `json:"blob,omitempty"`
153}