| 1 | package control |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | |
| 6 | "reasonix/internal/agent" |
| 7 | "reasonix/internal/autoresearch" |
| 8 | "reasonix/internal/billing" |
| 9 | "reasonix/internal/checkpoint" |
| 10 | "reasonix/internal/command" |
| 11 | "reasonix/internal/config" |
| 12 | "reasonix/internal/event" |
| 13 | "reasonix/internal/evidence" |
| 14 | "reasonix/internal/hook" |
| 15 | "reasonix/internal/jobs" |
| 16 | "reasonix/internal/memory" |
| 17 | "reasonix/internal/plugin" |
| 18 | "reasonix/internal/provider" |
| 19 | "reasonix/internal/skill" |
| 20 | ) |
| 21 | |
| 22 | // This file defines the driving port: the typed, segregated interface surface |
| 23 | // that frontends (cli, desktop, bot, acp, serve) consume instead of coupling to |
| 24 | // the concrete *Controller and its ~99 methods. Each frontend depends only on |
| 25 | // the sub-ports it actually uses (interface segregation), so e.g. the bot never |
| 26 | // sees checkpoint or memory methods. |
| 27 | // |
| 28 | // The sub-ports are also the intended decomposition boundary for Controller |
| 29 | // itself: the port comes first and gives the later collaborator splits a spec to |
| 30 | // follow. *Controller implements every sub-port (asserted below). The full |
| 31 | // SessionAPI composition will accrete here as the remaining frontends migrate. |
| 32 | |
| 33 | // Lifecycle covers a session's identity and lifecycle: minting, resuming, |
| 34 | // clearing, and locating the active session. |
| 35 | type Lifecycle interface { |
| 36 | NewSession() error |
| 37 | ClearSession() error |
| 38 | Resume(s *agent.Session, path string) |
| 39 | SetSessionPath(p string) |
| 40 | SessionPath() string |
| 41 | SessionDir() string |
| 42 | Label() string |
| 43 | ModelRef() string |
| 44 | WorkspaceRoot() string |
| 45 | Close() |
| 46 | } |
| 47 | |
| 48 | // TurnControl covers driving a model turn and observing its run state: the |
| 49 | // various submit/run entry points, cancellation, steering, and status reads. |
| 50 | type TurnControl interface { |
| 51 | Submit(input string) |
| 52 | SubmitDisplay(display, input string) |
| 53 | SubmitDeliveryRecovery(display, input string) |
| 54 | SubmitInvocationDisplay(display, input string, invocations []InvocationRequest) |
| 55 | SubmitEditedDisplay(display, input, original string) |
| 56 | SubmitHTTP(input string) |
| 57 | SubmitHTTPFormat(input, format string) |
| 58 | SubmitUserTurn(input, display string) |
| 59 | Send(input string) |
| 60 | SendWithRaw(input, raw string) |
| 61 | Run(ctx context.Context, input string) error |
| 62 | RunTurn(ctx context.Context, input string) error |
| 63 | RunShell(command string) |
| 64 | Cancel() |
| 65 | Steer(text string) |
| 66 | SteerConsumed() bool |
| 67 | Running() bool |
| 68 | CancelRequested() bool |
| 69 | RuntimeStatus() RuntimeStatus |
| 70 | Turn() int |
| 71 | History() []provider.Message |
| 72 | ToolResult(toolID string) *ToolResultData |
| 73 | } |
| 74 | |
| 75 | // Approvals covers tool-approval and ask prompts plus the runtime approval |
| 76 | // posture (ask/auto/yolo). It mirrors the approvalManager surface. |
| 77 | type Approvals interface { |
| 78 | Approve(id string, allow, session, persist bool) |
| 79 | ResolvePlanDecision(id string, action PlanDecisionAction) error |
| 80 | // ResolveRecovery answers an Auto Guard card: continue|continue_task|revise. Revise |
| 81 | // refuses the mutation and steers feedback. |
| 82 | ResolveRecovery(id string, action agent.RecoveryAction, feedback string) error |
| 83 | AnswerQuestion(id string, answers []event.AskAnswer) |
| 84 | Ask(ctx context.Context, questions []event.AskQuestion) ([]event.AskAnswer, error) |
| 85 | ReplayPendingPrompts() |
| 86 | ReplayPendingPromptsTo(sink event.Sink) |
| 87 | ReplayPendingPromptsWith(sinkFactory func() event.Sink) |
| 88 | PendingPrompt() bool |
| 89 | EnableInteractiveApproval() |
| 90 | ToolApprovalMode() string |
| 91 | SetToolApprovalMode(mode string) |
| 92 | AutoApproveTools() bool |
| 93 | SetAutoApproveTools(on bool) |
| 94 | Bypass() bool |
| 95 | SetBypass(on bool) |
| 96 | SetMode(plan, autoApproveTools bool) |
| 97 | } |
| 98 | |
| 99 | // Goals covers the active-goal FSM and plan mode. |
| 100 | type Goals interface { |
| 101 | Goal() string |
| 102 | GoalStatus() string |
| 103 | SetGoal(goal string) |
| 104 | SetGoalWithResearchMode(goal string, researchMode GoalResearchMode) |
| 105 | ResumeGoal() bool |
| 106 | PauseGoal() bool |
| 107 | GoalRuntime() GoalRuntimeView |
| 108 | GoalStrict(strict bool) |
| 109 | ClearGoal() |
| 110 | AutoResearchSummary() (*autoresearch.Summary, bool) |
| 111 | AutoResearchList() ([]autoresearch.Summary, bool) |
| 112 | AutoResearchFindings(limit int) ([]autoresearch.Finding, bool) |
| 113 | RecordAutoResearchEvidence(criterionID string, input AutoResearchEvidenceInput) error |
| 114 | ResetPlannerSession() |
| 115 | PlanMode() bool |
| 116 | SetPlanMode(v bool) |
| 117 | } |
| 118 | |
| 119 | // SessionHistory covers checkpoint/rewind, branch/fork, and the log-restructuring |
| 120 | // operations (compact, summarize). |
| 121 | type SessionHistory interface { |
| 122 | Checkpoints() []checkpoint.Meta |
| 123 | CheckpointFileState(path string) (checkpoint.FileState, bool) |
| 124 | CheckpointTurnsByMessageIndex() map[int]int |
| 125 | CheckpointHasBoundary(turn int) bool |
| 126 | Rewind(turn int, scope RewindScope) error |
| 127 | PrepareRewind(turn int, scope RewindScope) (checkpoint.RewindPlan, error) |
| 128 | CommitRewind(planID string) (checkpoint.RewindResult, error) |
| 129 | UndoRewind(transactionID string) (checkpoint.RewindResult, error) |
| 130 | PrepareFileRevert(path string) (checkpoint.RewindPlan, error) |
| 131 | CommitFileRevert(planID string, resolution checkpoint.ConflictResolution) (checkpoint.RewindResult, error) |
| 132 | Fork(turn int) (string, error) |
| 133 | ForkNamed(turn int, name string) (string, error) |
| 134 | ForkSession(turn int, name string) (string, error) |
| 135 | Branch(name string) (string, error) |
| 136 | Branches() ([]agent.BranchInfo, error) |
| 137 | BranchTreeText() string |
| 138 | SwitchBranch(ref string) (agent.BranchInfo, error) |
| 139 | Compact(ctx context.Context, instructions string) error |
| 140 | CompactRatio() float64 |
| 141 | SummarizeFrom(ctx context.Context, turn int) error |
| 142 | SummarizeUpTo(ctx context.Context, turn int) error |
| 143 | } |
| 144 | |
| 145 | // MemoryControl covers session/project memory reads and mutations. |
| 146 | type MemoryControl interface { |
| 147 | Memory() *memory.Set |
| 148 | QuickAdd(scope memory.Scope, note string) (string, error) |
| 149 | SaveDoc(path, body string) (string, error) |
| 150 | SaveMemory(m memory.Memory) (string, error) |
| 151 | ForgetMemory(name string) error |
| 152 | QueueMemory(note string) |
| 153 | MemoryRevisions(ref string) []memory.Memory |
| 154 | RestoreMemory(ref string, revision int) (memory.Memory, error) |
| 155 | RestoreArchivedMemory(archivePath string) (memory.Memory, error) |
| 156 | LastMemoryRecall() memory.RecallResult |
| 157 | } |
| 158 | |
| 159 | // Capabilities covers the session's pluggable surface — MCP servers, skills, |
| 160 | // slash commands, hooks — and resolving prompt/command/skill inputs. |
| 161 | type Capabilities interface { |
| 162 | Host() *plugin.Host |
| 163 | Commands() []command.Command |
| 164 | ReloadCommands(ctx context.Context) error |
| 165 | Skills() []skill.Skill |
| 166 | SlashSkills() []skill.Skill |
| 167 | AllSkills() []skill.Skill |
| 168 | DisabledSkills() []skill.Skill |
| 169 | SkillEnabled(name string) bool |
| 170 | SetSkillEnabled(name string, enabled bool) error |
| 171 | CreateSkill(name string, scope skill.Scope, content string) (string, error) |
| 172 | UpdateSkill(name string, scope skill.Scope, content string) error |
| 173 | DeleteSkill(name string, scope skill.Scope) error |
| 174 | HookRunner() *hook.Runner |
| 175 | CustomCommand(input string) (sent string, found bool) |
| 176 | MCPPrompt(ctx context.Context, input string) (sent string, found bool, err error) |
| 177 | RunSkill(input string) (sent string, found bool) |
| 178 | AddMCPServer(e config.PluginEntry) (int, error) |
| 179 | ConnectMCPServer(e config.PluginEntry) (int, error) |
| 180 | RegisterMCPServerOnDemand(e config.PluginEntry) (int, error) |
| 181 | ConnectConfiguredMCPServer(name string) (int, error) |
| 182 | DisconnectMCPServer(name string) bool |
| 183 | RemoveMCPServer(name string) (disconnected bool, err error) |
| 184 | ConfiguredMCPNames() []string |
| 185 | DisconnectedMCPNames() []string |
| 186 | UnregisterMCPServerTools(name string) bool |
| 187 | ImportMCPEntries(entries []config.PluginEntry) (total, added, updated, connected, failed, skipped int, err error) |
| 188 | // Extension UI (stage 8a): enumerate handshake-declared extension actions |
| 189 | // and invoke one by its public /<plugin>:<action> name. Nil hub → empty / |
| 190 | // error; the stage-8b slash dispatch resolves these. |
| 191 | ExtensionActions() []ExtensionActionView |
| 192 | InvokeExtensionAction(ctx context.Context, name string, args map[string]string) (string, error) |
| 193 | // ProviderCatalog is the session's merged provider catalog — config/broker |
| 194 | // base plus sidecar-declared extension providers (plugin/... refs). Nil |
| 195 | // when no extension declared providers; frontends merge it into their |
| 196 | // model pickers and skip nil. |
| 197 | ProviderCatalog() []provider.Descriptor |
| 198 | } |
| 199 | |
| 200 | // Status covers read-only run/usage/billing telemetry and task list state. |
| 201 | type Status interface { |
| 202 | ContextSnapshot() (int, int) |
| 203 | LastUsage() *provider.Usage |
| 204 | Balance(ctx context.Context) (*billing.Balance, error) |
| 205 | Jobs() []jobs.View |
| 206 | Todos() []evidence.TodoItem |
| 207 | } |
| 208 | |
| 209 | // SessionPersistence covers snapshotting a session and tearing down its on-disk |
| 210 | // state. |
| 211 | type SessionPersistence interface { |
| 212 | Snapshot() error |
| 213 | SnapshotForShutdown() error |
| 214 | SnapshotActivity() error |
| 215 | SessionCache() (hit, miss int) |
| 216 | BeginDestroySession(sessionPath string) SessionDestroyHandle |
| 217 | CloseAfterDestroy() |
| 218 | IsDestroyingSession(sessionPath string) bool |
| 219 | ReleaseResources() |
| 220 | } |
| 221 | |
| 222 | // Input covers composing a turn's text (plan/goal/memory injection) and |
| 223 | // resolving @-references before submission. |
| 224 | type Input interface { |
| 225 | Compose(text string) string |
| 226 | ComposeSynthetic(text string) string |
| 227 | ResolveRefs(ctx context.Context, line string) (block string, errs []string) |
| 228 | HasRefs(line string) bool |
| 229 | ImageInputEnabled() bool |
| 230 | RegisterExternalFolderRef(path string) (token, displayPath string, err error) |
| 231 | } |
| 232 | |
| 233 | // Settings covers runtime session settings that don't fit a richer domain. |
| 234 | type Settings interface { |
| 235 | SetResponseLanguage(lang string) |
| 236 | SetReasoningLanguage(lang string) |
| 237 | SetDisplayRecorder(fn func(content, display string)) |
| 238 | } |
| 239 | |
| 240 | // SessionAPI is the full driving port — the composition of every sub-port. A |
| 241 | // rich frontend (the HTTP server, the desktop app, the TUI) depends on this; |
| 242 | // leaner frontends (bot, acp) depend on just the sub-ports they use. |
| 243 | type SessionAPI interface { |
| 244 | Lifecycle |
| 245 | TurnControl |
| 246 | Approvals |
| 247 | Goals |
| 248 | SessionHistory |
| 249 | MemoryControl |
| 250 | Capabilities |
| 251 | Status |
| 252 | SessionPersistence |
| 253 | Input |
| 254 | Settings |
| 255 | } |
| 256 | |
| 257 | // Compile-time proof that the concrete controller satisfies each sub-port and |
| 258 | // the full port, so frontend migrations to the interfaces are mechanical and can |
| 259 | // never silently drift from the implementation. |
| 260 | var ( |
| 261 | _ Lifecycle = (*Controller)(nil) |
| 262 | _ TurnControl = (*Controller)(nil) |
| 263 | _ Approvals = (*Controller)(nil) |
| 264 | _ Goals = (*Controller)(nil) |
| 265 | _ SessionHistory = (*Controller)(nil) |
| 266 | _ MemoryControl = (*Controller)(nil) |
| 267 | _ Capabilities = (*Controller)(nil) |
| 268 | _ Status = (*Controller)(nil) |
| 269 | _ SessionPersistence = (*Controller)(nil) |
| 270 | _ Input = (*Controller)(nil) |
| 271 | _ Settings = (*Controller)(nil) |
| 272 | _ SessionAPI = (*Controller)(nil) |
| 273 | ) |
| 274 |