diff --git a/CHANGELOG.md b/CHANGELOG.md index 89d7fac..0dff517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ submodule. ## [Unreleased] +### Changed +- **Update the bundled engine to the MandoCode 0.16 generation.** Pin the shared engine to `9e25af4`, including the RazorConsole 0.6 migration, maintenance improvements, and merged Windows/Linux CI fixes. The engine now separates commands, requests, lifecycle, model inspection, and history handling into focused code. Desktop keeps its existing WinUI conversation interface; terminal features are described in `MandoCode/docs/CHANGELOG.md`. +- **Plan mode starts only through /plan.** Ordinary requests run directly, including multi-step tasks and prose outlines. Desktop no longer automatically proposes structured plans based on task complexity or model tool calls. Existing plan review, editing, execution, and recovery remain available. Settings now explain the explicit command instead of offering automatic planning. + +### Fixed +- **Resolve Desktop snapshot types explicitly.** Avoid ambiguous references when both the updated engine and Desktop expose snapshot helpers. +- **Clear guidance when Ollama retires a model.** HTTP 410 Gone errors, including wrapped provider errors, identify the unavailable model and recommend choosing another with `/model` instead of restarting Ollama or pulling a retired cloud model. The shared engine does not retry these failures. + ## [0.15.1] — 2026-09-24 **See what your agent is doing while it does it.** In 0.15.0 you sent a message and then waited, diff --git a/MandoCode b/MandoCode index 91d640e..9e25af4 160000 --- a/MandoCode +++ b/MandoCode @@ -1 +1 @@ -Subproject commit 91d640ef4806ec0d78438483fb75541ef26e19af +Subproject commit 9e25af4f178e9779de1d426f16dcfa0fa5174c9f diff --git a/src/MandoCode.Desktop.Tests/ResponseStreamerTests.cs b/src/MandoCode.Desktop.Tests/ResponseStreamerTests.cs index a38b2d6..9ea1468 100644 --- a/src/MandoCode.Desktop.Tests/ResponseStreamerTests.cs +++ b/src/MandoCode.Desktop.Tests/ResponseStreamerTests.cs @@ -329,4 +329,14 @@ public async Task StreamError_SurfacesAsErrorCard_NotThrow() Assert.Equal("", result); Assert.Contains(blocks, b => b.StartsWith("ERR:") && b.Contains("boom")); } + [Fact] + public async Task RetiredModelError_ShowsModelSwitchGuidance() + { + var error = new HttpRequestException("Response status code does not indicate success: 410 (Gone).", null, System.Net.HttpStatusCode.Gone); + var (streamer, blocks) = Make(new FakeAiService(Array.Empty(), error)); + var result = await streamer.StreamAsync("hi", CancellationToken.None); + Assert.Equal("", result); + Assert.Contains(blocks, b => b.StartsWith("ERR:") && b.Contains("no longer available on Ollama") && b.Contains("/model")); + Assert.DoesNotContain(blocks, b => b.Contains("ollama serve") || b.Contains("ollama pull")); + } } diff --git a/src/MandoCode.Desktop/App.xaml.cs b/src/MandoCode.Desktop/App.xaml.cs index ef31882..1dbded2 100644 --- a/src/MandoCode.Desktop/App.xaml.cs +++ b/src/MandoCode.Desktop/App.xaml.cs @@ -1,3 +1,4 @@ +using SnapshotStore = MandoCode.Desktop.Services.SnapshotStore; using MandoCode.Models; using MandoCode.Services; using MandoCode.Desktop.Services; diff --git a/src/MandoCode.Desktop/Controls/SettingsForm.xaml b/src/MandoCode.Desktop/Controls/SettingsForm.xaml index a2260e0..da81e80 100644 --- a/src/MandoCode.Desktop/Controls/SettingsForm.xaml +++ b/src/MandoCode.Desktop/Controls/SettingsForm.xaml @@ -180,9 +180,8 @@ - + diff --git a/src/MandoCode.Desktop/Controls/SettingsForm.xaml.cs b/src/MandoCode.Desktop/Controls/SettingsForm.xaml.cs index 239fd96..f8518dc 100644 --- a/src/MandoCode.Desktop/Controls/SettingsForm.xaml.cs +++ b/src/MandoCode.Desktop/Controls/SettingsForm.xaml.cs @@ -152,7 +152,6 @@ private void ReloadControls() S_MaxTokens.Value = cfg.MaxTokens; S_Streaming.SelectedItem = cfg.ResponseStreaming; S_AgentCallsigns.IsOn = _draftCallsigns; - S_TaskPlanning.IsOn = cfg.EnableTaskPlanning; S_DiffApprovals.IsOn = cfg.EnableDiffApprovals; S_AutoContinue.IsOn = cfg.EnableAutoContinuation; S_MaxContinuations.Value = cfg.MaxAutoContinuations; @@ -447,7 +446,6 @@ private void ResetFactory_Click(object sender, RoutedEventArgs e) if (s == Tab_Behavior) { tabName = "Behavior"; - resets.Add(("taskPlanning", Bool(d.EnableTaskPlanning))); resets.Add(("diffApprovals", Bool(d.EnableDiffApprovals))); resets.Add(("autoContinue", Bool(d.EnableAutoContinuation))); resets.Add(("maxContinuations", Num(d.MaxAutoContinuations))); diff --git a/src/MandoCode.Desktop/Services/AgentSession.cs b/src/MandoCode.Desktop/Services/AgentSession.cs index 57bcb2d..893668b 100644 --- a/src/MandoCode.Desktop/Services/AgentSession.cs +++ b/src/MandoCode.Desktop/Services/AgentSession.cs @@ -130,6 +130,7 @@ public AgentSession( Agents = globals.GetRequiredService(); CommandLog = new AgentCommandLog(); Ai = new AIService(ProjectRoot, Config, Tokens, PlanHandoff, Skills, mcpManager, McpGate, spinner, CommandLog); + Ai.UseExplicitPlanningOnly(); PreviewTools = new DesktopPreviewTools(ProjectRoot) { RequireTabId = true, ImageSink = new AgentImageSink(new AiServiceAdapter(Ai)) }; // Cross-agent observation. Registered alongside the browser tools because both are host // knowledge the engine cannot have: the engine sees one project root, the host sees the @@ -216,7 +217,7 @@ public AgentSession( Shell = new ShellRunner(ProjectRoot, Transcript, html); Controller = new ChatController( - new AiServiceAdapter(Ai), Config, Tokens, PlanHandoff, Planner, PlanRunners, + new AiServiceAdapter(Ai), Config, Tokens, PlanHandoff, PlanRunners, mcpManager, McpGate, Skills, FileProvider, ProjectRoot, music, updateCheck, Approvals, Transcript, html, Busy, Shell, PromptGate, configs, mcp, Snapshots, diff --git a/src/MandoCode.Desktop/ViewModels/ChatController.cs b/src/MandoCode.Desktop/ViewModels/ChatController.cs index 4af870c..36441db 100644 --- a/src/MandoCode.Desktop/ViewModels/ChatController.cs +++ b/src/MandoCode.Desktop/ViewModels/ChatController.cs @@ -1,9 +1,13 @@ +using SnapshotEnhancer = MandoCode.Desktop.Services.SnapshotEnhancer; +using SnapshotNaming = MandoCode.Desktop.Services.SnapshotNaming; using System.Text; using System.Text.RegularExpressions; using MandoCode.Models; using MandoCode.Services; using MandoCode.Desktop.Services; using ModelContextProtocol.Client; +using SnapshotStore = MandoCode.Desktop.Services.SnapshotStore; +using ContextSnapshot = MandoCode.Desktop.Services.ContextSnapshot; namespace MandoCode.Desktop.ViewModels; @@ -22,10 +26,7 @@ public sealed partial class ChatController private readonly TokenTrackingService _tokenTracker; private readonly PlanHandoff _planHandoff; private readonly DeferredPlanCompletion _deferredPlans; - private readonly TaskPlannerService _taskPlanner; - // Which engine runs a plan is re-read per plan, so `planner` can be flipped - // mid-session. _taskPlanner is still needed for RequiresPlanning, which is a - // planning-trigger heuristic rather than part of IPlanRunner. + // Which engine runs an explicitly requested plan is re-read per plan. private readonly PlanRunnerSelector _planRunners; private readonly McpClientManager _mcpManager; private readonly McpApprovalGate _mcpGate; @@ -269,7 +270,6 @@ public ChatController( MandoCodeConfig config, TokenTrackingService tokenTracker, PlanHandoff planHandoff, - TaskPlannerService taskPlanner, PlanRunnerSelector planRunners, McpClientManager mcpManager, McpApprovalGate mcpGate, @@ -300,7 +300,6 @@ public ChatController( _tokenTracker = tokenTracker; _planHandoff = planHandoff; _deferredPlans = new DeferredPlanCompletion(_planHandoff); - _taskPlanner = taskPlanner; _planRunners = planRunners; _mcpManager = mcpManager; _mcpGate = mcpGate; @@ -640,8 +639,6 @@ public async Task SubmitAsync(string input, string? browserContext = null) ConversationLogger?.Invoke("u", input); - // Plan heuristic BEFORE @file expansion so attachments don't inflate it. - var planning = _taskPlanner.GetPlanningDecision(input); var processedInput = ProcessFileReferences(input); // Fold the invisible ride-alongs (imported recaps, emoji reactions, external workspace @@ -668,13 +665,7 @@ public async Task SubmitAsync(string input, string? browserContext = null) _pendingReactions.Clear(); _pendingWorkspaceNotes.Clear(); - if (planning.Required) - { - _transcript.Append(_html.Dim($"Planning automatically: {planning.Reason}.")); - await ForcePlanAsync(processedInput, input); - } - else - await ProcessDirectRequestAsync(processedInput, input); + await ProcessDirectRequestAsync(processedInput, input); } finally { diff --git a/src/MandoCode.Desktop/ViewModels/ResponseStreamer.cs b/src/MandoCode.Desktop/ViewModels/ResponseStreamer.cs index e260af6..6b9bb34 100644 --- a/src/MandoCode.Desktop/ViewModels/ResponseStreamer.cs +++ b/src/MandoCode.Desktop/ViewModels/ResponseStreamer.cs @@ -167,7 +167,9 @@ public async Task StreamAsync(string input, CancellationToken token, str catch (Exception ex) { DiscardLiveTurn(); - _transcript.Append(_html.Error($"Error: {ex.Message}")); + _transcript.Append(_html.Error(OllamaModelAvailability.IsUnavailable(ex) + ? OllamaModelAvailability.Message(_config.GetEffectiveModelName()) + : $"Error: {ex.Message}")); return ""; } finally