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 "git.secluded.site/crush/internal/agent/tools/mcp"
 14	"git.secluded.site/crush/internal/config"
 15	"git.secluded.site/crush/internal/history"
 16	"git.secluded.site/crush/internal/lsp"
 17	"git.secluded.site/crush/internal/message"
 18	"git.secluded.site/crush/internal/oauth"
 19	"git.secluded.site/crush/internal/permission"
 20	"git.secluded.site/crush/internal/session"
 21	"git.secluded.site/crush/internal/skills"
 22)
 23
 24// LSPClientInfo holds information about an LSP client's state. This is
 25// the frontend-facing type; implementations translate from the
 26// underlying app or proto representation.
 27type LSPClientInfo struct {
 28	Name            string
 29	State           lsp.ServerState
 30	Error           error
 31	DiagnosticCount int
 32	ConnectedAt     time.Time
 33}
 34
 35// LSPEventType represents the type of LSP event.
 36type LSPEventType string
 37
 38const (
 39	LSPEventStateChanged       LSPEventType = "state_changed"
 40	LSPEventDiagnosticsChanged LSPEventType = "diagnostics_changed"
 41)
 42
 43// LSPEvent represents an LSP event forwarded to the TUI.
 44type LSPEvent struct {
 45	Type            LSPEventType
 46	Name            string
 47	State           lsp.ServerState
 48	Error           error
 49	DiagnosticCount int
 50}
 51
 52// AgentModel holds the model information exposed to the UI.
 53type AgentModel struct {
 54	CatwalkCfg catwalk.Model
 55	ModelCfg   config.SelectedModel
 56}
 57
 58// Workspace is the main abstraction consumed by the TUI and CLI. It
 59// groups every operation a frontend needs to perform against a running
 60// workspace, regardless of whether the workspace is in-process or
 61// remote.
 62type Workspace interface {
 63	// Sessions
 64	CreateSession(ctx context.Context, title string) (session.Session, error)
 65	GetSession(ctx context.Context, sessionID string) (session.Session, error)
 66	ListSessions(ctx context.Context) ([]session.Session, error)
 67	SaveSession(ctx context.Context, sess session.Session) (session.Session, error)
 68	DeleteSession(ctx context.Context, sessionID string) 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	ListSkills(ctx context.Context) ([]skills.CatalogEntry, error)
132	ReadSkill(ctx context.Context, skillID string) ([]byte, skills.SkillReadResult, error)
133
134	// MCP operations (server-side in client mode)
135	MCPGetStates() map[string]mcptools.ClientInfo
136	MCPRefreshPrompts(ctx context.Context, name string)
137	MCPRefreshResources(ctx context.Context, name string)
138	RefreshMCPTools(ctx context.Context, name string)
139	ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
140	GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
141	EnableDockerMCP(ctx context.Context) error
142	DisableDockerMCP() error
143
144	// Events
145	Subscribe(program *tea.Program)
146	Shutdown()
147}
148
149// MCPResourceContents holds the contents of an MCP resource.
150type MCPResourceContents struct {
151	URI      string `json:"uri"`
152	MIMEType string `json:"mime_type,omitempty"`
153	Text     string `json:"text,omitempty"`
154	Blob     []byte `json:"blob,omitempty"`
155}