App name: WingmanAI AI persona name: Goose Stack: Flutter macOS + thin Swift method channel (~200 lines) Location:
/Users/daneyand/Documents/Bob/ProjectO/WingmanAIPhilosophy: Ambient, proactive, thermally respectful, 100% local by default
- What Is WingmanAI
- Why Build This
- Competitive Landscape
- Architecture Overview
- VisionProvider Abstraction
- Task Queue Architecture
- Play / Pause / Stop Controls
- Focus Awareness
- Sub-Tasks
WingmanAI is a macOS desktop overlay app powered by local AI inference. Its AI persona, Goose, watches your screen continuously, understands what you are doing in any app, and surfaces proactive advice β all without sending anything off your machine. When Goose wants to act, it proposes a shell command or AppleScript action and waits for your approval before executing. You can also talk to Goose directly, by voice or text, at any time.
Goose runs in a compact always-on-top floating panel in your chosen screen corner. One click expands it to a full interactive chat. It speaks aloud when it has something to say. It listens when you speak to it. It keeps working on tasks in the background while you focus on other things. When its context window fills, it writes a concise summary of what was happening and loads it fresh β so it never loses the thread of your work.
WingmanAI is named after Goose, Maverick's wingman in Top Gun β always watching your back, speaking up when it matters, executing when asked.
Every AI assistant tool available today is reactive β you open it, ask a question, get an answer, close it. None of them watch your screen. None of them understand what you are doing right now. None of them speak up without being asked. WingmanAI fills that gap.
Two years ago this was not viable. Local vision models were too weak. Local reasoning models had insufficient context. Today:
llava:7bvia Ollama can read a screenshot and understand itgranite3.1-dense:8bhas 128K context β your entire codebase in one window- A 64GB Apple Silicon Mac can run both simultaneously without breaking a sweat
- Ollama is mature, bundled, and proven in deepThinkER and deepThink
Approximately 40% of WingmanAI's core is already written in the deepThinkER and deepThink projects:
OllamaClientβ streaming inference, model managementOllamaLauncherβ bundled runtime lifecycleModelManagerβ download, check, statusHardwareDetectorβ RAM tier, GPU detectionResourceMonitorβ CPU, RAM, process pollingAppSettings+AppPathsβ settings persistence patternNetworkFetcher+NetworkSearchTool+NetworkFetchToolβ web searchMemoryStoreβ persistent memoryAuditLogβ append-only action loggingContextManagerβ token tracking and resetResearchEngineβ background research with phase transitionsAgentTool/ToolRegistry/ToolCallInterceptorβ tool call frameworkFileReadTool/FileWriteToolβ file I/O tools- Entitlements, AppDelegate pattern, Ollama PID lifecycle β all solved
Microsoft Copilot Recall, Google AI features, and Apple Intelligence all read your screen and send data to cloud servers. A significant number of developers actively want the opposite β full ambient AI awareness with zero data leaving the machine. WingmanAI owns that position entirely.
No API fees. No subscriptions. Inference is free after the hardware is paid for. Frontier model escalation is optional and throttled by the user.
Ollama
A local inference runtime β the engine under the hood. Not a UI, not an assistant. You run ollama run granite3.1-dense:8b in a terminal. No screen watching, no overlay, no voice, no tools. WingmanAI uses Ollama as its bundled inference backend. Ollama is infrastructure, not competition.
Jan.ai A polished desktop chat app (6.3M downloads, 44K GitHub stars). Runs local models, has an OpenAI-compatible API server, MCP connectors, and an autonomous agent mode. Beautiful UI. Think local ChatGPT replacement β you open the window, type, get answers. No screen watching, no overlay, no proactive advice. You still have to go to it.
LM Studio (Bionic) Recently rebranded with "Bionic," their agent for work and code. Local model runner with a polished UI, model library, and an OpenAI-compatible local server. Strong developer focus β VS Code integrations, local API for tools. Again: you open the window and ask. No ambient awareness, no overlay, no vision of your screen.
Unsloth The most different from the rest. Its primary superpower is fine-tuning and training models 2Γ faster with 70% less VRAM. It also runs models, has web search, RAG, and "Unsloth Start" which bridges local models to Claude Code and Codex. Not an assistant β a training platform. The right tool when you want to make a model better, not when you want help while working.
OpenClaw A self-hosted Node.js messaging gateway. Connects chat apps (WhatsApp, Telegram, Discord, Slack, Signal, iMessage, and 20+ more) to AI coding agents. Remote-first β you message it from your phone or laptop and it responds. Has a macOS menu bar app and browser control UI, but its core interaction model is chat-in-a-messaging-app. Requires cloud API keys (Anthropic, OpenAI, etc.). No screen watching, no proactive advice, no local inference. Open source, MIT licensed, community-driven.
| Capability | Ollama | Jan.ai | LM Studio | Unsloth | OpenClaw | WingmanAI |
|---|---|---|---|---|---|---|
| Core purpose | Inference runtime | Local chat UI | Local chat + dev API | Model training | Messaging gateway | Ambient desktop AI |
| Screen watching | β | β | β | β | β | β Unique |
| Proactive advice | β | β | β | β | β | β Unique |
| Always-on overlay | β | β | β | β | Menu bar only | β Unique |
| App control / keyboard injection | β | β | β | β | β | β Unique |
| Works in any app | β | β | β | β | β | β Unique |
| Voice two-way | β | β | β | β | TTS only | β |
| Live screen vision | β | β | β | β | β | β |
| Shell execution | β | β | β | β | β | β + approval gate |
| Web search | β | β | β | β | β | β |
| Persistent memory | β | β | β | β | MEMORY.md | β |
| Git awareness | β | β | β | β | β | β |
| Session continuity files | β | β | β | β | β | β Unique |
| Model fine-tuning | β | β | β | β Best-in-class | β | β |
| 100% local / offline | β | β | β | β | β | β |
| Zero API cost | β | β | β | β | β | β optional |
| Bundled β no setup | β | β | β | β | β npm + Node | β |
| IBM Granite first-class | Runs it | Runs it | Runs it | Runs it | β | β Default |
| Thermal management | β | β | β | β | N/A | β diff-gated |
| Frontier model escalation | β | β | β | β cloud | β cloud only | β local-first + optional cloud |
| Parallel task queue | β | β | β | β | β | β |
OpenClaw and WingmanAI solve fundamentally different problems. OpenClaw answers: "How do I reach my AI from anywhere β my phone, my messaging apps, remotely?" WingmanAI answers: "How do I have an AI that watches what I am doing right now, on this machine, and helps without me having to ask?"
OpenClaw's power comes from cloud models (Anthropic, OpenAI) β your conversations go to external servers. WingmanAI never sends anything off your Mac.
OpenClaw has zero screen watching capability. Its macOS presence is a menu bar icon and a browser dashboard. WingmanAI's entire value proposition is built on ambient screen awareness.
They are complementary, not competing. OpenClaw is the AI you reach from anywhere. WingmanAI is the AI that watches your back while you work.
Every tool above is reactive. You have to go to them. WingmanAI is ambient and proactive β it sees your screen, knows what app you are in, speaks up when it has something useful, and acts when you approve. No other tool in this space does this. That is the moat.
WingmanAI.app
βββ Swift layer (~200 lines total)
β βββ NSWindow.level = .floating (always-on-top)
β βββ NSWindow.setFrame() (corner snapping)
β βββ NSWindow.alphaValue (opacity)
β βββ SFSpeechRecognizer (speech-to-text)
β βββ AVSpeechSynthesizer (text-to-speech)
β βββ NSWorkspace.frontmostApplication (active app detection)
β βββ FlutterMethodChannel (bridge to Dart)
β
βββ Dart / Flutter layer
βββ core/ (pure Dart, zero Flutter imports)
β βββ ollama/ OllamaClient, OllamaLauncher, ModelManager, HardwareDetector
β βββ vision/ VisionProvider (abstract), OllamaVisionProvider (default)
β βββ screen/ ScreenWatcher, PerceptualHasher, RegionSelector
β βββ reasoning/ ReasoningEngine (Granite chat + tool calls)
β βββ tasks/ TaskScheduler (abstract), SerialTaskScheduler (v1)
β β ParallelTaskScheduler (hook, dormant in v1)
β βββ tools/ AgentTool, ToolRegistry, ToolCallInterceptor
β β βββ network/ NetworkSearchTool, NetworkFetchTool (reused)
β β βββ shell/ ShellTool (enabled with approval gate)
β β βββ applescript/ AppleScriptTool (new)
β β βββ file/ FileReadTool, FileWriteTool (reused)
β βββ network/ NetworkFetcher, RateLimiter (reused)
β βββ research/ ResearchEngine (reused from deepThinkER)
β βββ frontier/ FrontierClient, OpenAIProvider, AnthropicProvider, GrokProvider
β βββ memory/ MemoryStore (reused), ProjectProfile
β βββ session/ SessionSummaryWriter, SessionLoader
β βββ context/ ContextManager (reused)
β βββ system/ ResourceMonitor (reused), ThermalGuard, ActivityDetector
β βββ clipboard/ ClipboardWatcher
β βββ git/ GitInspector
β βββ rag/ RagIndexer (hook in v1)
β βββ snippets/ SnippetLibrary
β βββ feedback/ FeedbackLog, QualityTracker
β βββ audit/ AuditLog (reused)
β βββ settings/ AppSettings (new Goose fields)
β βββ paths/ AppPaths (new WingmanAI paths)
β
βββ ui/ (Flutter widgets)
βββ overlay/ CompactCard, ExpandedChat, ActionApprovalCard
βββ performance/ PerformanceStrip, PerformancePanel
βββ tasks/ TaskQueuePanel, TaskCard
βββ settings/ SettingsScreen (full parity + Goose-specific)
βββ firstlaunch/ SplashScreen, DownloadScreen, PermissionsScreen
β CornerPickerScreen, PersonaPickerScreen, TutorialScreen
βββ transparency/ VisionTransparencyLog
βββ snippets/ SnippetLibraryPanel
βββ theme/ AppTheme, AppColors (Goose palette)
Same pattern as deepThinkER β pure Dart streams, setState() in widgets, no external state management packages.
com.apple.security.app-sandbox= false (Ollama subprocess)com.apple.security.automation.apple-events= true (AppleScript)com.apple.security.network.client= true (web search, frontier APIs)- Screen Recording β user-granted at first launch via System Settings
- Accessibility β user-granted at first launch via System Settings
LLaVA 7B is the default vision model but it is not the final word. The vision layer is abstracted behind an interface from day one so swapping to a better model requires changing one implementation, not touching the rest of the codebase.
abstract class VisionProvider {
/// Analyze a screen capture and return a natural-language description.
/// [imageBytes] is the raw PNG/JPEG bytes of the capture.
/// [context] is optional hint text (active app name, user task, etc.)
Future<String> analyzeScreen(Uint8List imageBytes, String context);
/// Human-readable name shown in Settings.
String get displayName;
/// Whether this provider is currently available and healthy.
Future<bool> get isAvailable;
}| Class | Model | Notes |
|---|---|---|
OllamaVisionProvider |
llava:7b |
Default β bundled, local, free |
MoondreamProvider |
moondream2 |
Hook β ~1.7GB, faster for screen reading |
Qwen2VLProvider |
qwen2-vl:7b |
Hook β better quality vision |
FrontierVisionProvider |
GPT-4o Vision / Claude Vision | Hook β cloud, requires API key |
To replace LLaVA with a better model:
- Implement
VisionProviderin a new class - Register it in
VisionProviderRegistry - Expose it in Settings vision model dropdown
- Done β zero changes to
ScreenWatcher,ReasoningEngine, or any UI
- moondream2 β already on Ollama at ~1.7GB, significantly faster than LLaVA for screen reading tasks specifically, likely first upgrade
- qwen2-vl:7b β stronger general vision quality
- GPT-4o Vision β frontier quality when accuracy matters more than privacy
Multiple tasks run simultaneously. All I/O (web searches, file reads, shell commands, AppleScript calls) executes in parallel across tasks. Inference (the GPU-heavy reasoning step) is serialized β one at a time, queued. This gives the feel of concurrent work with zero thermal penalty.
abstract class TaskScheduler {
/// Max concurrent inference tasks. v1 default: 1 (serial, thermally safe).
/// Future: set to N for true parallel inference with cloud models or
/// more powerful hardware.
int get maxConcurrentInference;
Future<TaskResult> schedule(GooseTask task);
void cancel(String taskId);
void cancelAll();
Stream<TaskEvent> get taskStream;
}maxConcurrentInference = 1- I/O steps run via
Future.wait()across all queued tasks - Inference steps join a single queue, processed one at a time
- Thermal guard: if CPU/GPU hits red threshold, I/O concurrency also throttles
maxConcurrentInference = N(user configurable in Advanced Settings)- All steps β I/O and inference β run fully in parallel
- Built and tested in v1, but not exposed in the Settings UI
- Activated by: frontier cloud models enabled (thermals irrelevant) OR user explicitly unlocks in Advanced Settings
- Thermal interlock: bypassed when all tasks route to cloud models
In AppSettings:
/// Hidden in v1 UI. Exposed in v2 Advanced Settings.
/// 1 = serial (default). N = parallel inference pool size.
final int maxConcurrentInferenceTasks; // default: 1v1 today: Local Granite, serial, maxConcurrent=1, thermally guarded
Near term: User gets M4 Ultra, bumps to 2, still local, still cool
Cloud on: Claude handles tasks, maxConcurrent=5, thermals irrelevant
Future: Apple Silicon 10x faster, maxConcurrent=N, all local, all fast
Three always-visible transport controls in the compact overlay. They represent three distinct system states with clear resource implications.
| Button | State | Models | Inference | Screen Capture | Resume Speed |
|---|---|---|---|---|---|
| βΆ Play | Active | Loaded in GPU RAM | Enabled | Running | Instant |
| βΈ Pause | Paused | Stay loaded in GPU RAM | Frozen | Stopped | Instant β models already warm |
| βΉ Stop | Stopped | Unloaded from all memory | Disabled | Stopped | Seconds β models must reload |
Pause is a temporary hold β like putting a conversation on hold. Models stay resident in VRAM. Pressing Play resumes in milliseconds with zero reload cost. Use this when you need a thermal break or want to focus without Goose interrupting.
Stop is a full resource release β models are explicitly evicted from GPU RAM and system memory via OllamaClient.unloadModel() (POST to /api/generate with keep_alive=0). This is already implemented in deepThinkER. Use this when you need all your RAM and GPU for something else β a game, video editing, a heavy build. When you press Play again Goose reloads exactly as it did at first launch.
The three buttons live in the compact card header β always visible regardless of overlay state. They are large enough to tap without expanding the overlay. Color coded:
- βΆ Green when stopped/paused (actionable β press to activate)
- βΈ Amber when active (press to pause)
- βΉ Red when active or paused (press to fully stop)
The last state is saved to AppSettings. If Goose was paused when the app quit, it reopens paused. If it was stopped, models are not reloaded until the user presses Play.
Two complementary layers that tell Goose where the user's attention is and what they want help with.
Goose tracks NSEvent.mouseLocation continuously via the Swift method channel. When analyzing a screenshot, the mouse coordinates are passed as context to the VisionProvider. The vision model is instructed to treat the region around the cursor as the highest-priority area β if the mouse is hovering over a terminal pane in a split editor, that pane is more important than the browser window behind it.
This is cheap β a simple coordinate stream from Swift to Dart, no additional inference cost.
When the user highlights text anywhere on screen, Goose:
- Detects the selection via the macOS Accessibility API (
AXSelectedTextattribute on the focused element) β exact text, no OCR needed - Reads the full selected text directly
- Understands the context β which app, what type of content (email, code, document, chat), conversation history, active project, memory
- Reasons about what the user most likely wants β generating contextually intelligent suggestions specific to that text in that moment, informed by everything Goose knows about what the user has been doing
- Surfaces a selection action card in the compact overlay immediately
- Executes the chosen action and injects the result back into the app in-place, replacing the selected text
The suggestions on the card are not hardcoded buttons β they are generated by Granite reasoning over the selected text plus full conversation context. The card always includes a free-text input so the user can ask anything beyond what Goose suggested.
Example β user has been discussing a client presentation for 20 minutes, then highlights a paragraph in Mail:
βββββββββββββββββββββββββββββββββββββββββββββββ
β πͺΏ You selected: β
β "We need to circle back on the Q3 β
β deliverables ASAP" β
β β
β Given your presentation context: β
β β’ Rewrite for the client (formal tone) β
β β’ Convert to a clear action item β
β β’ Flag this as a blocker in your notes β
β β’ Translate to French for Marie β
β β
β Or ask me anything about it... β
β [_________________________________________] β
βββββββββββββββββββββββββββββββββββββββββββββββ
The suggestions change entirely based on context:
- Same text highlighted in Xcode β "Explain this code", "Find the bug", "Write a test for this"
- Same text highlighted in Terminal β "Explain this error", "Suggest a fix", "Search for this"
- Same text highlighted in a browser β "Summarize this", "Fact-check this", "Save to memory"
When Goose rewrites or transforms the selected text, it injects the result back in-place:
Primary method β app-specific AppleScript:
tell application "Mail"
set selected text of front message to rewrittenText
end tellUniversal fallback β keyboard simulation:
CGEventPost: type rewritten text over selection
(selection already active β typing replaces it)
The diff view (already in the feature set) shows old vs new before injection is confirmed, so the user always has final approval.
- Accessibility permission β already in the first launch permission setup flow
AXSelectedTextpolling β firesSelectionEventto Dart when selection changesNSEvent.mouseLocationβ already part of Sub-Task 3 Swift method channel
This is the highest-leverage feature in WingmanAI. It transforms Goose from a passive observer into an active collaborator in any app β Mail, Xcode, VS Code, Pages, Notion, Terminal, browser, Slack β without requiring those apps to have any AI integration of their own. The user's selection is the universal interface.
Status: [x] done
Intent Create the WingmanAI Flutter macOS project from scratch, mirroring the deepThinkER project structure. Establish the core directory layout, pubspec.yaml with all required dependencies, base entitlements, AppDelegate, and the folder structure for all planned modules. No business logic yet β just the skeleton every subsequent sub-task builds on.
Expected Outcomes
flutter createproject exists at/Users/daneyand/Documents/Bob/ProjectO/WingmanAIlib/core/andlib/ui/directory structure matches the architecture in Section 4pubspec.yamlincludes all dependencies- macOS entitlements configured (sandbox off, Apple Events, network)
AppPathsandAppSettingsscaffolded with all WingmanAI-specific fields- App builds and launches to a blank window without errors
Todo List
- Run
flutter create WingmanAIin ProjectO directory - Create full
lib/core/subdirectory tree matching Section 4 architecture - Create full
lib/ui/subdirectory tree - Configure
pubspec.yamlwith all dependencies (see below) - Copy and adapt
DebugProfile.entitlementsandRelease.entitlementsfrom deepThinkER - Copy and adapt
AppDelegate.swift(Ollama lifecycle + PID file) - Write
AppPathsfor WingmanAI (~/Library/Application Support/WingmanAI/) - Write
AppSettingswith all Goose-specific fields - Write placeholder
main.dartthat launches to a blank window - Confirm
flutter build macossucceeds
Dependencies to add to pubspec.yaml
http: ^1.2.0 # Ollama API + web search + frontier APIs
path_provider: ^2.1.0 # App support directory
url_launcher: ^6.2.0 # Open URLs, System Settings deep links
flutter_local_notifications: ^18.0.0 # Desktop notifications
audioplayers: ^6.0.0 # Sound cues
archive: ^3.6.0 # Session export ZIPRelevant Context
- deepThinkER pubspec:
deepThinkER/pubspec.yaml - deepThinkER AppDelegate:
deepThinkER/macos/Runner/AppDelegate.swift - deepThinkER entitlements:
deepThinkER/macos/Runner/DebugProfile.entitlements - deepThinkER AppPaths:
deepThinkER/lib/core/paths/app_paths.dart - deepThinkER AppSettings:
deepThinkER/lib/core/settings/app_settings.dart
Status: [x] done
Intent
Port the bundled Ollama runtime from deepThinkER into WingmanAI. Copy the Ollama binary and libraries, adapt OllamaLauncher, OllamaClient, ModelManager, and HardwareDetector. Establish the two default models (llava:7b, granite3.1-dense:8b) in a new ModelRegistry for WingmanAI.
Expected Outcomes
- Ollama binary ships in
.app/Contents/Resources/ollama/ OllamaLauncher.start()spawns Ollama and writes PID fileOllamaClient.generateStream()works for both chat and vision (base64 image) requestsModelManager.checkModels()detects llava:7b and granite3.1-dense:8bHardwareDetector.detect()returns RAM tier and GPU backend- Model management panel: shows installed models, size, re-download, delete
Todo List
- Copy
macos/Runner/ollama/directory from deepThinkER - Copy and adapt
copy_ollama.shbuild phase script - Port
OllamaLauncherβ update PID file name towingmanai_ollama.pid - Port
OllamaClientβ addanalyzeImage(base64, prompt)method for vision - Port
ModelManagerβ update default models to llava:7b + granite3.1-dense:8b - Port
HardwareDetector - Write
ModelRegistrywith WingmanAI model entries including Granite variants - Write model management panel UI
- Confirm Ollama starts, models listed, health check passes
Relevant Context
- deepThinkER OllamaLauncher:
deepThinkER/lib/core/ollama/ollama_launcher.dart - deepThinkER OllamaClient:
deepThinkER/lib/core/ollama/ollama_client.dart - deepThinkER ModelManager:
deepThinkER/lib/core/ollama/model_manager.dart - deepThinkER ModelRegistry:
deepThinkER/lib/core/ollama/model_registry.dart - deepThinkER copy_ollama.sh:
deepThinkER/macos/Runner/copy_ollama.sh
Status: [x] done
Intent Write the thin Swift layer (~200 lines) that gives Dart control over macOS-native capabilities: floating window positioning, opacity, speech recognition, text-to-speech, active app detection, text selection monitoring, mouse position tracking, and the macOS Services menu item.
Expected Outcomes
- Window stays above all other windows (
NSWindow.level = .floating) - Window snaps to any of 4 corners on demand from Dart
- Window opacity controllable from Dart
SFSpeechRecognizertranscribes speech and sends text to Dart via method channelAVSpeechSynthesizerspeaks text received from DartNSWorkspace.shared.frontmostApplicationpolls active app name every 2s to DartNSEvent.mouseLocationstreams cursor coordinates to Dart continuouslyAXSelectedTextpolls focused UI element for selected text β firesSelectionEventto Dart on change- macOS Services "Ask Goose" item registered and fires into Dart with selected text
- macOS URL scheme
wingmanai://ask?q=...registered and fires into Dart
Todo List
- Add
FlutterMethodChanneltoMainFlutterWindow.swift - Implement
window/setCornerβNSWindow.setFrame()to 4 corner positions - Implement
window/setOpacityβNSWindow.alphaValue - Implement
window/setFloatingβNSWindow.level = .floating - Implement
speech/startListeningβSFSpeechRecognizer+AVAudioEngine - Implement
speech/stopListeningβ cancel recognition task - Implement
tts/speakβAVSpeechSynthesizer.speak() - Implement
tts/stopβ cancel current utterance - Implement
tts/listVoicesβ return availableAVSpeechSynthesisVoicelist - Implement
system/frontmostAppβNSWorkspacepolling timer - Implement
focus/mouseLocationβNSEvent.mouseLocationevent stream to Dart - Implement
focus/selectedTextβAXUIElementCopyAttributeValue(kAXSelectedTextAttribute)polling, emit on change - Implement
focus/injectTextβCGEventPostkeyboard simulation for universal text injection - Register macOS Services "Ask Goose" in
Info.plist+ handler - Register URL scheme
wingmanaiinInfo.plist+AppDelegatehandler - Write Dart
WindowChannel,SpeechChannel,SystemChannel,FocusChannelwrappers
Relevant Context
- deepThinkER MainFlutterWindow:
deepThinkER/macos/Runner/MainFlutterWindow.swift - deepThinkER AppDelegate:
deepThinkER/macos/Runner/AppDelegate.swift - Focus Awareness design: Section 8 of this plan
Status: [x] done
Intent
Build the screen capture pipeline, perceptual diff engine, screen region selector, and the VisionProvider abstraction with OllamaVisionProvider as the default implementation. This is the core sensing layer of WingmanAI.
Expected Outcomes
ScreenWatchercaptures screenshots at configurable intervals viascreencapture -xPerceptualHashercomputes image hash and skips inference when screen is unchangedRegionSelectorallows user to define a screen rectangle β only that region is capturedOllamaVisionProvidersends base64 image to llava:7b and returns descriptionVisionProviderinterface is fully abstracted β swap requires one class change- Privacy exclusions respected β if frontmost app is excluded, capture is skipped
- Multi-monitor aware β captures correct display
- Vision transparency log records last N captures + conclusions
- Screenshot annotation renders bounding boxes from LLaVA response when available
- Manual snapshot trigger fires immediately on demand
Todo List
- Write
VisionProviderabstract interface - Write
OllamaVisionProviderimplementingVisionProviderviaOllamaClient - Write
VisionProviderRegistryβ register default + hook slots for future providers - Write
ScreenWatcherβ timer-based capture loop, configurable interval - Implement capture via
Process.run('screencapture', ['-x', '-t', 'png', path]) - Write
PerceptualHasherβ MD5 of sampled pixels, skip if hash unchanged - Write
RegionSelectorβ stores CGRect, clips capture to region - Write privacy exclusion check β compare frontmost app against exclusion list
- Write
VisionTransparencyLogβ circular buffer of last 20 captures + conclusions - Write annotation renderer β draw bounding boxes on thumbnail in transparency log
- Connect manual snapshot trigger to
ScreenWatcher.captureNow() - Wire
ScreenWatcheroutput toReasoningEngine(Sub-Task 5)
Relevant Context
- VisionProvider abstraction rationale: Section 5 of this plan
- deepThinkER shell pattern:
deepThinkER/lib/core/ollama/ollama_launcher.dartlines 166-243 - deepThinkER AppSettings:
deepThinkER/lib/core/settings/app_settings.dart
Status: [x] done
Intent
Build ReasoningEngine β the Granite-powered core that receives screen descriptions, user messages, and tool results, and produces advice or actions. Port the tool framework from deepThinkER and enable ShellTool, AppleScriptTool, and NetworkSearchTool with the approval gate for destructive tools.
Expected Outcomes
ReasoningEngineaccepts screen context + user message + memory + session context and calls Granite- Tool call interception works β
[SEARCH:],[FETCH:],[SHELL:],[APPLESCRIPT:],[REMEMBER:],[RECALL:],[FILE_READ:],[FILE_WRITE:]all intercepted and dispatched ShellToolenabled with approval gate β proposes command, waits for user tap before executingAppleScriptToolenabled with approval gate β same pattern- Network tools rate limited
ContextManagertracks token usage, triggers session summary write at 90% threshold- Confidence signal extracted from Granite response and surfaced on advice card
- Frontier escalation triggered when confidence low (if enabled in settings)
Todo List
- Port
AgentTool,ToolRegistry,ToolCallInterceptorfrom deepThinkER - Port
NetworkFetcher,NetworkSearchTool,NetworkFetchToolfrom deepThinkER - Port
FileReadTool,FileWriteToolfrom deepThinkER - Port
MemoryStore,RememberTool,RecallToolfrom deepThinkER - Port
ContextManagerfrom deepThinkER β update threshold and session writer hook - Enable
ShellToolβ implementProcess.runexecution, wire to approval gate stream - Write
AppleScriptToolβProcess.run('osascript', ['-e', script])with approval gate - Write
ReasoningEngineβ builds system prompt, callsOllamaClient.generateStream() - Write
ApprovalGateβ stream that pauses tool execution until user approves/denies - Write confidence extractor β parses Granite response for uncertainty signals
- Register all tools in
main.dart - Write
AuditLogport β logs all tool executions with timestamp and result
Relevant Context
- deepThinkER AgentTool:
deepThinkER/lib/core/tools/agent_tool.dart - deepThinkER ToolRegistry:
deepThinkER/lib/core/tools/tool_registry.dart - deepThinkER ShellTool stub:
deepThinkER/lib/core/tools/shell/shell_tool.dart - deepThinkER NetworkFetcher:
deepThinkER/lib/core/network/network_fetcher.dart - deepThinkER ContextManager:
deepThinkER/lib/core/context/context_manager.dart - deepThinkER AuditLog:
deepThinkER/lib/core/audit/audit_log.dart
Status: [x] done
Intent Build the always-on-top floating overlay in both its compact card state and expanded full chat state. Wire the Swift method channel for window positioning and opacity. Implement the performance strip, token counter, status indicator, confidence indicator, diff view, advice history, Play/Pause/Stop transport controls, and the selection action card for Focus Awareness.
Expected Outcomes
- Compact card visible in configured corner, always above other windows
- βΆ βΈ βΉ transport controls always visible in compact card header β color coded (Section 7)
- Play activates screen watching + inference, Pause freezes inference with models loaded, Stop unloads all models
- Goose status indicator: Watching / Thinking / Idle / Paused / Stopped / DND / Error
- Performance strip: tokens used/total + RAM + CPU + GPU β color coded
- Confidence indicator on each advice card
- Thumbs up/down on each advice card
- "Why did you say that?" button on each card
- Action approval card β shows proposed shell/AppleScript command, Approve/Deny buttons
- Diff view β old vs new side by side before approving text/code changes
- Selection action card β appears immediately on text selection, shows AI-generated contextual suggestions + free-text input (Section 8)
- Click anywhere expands to full chat with message history and input bar
- Advice history scrollable in expanded view
- Conversation branching β fork thread button
- "?" button shows keyboard shortcut cheat sheet
- Focus/DND toggle accessible from compact card
- Quick profile switcher in compact card
- Attribution label: "πͺΏ Goose" vs "π©οΈ Claude" vs "π©οΈ ChatGPT"
Todo List
- Write
CompactCardwidget β fixed width ~300px, corner-positioned - Wire
WindowChannel.setCorner()andWindowChannel.setOpacity()to compact card - Write
TransportControlswidget β βΆ βΈ βΉ buttons, color coded, always in header - Write
GooseStateManagerβ manages Active/Paused/Stopped transitions, callsOllamaClient.unloadModel()on Stop - Write
PerformanceStripwidget β token progress + RAM + CPU + GPU bars - Write
GooseStatusIndicatorwidget β emoji + label, color coded - Write
AdviceCardwidget β message, confidence, thumbs, why-button, attribution - Write
ActionApprovalCardwidget β command preview, approve/deny buttons, timer - Write
DiffViewwidget β side-by-side old/new with change highlighting - Write
SelectionActionCardwidget β selected text preview + AI-generated suggestions + free-text input - Write
SelectionDetectorDart service β subscribes toFocusChannel.selectedTextstream, triggers Granite reasoning for suggestions - Write
MouseContextTrackerβ subscribes toFocusChannel.mouseLocation, passes coordinates toVisionProvider - Write
ExpandedChatwidget β full message history, input bar, advice history - Write
ConversationBranchβ fork thread UI - Write
ShortcutCheatSheetoverlay - Write
QuickProfileSwitcherdropdown in compact card - Implement free drag with snap-to-corner on drag end
- Implement opacity slider in expanded settings panel
- Wire
FeedbackLogto thumbs up/down taps - Connect
ScreenWatcherβVisionProviderβReasoningEngineβCompactCard - Wire
SelectionDetectorβReasoningEngineβSelectionActionCard - Wire approved selection action β
AppleScriptToolorFocusChannel.injectText()fallback
Relevant Context
- deepThinkER AiQuadrant widget patterns:
deepThinkER/lib/ui/quadrants/ai_quadrant.dart - Swift method channel: Sub-Task 3 of this plan
- Play/Pause/Stop design: Section 7 of this plan
- Focus Awareness design: Section 8 of this plan
- deepThinkER OllamaClient.unloadModel:
deepThinkER/lib/core/ollama/ollama_client.dart
Status: [x] done
Intent Wire the Swift method channel voice capabilities into the Dart layer. Implement wake word detection, global hotkey for voice input, voice on/off settings, multi-language support, and the voice picker.
Expected Outcomes
- Tapping microphone button or pressing global hotkey activates speech recognition
- Transcribed text appears in input bar and auto-sends
- "Hey Goose" wake word activates listening (when enabled in Settings)
- Goose speaks advice aloud via
AVSpeechSynthesizer(when voice output enabled) - Voice picker in Settings β lists available macOS neural voices
- Language picker β 50+ languages via
SFSpeechRecognizer - Voice on/off toggles independently for input and output
- Wake word on/off toggle
Todo List
- Write
VoiceInputServiceβ callsSpeechChannel.startListening(), handles transcription stream - Write
VoiceOutputServiceβ callsSpeechChannel.speak(),stop(), manages queue - Implement global hotkey registration (macOS
NSEvent.addGlobalMonitorForEvents) - Implement wake word detection β lightweight keyword spotting on transcription stream
- Write
VoiceSettingsmodel β input on/off, output on/off, language, voice, hotkey, wake word - Write voice picker UI β dropdown of available neural voices with preview button
- Write language picker UI β dropdown of SFSpeechRecognizer supported locales
- Wire voice input to chat input bar β auto-fill and send on recognition complete
- Wire voice output to
ReasoningEngineresponse stream β speak each new advice card
Relevant Context
- Swift method channel: Sub-Task 3 of this plan
- deepThinkER audioplayers pattern:
deepThinkER/pubspec.yamlaudioplayers dependency
Status: [x] done
Intent Port web search from deepThinkER (DuckDuckGo search + direct URL fetch), integrate it with the tool framework, and implement the proactive research mode that runs background research while the user keeps working β reusing deepThinkER's ResearchEngine.
Expected Outcomes
[SEARCH: query]and[FETCH: url]work in Granite responses- Rate limiting enforced (configurable, default 10/min)
- Search results shown as collapsible cards in expanded chat
- "Research this for me" command queues a background research task
- Research runs via
ResearchEngine(gather β synthesize phases) - Result surfaces as an advice card when complete
- User can cancel research task from task queue panel
Todo List
- Port
NetworkFetcher,NetworkSearchTool,NetworkFetchTool(already in Sub-Task 5) - Port simplified
RateLimiterβ flat rate cap, no trust tiers - Port
ResearchEnginefrom deepThinkER β adapt for single-model (Granite only) - Write
ProactiveResearchTaskβ wraps ResearchEngine as aGooseTask - Write search result card UI β collapsible, shows query + snippet + source URL
- Add "Research this for me" intent detection in
ReasoningEngine - Wire to task queue (Sub-Task 10)
Relevant Context
- deepThinkER NetworkFetcher:
deepThinkER/lib/core/network/network_fetcher.dart - deepThinkER ResearchEngine:
deepThinkER/lib/core/research/research_engine.dart - deepThinkER RateLimiter:
deepThinkER/lib/core/network/rate_limiter.dart
Status: [x] done
Intent Build the optional cloud frontier model escalation layer. Off by default. Supports ChatGPT (GPT-4o), Claude (claude-opus-4), and Grok (grok-3) with user-configurable priority order, per-minute throttle, and automatic failback chain.
Expected Outcomes
FrontierClientabstract interface with three provider implementations- Priority order respected β tries provider 1, falls back to 2, then 3, then Granite
- Per-minute throttle enforced via sliding window
- API keys stored in
AppSettings, never transmitted except to the respective API - GPT-4o Vision and Claude Vision usable for vision escalation
- Three escalation triggers work: explicit user request, auto on low confidence, frustration signal
- Attribution shown in overlay: "π©οΈ Claude says:" vs "πͺΏ Goose says:"
- Feature is completely off and invisible when disabled in Settings
Todo List
- Write
FrontierClientabstract interface - Write
OpenAIProviderβ GPT-4o chat + GPT-4o Vision via OpenAI API - Write
AnthropicProviderβ claude-opus-4 chat + vision via Anthropic API - Write
GrokProviderβ grok-3 via xAI API - Write
FrontierRouterβ priority order + sliding window throttle + failback - Write
FrustrationTrackerβ counts consecutive dismissals, triggers escalation at threshold - Write
QualityTrackerβ tracks thumbs up/down per category, triggers escalation on low rate - Integrate escalation decision into
ReasoningEngineresponse pipeline - Write frontier settings UI β on/off toggle, API key fields with test buttons, priority picker, throttle slider
- Ensure feature is fully invisible when disabled
Relevant Context
- deepThinkER RateLimiter sliding window:
deepThinkER/lib/core/network/rate_limiter.dart - AppSettings pattern:
deepThinkER/lib/core/settings/app_settings.dart
Status: [x] done
Intent
Build the TaskScheduler infrastructure with SerialTaskScheduler as the v1 implementation and ParallelTaskScheduler as a built-but-dormant hook. Implement the task queue UI panel showing all active and queued tasks.
Expected Outcomes
SerialTaskSchedulerqueues inference tasks, runs them one at a time- I/O steps (
Future.wait()) run in parallel across all queued tasks ParallelTaskSchedulerbuilt and tested butmaxConcurrentInferenceTaskshidden in UI- Task queue panel shows active tasks with progress, cancel button
- Thermal guard reduces I/O concurrency when CPU/GPU hits red threshold
GooseTaskmodel supports: research, shell, AppleScript, file read/write, web search- Timed task mode: task auto-cancels after user-specified duration
- Scheduled tasks: cron-style scheduler fires tasks at configured times (hooks, full feature if stable)
- Goose away message: tasks queued while idle, summary card on return
Todo List
- Write
GooseTaskmodel β id, type, description, status, result, createdAt, cancelledAt - Write
TaskEventβ state change stream events - Write
TaskSchedulerabstract interface (see Section 6) - Write
SerialTaskSchedulerβ inference queue + parallel I/O - Write
ParallelTaskSchedulerβ fully parallel, activated by settings flag - Write
ThermalGuardβ subscribes toResourceMonitor, emits throttle signals - Write
TaskQueuePanelUI β list of active/queued tasks, cancel buttons - Write
TaskCardUI β task description, progress indicator, result preview - Implement timed task cancellation
- Write scheduled task hooks β
ScheduledTaskStorewith cron expressions, executor fires on time - Write idle detection + away message logic in
ActivityDetector
Relevant Context
- deepThinkER ResourceMonitor:
deepThinkER/lib/core/system/resource_monitor.dart - Task queue architecture: Section 6 of this plan
Status: [x] done
Intent Port MemoryStore from deepThinkER, build project profiles, implement session summary file writing and loading, build the snippet library, and wire the context rollover logic that loads previous session summaries into a fresh context window.
Expected Outcomes
MemoryStorepersists facts across sessions in~/Library/Application Support/WingmanAI/memory/- Project profiles store: name, folder path, pinned files, persona prompt β switchable from overlay
- Session summary file written continuously as Goose works β one file per context window
- Format:
gooseSession1-2025-07-14.mdβ living summary, not transcript - On context rollover: loads most recent session files until token budget reached
- Snippet library: save, search, copy, delete β stored locally
- Export session: one tap saves today's summary to
~/Desktop - Auto-learn: dismissals and thumbs-down stored in memory to adjust future advice
Todo List
- Port
MemoryStore,MemoryEntry,MemoryPersistencefrom deepThinkER - Port
RememberTool,RecallToolfrom deepThinkER - Write
ProjectProfilemodel β name, folder, pinned files, persona, model overrides - Write
ProjectProfileStoreβ CRUD + JSON persistence - Write
QuickProfileSwitcherUI component (used in Sub-Task 6) - Write
SessionSummaryWriterβ continuously updates summary file on meaningful events - Write
SessionLoaderβ loads N most recent session files within token budget - Write
SnippetLibraryβ save/search/delete, JSON persistence - Write
SnippetLibraryPanelUI - Write session export β copies today's file to
~/Desktop - Wire
ContextManager90% threshold βSessionSummaryWriter.finalize()+ new session start - Wire new context window start β
SessionLoader.loadRecent()β inject into system prompt
Relevant Context
- deepThinkER MemoryStore:
deepThinkER/lib/core/memory/ - deepThinkER ContextManager:
deepThinkER/lib/core/context/context_manager.dart - deepThinkER AppPaths:
deepThinkER/lib/core/paths/app_paths.dart
Status: [x] done
Intent Build all input sources beyond screen watching and voice: git awareness, clipboard watcher, drag-and-drop into overlay, macOS Services integration, and URL scheme handler.
Expected Outcomes
GitInspectorreadsgit status,git diff --stat,git log --oneline -10from active project folder- Auto commit message generator fires when Goose detects uncommitted changes in active folder
ClipboardWatcherpolls for clipboard changes, offers to act on copied text- Drag-and-drop: files, images, and PDFs dragged onto overlay are read and fed to Goose
- "Ask Goose" macOS Services item sends selected text from any app to Goose
wingmanai://ask?q=...URL scheme fires query into Goose from any app
Todo List
- Write
GitInspectorβProcess.run('git', ...)with output parsing - Write commit message generator β triggered by
GitInspectordetecting staged changes - Write
ClipboardWatcherβdart:isolatetimer pollingClipboard.getData() - Write clipboard action card UI β "Rewrite?", "Translate?", "Explain?" options
- Implement drag-and-drop target on overlay β
DragTargetwidget - Write file/image/PDF reader for drag-and-drop β pipe to
ReasoningEngine - Wire macOS Services handler (registered in Sub-Task 3) to Goose chat input
- Wire URL scheme handler (registered in Sub-Task 3) to Goose chat input
- Write folder watcher β
Directory.watch()on active project folder
Relevant Context
- deepThinkER shell pattern for Process.run:
deepThinkER/lib/core/ollama/hardware_detector.dart - deepThinkER FileReadTool:
deepThinkER/lib/core/tools/file/
Status: [x] done
Intent Build the higher-order intelligence features: stuck detection, meeting mode, active app awareness, error message explainer, terminal suggester, draft improver, tone analyzer, translation, developer tools, daily brief hook, and reminder hook.
Expected Outcomes
- Active app name from Swift channel used to tailor advice context
- Stuck detection fires gentle prompt after same screen persists 3+ minutes
- Meeting mode auto-activates DND when Zoom/Teams/Meet is frontmost
- Idle detection pauses screen capture after N minutes of keyboard inactivity
- Error message explainer fires when stack trace or compiler error detected on screen
- Terminal command suggester fires when failed command detected in Terminal
- Draft improver: select text β Goose rewrites β diff view β approve
- Email tone analyzer: when Mail/Outlook frontmost, offers tone reading on visible email
- Translation: "translate this" intent detected in user message
- Code review: "review this file" intent triggers FileReadTool + structured review
- Daily brief hook: stub that fires at configured time β full feature if scheduler is stable
- Reminder hook: intent detected, stored in memory, timer fires TTS reminder
Todo List
- Write
StuckDetectorβ compares consecutive screen hashes over time window - Write
MeetingDetectorβ checks frontmost app against video call app list - Write
ActivityDetectorβ keyboard event monitoring via Swift channel, idle timer - Write
ErrorDetectorβ regex patterns for stack traces and compiler errors on screen text - Write
TerminalDetectorβ detects Terminal/iTerm frontmost + failed command pattern - Write
ToneAnalyzerprompt template β injected when Mail/Outlook frontmost - Write
DraftImproverflow β selection β rewrite request β diff view β AppleScript inject - Write intent classifier in
ReasoningEngineβ routes "translate", "review", "research" etc. - Write daily brief stub β
DailyBriefSchedulerwith hook, fires if scheduler is stable - Write reminder stub β
ReminderStore+ timer + TTS trigger, hooks if unstable - Wire time-of-day greetings β morning / afternoon / end-of-day context injections
Relevant Context
- deepThinkER ResearchEngine:
deepThinkER/lib/core/research/research_engine.dart - Sub-Task 10 scheduled tasks hook for daily brief and reminders
Status: [x] done
Intent Build the performance monitoring display and thermal management system. Port ResourceMonitor and ContextManager from deepThinkER. Implement the auto-throttle that slows capture interval when the system runs hot.
Expected Outcomes
PerformanceStripshows: context tokens used/total, RAM in GB, CPU %, GPU % β always visible- Color coding: green β amber (80%) β red (90%) for each metric
PerformancePanelshows full progress bars + Ollama/model status in expanded view- Auto-throttle: when CPU or GPU hits red threshold, capture interval increases automatically
- User can override throttle with "Ignore thermal limits" toggle
- Thermal guard feeds into task scheduler (Sub-Task 10) to limit concurrency
- GPU % via
powermetricsor Metal performance counters
Todo List
- Port
ResourceMonitorfrom deepThinkER β add GPU % polling viapowermetrics - Port
ContextManagerfrom deepThinkER (already in Sub-Task 5, wire to UI here) - Write
ThermalGuardβ subscribes toResourceMonitor, emitsThermalState(green/amber/red) - Write
PerformanceStripwidget β single line, always in compact card - Write
PerformancePanelwidget β full detail in expanded view - Implement auto-throttle logic β
ThermalGuardred βScreenWatcher.increaseInterval() - Add "Ignore thermal limits" toggle in Settings
- Wire thermal state to task scheduler concurrency cap
Relevant Context
- deepThinkER ResourceMonitor:
deepThinkER/lib/core/system/resource_monitor.dart - deepThinkER HardwareDetector:
deepThinkER/lib/core/ollama/hardware_detector.dart
Status: [x] done
Intent Build the full Settings screen covering all configurable options, and the complete first launch flow: splash, hardware detection, model downloads, permissions setup, corner picker, persona preset picker, and interactive onboarding tutorial.
Expected Outcomes
- Settings screen covers all options listed in the feature set
- Persona presets: Developer, Writer, Executive, Default β user picks at first launch, editable after
- First launch flow navigates: Splash β Hardware β Download β Permissions β Corner β Persona β Tutorial β Live
- Permission setup screen has guided instructions + deep links to System Settings for Screen Recording and Accessibility
- Interactive tutorial walks through: how to summon Goose, how to approve actions, how to use voice, how to configure
- Update check on launch β compares version, shows notification + GitHub link if newer version available
- Quiet hours configurable β time range, Goose silent during those hours
- RAG folder picker β selects folder to index (hook in v1, indexer stub)
- Notification quiet hours enforced in
ReasoningEnginebefore surfacing advice
Todo List
- Write
SplashScreenβ animated, shows Ollama start status - Write
HardwareCheckScreenβ RAM tier display, GPU detection - Write
ModelDownloadScreenβ progress bars for llava:7b + granite3.1-dense:8b - Write
PermissionsScreenβ Screen Recording + Accessibility guided setup - Write
CornerPickerScreenβ visual 4-corner picker with live preview - Write
PersonaPresetPickerScreenβ 4 presets with descriptions - Write
TutorialScreenβ interactive walkthrough of core features - Write full
SettingsScreenwith all sections - Implement update checker β
http.getto GitHub releases API, compare versions - Implement quiet hours logic in
ReasoningEngineβ suppress proactive advice during quiet window - Write RAG folder picker UI +
RagIndexerstub with hook - Write
LaunchAgentplist for auto-restart on crash β installed at first launch
Relevant Context
- deepThinkER WelcomeScreen:
deepThinkER/lib/ui/screens/welcome_screen.dart - deepThinkER StartupConfigScreen:
deepThinkER/lib/ui/screens/startup_config_screen.dart - deepThinkER ResourceGateScreen:
deepThinkER/lib/ui/screens/resource_gate_screen.dart - deepThinkER AppSettings:
deepThinkER/lib/core/settings/app_settings.dart
Status: [x] done
Intent Build the reliability and trust layer: crash recovery, LaunchAgent auto-restart, audit log, local-only guarantee badge, feedback quality log viewer, and the vision transparency log UI.
Expected Outcomes
LaunchAgentplist installed at first launch β macOS auto-restarts WingmanAI on crash- State snapshot written before every inference call β recoverable if crash during inference
- Audit log records every tool execution: timestamp, tool, argument, result, approved/denied
- Audit log viewer accessible from Settings
- Local-only badge visible in compact card β pulsing indicator that nothing has left the Mac
- Feedback quality log viewer shows thumbs up/down history by category
- Vision transparency log UI shows last 20 captures with Goose's conclusions and bounding boxes
- Crash recovery on next launch: detects incomplete session, offers to resume
Todo List
- Port
AuditLogfrom deepThinkER β adapt for WingmanAI tool set - Write
AuditLogViewerscreen - Write
StateSnapshotβ serializes current session state to disk before each inference - Write crash recovery check in
_AppLoaderβ detects stale snapshot, offers resume - Write
LaunchAgentplist generator β installs~/Library/LaunchAgents/ai.wingman.goose.plist - Write
LocalOnlyBadgewidget β animated indicator in compact card - Write
FeedbackLogViewerscreen β thumbs up/down by category, over time - Wire
VisionTransparencyLogdata (Sub-Task 4) to UI panel - Write update notification UI β banner in compact card when new version detected
Relevant Context
- deepThinkER AuditLog:
deepThinkER/lib/core/audit/audit_log.dart - deepThinkER AppDelegate PID lifecycle:
deepThinkER/macos/Runner/AppDelegate.swift
Status: [x] done
Intent Build the RAG (Retrieval-Augmented Generation) indexer as a v1 hook. The folder picker, index file format, and retrieval interface are fully implemented. The actual embedding generation is stubbed β if Granite embedding support proves stable, the stub is replaced with a real implementation. If not, the hook remains for v2.
Expected Outcomes
RagIndexerinterface defined βindexFolder(path),query(text)β relevant chunksRagIndexerStubreturns empty results β safe no-op if indexing not availableGraniteRagIndexerattempts to use Granite embedding endpoint β activates if available- Index stored in
~/Library/Application Support/WingmanAI/rag/ - RAG folder picker in Settings wired to
RagIndexer.indexFolder() ReasoningEnginecallsRagIndexer.query()before each inference β injects relevant chunks if any
Todo List
- Write
RagIndexerabstract interface - Write
RagIndexerStubβ safe no-op, always returns empty - Write
GraniteRagIndexerβ attempts Granite/api/embeddingsendpoint - Write
RagStoreβ stores chunks + embeddings as NDJSON - Write
RagRetrieverβ cosine similarity search over stored embeddings - Wire RAG query into
ReasoningEnginesystem prompt builder - Test with real Granite embeddings β if stable, activate; if not, leave stub
Relevant Context
- Granite 3.1 supports embedding generation via
/api/embeddingsendpoint in Ollama - deepThinkER tool pattern for extensibility:
deepThinkER/lib/core/tools/agent_tool.dart
The following features are built in v1 but may be left as stubs if they prove unstable during testing. Each has a clean interface so the stub can be replaced with a full implementation without touching other code.
| Feature | Hook Interface | Activation Condition |
|---|---|---|
| Daily brief | DailyBriefScheduler stub |
If scheduled tasks (Sub-Task 10) are stable |
| Reminder setting | ReminderStore + timer stub |
If scheduled tasks are stable |
| Local RAG | RagIndexer abstract + stub |
If Granite embeddings work reliably |
| Parallel inference | ParallelTaskScheduler built, hidden |
User unlocks in Advanced Settings (v2) |
| Scheduled tasks | ScheduledTaskStore + cron hook |
If cron-style scheduler is stable in v1 |
| Screenshot annotation | Bounding box renderer stub | If LLaVA returns usable bounding box data |
| Goose away message | AwayMessageTask stub |
If idle detection + task queue are stable |
| Conversation branching | ThreadStore stub |
If session continuity is stable first |
Plan written by Bob. Ready for implementation in Agent mode.