Skip to content

[Local|Release] Release desktop 2.2.0 - #2214

Merged
MODSetter merged 409 commits into
mainfrom
dev
Oct 9, 2026
Merged

MODSetter merged 409 commits into
mainfrom
dev

Conversation

@MODSetter

Copy link
Copy Markdown
Owner

Summary

Brings main up to dev for desktop 2.2.0. The v2.2.0 tag goes on this PR's merge commit.

Status

  • No dry run since 2.1.0. The last Desktop Release run is the v2.1.0 tag build, 37202706962, on 4 Oct. The v2.2.0 tag build is therefore the first release build to carry opencode, the afterSign check, the macOS signIgnore for the vendor-signed binary and the build-sdcpp image gate. Keep the release a draft until Mac, Windows and Linux are green and all six stable*/latest* manifests are attached.
  • Desktop Gate is red on one stale test. dashboard-page.test.tsx:1119 still looks for the "Start a new chat in Agentic mode" menu item that feat(chat): simplify the composer's mode menus and hide model capability tags in production #2211 replaced with radio rows. The behaviour is covered by mode-picker.test.tsx. On 1a7575811, the Backend (4,121 unit, 1,148 integration) and Electron (174) jobs passed: Desktop Tests run 37990857120.
  • Expected red checks:
    • Vercel and recurseml;
    • the hosted E2E specs;
    • Security Scan, whose detect-secrets flags test fixtures and samples that hold no secrets.
  • Hosted Docker images: merging publishes them, as it did for 2.1.0.
  • Download grid: once this deploys, the site asks GitHub for v2.2.0 and shows the releases link until that release is published.

Follows #2167

CREDO23 and others added 30 commits October 5, 2026 22:21
File agent: folders, PowerPoint and Excel, templates, per-chat folders, inline previews and model capability levels
…efusal-offers-new-chat

# Conflicts:
#	docs/architecture/agent.md
#	docs/architecture/chat.md
#	surfsense_local/backend/modules/agent/agent_threads/turn.py
#	surfsense_local/frontend/src/features/agent/agent-thread.test.tsx
#	surfsense_local/frontend/src/features/chat/chat-error-notice.tsx
#	surfsense_local/frontend/src/features/chat/message.tsx
#	surfsense_local/frontend/src/features/chat/use-chat-runtime.ts
#	surfsense_local/frontend/translations/de.json
#	surfsense_local/frontend/translations/en.json
#	surfsense_local/frontend/translations/es.json
#	surfsense_local/frontend/translations/fr.json
#	surfsense_local/frontend/translations/hi.json
#	surfsense_local/frontend/translations/ja.json
#	surfsense_local/frontend/translations/ko.json
#	surfsense_local/frontend/translations/pt-BR.json
#	surfsense_local/frontend/translations/ru.json
#	surfsense_local/frontend/translations/zh-CN.json
A reply is now a run in the API, not a request tied to the window: it
keeps generating when the thread changes, the window reloads or closes,
and several threads can reply at once, on local and remote models and
the ChatGPT subscription.

- Runs: numbered frames a window can replay and follow, stop per thread,
  stop-all on quit; live text saved every 5 s, interrupted turns settled
  at startup, and each turn's ending (error, stopped, interrupted) kept
  on the message. Retry replaces the failed turn; history skips
  unanswered ones.
- Parallel slots: the local runtime loads with up to four slots sharing
  one cache (kv-unified), chosen by the load plan the catalog badge
  shares.
- Admission: one pool per loaded model, counting slots and cache tokens
  from /props; chat ahead of Studio, first come first served, a line of
  64. A full cache is the new runtime_busy error.
- Model route: Studio's text generation goes through the API for every
  model, so admission counts it; image generation and podcast voicing
  hold the runtime through the API instead of unloading the chat model
  under running replies. ChatGPT subscription retries 429 and 5xx twice,
  honouring Retry-After up to 60 s.
- Frontend: a run store outside React reattaches to running threads,
  shows Writing, Waiting with its place in line, and unread replies in
  the Chats dialog and the sidebar, in all ten languages.
- Electron asks before quitting with replies still running.
- Requests arriving together for one model share one pool, and the model
  route marks a model in use only once its stream starts.

ADR 0039 records why the API is the only path to a text model. The
background-chats proposal is removed now that it is built.
…udio-list-query

