Skip to content

Latest commit

 

History

84 Commits

Folders and files

Repository files navigation

Resolve

A macOS command palette that puts five AI models in a debate and returns one answer.

ResolveDemo.mp4

Select text in any app, press Cmd + Shift + A, and five models answer independently.

What it is

Ask a single model a hard question and you get an answer with no error bars. You cannot tell a confident correct answer from a confident wrong one.

Resolve sends your question to five models at once, groups their answers by the position each one took, and has an arbiter synthesise a single response with the disagreements shown rather than hidden. If they disagree, you can run a debate round: each model sees the opposing positions and decides whether to hold or change its answer.

It lives in a floating panel summoned with a global hotkey, so it works over whatever you are already reading.

How it works

flowchart TD
    Q[Question or selected text] --> L{Multiple choice?}
    L -- yes --> LB[Labeller<br/>normalises options]
    L -- no --> FO[Fan-out]
    LB --> FO
    FO --> A1[Mistral]
    FO --> A2[DeepSeek]
    FO --> A3[Gemini]
    FO --> A4[Anthropic]
    FO --> A5[OpenAI]
    A1 --> CL[Classifier<br/>groups answers by stance]
    A2 --> CL
    A3 --> CL
    A4 --> CL
    A5 --> CL
    CL --> AR[Arbiter<br/>synthesises]
    AR --> OUT[Answer + open disagreements]
    OUT --> R{Resolve round?}
    R -- yes --> RD[Advocates see opposing<br/>stances and reconsider]
    RD --> CL
    R -- no --> DONE[Done]
Loading

Fan-out: The prompt goes to every enabled advocate concurrently. Each returns a position and its reasoning.

Classifier: A separate model pass groups the five answers by the position they took, so the UI can tell consensus from a genuine split and colour the cards accordingly. Counting identical strings would not catch two models agreeing in different words. Advocate responses are stripped of provider identity before this pass.

Arbiter: Synthesises a single answer and states where the advocates diverged. It sees the same anonymised responses as the classifier, so it cannot prefer its own provider's answer. Tone is configurable between balanced and decisive.

Resolve rounds: Each advocate is re-prompted with its own prior position plus the opposing stances, then decides whether to hold or revise. Classification and arbitration re-run. Every round is stored as a snapshot, so you can step back and forward through the debate with Cmd + [ and Cmd + ]. Three rounds per question by default, configurable in settings.

Resolving a disagreement

ResolvingDemo.mp4

The clip in the header asks whether woolly mammoths were still alive when the Egyptian pyramids were built. Gemini says they were extinct by then. The other advocates disagree.

Cmd + Shift + R runs a debate round. Each advocate sees the opposing positions and decides whether to hold or revise. Gemini revises, and the arbiter converges on the Wrangel Island population surviving to roughly 1650 BCE, which overlaps pyramid construction.

Features

  • Five advocates: OpenAI, Anthropic, Gemini, DeepSeek and Mistral, individually toggleable
  • Stance classification, so the UI shows agreement and disagreement rather than five loose answers
  • Iterative resolve rounds with per-round snapshots and history navigation
  • Multiple-choice handling through a labelling pass that normalises options before fan-out
  • Advocate drawer for the full reasoning behind any card (Cmd + 1 to Cmd + 5)
  • Multiple panel instances, each with its own conversation
  • Persisted conversations with search, rename and delete
  • Markdown export of any debate
  • Configurable arbiter tone, panel size, translucency, stance colours and theme

Keyboard shortcuts

Resolve is keyboard-first. The full list is in the app under Cmd + H.

Shortcut Action
Cmd + ; Toggle the palette from anywhere
Cmd + Shift + A Ask Resolve with text selected in another app
Cmd + N New chat
Cmd + Shift + R Run a resolve round
Cmd + [ / Cmd + ] Previous / next round
Cmd + 1 to Cmd + 5 Open an advocate's reasoning
Cmd + Shift + N New panel instance
Cmd + F Search past chats
Cmd + Shift + C Copy the arbiter summary
Cmd + Shift + E Export the debate as markdown

Architecture

App. SwiftUI and AppKit, macOS 15.5 and above. Runs as LSUIElement, so it is a floating utility panel rather than a Dock application. Panel lifecycle is owned by CommandPanelManager and commandPanelController.

Backend. Node and Express with Postgres, in a separate repository. Handles fan-out, classification, arbitration and persistence.

Auth. Clerk OAuth with PKCE through ASWebAuthenticationSession. Tokens live in the Keychain under the resolve.auth service. The callback scheme is resolve://auth-callback.

Persistence. Conversations, messages, runs, advocate outputs, arbiter outputs, classifier outputs and label outputs. A run is one initial pass or one resolve round. Conversation detail returns the latest completed run, so a failed or in-flight round never hides the last good result. resolve_count is maintained by a database trigger on run completion rather than in application code, which keeps it correct if a request dies midway.

Running it locally

Resolve needs the backend running first.

cd resolve-backend
npm install
npm run dev

Check it is up:

curl http://localhost:3000/health
# {"status":"ok","time":"..."}

Then open resolve.xcodeproj, select the resolve target, and press Cmd + R.

Environment

The backend reads a .env in its own repository root:

PORT=3000
DATABASE_URL=
CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=

ADVOCATE_ONE=      # OpenAI
ADVOCATE_TWO=      # Anthropic
ADVOCATE_THREE=    # Gemini
ADVOCATE_FOUR=     # DeepSeek
ADVOCATE_FIVE=     # Mistral

LABELLER=          # OpenAI, multiple-choice normalisation
CLASSIFIER=        # OpenAI, stance grouping
ARBITER=           # OpenAI, synthesis
NAMER=             # OpenAI, automatic conversation titles (optional)

Restart the backend after any .env change.

API

Method Route Purpose
GET /health Database connectivity
GET /me Verify Clerk auth
POST /conversations Create a conversation
GET /conversations List conversations
GET /conversations/:id Conversation detail and messages
PATCH /conversations/:id Rename
DELETE /conversations/:id Delete with child rows
POST /conversations/:id/messages Add a message and run the initial debate
POST /conversations/:id/messages/:messageId/resolve Run a resolve round
GET /conversations/:id/runs/:runId Full run payload
GET /conversations/:id/messages/:messageId/runs Rounds for a message

Design decisions

A classifier pass instead of string comparison. Two models can give the same answer in wording that shares almost no tokens. Grouping by classified stance is what makes "four agree, one dissents" something the UI can show.

Anonymised advocate responses. The classifier and the arbiter never see which provider wrote which answer. The arbiter is itself a model, and a model shown a labelled response from its own provider has a measurable preference for it. Stripping the labels removes that.

A floating panel rather than a window. Resolve runs as an LSUIElement panel so it can sit above other applications, including full-screen ones. You keep the debate visible while working in whatever raised the question, and a single key gets it out of the way.

User-triggered resolve rounds. Rounds run when you ask for one rather than automatically until convergence. You see the state after each round and decide whether another is worth it, and often it is not. Watching advocates flip each other's positions is also more informative than being handed the end state.

Limitations

  • Advocates sometimes talk each other out of a correct answer during a resolve round.
  • Each question costs roughly five model calls plus two more for classification and arbitration, and every resolve round repeats that.
  • No benefit on questions with no verifiable answer.
  • The backend URL is currently hardcoded to http://localhost:3000 in BackendAPIClient.swift and BackendAIClient.swift.

About

A macOS app that arbitrates multiple AI reasonings to deliver a single, high-confidence answer.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages