- chat.md, overview.md: how a streamed token renders only the live reply
(runSummaries, the 32 ms notice gap, LiveThreadRuntime, the bounded
highlighter, memoized panels) and that the columns resize.
- model-capabilities.md, agent.md, data-model.md, 07-dev-setup.md: the two
chat modes, their gates and defaults, where a pick is stored, the 0-id fix,
and how to re-run the OpenRouter sweep and regenerate capabilities.json.
- RELEASE.md: the steps 2.2.0 was released by, from the bump (with its
PowerShell form) to the tag on the merge, the Known issues, an optional
update check and publishing with --latest; dry runs are signed.
- 06-repo-readme.md, AGENTS.md, README.md's comment: the README's structure,
how to change it and its translations, re-checking the format table, and
replacing the demo video.
- Stale statements fixed (the Apple agreement, opencode never shipped, three
languages, Studio's exec(), proposal statuses, the roadmap), and each gap
still open after the release in its doc's Known gaps, linked to #2219-#2230.
capability_list/sweep/openrouter-screen-v1.json is the input the shipped
list's 259 openrouter-screen and assumed rows come from: the screen of 7 and
8 Oct 2026 with the 18 models re-run after the optional-id-0 fix in place of
their first verdicts. With only the ladder's rows in capabilities.json,
catalog_rows.py on it writes the shipped list back unchanged.
Its two runs are beside it under sweep/runs/, each a sweep-results.json and
its attempts.jsonl, with the listing's absolute path made repo-relative. The
run folders, about 840 MB of transcripts and files, stay out.
The app's plugin module mirrors plugins/: what every kind shares at the
top (routes, gateway, installed, results), both plugin lists in lists/,
the remote kind in remote/ (MCP client, sign-in, RemoteMcpSource), and
bundles in bundles/. The license key, SurfSense's row in who can publish
what, and withdrawing a built-in plugin now cover bundles as well as
remote plugins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The earlier design's runner, its plugin_runs model, task and stop
helpers move from modules/plugins/ to modules/plugins/bundles/, with
their tests, mirroring plugins/bundles/. modules/plugins/ is left for
the remote plugin code. Docs and links follow the move. No behaviour
change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SurfSense's plugins are listed in a built-in list packaged into the app
and shown with no network or consent. The registry holds only partners'
and community plugins; the app fetches it, signed, when the user turns
on other publishers, and no installer carries it. Only the built-in list
can say `surfsense` or carry `license`, and no list holds plugin code,
so the app does not grow with plugins. ADR 0053 records the split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SurfSense's plugins, free and paid, run in one container on Azure
Container Apps at plugins.surfsense.com, which also serves the signed
registry. Their code moves to plugins/remote/, with paid code in
plugins/remote/proprietary/: the folder decides the license, `access`
the price. The registry ships with the app and shows with no consent.
ADR 0054 records the change and supersedes ADR 0047 in part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plugins are being built as remote MCP servers first. The earlier, local
design's SDK, CLI, manifest rules and example move from plugins/core/ and
plugins/example/ to plugins/bundles/, deferred with bundles, so the top of
plugins/ is free for the plugin host and the registry. No behaviour change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bootSidecars() moves to sidecars/boot.ts and takes its paths, the packaged flag and the secret as a BootContext, instead of reading app, process.resourcesPath and safeStorage. The three watchers get their own files and tests. index.ts builds the context from Electron and passes the crash notice to the window in as the crash handler.
Nothing under sidecars/ imports electron, so the container can run the same code.
Fixes#2103
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.
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.
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>
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>
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>
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.
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.
An opened Hugging Face embedder repo that cannot run says why in a sentence
the backend words, so it was English in every language. Four of its five
refusals now carry a NotAnEmbedderCode in the row's not_runnable_code:
gated, no ONNX build, no tokenizer.json, flagged by Hugging Face's scan.
The interface has a line for each, in ten languages. The fifth names a
file and keeps travelling as its English sentence.
Refs #1967
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.
With an optional id of 0 left out, 18 OpenAI and Grok models ran the
screening again: o3, GPT-4.1, GPT-5 mini, GPT-5.4 mini, GPT-5.1 Codex
mini, GPT-5.6 Luna Pro, GPT-6 Luna and both Grok models now pass both
cases and start chats in Agentic. 106 screened models are now agent, 102
below.
The chat router now decides per model whether it is on by default. It is
on for models that pass the router test, remote models the catalog marks
as supporting structured output, and ChatGPT plan models. It is off
until the user turns it on for unmeasured local models, models the
catalog says nothing about, and custom connections. It never runs on a
model the catalog marks as lacking structured output.
A schema does not guarantee the reply matches it, so every router reply
is now checked against its schema. A choose reply that does not match
counts as none, a fill reply that does not match ends the router with no
call, and three mismatches in a row switch the router off for that model
with a notice. If an @ mention cannot be filled, the composer shows the
tool's inputs as a small form. ADR 0052 is updated to match and notes
that router calls on a ChatGPT plan count against the user's plan.
The plugins README gains a prior-art table (Pi, Claude's connectors,
Obsidian), an order of work, what this design gives up, a
contributor-guide workstream and the offline wording for the interface.
02-registry gains a "who can publish what" table, and bundles/README a
side-by-side comparison of remote and bundle plugins.
Rename the registry file from connectors.json to plugins.json. It is now
the only list: every plugin the app shows is in it, SurfSense's own
included. An entry points to the plugin, and the code stays with its
publisher.
On each merge to main, CI signs the list with Ed25519 and uploads it to
SurfSense's plugin host. The app fetches it from there and checks the
signature against a key built into the app, so new plugins show up
without an app update. The copy in the installer is only used when there
is no network. This replaces the unsigned fetch from
raw.githubusercontent.com.
Describe how bundle entries will work later. A bundle's code and release
files stay in its author's repository. A scanner checks each release and
records its file URLs and sha256s in the entry, and the app refuses any
file whose hash does not match.
Also: a tool whose description changes is switched off again, tool
descriptions are capped at 2,000 characters, and CODEOWNERS requires a
maintainer to approve surfsense and partner entries. ADR 0053, the
plugins and bundles READMEs, 03-remote-plugins and 07-later are updated
to match.