# Conflicts:
#	docs/architecture/studio.md
#	surfsense_local/frontend/src/features/studio/use-studio.ts
Brings in dev_mod (#2177): folders and server-side source scope,
per-chat agent folders, Studio's Word and PDF paths and Refine.

- The run-based send path carries the source scope: _ground returns
  both the retried turn and the scope record, and _open_turn stores
  both.
- The run store folds agent-preparing, agent-scope and a step's
  artifact in applyFrame; the agent turn's sources show on the
  question, and the outdated-thread refusal becomes the turn's ending.
- The thinking header keeps both the queue place and the preparing
  count.
- ADR 0039 (the API is the only text path) becomes 0048, since dev
  took 0039 and the file-agent proposal reserves 0040 to 0047; it is
  now listed in the ADR index.
- Studio tests patch resolve_routed_generation, the name job.py now
  uses.
- Chats dialog: a writing thread shows a spinner and a waiting one a
  clock, both where the unread dot goes. Every row keeps its time.
- Screen readers now hear each row's status ("Writing a reply",
  "Waiting for another reply (2nd in line)", "New reply"). The row's
  aria-label used to hide it; the status is now listed in
  aria-describedby with the time.
- Sidebar Chats button: one mark, no count. The dot shows when another
  chat has an unread reply, else the spinner while any is writing or
  waiting. The counts stay in its label for screen readers.
- Translations: "Writing a reply" and the waiting sentence in all ten
  languages.
An agent turn ends when its window disconnects. The proposal moves it
onto the run layer the chat already uses, so it keeps working when the
user switches thread or reloads. The run layer stays engine-free.

- Behaviour: a watched turn is unchanged. Leaving, reloading, quitting
  and reopening now work as they do for a chat. A background turn keeps
  calling its model until it ends.
- Endings: on 1.18.34, opencode exits on SIGTERM without aborting and
  stores no text until a reply ends. So a quit aborts as a stop does,
  and SurfSense records the interruption in agent_reply_endings.
- Run state: queued, running or needs-approval, reported for every
  thread, so any window can show a place in line or a waiting approval.
- Limits: at most six agent turns at once. Deleting a thread stops its
  run first, for both engines.
- Out of scope: ChatGPT-subscription retries, and a steps limit.
An agent turn is now a run, as a chat reply is: it keeps going when the
window switches thread or reloads, is followed again on return, and
Stop and quit end it through the API.

- Endings: opencode stores a Stop and a quit alike, so Stop notes
  itself in the opencode session's metadata. An abort without the note
  reads as interrupted, a reply opencode never finished as interrupted,
  any other error as the error the live turn showed. No migration.
- Run state: the run layer carries queued, running or needs-approval,
  set by each engine and reported in the thread list, so every window
  shows a place in line or a turn waiting on the user.
- Approvals are kept per thread, so returning to a turn that asked
  while another thread was open shows the request again. The Chats
  dialog and the sidebar mark it with an alert in the accent colour.
- Deleting a thread or a workspace stops its replies first, for both
  engines.
feat(chat): keep chat and agent replies running in the background
… filter

Upload files, upload folder, new folder and new note move into one "+"
menu, and Select all becomes a count that toggles them all and shows
only while the panel is hovered. The name filter opens from a search
button in the header instead of always taking a row above the list, and
closing it clears it. Open and Edit note get their own icons.
Rows moved between folders and files dropped from the desktop now go
through one drag system. The list scrolls while a drag nears its top or
bottom, a dragged row follows the pointer as a small chip instead of a
picture of the whole list, and the folder a drop lands in lights up for
both. The drop overlay is now opaque and centred. Replaces the
hand-written file drop and the per-row folder marker.
The panel's file drop target accepted any drag from outside the app, so
dragged text lit up the "Drop files" overlay. It now takes only drags
carrying files, and none while an upload runs.
feat(sources): tidy the sources panel and rebuild its drag and drop
[Local|Cache] Let the model reuse the start of every prompt
…efusal-offers-new-chat

# Conflicts:
#	docs/architecture/chat.md
#	surfsense_local/frontend/src/features/chat/use-chat-runtime.ts
The "1/4" count in the sources header read as a fraction, not an
action. It is now a text button, Select all, that reads Clear once
every source is selected, so the tooltip that explained it goes.
"Deselect all" becomes "Clear" in every language.

The header controls now match the heading: the filter text stays
text-sm below md, the + button is muted like the search button, and
the header row drops its gap.

