Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
233 commits
Select commit Hold shift + click to select a range
06aa828
fix(web): replace the OG image and fix pages that shared without it
AnishSarkar22 Oct 4, 2026
ad631c9
fix(local): fail a reply the endpoint cuts off with an in-stream error
MohammadHijjawi97 Oct 5, 2026
32a0c0d
feat(local): encode a podcast episode as MP3
Cedric921 Oct 6, 2026
6f5eff0
feat(local): generate third-party notices at build time
Cedric921 Oct 6, 2026
82ab15f
Merge remote-tracking branch 'upstream/dev' into feat/homepage-new
AnishSarkar22 Oct 6, 2026
6abbd7e
feat(homepage): made homepage full width
AnishSarkar22 Oct 6, 2026
8dc40e2
refactor(homepage): remove dithered hero backdrop
AnishSarkar22 Oct 6, 2026
43f6840
feat(web): make the site navbar a floating sticky bar
AnishSarkar22 Oct 6, 2026
e491ea9
refactor(web): move the marketing site from custom CSS to Tailwind
AnishSarkar22 Oct 6, 2026
c895504
feat(homepage): replace the logo grid with a scrolling marquee
AnishSarkar22 Oct 6, 2026
2aac273
feat(web): switch the marketing site to the light palette
AnishSarkar22 Oct 7, 2026
bd9c15b
feat(homepage): replace the top announcement bar with a hero pill
AnishSarkar22 Oct 7, 2026
6c3e13a
feat(homepage): set the hero and a closing CTA on a dithered scene
AnishSarkar22 Oct 7, 2026
195c094
feat(homepage): restyle the navbar and soften the hero scene
AnishSarkar22 Oct 7, 2026
61f51e1
feat(homepage): draw the air gap as a scroll-driven isometric diorama
AnishSarkar22 Oct 7, 2026
ffced41
refactor(homepage): import the air-gap diorama as an SVG through SVGR
AnishSarkar22 Oct 7, 2026
3f4c9a1
feat(homepage): close every page with a scenic CTA footer
AnishSarkar22 Oct 7, 2026
dd22d77
feat(web): hold marketing content to one centred column, smooth-scrol…
AnishSarkar22 Oct 7, 2026
930f2be
feat(homepage): scale the marketing site up on monitors wider than 19…
AnishSarkar22 Oct 7, 2026
20e9784
fix(local): print a script's traceback relative to its run folder
TonyWu2333 Oct 8, 2026
90c22e6
fix(web): line every marketing divider up with the text
AnishSarkar22 Oct 8, 2026
f39628b
feat(homepage): show the twelve artifact formats as a bento of previews
AnishSarkar22 Oct 8, 2026
8c0fda2
feat(homepage): draw a chat on the air-gap laptop screen instead of code
AnishSarkar22 Oct 8, 2026
3a200af
feat(homepage): turn the confidential-work section into a meadow banner
AnishSarkar22 Oct 8, 2026
8041f85
fix(web): say llama.cpp, not Ollama, is the desktop app's local runtime
AnishSarkar22 Oct 8, 2026
fcb4afc
chore(web): drop the unused feature_ollama translation string
AnishSarkar22 Oct 8, 2026
56845fa
feat(homepage): redesign the comparison as a card with a raised SurfS…
AnishSarkar22 Oct 8, 2026
2969d3b
fix(homepage): cast every air-gap diorama shadow from one light
AnishSarkar22 Oct 8, 2026
57fd272
feat(homepage): label the on-your-machine section as how it works
AnishSarkar22 Oct 8, 2026
fe4a07f
fix(homepage): smooth the air-gap database and plug its cables in
AnishSarkar22 Oct 8, 2026
231b05d
feat(homepage): rebuild "What you get" as three scene rows
AnishSarkar22 Oct 8, 2026
4572e0d
perf(homepage): stop the plugin pills from repainting every frame
AnishSarkar22 Oct 8, 2026
ae5486c
feat(homepage): turn "Why it is different" into three preview cards
AnishSarkar22 Oct 8, 2026
c79b17b
feat(blog): redesign the blog index and posts in the homepage style
AnishSarkar22 Oct 8, 2026
a173f04
fix(web): give long-form marketing text the homepage's light colours
AnishSarkar22 Oct 8, 2026
1b79921
fix(web): centre the privacy and terms pages
AnishSarkar22 Oct 8, 2026
1f1053d
feat(web): redesign /plugins in the homepage style
AnishSarkar22 Oct 8, 2026
4167cdb
feat(web): redesign /pricing in the homepage style
AnishSarkar22 Oct 8, 2026
6c5233c
feat(web): redesign /contact in the homepage style
AnishSarkar22 Oct 8, 2026
3c6fea8
feat(web): redesign /downloads in the homepage style
AnishSarkar22 Oct 8, 2026
931b4dd
feat(web): redesign /sunset and the shared guide steps
AnishSarkar22 Oct 8, 2026
b163c94
feat(web): redesign /changelog and /announcements in the homepage style
AnishSarkar22 Oct 8, 2026
4dcad77
feat(web): redesign /private-ai-for-business in the homepage style
AnishSarkar22 Oct 8, 2026
4d95899
feat(homepage): reword the confidential-work heading around the vendo…
AnishSarkar22 Oct 8, 2026
ba589cb
feat(homepage): show the install card as a macOS-style dock
AnishSarkar22 Oct 8, 2026
a41fde6
feat(homepage): make the model picker preview selectable
AnishSarkar22 Oct 8, 2026
1dbdefa
Merge branch 'dev' of github.com:MODSetter/SurfSense into feat/podcas…
Cedric921 Oct 8, 2026
5d5d3c7
fix(local): check the packaged voicing as MP3 and encode off the even…
Cedric921 Oct 8, 2026
ee66894
Merge branch 'dev' of github.com:MODSetter/SurfSense into feat/third-…
Cedric921 Oct 8, 2026
f2dc6d0
fix(local): require real licence text from NOTICE files, native runti…
Cedric921 Oct 8, 2026
17bbdf0
fix(local): keep Python NOTICE files apart from licence text and pin …
Cedric921 Oct 8, 2026
26e59ce
feat(homepage): set the footer CTA on a painted summer sky
AnishSarkar22 Oct 9, 2026
60a1220
feat(web): use the Palace of Fine Arts artwork for the OG image
AnishSarkar22 Oct 9, 2026
e71ab3b
Merge branch 'dev' of github.com:MODSetter/SurfSense into feat/podcas…
Cedric921 Oct 12, 2026
90add73
fix(sidecars): report a sidecar that cannot be spawned as a crash
MannXo Oct 12, 2026
c2deb72
fix(agent): back off opencode restarts and fail Agentic fast when it …
MannXo Oct 12, 2026
85d0b1f
test(sidecars): allowlist the fake secret in the supervisor env test
MannXo Oct 12, 2026
f6d3839
ci(desktop): run desktop tests on pushes to dev, and on Windows and m…
MannXo Oct 12, 2026
e4cde4d
fix(agent): earn opencode another start after Electron gave up
MannXo Oct 13, 2026
821bc4e
Merge remote-tracking branch 'upstream/dev' into ci/2226-desktop-test…
MannXo Oct 13, 2026
a2149d0
ci(desktop): green the macOS and Windows legs and time out every job
MannXo Oct 13, 2026
6771da4
Merge branch 'dev' of github.com:MODSetter/SurfSense into feat/third-…
Cedric921 Oct 13, 2026
a1ae7c6
fix(local): ship lameenc's and LAME's licences with the worker, and e…
Cedric921 Oct 13, 2026
813fec6
fix(local): write the notices to resources/licenses and carry OpenMP'…
Cedric921 Oct 13, 2026
bd28bca
feat(local): give a refine's refusals codes the interface can translate
ybai08 Oct 12, 2026
8362bd1
fix(local): keep an episode as WAV when its MP3 encode fails
Cedric921 Oct 13, 2026
46cfe86
fix(local): encode the episode in the podcast pipeline, keeping each …
Cedric921 Oct 13, 2026
ee8ddaa
Merge remote-tracking branch 'upstream/dev' into fix/2228-sidecar-spa…
MannXo Oct 14, 2026
7a47d7c
Merge remote-tracking branch 'upstream/dev' into ci/2226-desktop-test…
MannXo Oct 14, 2026
7c0a7a8
fix(plugins): keep a plugin's log tail in Unix line endings on Windows
MannXo Oct 14, 2026
76deabd
test(plugins): kill the worker's tree and skip the grace period on Wi…
MannXo Oct 14, 2026
c60bd7d
test(sidecars): allowlist the fake password in the opencode leftovers…
MannXo Oct 14, 2026
70c3ad0
test(llamacpp): build the CRLF licence from a CRLF checkout too
MannXo Oct 14, 2026
35b12d8
test(studio): build the workbook once where its bytes are compared
MannXo Oct 14, 2026
cba3b2f
docs(architecture): drop the desktop-tests Known gap now every leg pa…
MannXo Oct 14, 2026
55b3ddc
docs(proposals): local speech recognition on a second audio.cpp server
MODSetter Oct 14, 2026
cc6868c
Merge pull request #2254 from MODSetter/docs/local-speech-recognition…
MODSetter Oct 14, 2026
b84da18
docs(plugins): fold the built remote plugins into architecture, keep …
AnishSarkar22 Oct 14, 2026
a3e0840
docs(agent): link analysis runs to Studio's script documents
AnishSarkar22 Oct 14, 2026
31c16e4
docs(plugins): the contributor guide's entry fields as the registry c…
AnishSarkar22 Oct 14, 2026
37d000f
docs(contracts): the plugin host and the app as contract 2's built sides
AnishSarkar22 Oct 14, 2026
8ddefbc
docs(plugins): a guide to listing a plugin, for publishers and agents
AnishSarkar22 Oct 14, 2026
00d1da4
docs(plugins): plugins.surfsense.com is live
AnishSarkar22 Oct 14, 2026
dd4486c
chore(github): a form to report a plugin, and Plugins in the bug and …
AnishSarkar22 Oct 14, 2026
e98038c
Merge pull request #2255 from AnishSarkar22/feat/remote-plugins
AnishSarkar22 Oct 14, 2026
b8e3c5d
feat(models): a Speech recognition section in Settings, and the set-u…
MODSetter Oct 14, 2026
c357fb1
feat(plugins): search connected and listed plugins together
AnishSarkar22 Oct 14, 2026
5d4dc25
feat(local): stage speech recognition's audio.cpp server, not yet sta…
MODSetter Oct 14, 2026
4397a53
fix(models): a speech section that fails honestly and never waits on …
MODSetter Oct 14, 2026
d724420
Merge pull request #2256 from AnishSarkar22/feat/plugin-search
AnishSarkar22 Oct 14, 2026
75603e3
fix(local): never leave a second speech recognition server running
MODSetter Oct 14, 2026
e6fbf2b
feat(sources): recordings in the sources panel, with how far their tr…
MODSetter Oct 14, 2026
a73e6d5
feat(registry): Verified by maintainers, and CI connects to each new …
AnishSarkar22 Oct 14, 2026
cce8269
Merge pull request #2257 from AnishSarkar22/feat/registry-live-check
AnishSarkar22 Oct 14, 2026
357795d
feat(registry): DeepWiki as Verified, and Notion listed
AnishSarkar22 Oct 14, 2026
54c10d1
fix(registry): Notion's Custom Agent session tools
AnishSarkar22 Oct 14, 2026
057fa19
fix(sources): keep a recording's set-up open, and say when its langua…
MODSetter Oct 14, 2026
bd98665
Merge pull request #2258 from AnishSarkar22/feat/registry-deepwiki-no…
AnishSarkar22 Oct 14, 2026
06c729a
chore(registry): sign the plugin list
github-actions[bot] Oct 14, 2026
b5b1c3a
feat(models): sign in with a ChatGPT plan in one click, beside Use a …
AnishSarkar22 Oct 14, 2026
87006ca
feat(models): ChatGPT only beside Use a server, which comes first
AnishSarkar22 Oct 14, 2026
3ba90ae
Merge pull request #2259 from AnishSarkar22/feat/chatgpt-connect-entry
AnishSarkar22 Oct 14, 2026
f8e5ba0
feat(microphone): grant the mic to the app's own page, and bridge the…
MODSetter Oct 14, 2026
f36c5c3
perf(agent): get plugins ready beside the sync, and stop waiting on t…
MODSetter Oct 14, 2026
a08ef99
feat(local): offer Nemotron 3.5 ASR for a new speech recognition slot
MODSetter Oct 14, 2026
4640416
fix(plugins): a paid plugin's license verdict shows at once, from the…
AnishSarkar22 Oct 14, 2026
b193fcf
fix(microphone): let CI run the macOS check, and note the sidecars' e…
MODSetter Oct 14, 2026
3f7a374
style(ui): the external-link icon as an up-right arrow
AnishSarkar22 Oct 14, 2026
e7bfe80
style(license): Expiring soon as a secondary notice
AnishSarkar22 Oct 14, 2026
627455e
chore(plugins): plugin host 0.0.1 and registry check 0.0.1
AnishSarkar22 Oct 14, 2026
88fd9ad
fix(local): refuse a listed speech language with no locale for its fa…
MODSetter Oct 14, 2026
c84ef18
refactor(plugins): no listing fallback for a plugin host without /lic…
AnishSarkar22 Oct 14, 2026
0458f59
Merge pull request #2261 from AnishSarkar22/fix/plugin-license-status
AnishSarkar22 Oct 14, 2026
a649e86
Merge remote-tracking branch 'upstream/dev' into feat/homepage-new
AnishSarkar22 Oct 14, 2026
8b3dd42
feat(dictation): voice input in the composer, a phrase at a time
MODSetter Oct 14, 2026
45e66c8
Merge remote-tracking branch 'origin/dev' into perf/plugin-calls-app
MODSetter Oct 14, 2026
f144804
fix(dictation): never send the quiet alone, and say when memory stopp…
MODSetter Oct 14, 2026
939de65
feat(local): transcribe speech through one gate in the API
MODSetter Oct 14, 2026
e1d734f
feat(plugins): a fail-fast proxied transport, keep-alive sessions and…
MODSetter Oct 14, 2026
53b3acc
feat(plugins): one browser pool for every scraper plugin
MODSetter Oct 14, 2026
2fb720c
feat(plugin-host): plugin lifespans, one license cache, and results t…
MODSetter Oct 14, 2026
676be68
perf(google-search): render pages side by side, without idle waits, a…
MODSetter Oct 14, 2026
e64a2ae
perf(indeed): keep a solved browser context per country, and fetch de…
MODSetter Oct 14, 2026
0fa2f1f
perf(youtube): native subtitles alongside the watch page, fail-fast e…
MODSetter Oct 14, 2026
36ee2c9
perf(google-maps): fail fast, run places and their reviews together, …
MODSetter Oct 14, 2026
386ebd8
perf(amazon): bounded tails, no fake Google referer, and real prices …
MODSetter Oct 14, 2026
92a52c8
perf(walmart): review pages in parallel, fail-fast exits, and ratings…
MODSetter Oct 14, 2026
e9da419
perf(tiktok): comments and trending over TikTok's JSON API, listings …
MODSetter Oct 14, 2026
affc5b1
perf(web-crawl): crawl pages concurrently, cap the HTML before extrac…
MODSetter Oct 14, 2026
d908693
fix(reddit, instagram): fail fast where anonymous reads are closed
MODSetter Oct 14, 2026
b249438
Merge remote-tracking branch 'origin/dev' into perf/plugins-speed
MODSetter Oct 14, 2026
99e7bd8
feat(web): footer on the Golden Gate engraving
AnishSarkar22 Oct 14, 2026
567b727
feat(web): icons on the Resources menu items
AnishSarkar22 Oct 14, 2026
af8eef8
Merge pull request #2263 from AnishSarkar22/feat/homepage-new
AnishSarkar22 Oct 14, 2026
6c952c2
fix(local): answer a hung speech server with 503 and keep numbers in …
MODSetter Oct 14, 2026
553048c
feat(local): transcribe recordings as sources in the ingest worker
MODSetter Oct 14, 2026
0de99e8
fix(local): decode Ogg recordings under any path and clear a stopped …
MODSetter Oct 14, 2026
5f393ac
Merge branch 'feat/local-speech-recognition-ui' into feat/local-speec…
MODSetter Oct 14, 2026
b10d568
Merge remote-tracking branch 'upstream/dev' into ci/2226-desktop-test…
MannXo Oct 14, 2026
f993974
fix(agent): earn a given-up opencode another start only on a turn or …
MannXo Oct 14, 2026
d9c0a86
Merge remote-tracking branch 'origin/dev' into feat/local-speech-reco…
MODSetter Oct 14, 2026
ad52df2
fix(agent): fail a turn closed when opencode cannot list its servers,…
MODSetter Oct 14, 2026
433d266
fix(plugins): review fixes before merge
MODSetter Oct 14, 2026
531c1f6
fix(local): keep a ChatGPT plan out of speech settings, and docs that…
MODSetter Oct 14, 2026
56f9b43
Merge pull request #2264 from MODSetter/feat/local-speech-recognition
MODSetter Oct 14, 2026
297c057
Merge pull request #2262 from MODSetter/perf/plugins-speed
MODSetter Oct 14, 2026
fd3fe4f
Merge pull request #2260 from MODSetter/perf/plugin-calls-app
MODSetter Oct 14, 2026
6765eb5
Merge pull request #2247 from ybai08/feat/studio-refine-refusal-codes
MODSetter Oct 15, 2026
6207e51
Merge branch 'dev' of github.com:MODSetter/SurfSense into feat/third-…
Cedric921 Oct 15, 2026
9ab8af0
fix(local): list the Silero VAD pack in the notices, with its own lic…
Cedric921 Oct 15, 2026
2f63162
Merge pull request #2240 from MannXo/ci/2226-desktop-tests-dev-and-os…
MODSetter Oct 15, 2026
b17b3e9
Merge pull request #2185 from Cedric921/feat/third-party-notices
MODSetter Oct 15, 2026
15c6447
Merge branch 'dev' of github.com:MODSetter/SurfSense into feat/podcas…
Cedric921 Oct 15, 2026
e7d4e05
fix(local): write run-folder paths relative across a script's whole s…
TonyWu2333 Oct 15, 2026
a831351
Merge remote-tracking branch 'upstream/dev' into fix/2228-sidecar-spa…
MannXo Oct 15, 2026
1914117
Merge remote-tracking branch 'upstream/dev' into fix/mid-stream-error…
MohammadHijjawi97 Oct 15, 2026
eec650e
fix(local): answer the chat test's in-stream error with a 502
MohammadHijjawi97 Oct 15, 2026
cec4ba1
fix(local): make secondary buttons stand out from cards and rows
AnishSarkar22 Oct 15, 2026
4bf6b1c
fix(local): enlarge the settings and add-plugin dialogs
AnishSarkar22 Oct 15, 2026
96290c2
feat(local): allow, ask or block each plugin tool, as Claude does
AnishSarkar22 Oct 15, 2026
7897d0f
docs(import): align thread recovery documentation
fukalous Oct 14, 2026
a7c57da
refactor(local): every button is the shared Button, with one press mo…
AnishSarkar22 Oct 15, 2026
2996661
fix(local): a nested dialog blocks the dialog under it
AnishSarkar22 Oct 15, 2026
cbf028d
fix(local): narrow the settings dialog to w-5xl
AnishSarkar22 Oct 15, 2026
88ec68e
fix(local): border the left-sidebar file viewer on its right
AnishSarkar22 Oct 15, 2026
ca154e9
fix(local): widen the conversation column to max-w-2xl
AnishSarkar22 Oct 15, 2026
62107da
fix(local): a group permission never switches on a tool that cannot t…
AnishSarkar22 Oct 15, 2026
1c3b529
Merge pull request #2270 from AnishSarkar22/feat/desktop-ui-changes
AnishSarkar22 Oct 15, 2026
8afccad
fix(local): name Visual Studio's generator when compiling audio.cpp o…
MODSetter Oct 15, 2026
25a8acc
Merge pull request #2271 from MODSetter/fix/audiocpp-cmake-generator
MODSetter Oct 15, 2026
23eaead
Merge pull request #2248 from fukalous/docs/import-thread-recovery-fo…
MODSetter Oct 15, 2026
b43bee9
Merge pull request #2183 from Cedric921/feat/podcast-mp3
MODSetter Oct 15, 2026
6382dec
Merge pull request #2170 from MohammadHijjawi97/fix/mid-stream-error-…
MODSetter Oct 15, 2026
947e9f4
Merge pull request #2235 from MannXo/fix/2228-sidecar-spawn-errors
MODSetter Oct 15, 2026
c45d0fb
Merge pull request #2203 from TonyWu2333/fix/script-traceback-relativ…
MODSetter Oct 15, 2026
256c3ee
feat(local): add Canary 180M Flash as a second speech model
AnishSarkar22 Oct 15, 2026
cef357a
refactor(local): drop the speech set-up card's memory while transcribing
AnishSarkar22 Oct 15, 2026
4a05a11
chore: plain Apache LICENSE, and the site and README say the plugins …
MODSetter Oct 15, 2026
24fc3dc
Merge pull request #2273 from AnishSarkar22/feat/speech-model-choice
AnishSarkar22 Oct 15, 2026
d47215f
Merge pull request #2272 from MODSetter/docs/plugins-launch
MODSetter Oct 15, 2026
b01a265
chore: trim the pricing notes and the Reddit line, and list the plugi…
MODSetter Oct 15, 2026
48a5939
Merge pull request #2274 from MODSetter/docs/plugins-copy-tidy
MODSetter Oct 15, 2026
c2159a2
docs(readme): the Plugins section and the BSL folders in every transl…
MODSetter Oct 15, 2026
bbf1ac5
Merge pull request #2275 from MODSetter/docs/readme-plugins-locales
MODSetter Oct 15, 2026
c26d3b2
docs(readme): License says Apache-2.0 only, in every README
MODSetter Oct 15, 2026
5470b1d
Merge pull request #2276 from MODSetter/docs/readme-license-plain
MODSetter Oct 15, 2026
fd7f1b9
chore: link the new subreddit, r/SurfSenseAI
MODSetter Oct 15, 2026
b0b5a92
Merge pull request #2277 from MODSetter/chore/reddit-subreddit
MODSetter Oct 15, 2026
4baeeb5
docs(readme): Plugins before How it compares, in every README
MODSetter Oct 15, 2026
3133d68
Merge pull request #2278 from MODSetter/docs/readme-plugins-before-co…
MODSetter Oct 15, 2026
be961a5
fix(local): stage audio.cpp again when it lacks a curated family
MODSetter Oct 15, 2026
26b6a73
Merge pull request #2279 from MODSetter/fix/audiocpp-restage-missing-…
MODSetter Oct 15, 2026
42b6ab6
docs(changelog): the desktop releases, 2.0.0 to 2.2.1
MODSetter Oct 15, 2026
aece27e
feat(web): each announcement gets its own page, like the blog
MODSetter Oct 16, 2026
2911f76
Merge pull request #2280 from MODSetter/docs/changelog-catch-up
MODSetter Oct 16, 2026
53818a4
Merge pull request #2281 from MODSetter/feat/announcement-pages
MODSetter Oct 16, 2026
d67f71f
chore(web): drop AnythingLLM from the homepage comparison
MODSetter Oct 16, 2026
eae5717
Merge pull request #2282 from MODSetter/chore/home-compare-no-anythin…
MODSetter Oct 16, 2026
77ee695
chore(web): the downloads heading says the license comes with it
MODSetter Oct 16, 2026
d38e3f2
Merge pull request #2283 from MODSetter/chore/downloads-heading
MODSetter Oct 16, 2026
4bb030d
feat(web): announce SurfSense 2.0, and retire the old announcements
MODSetter Oct 16, 2026
068a2d0
Merge pull request #2284 from MODSetter/feat/announce-2-0
MODSetter Oct 16, 2026
549e3ef
docs(proposals): use your Claude plan through the user's own Claude Code
AnishSarkar22 Oct 16, 2026
3ad5dbb
docs(proposals): resume each engine's own session across a model switch
AnishSarkar22 Oct 16, 2026
e133296
Merge pull request #2285 from AnishSarkar22/feat/claude-plan
AnishSarkar22 Oct 16, 2026
a742536
docs(adr): a Claude plan runs through the user's own Claude Code
AnishSarkar22 Oct 16, 2026
a836687
Merge pull request #2286 from AnishSarkar22/feat/claude-plan
AnishSarkar22 Oct 16, 2026
65d6167
perf(local): run the bundled embedder at fp32 on x86-64
MODSetter Oct 16, 2026
aefef6b
perf(local): faster Word reads, cheapest-first uploads, and a still-r…
MODSetter Oct 16, 2026
3bf74e7
perf(local): read born-digital pages layout-only in the figures-only …
MODSetter Oct 16, 2026
6c2e2df
perf(local): read born-digital PDFs from their text layer
MODSetter Oct 16, 2026
037876b
Merge branch 'perf/fast-figures' into perf/fast-ingest
MODSetter Oct 16, 2026
9d583bd
perf(local): read Word files without Docling
MODSetter Oct 16, 2026
955bbc8
Merge branch 'perf/fast-docx' into perf/fast-ingest
MODSetter Oct 16, 2026
bc2634b
perf(local): paragraph breaks in a PDF's text layer, from line gaps
MODSetter Oct 16, 2026
c2ce291
Merge branch 'perf/integrate-docx' into perf/fast-ingest
MODSetter Oct 16, 2026
1493e1b
wip: mixed PDFs, batched Docling readings, figures pass giving way (u…
MODSetter Oct 16, 2026
37c16aa
Merge branch 'perf/fast-ingest' into perf/fast-mixed
MODSetter Oct 16, 2026
a70362b
perf(local): an idle worker notices a new job within half a second
MODSetter Oct 16, 2026
96bf2ca
fix(local): figures passes behind notes and uploads, and no figures f…
MODSetter Oct 16, 2026
d06e530
Merge branch 'perf/fast-poll' into perf/fast-ingest
MODSetter Oct 16, 2026
1a3c534
perf(local): Docling reads only a PDF's flagged pages, in batches tha…
MODSetter Oct 16, 2026
815f63e
Merge branch 'perf/fast-mixed' into perf/fast-ingest
MODSetter Oct 16, 2026
40567ee
docs(local): ADR 0059 and documents.md match the merged figures pass,…
MODSetter Oct 16, 2026
078b121
Merge pull request #2289 from MODSetter/perf/fast-ingest
MODSetter Oct 16, 2026
26734a5
fix(local): tunnels through SurfSense's proxy work under uvloop
AnishSarkar22 Oct 16, 2026
8990d4a
Merge pull request #2291 from AnishSarkar22/hotpatch/plugin-calls
MODSetter Oct 16, 2026
50a5e1b
docs(changelog): v2.2.2, sources ready in seconds and speech recognition
MODSetter Oct 16, 2026
255466e
Merge pull request #2292 from MODSetter/docs/changelog-2-2-2
MODSetter Oct 16, 2026
9704730
chore(local): release 2.2.2
MODSetter Oct 16, 2026
ca5e325
docs(readme): recordings in the format table, in every language
MODSetter Oct 16, 2026
84c448d
fix(local): build audio.cpp again on Windows and Linux CI
MODSetter Oct 16, 2026
a54a669
fix(local): the plugin proxy's tests collect on Windows again
MODSetter Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions .agents/skills/frontend-workflow/base-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ These are the rules that compile fine and still break. Check
- A link styled as a button is a real `<a>` with `buttonVariants(...)`. Base
UI's `Button` gives whatever it renders `role="button"`.

## Buttons

- Every button is `Button` from `components/ui/button.tsx`; ESLint rejects a raw
`<button>`. An element with its own layout (a card, a stretched row link, a
flip surface) is `variant="bare"`: the focus ring and transition, no looks.
- Press feedback lives only in `Button`: a 0.95 scale on every button, popup
triggers included, in 150ms and back over 250ms on a no-overshoot curve. A
wide button or a row passes `static` and presses by colour instead. No
`active:` scale or translate in a className; ESLint rejects that too.

## Menus

- Items act on `onClick`. `onSelect` still typechecks (it is a DOM prop) and
Expand Down Expand Up @@ -46,6 +56,11 @@ These are the rules that compile fine and still break. Check
its state in a store (see `features/egress/ask-egress.ts`).
- A nested dialog renders no backdrop. The parent dims and shrinks through
`data-nested-dialog-open` and `--nested-dialogs`, already in the wrappers.
- Popups carry no `z-index`. `#root` has `isolation: isolate` (Base UI's
setup), so portals sit above the app and stack by the order they opened:
a nested dialog's own click blocker then covers its parent. A `z-index` on a
popup lifts it above that blocker and leaves the parent clickable. The
toaster portals to `<body>`, outside `#root`, to stay above dialogs.
- A popup inside a modal dialog (the combobox, through `container`) portals
into a host element within the dialog, which hides everything outside itself from
assistive technology.
Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/frontend-workflow/color/PALETTE.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
--spacing: 0.25rem;
--font-mono: "IBM Plex Mono", monospace;
--font-sans: "Instrument Sans", sans-serif;
--secondary: #f1f0ec;
--secondary: #e8e7e2;
--background: #f7f6f2;
--app-shell: #f3f2ee;
--font-brand: "Fraunces", serif;
Expand Down Expand Up @@ -67,7 +67,7 @@
--spacing: 0.25rem;
--font-mono: "IBM Plex Mono", monospace;
--font-sans: "Instrument Sans", sans-serif;
--secondary: #222222;
--secondary: #2f2f2f;
--background: #141414;
--app-shell: #101010;
--font-brand: "Fraunces", serif;
Expand Down
175 changes: 175 additions & 0 deletions .agents/skills/illustration-isometric-mono/SKILL.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .agents/skills/translate/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ SurfSense, Studio, Markdown, llama.cpp, GGUF, Hugging Face, API, MCP, and every
| study guide | 学習ガイド | 학습 가이드 | 学习指南 | स्टडी गाइड | Lernzettel † | guía de estudio | Guide de révision | Guia de estudos | конспект |
| podcast | ポッドキャスト | 팟캐스트 | 播客 | पॉडकास्ट | Podcast | podcast | Podcast | Podcast | подкаст |
| speech (a model reading text aloud) | 音声合成 | 음성 | 语音 | वॉइस | Sprache | voz | voix | voz | речь |
| speech recognition (turning recordings and the voice into text, on this computer) | 音声認識 | 음성 인식 | 语音识别 | स्पीच रिकग्निशन | Spracherkennung | reconocimiento de voz | reconnaissance vocale | reconhecimento de voz | распознавание речи |
| voice input (dictating into the chat composer) | 音声入力 | 음성 입력 | 语音输入 | वॉइस इनपुट | Spracheingabe | entrada de voz | saisie vocale | entrada de voz | голосовой ввод |
| transcript (a recording’s text) | 文字起こし | 스크립트 | 文字稿 | ट्रांसक्रिप्ट | Transkript | transcripción | transcription | transcrição | расшифровка |
| recording (an audio file added as a source) | 録音 | 녹음 | 录音 | रिकॉर्डिंग | Aufnahme | grabación | enregistrement | gravação | запись |
| sidebar (the workspace’s left column) | サイドバー | 사이드바 | 侧边栏 | साइडबार | Seitenleiste | barra lateral | barre latérale | barra lateral | боковая панель |
| workspace | ワークスペース | 워크스페이스 | 工作区 | वर्कस्पेस | Workspace | espacio de trabajo | espace de travail | Workspace | рабочая область |
| artifact (a Studio output) | 生成物 | 생성물 | 产物 | आर्टिफ़ैक्ट | Artefakt | artefacto | artefact | artefato | артефакт |
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ body:
label: Which part?
options:
- Desktop app
- Plugins
- MCP
- Backend / scraper API
- Web UI
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ body:
label: Which part?
options:
- Desktop app
- Plugins
- MCP
- Backend / scraper API
- Web UI
Expand Down
57 changes: 57 additions & 0 deletions .github/ISSUE_TEMPLATE/plugin-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Report a plugin
description: A plugin in Settings → Plugins is broken, misleading or unsafe
title: "[plugin] "
labels: [plugins]
body:
- type: markdown
attributes:
value: |
Thanks for telling us. A maintainer looks at every report, and a third-party plugin that breaks the rules can be removed from the list for everyone.

**A security problem, such as a plugin taking data it should not?** Please report it privately through a [security advisory](https://github.com/MODSetter/SurfSense/security/advisories/new), not here.
- type: input
id: plugin
attributes:
label: Which plugin?
description: Its name as Settings → Plugins shows it.
placeholder: DeepWiki
validations:
required: true
- type: dropdown
id: problem
attributes:
label: What is wrong?
options:
- It does not work or cannot be reached
- Its tools do something other than they say
- A tool's description tells the AI what to do
- It sends or keeps more of my data than it should
- Its listing is wrong (who runs it, its links, its privacy policy)
- Something else
validations:
required: true
- type: textarea
id: what
attributes:
label: What happened?
description: What you asked, which tool ran, and what it did.
validations:
required: true
- type: input
id: when
attributes:
label: When?
description: Roughly when it happened, and your SurfSense version if you know it.
- type: textarea
id: logs
attributes:
label: Logs
description: In the desktop app, Report issue copies this session's log to your clipboard. Paste it here, and remove anything you would rather not share.
render: text
- type: checkboxes
id: no-secrets
attributes:
label: Before you send
options:
- label: I removed every key, token and password from this report.
required: true
80 changes: 78 additions & 2 deletions .github/workflows/build-audiocpp.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Compile audio.cpp's server for Windows and Linux with the recipe in
# surfsense_local/electron/scripts/audiocpp/, check it against the app's
# floors, and hand the staged folder to the desktop release as an artifact.
# A pull request that changes the recipe, or the app code that drives the
# server, runs it too.
# Check that upstream's macOS archive, which the release ships as it is, has
# every curated family too. A pull request that changes the recipe, or the
# app code that drives the server, runs it too.
#
# TODO: ask upstream (0xShug0/audio.cpp) to publish Linux archives built on
# Ubuntu 22.04 and Windows ones with per-CPU ggml libraries, as llama.cpp does.
Expand All @@ -20,7 +21,14 @@ on:
- surfsense_local/backend/modules/llm/providers/audiocpp/**
- surfsense_local/backend/modules/llm/catalog/local/engines/audiocpp/**
- surfsense_local/backend/modules/llm/catalog/local/manifest/models.json
- surfsense_local/backend/modules/llm/transcription/**
- surfsense_local/backend/worker/ingestion/audio/**
- surfsense_local/backend/worker/audio_decode/**
- surfsense_local/backend/modules/documents/recordings/**
- surfsense_local/backend/tests/packaging/test_audiocpp_voicing.py
- surfsense_local/backend/tests/packaging/test_audiocpp_transcription.py
- surfsense_local/backend/tests/packaging/test_audiocpp_transcription_config.py
- surfsense_local/backend/tests/packaging/test_recording_transcription.py
- .github/workflows/build-audiocpp.yml

permissions:
Expand Down Expand Up @@ -71,6 +79,8 @@ jobs:
echo "CC=gcc-13" >> "$GITHUB_ENV"
echo "CXX=g++-13" >> "$GITHUB_ENV"

# Staging starts the server once per curated family and fails unless
# that family's loader runs, so a family the recipe drops never ships.
- name: Compile and stage
if: steps.cache.outputs.cache-hit != 'true'
run: node scripts/audiocpp/stage.mjs --strict
Expand Down Expand Up @@ -139,6 +149,50 @@ jobs:
# addopts pins -m 'not packaging'; the later -m on the command line wins.
uv run pytest tests/packaging/test_audiocpp_voicing.py -m packaging

- id: recogniser
name: Read the curated recogniser's pin
working-directory: surfsense_local/backend
run: |
uv run python - >> "$GITHUB_OUTPUT" <<'EOF'
from modules.llm.catalog.local.manifest import load_local_manifest

# The gate's tests are written for Nemotron; Canary is a second recogniser.
(model,) = [
m for m in load_local_manifest().models
if m.id == "nemotron-3.5-asr-streaming-0.6b"
]
pinned = model.builds[0].files[0]
print(f"sha256={pinned.sha256}")
print(f"url=https://huggingface.co/{pinned.repo}/resolve/{pinned.revision}/{pinned.path}")
print(f"name={pinned.path.rsplit('/', 1)[-1]}")
EOF

# 930 MB, so kept by its pinned hash rather than downloaded every run.
- uses: actions/cache@v5
with:
path: ${{ runner.temp }}/recogniser
key: recogniser-${{ steps.recogniser.outputs.sha256 }}

# Kokoro voices sentences in six languages and the transcription server,
# started as Electron's second sidecar from the transcription.json the app
# writes, writes them down through the app's client; then a recording goes
# through ingest, the API and that server. The test checks the file
# against the manifest's hash; Silero's check runs once it is staged.
- name: Gate on speech recognition through the app
working-directory: surfsense_local/backend
env:
SURFSENSE_TEST_AUDIOCPP_DIR: ${{ github.workspace }}/surfsense_local/electron/audiocpp
SURFSENSE_TEST_AUDIO_MODELS: ${{ runner.temp }}/audio-models
RECOGNISER: ${{ runner.temp }}/recogniser/${{ steps.recogniser.outputs.name }}
run: |
if [ ! -f "$RECOGNISER" ]; then
mkdir -p "$(dirname "$RECOGNISER")"
curl -fsSL --retry 3 -o "$RECOGNISER" "${{ steps.recogniser.outputs.url }}"
fi
cp "$RECOGNISER" "$SURFSENSE_TEST_AUDIO_MODELS/"
uv run scripts/fetch_silero_vad.py models
uv run pytest tests/packaging/test_audiocpp_transcription_config.py tests/packaging/test_audiocpp_transcription.py tests/packaging/test_recording_transcription.py -m packaging

# An artifact drops symlinks and file modes; a tar keeps both.
- name: Pack
run: tar -cf audiocpp.tar audiocpp
Expand All @@ -148,3 +202,25 @@ jobs:
name: audiocpp-${{ runner.os }}
path: surfsense_local/electron/audiocpp.tar
retention-days: 7

# macOS ships upstream's archive, which the recipe does not build, so a
# family added to the pin is checked against the archive itself. The release
# job stages it again; this only fails earlier, on the pull request.
macos-archive:
name: macOS arm64, upstream's archive
runs-on: macos-15
defaults:
run:
working-directory: surfsense_local/electron
steps:
- uses: actions/checkout@v6

- uses: actions/setup-node@v5
with:
node-version: 24
package-manager-cache: false

# Downloads the pinned archive, copies each curated family's spec, which
# fails on one it lacks, and starts the server once per family.
- name: Stage and check every curated family
run: node scripts/audiocpp/stage.mjs --strict
41 changes: 32 additions & 9 deletions .github/workflows/desktop-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ on:
pull_request:
branches: [main, dev]
types: [opened, synchronize, reopened, ready_for_review]
# A merge that breaks dev shows here, not on the next pull request.
push:
branches: [dev]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# A later push to dev must not cancel the run that checks the one before it.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
# Filtered per job, not per workflow: a workflow skipped by its paths leaves
# its checks pending, while a skipped job reports success.
changes:
name: Changes
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event.pull_request.draft == false
outputs:
backend: ${{ steps.filter.outputs.backend }}
Expand All @@ -29,7 +34,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v7

# A manual run has no pull request to diff; it starts every job instead.
# A push or a manual run has no pull request to diff; it starts every job instead.
- name: Filter changed paths
id: filter
if: github.event_name == 'pull_request'
Expand Down Expand Up @@ -57,10 +62,21 @@ jobs:
- '.github/workflows/desktop-tests.yml'

backend:
name: Backend
runs-on: ubuntu-latest
name: Backend (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 45
needs: changes
if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.backend == 'true'
if: github.event_name != 'pull_request' || needs.changes.outputs.backend == 'true'
# Hard links, file locks, long paths and the staging rename differ by OS.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

env:
# The runner's macOS Python cannot load SQLite extensions, which sqlite_vec
# needs; the release freezes with uv's own Python (release-local.yml).
UV_PYTHON_PREFERENCE: only-managed

steps:
- name: Checkout code
Expand Down Expand Up @@ -97,8 +113,9 @@ jobs:
frontend:
name: Frontend
runs-on: ubuntu-latest
timeout-minutes: 30
needs: changes
if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.frontend == 'true'
if: github.event_name != 'pull_request' || needs.changes.outputs.frontend == 'true'

steps:
- name: Checkout code
Expand Down Expand Up @@ -150,10 +167,15 @@ jobs:
run: pnpm test

electron:
name: Electron
runs-on: ubuntu-latest
name: Electron (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
needs: changes
if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.electron == 'true'
if: github.event_name != 'pull_request' || needs.changes.outputs.electron == 'true'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Checkout code
Expand Down Expand Up @@ -190,6 +212,7 @@ jobs:
gate:
name: Desktop Gate
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [changes, backend, frontend, electron]
if: always()

Expand Down
20 changes: 18 additions & 2 deletions .github/workflows/plugins-registry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ name: Plugins Registry

# plugins/registry/: the third-party plugin list, its icons, its check and the
# OAuth files the registry container serves.
# - Pull requests: check the entry rules and icons. A maintainer connects each
# new listing on dev and reviews its tools before it reaches main.
# - Pull requests: check the entry rules and icons, then connect to each plugin
# the pull request adds or changes. A maintainer connects each new listing on
# dev, signing in where it needs it, and reviews its tools before it reaches main.
# - Dev: write plugins/registry/published/, a copy of plugins.json stamped with
# generated_at and each icon's SHA-256, and plugins.json.sig, its signature.
# The release pull request from dev carries it to main, which the registry
Expand Down Expand Up @@ -92,6 +93,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
# The merge's first parent is the list this pull request is based on.
fetch-depth: 2

- name: Setup uv
uses: astral-sh/setup-uv@v10.2.0
Expand Down Expand Up @@ -120,6 +124,18 @@ jobs:
working-directory: plugins/registry/check
run: uv run python -m registry_check static

# Connects to each plugin the pull request adds or changes, after the entry
# rules refused loopback and credentials. Pull requests only: a third-party
# server down for a minute must never hold up signing the list.
- name: Connect to each new or changed plugin
if: github.event_name == 'pull_request'
working-directory: plugins/registry/check
run: |
base="$RUNNER_TEMP/base-plugins.json"
git show HEAD^1:plugins/registry/plugins.json > "$base" 2>/dev/null \
|| echo '{"schema_version": 1, "plugins": []}' > "$base"
uv run python -m registry_check live --base "$base"

- name: Setup Node.js
uses: actions/setup-node@v7
with:
Expand Down
Loading
Loading