Reconciles 17 commits of this session's work (self-alteration tools,
vendored Curry, slash-command dispatch, Windows toolchain/gate fixes)
against origin/main's v1.2.0 sync (Projects/RAG scoping, a new modules/
system for mail and network, in-app updates, the standalone memory
microservice folded into an in-process curator, KDE desktop theme
overhaul). Nine real conflicts, each resolved by hand after reading both
sides' actual diffs rather than picking one side wholesale:
- synapse/tools.py, tests/test_tools.py: origin/main's diff here was
small and clean (read_file/list_files, two new tests) despite git's
diff3 flagging the whole file as one conflict blob -- reset to this
branch's version and hand-spliced their addition in at the same
points they used, rather than trying to reconcile a false 800-line
conflict. Found and fixed a real bug while verifying: _list_files
returned backslash-separated paths on Windows, which don't match the
forward-slash glob patterns the tool's own schema documents.
- synapse/main.py: kept this branch's cue-based standing advertisement
of render_preview/run_snippet (independent of any playbook granting
them) AND adopted origin/main's fix for routed reference playbooks
not bringing their own tools along -- dropping either would have been
a real regression, not just a style difference. Also: the standalone
memory service (port 8001) is gone upstream, so its dead CORS/kill-
target entries were removed; NEXUS_BACKEND_PORT parameterization and
the manage_ollama-conditional kill logic (this branch's remote-Ollama
support) were kept over origin/main's hardcoded equivalents.
- synapse/memory/store.py: kept this branch's _delete_message_vectors
helper (already reused elsewhere, batches to stay under SQLite's
variable limit) over origin/main's inline duplicate of the same fix.
- synapse/nexus_config.py, nexusos_cli/ncp.py: dropped the now-dead
memory-service port/service entries; kept NEXUS_BACKEND_PORT env
override and the manage_ollama-conditional kill-target list.
- CLAUDE.md, README.md: merged both sides' additions, no real conflict.
Found and fixed three more issues while independently verifying the
merged tree, none of them mine or origin/main's alone -- only visible
once both sides actually ran together:
- modules/ (the new mail+network package) was never added to
pyproject.toml's wheel `packages` list OR the sdist's `include`
allowlist, so `from modules.registry import ROUTERS` in main.py would
ImportError on any wheel install. Fixed both; bin/check.sh's
packaging gate now asserts modules/ actually ships. tests/
test_packaging_deps.py's FIRST_PARTY/SHIPPED_PACKAGES sets were
updated to recognize the new package.
- tests/test_mail_creds.py's 0600-mode assertions are POSIX-only --
NTFS has no equivalent permission bits, so os.open(path, 0o600) on
Windows just creates a normal file and stat.S_IMODE reports 0o666
regardless. Made the assertions platform-aware rather than skip real
coverage (the temp-file-cleanup and password round-trip checks in the
same test still run on Windows) or paper over a genuine OS
limitation with a fake pass.
- tests/test_kde_theme.py used bare Path.read_text() in fifteen places;
Windows' default locale encoding (cp1252, not UTF-8) can't decode a
real UTF-8 byte in the QML it reads, and did fail on one of the
fifteen. Fixed all fifteen, not just the one that happened to trip
today, since the other fourteen were equally fragile.
Verified: full bin/check.sh reports OK end-to-end on this Windows
checkout -- pytest (tests + management): 295 passed, 0 failed, 9
skipped; eslint clean; frontend node:test 57/57; PowerShell/shell
parse clean; wheel + sdist pass twine check and now correctly carry
modules/ (60 files, up from 52 pre-merge). synapse.main:app builds
with 74 routes (up from 54 pre-merge, matching the new Projects/mail/
network endpoints).
Brings the public tree back in line with the development repo after several
weeks of drift caused by a stale publish include list.
New:
- In-app update path: GET /update/check compares the checkout against
origin/main and POST /update/apply runs `ncp upgrade` detached (pull,
rebuild, restart). The sidebar shows the version, checks on click, and
offers an "update available" pill.
- Projects: a project workspace groups chats and RAG documents, with
per-project instructions and document retrieval scoped to the active
project. Replaces the standalone Documents page.
- modules/: auto-discovered feature plugins (mail, network) with their
frontend counterparts and tests.
- Memory curation runs in-process (synapse/memory/curator.py) on the chat
model when a conversation goes idle. The separate memory service on :8001
is gone, along with the launcher lines that started it.
Also: the KDE theme, panel and Promethean terminal assets, the full test
suite, and VERSION 1.2.0.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Close SQLite handles safely on Windows, clean orphaned vectors, normalize Ollama endpoints, and surface model errors without leaking reasoning tags.
Co-authored-by: Cursor <cursoragent@cursor.com>
Ported from downstream development. Four independent defects.
1. The memory dump was unrestorable. iterdump() serializes sqlite_vec virtual
tables as a raw INSERT INTO sqlite_master(...) followed by inserts into a
table the replaying connection cannot see, so replaying memory.db.sql died
on "no such table: vec_messages" and left ZERO tables behind. dump_db() now
loads the vec0 extension and filters the derived vec tables out of the
iterdump stream, matched on each statement's target table rather than as a
substring - a chat message whose text mentions vec_messages is an
INSERT INTO "messages" and has to survive.
compare() reported an unreadable dump as "diverged", which read like a real
verdict and made both guards refuse backup AND restore, locking the machine
out of syncing in either direction. Unreadable is now its own verdict.
_extra() compared updated_at against a "" default, but the column is REAL,
so the comparison raises TypeError on the first conversation the other side
lacks - exactly the case it counts. It tests membership first now. The
direction test declared updated_at TEXT, which is why this survived: the
test compared str to str while the field compared str to float.
2. The memory curator invented facts. It attributed the ASSISTANT's words to
the user, wrote absence claims read off the existing-memory block, and added
judgements ("favorite") the user never used. The prompt now scopes the USER
line as the only source, and two deterministic guards drop absence claims
and facts whose distinctive tokens appear nowhere in the user's message -
prompt wording alone did not hold on a 7B curator.
3. _best_vulkan_device scored Mesa's llvmpipe above an integrated GPU, pinning
Ollama to a software rasterizer advertising 31 GiB of "VRAM" - CPU inference
with Vulkan overhead on top. Software rasterizers are dropped.
4. Models.jsx compared catalog names to installed names literally, but Ollama
resolves a bare name to ":latest", so an untagged entry (nomic-embed-text)
read as missing forever and the Required gate never opened. Chatbot.jsx
fetched the model list once on mount although App keeps the page mounted
behind display:none, so a newly pulled model never appeared in the picker
until a full browser reload.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Synapse backend and memory service bound 0.0.0.0 with wildcard CORS and no
auth, exposing the full unauthenticated admin/data API to the LAN. Default the
uvicorn bind to 127.0.0.1 (NEXUS_BIND_HOST override), scope CORS to known local
origins instead of "*", and add TrustedHostMiddleware to reject foreign Host
headers (which defeats DNS-rebinding, something same-origin CORS cannot stop).
NEXUS_ALLOWED_HOSTS / NEXUS_ALLOWED_ORIGINS allow opt-in LAN exposure, intended
to be paired with real authentication.
Co-authored-by: Cursor <cursoragent@cursor.com>
Settings "Auto model routing" picks which installed model fires for chat vs
coding intent when no model is pinned (auto_chat_model / auto_code_model).
_auto_select_model honors the remap; _MODEL_PREFERENCE["code"] prefers real
coder models first.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3-way action_tool_policy (off/ask/allow). In "ask", the chat stream stays
open and the tool loop awaits approval: emits event:tool_request, the UI
shows Approve/Deny, POST /chat/approve resumes the same stream. Declined
actions return a denied result; a timeout denies.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Conversations bind to a project on creation; RAG scopes to the
conversation's project, not the global setting.
- Action tools (web_search/fetch_url/remember) are withheld unless
allow_action_tools is enabled (off by default). Settings toggle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Projects/workspaces: documents grouped into projects; chat RAG scopes to the
active project. Switcher in the Documents page.
- Agentic action tools: web_search, fetch_url, and remember (first write tool),
allowlist-gated per playbook.
- Local Whisper STT (faster-whisper, no torch): on-device dictation replacing
the browser Web Speech API. POST /stt + GET /stt/status; browser fallback.
- Vector index extended to conversation recall (message_vectors), with the
brute-force cosine scan kept as the fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Chunker: char overlap across boundaries + hard-split of oversized paragraphs.
- Retrieval knobs: rag_top_k / rag_min_score in settings + Settings UI.
- Vector index: sqlite-vec ANN over document embeddings, dual-written and
backfilled, with brute-force cosine as the guaranteed fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>