Icon buttons that shrank icon-sm with size-6 lose the override, so
the Artifacts filter and the chat row "…" menu match the sources
header. In the Chats dialog, the separators around the open chat
hide like they do around a hovered row.
The Refine box took a band under every Word document and PDF, and
after a click on Refine nothing showed a version was being made until
the panel closed. It is now a round button over the document's
bottom-right corner that grows into a one-line instruction, focused,
in one CSS width transition run either way. Enter sends and
Shift+Enter starts a line, as in the chat composer; Escape or a click
elsewhere folds it back and keeps the draft, which a dot marks. While
a version is written the button becomes a "Writing v<n>…" pill and
the open version stays in view.

It takes the chat composer's surface and send button, and fills no
inset of its own, so it reads as one surface whether empty or not.
The "Writing v<n>…" pill was swapped in for the button, so sending
and a version finishing jumped instead of animating. The pill is now
the box's third face beside the button and the instruction: the box
takes its measured width while a version is made and returns to the
button when it is ready, all one CSS transition of width and height.
interpolate-size lets the height reach a long draft's auto, and the
pill's text is held while it fades rather than blanking. A
screen-reader status says the version once.

The pill reads "Making version <n>…" in every language. The box is
50px with the chat composer's border and hover and focus ring, the
send button is round at the composer's size, and the button takes the
AI editing icon.
The Refine box was keyed on the open version, so switching versions
remounted it: the draft was lost, and the version being made could
not turn back into the button in place. It is now keyed on the
document and stays through a switch, keeping what was typed; only the
title, body and downloads change. The title comes from the list, so
it never reads "Loading…", and nothing is sent until the version's
body is on screen. A refusal belongs to the version it refused and
clears when another opens.

The version menu scrolls past six and a half rows under a fixed
"Versions" label, with a fade at the cut.
ybai08 and others added 25 commits October 8, 2026 16:23
The test read the staging folder as the program's grandparent, which it
is on Windows and Linux (program/soffice). On macOS the program is
Contents/MacOS/soffice, the grandparent is Contents, and the test
failed.
fix(local): offer no thinking switch in an agent thread
…-again

fix(local): revoke the grant a ChatGPT sign-in replaces
feat(local): give a model that cannot run a code the interface can translate
perf(models): follow-ups to search debounce and cache
…sion-log

feat(desktop): route main process warnings into the session log
test(local): find the office pack's staging folder on macOS too
docs(plugins): redesign plugins as MCP servers behind one tool gateway
…ck start

The README is shorter and leads with a table of the file formats SurfSense
reads, makes, edits as a copy and transforms. A five-step Quick start, from
install to a cited answer and a Word file from Studio, replaces the old one.
Private by default and Models are gone; Star history stays.

Only the English README changes. The translations still follow the old layout.
The hero GIF and the five Quick start images are still to record; a comment
above each one says what to show.
The hero GIF placeholder becomes the recorded demo: the agent revises a city
contract as a copy, with tracked changes and a comment on each change. The
video is a GitHub upload that plays inline; the top comment says how to
replace it.
…s only in dev

The mode menu already says each model's Agentic reason, so the line under a
new chat's composer repeated it. The model picker's capability label is now a
dev-only badge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each mode gets a short line. An Agentic that did not pass carries a tag, and
its reason or gate shows in a tooltip beside the row, as the add menu does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An open chat's menu now shows the same Basic and Agentic rows as a new chat,
with its own mode checked and the other marked "New chat". The doc also says
the model picker's capability tags show only in dev builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(chat): simplify the composer's mode menus and hide model capability tags in production
Capitalise NotebookLM, hyphenate privacy-focused, end the licence line with a
full stop and put a blank line before Quick start. The top comment drops the
Quick start image notes, since those images are gone, and says every
README.<locale>.md follows this file section for section.
Each README.<locale>.md is now a full translation of README.md, section for
section: the format table, Quick start, the demo video, the comparison and
Star history. Each one keeps its earlier register and terms, names what the
app shows with the app's own translation of that string, and links its nav
to its own translated headings. Arabic, which the app does not translate,
gives the English UI name in parentheses.
docs(readme): a shorter README led by the format table, translated into ten languages
@vercel

vercel Bot commented Oct 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
surf-sense-frontend Ready Ready Preview Oct 9, 2026 9:34pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown

Walkthrough

Warning

Review details and warnings were omitted to fit the comment limit.

@MODSetter
MODSetter merged commit 552e056 into main Oct 9, 2026
28 of 44 checks passed

This branch was successfully deployed

1 active deployment
Preview — 36cb688d Deployed Oct 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants