55edaa2ebd02aca3f3a10636ca0653e6d78a605c
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
26b471d259 |
Merge origin/main (v1.2.0: Projects, modules, in-app updates)
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). |
||
|
|
dd3ce09feb |
fix(windows): real toolchain probing, HOME/TEMP env, and gate portability
code_run.py: shutil.which() finding a compiler executable on PATH doesn't mean it's a usable toolchain on Windows -- rustc's MSVC target also needs Microsoft's linker, and an MSYS2 gcc/clang driver can remain resolvable after one of its runtime DLLs has broken. Both cases silently turned every C/C++/ Rust snippet into a compile error while the capability check said "ready". _compiled_tool() now actually compiles+links a trivial known-good program per candidate (Windows only; POSIX keeps the cheap which(1) check since release hosts install compiler packages atomically) and caches the result. Also fixes the run/compile child environment: HOME/TMPDIR don't control Windows' real temp/profile resolution (expanduser() reaches the actual user profile, GetTempPath() falls back to the Windows directory), letting a snippet escape the scratch directory or fail outright. _child_env() now also sets TEMP/TMP/USERPROFILE on Windows. tests/conftest.py (new): isolates curry_store's SQLite singleton into a per-run temp directory via NEXUS_CURRY_DB before any test module imports synapse, and cleans it up at session end -- the release gate no longer writes test constants into the checkout's live data/curry.db. .gitignore picks up /data/curry.db for whatever still lands there locally. bin/check.sh: falls back to Promethean/Scripts/python.exe when Promethean/bin/python doesn't exist, so the gate actually runs on a Windows venv instead of immediately exiting "no Promethean venv". Verified independently: 254 passed, 0 failed, 8 skipped (tests + management) -- the 12 C/C++/Rust toolchain failures present all session are gone. Full bin/check.sh run end-to-end on this Windows checkout: pytest, eslint, frontend node:test (57/57), PowerShell/shell parse, and the wheel/sdist packaging + twine + content checks all report OK. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
663e540b6b |
feat: direct tool invocation via /tool_name(arg=val) + wire Curry as real tools
Adds synapse/slash_commands.py: a chat message that's nothing but /tool_name(arg=val, arg=val) dispatches straight through tools.dispatch(), skipping model selection, RAG/playbook context assembly, and the ask-policy approval round-trip entirely. A human typing this IS the approval - there's no one else to ask - so it's a deliberate, reviewed bypass of the approval step specifically, not of anything a tool validates internally (path boundaries, size caps, Curry's own sandbox checks all still run). Argument values parse via ast.literal_eval only: strings/numbers/bools/None/literal containers, no names, no calls, no attribute access - a malformed or hostile-looking argument fails to parse rather than executing anything. Wired into chat_stream_endpoint (main.py) as an early short-circuit, before any of the RAG/model-selection work that a slash-command doesn't need. Web needed no changes (it already forwards raw text unchanged); the TUI previously swallowed every leading "/" locally and never reached the backend with it, so tui_app.py's _handle_slash now falls through to _start_chat for anything shaped like a tool call while still handling its own local meta-commands (/help, /model, /new, ...) exactly as before. Also finally wires Curry in as ten real tools (curry_declare_constant, curry_get_constant/_latest, curry_list_constants, curry_retire_constant, curry_declare_function, curry_get_function, curry_list_functions, curry_call_function, curry_retire_function) - deferred from the vendoring pass. The five write/execute ones are ACTION tools in the same always-ask-regardless-of-global-policy floor as edit_source (ALWAYS_ASK_ACTION_TOOLS, generalized in tools.py from the old self_edit-only ALWAYS_ASK_TOOLS so future tool families share one place to register into). curry_call_function is gated as an action for the same reason run_snippet is: it executes code, even sandboxed. Fixed a real bug surfaced while wiring this up: curry_db is a long-lived singleton holding one sqlite3 connection (unlike NexusOS's own memory store, which opens/closes a fresh connection per call specifically to dodge this), and sqlite3 forbids using a connection from a different thread than created it. That's a non-issue in production (uvicorn's single event-loop thread), but Starlette's TestClient runs the ASGI app through an anyio portal thread, so it broke immediately under test. Fixed at the source (curry_core.py, Curry.__init__) with check_same_thread=False, documented as a second deliberate vendoring deviation alongside the PR #4 sandbox fix - there was never real concurrent access here, just an overly strict same-thread assertion tripping on a thread-identity change with only one logical caller. Verified: 244 backend tests pass (18 new for the parser + endpoint wiring + curry tool registration, 4 new for the TUI passthrough); the 12 pre-existing C/C++/Rust toolchain failures are unrelated and unchanged. Confirmed by hand over the real HTTP endpoint: successful dispatch, zero tool_request events (approval bypass working as designed), a format()-dunder exploit attempt still rejected by the vendored sandbox fix even through the new tool registration, malformed arguments rejected before ever reaching dispatch, and an unknown tool name rejected cleanly. Wheel rebuilt and content-checked (bin/check.sh's gate now also asserts slash_commands.py ships). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
cb3d3f0a1f |
feat: vendor Curry, preloaded and callable across the one universal wheel
Vendors curry_core.py from Athena-Pro/Curry (with the str.format()/format_map() sandbox-escape fix from https://github.com/Athena-Pro/Curry/pull/4 already applied) into synapse/, since Curry itself isn't a pip-installable package - it's meant to be pointed at via a config path, which only works from a source checkout. Vendoring a single self-contained, stdlib-only file ships it inside NexusOS's own wheel with no extra dependency to reconcile. synapse/curry_store.py opens it into a module-level singleton (curry_db) at import time, the same pattern as memory.store.store and playbooks.store.playbook_store, and main.py imports it so it's genuinely initialized at process startup - preloaded, not lazy-on-first-use. Backed by its own CURRY_DB file (nexus_config.py), separate from memory.db. NexusOS builds exactly one wheel (py3-none-any, no compiled extensions) - there is no separate Windows/macOS/Linux artifact; platform differences are handled by requirement overlays at install time, not by building different wheels. Verified the same wheel actually carries this correctly: built it, confirmed twine check passes, confirmed synapse/curry_core.py and curry_store.py are present in the archive (bin/check.sh's packaging gate now asserts this too), then installed that exact wheel into a throwaway venv and round-tripped a declare_constant/get_constant_latest call against it with no source checkout present - proving "preloaded and ready to be called" holds from the shipped artifact, not just editable-install execution. Android/Termux is unaffected by this change in either direction: it already has a separate, documented, pre-existing blocker in docs/TERMUX.md (no published Android pydantic-core wheel) that has nothing to do with Curry, which is pure stdlib and adds no new native/binary dependency. Scope: preload only, nothing wired into a chat-facing tool yet - no model or user-authored content reaches declare_function/call_function today. Verified: 216 backend tests pass (4 new in test_curry_store.py, including a regression test proving the vendored sandbox fix survived the copy); the 12 pre-existing C/C++/Rust toolchain failures are unrelated and unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
6e8067fe7d |
feat(macos): native install path via Homebrew
install-macos.sh mirrors install.sh's split: every portable step - git pull, venv, pip with the right overlay, npm build - stays in bin/sync.py, shared with Linux and Windows. The script only does what sync.py cannot do for itself on a bare Mac, which is install the Homebrew packages needed before a Python exists to run sync.py with. Two stages had to learn about darwin. ensure_exec_bits() keyed off `os.name == "nt"`, which is false on macOS, so it ran the Linux path; and requirements() had no darwin branch. linux_stage() now no-ops there, which is what makes skipping the Ollama fetch correct rather than an omission: bin/fetch-ollama.sh only ships a Linux x86-64 binary, and _ollama_bin() in synapse/ollama_manager.py already prefers the bundled copy and falls back to whatever `ollama` is on PATH. On macOS that is the brewed one, with Metal acceleration and no flags needed. The XFCE desktop branding is Linux-only and was already gated off macOS the same way, so there is nothing to install for it here. install-macos.sh joins the shell-parse list in bin/check.sh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
425184a30b |
feat(packaging): move the CLI into nexusos_cli and make the wheel self-sufficient
The CLI shipped from `management/`, which also holds desktop-only pieces (the Tk control panel, the XFCE panel wiring, the shell wrappers). Packaging that directory meant the wheel either dragged in tkinter or shipped a broken import. Split it: `nexusos_cli/` is what the wheel ships and what `nexus`/`ncp`/ `nexusos` dispatch to, `management/` keeps the desktop half. Alongside the move: * hatch_build.py decides the interface/web/dist include at build time. dist/ is gitignored, so a static force-include aborts `pip install -e .` on a fresh clone - before the reader reaches the `npm run build` step. Editable installs now skip a missing dist; wheels and sdists hard-error naming the command to run. * synapse/proc_util.py gives frontend_manager and ncp process inspection and termination without psutil, which became an optional extra when the wheel landed. It routes around Windows having no signals, where os.kill(pid, 15) is an unblockable TerminateProcess rather than a polite request. * nexusos_cli/monitor.py adds `ncp monitor`, an ASCII dashboard with no curses or rich dependency so it works in Termux, plain SSH and Windows Terminal. Collector and renderer are separate so tests feed fixtures, no stack needed. * tests/test_packaging_deps.py fails the gate when synapse or nexusos_cli import a distribution pyproject does not declare, and when an optional dependency is imported at module scope instead of lazily. * bin/check.sh now builds the wheel, twine-checks it, and asserts the compiled UI and seed playbooks are actually inside it. A wheel that builds but ships no dist/ serves a blank page, which only shows up after release. tests/test_nexus_api.py moves to tests/ with the module it covers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
00bd43d32e |
feat(preview): add sandboxed live code previews
Render validated HTML, SVG, JSX, and TSX fences locally while preserving tool context and preventing explanatory JSON from triggering actions. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ee6e5bead7 |
Replace stale bin/install.sh with a wrapper over sync.py restore
bin/install.sh still rsynced --delete from a backup path retired in July, so the documented Linux install both failed and could erase a working tree. Every step it claimed to do already lives in bin/sync.py, shared with Windows. Root ./install.sh is now a thin wrapper over `sync.py restore`, so deployment stays one bash command. It also registers ncp/promethean in ~/.bashrc, which was the only thing the old script uniquely did. Split restore-linux.sh into runtime (Ollama binary, shell aliases) and desktop (XFCE panel, theme, os-release). Only desktop writes outside the repo, it now auto-skips off XFCE, and `--no-desktop` skips it explicitly. Two tests keep the stage names in sync and the $HOME writes confined to the desktop stage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
714b9fc890 | Initial commit: NexusOS - local AI assistant platform |