Commit graph

75 commits

Author SHA1 Message Date
Viktor Barzin
94330755d8 feat: add streamable-http MCP transport alongside SSE
SSE transport has reliability issues through Cloudflare/Traefik proxies
(connections drop, causing init failures on reconnect). Streamable HTTP
is stateless — each request carries its own session, avoiding persistent
connection issues.

New endpoint: POST/GET/DELETE /mcp/mcp (streamable-http)
Existing: GET /mcp/sse + POST /mcp/messages/ (SSE, unchanged)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:55:21 +00:00
Viktor Barzin
4d7988b6ac fix: replace BaseHTTPMiddleware with pure ASGI to fix SSE streaming
BaseHTTPMiddleware buffers response bodies, which prevents SSE events
from streaming to the client in real time. This caused MCP initialization
to never complete, resulting in -32602 errors on all tool calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:45:20 +00:00
Viktor Barzin
aa219071f9
fix lint: remove unused FileResponse import, fix empty f-string 2026-03-23 00:53:57 +02:00
Viktor Barzin
745b6ceea8
add cache-busting query params to static assets
Generates a unique hash on app startup and appends it to all .js and
.css URLs in index.html. Forces browser to fetch fresh assets on redeploy.
2026-03-23 00:50:20 +02:00
Viktor Barzin
aa6760cb0f
fix fullscreen button: use x-show instead of x-if templates
x-if with template tags inside a button element causes scope issues.
Switch to x-show on SVG elements directly.
2026-03-23 00:42:10 +02:00
Viktor Barzin
f8bbb2e993
add fullscreen toggle to graph visualization
Expand/collapse button in bottom-right corner lets the graph fill the
entire viewport for better exploration. Escape key exits fullscreen.
Category filters overlay in top-left when fullscreen.
2026-03-23 00:37:24 +02:00
Viktor Barzin
f3e61d8c77
feat: add Tags browser tab with tag cloud and filtered memory view
Add GET /api/tags endpoint returning distinct tags with counts,
add ?tag= filter to GET /api/memories, and a new Tags tab in the
Neural Archive UI with searchable tag cloud and expandable memory cards.
2026-03-23 00:29:02 +02:00
Viktor Barzin
9edb381c85
fix: graph SVG fills container height, fallback to viewport calc 2026-03-23 00:06:55 +02:00
Viktor Barzin
e8ae2b50ea
fix: graph tab breaks out of max-w-7xl container for full-width layout 2026-03-23 00:03:21 +02:00
Viktor Barzin
44338d0eff
feat: fullscreen graph layout, category legend, and node labels
- Graph container fills viewport (calc(100vh - 180px)) instead of 500px
- Node detail panel overlays graph as absolute-positioned panel
- Category legend with colored dots fixed in SVG top-left corner
- High-importance nodes (>= 0.7) show truncated content labels
2026-03-22 23:55:51 +02:00
Viktor Barzin
20f2f02dea
add project CLAUDE.md [ci skip] 2026-03-22 23:44:38 +02:00
Viktor Barzin
e5a69d4e3e
fix: graph visualization not rendering due to x-show timing
- Replace $nextTick with setTimeout(100) for D3 render (same fix as dashboard)
- Re-render graph when switching back to tab (not just on first load)
- Fallback width from parent element when container clientWidth is 0
2026-03-22 23:43:05 +02:00
Viktor Barzin
c130bcff33
feat: sharing tests, property tests, tag-share UI, inline errors, route fix
- Add 10 sharing endpoint tests (share/unshare memory, tag shares, shared-with-me,
  my-shares, recall with shared, update permission checks)
- Add hypothesis property-based tests for model validation (roundtrip, bounds,
  enum, sort_by, limit)
- Tighten model validation: sort_by Literal, limit ge=1/le=500, tags/keywords max_length
- Fix dashboard shared_with_me stat to include tag-based shares
- Add tag-sharing management UI (share/remove tags, user typeahead)
- Replace alert() with inline error messages
- Fix route ordering bug: share-tag routes before {memory_id} parameterized routes
2026-03-22 23:36:13 +02:00
Viktor Barzin
688be268b9
fix: remove extraneous f-string prefix (ruff F541) 2026-03-22 23:01:33 +02:00
Viktor Barzin
f242c45c73
feat: multi-user MCP SSE support + shared memories in recall/list
- Use contextvars to resolve user identity from Authorization header
  in SSE connections, replacing hardcoded "default" user_id
- memory_recall now includes shared memories (individual + tag-based)
  with deduplication and shared_by attribution
- memory_list now includes shared memories with same approach
- All 11 MCP tool functions use _current_user contextvar
2026-03-22 22:50:18 +02:00
Viktor Barzin
95dd937765
UI improvements: add memory form, fix dashboard charts, Neural Archive theme
- Add "New Memory" button with collapsible form (content, category, tags, importance)
- Add share memory with user typeahead via new GET /api/users endpoint
- Fix dashboard charts not rendering (replace $nextTick with setTimeout for x-if timing)
- Redesign theme: warm walnut/amber palette, Playfair Display + JetBrains Mono fonts
- Update Chart.js and D3 graph colors to match warm theme
2026-03-22 21:30:58 +02:00
Viktor Barzin
cf9baf4b8e
fix: add return type annotation to ui_root for mypy 2026-03-22 21:14:31 +02:00
Viktor Barzin
78e737146e
add web management UI for browsing, searching, and visualizing memories
Alpine.js + Tailwind CSS (CDN) served by FastAPI StaticFiles — no build step,
zero Docker changes. Features: memory browser with inline edit/delete, full-text
search with debounce, D3.js force-directed graph, Chart.js stats dashboard.
New endpoints: GET /api/stats, GET /api/categories, pagination on GET /api/memories.
2026-03-22 21:10:53 +02:00
Viktor Barzin
0e75b3f9e3
docs: add sharing feature to README
- Document 7 new MCP tools for memory sharing
- Add Memory Sharing section with usage examples and permission levels
- Update API Reference with 7 new sharing endpoints + PUT update
- Update migrations list with 004_add_sharing
- Add permissions.py to project structure
2026-03-22 19:58:30 +02:00
Viktor Barzin
e6dd89346f
fix: update tests for recall query changes and mypy asyncpg import
- Add shared_by/share_permission to mock row defaults
- Update recall tests to handle 3+1 fetch calls (own, shared, tag-shared, OR-fallback)
2026-03-22 15:45:38 +02:00
Viktor Barzin
6e2e57b443
fix: add type: ignore for asyncpg import in permissions.py
Matches the pattern used in database.py for untyped asyncpg stubs.
2026-03-22 15:42:19 +02:00
Viktor Barzin
983e592083
add sharing tools to stdio MCP server for API-connected clients
- 7 new tools: memory_share, memory_unshare, memory_share_tag,
  memory_unshare_tag, memory_update, memory_shared_with_me, memory_my_shares
- Tools only advertised when API key is configured (HTTP_ONLY or HYBRID mode)
- SQLite-only mode stays unchanged — sharing requires the API server
- Tool handlers proxy to REST API endpoints
2026-03-22 15:36:15 +02:00
Viktor Barzin
f45e8ce2b3
add multi-user memory sharing with r/w permissions
- New migration 004: memory_shares and tag_shares tables with indexes
- Share individual memories or entire tags with other users (read/write)
- Tag shares are live rules: future memories with shared tags auto-visible
- Recall query merges own + shared memories via UNION, returns shared_by field
- Owner-only delete enforcement (403 for non-owners, even with write access)
- PUT /api/memories/{id} update endpoint with permission checks
- 5 new MCP SSE tools: memory_share, memory_unshare, memory_share_tag,
  memory_unshare_tag, memory_update
- Permission helper checks ownership, individual shares, and tag shares
2026-03-22 15:34:01 +02:00
Viktor Barzin
1a275e976c
fix: mypy type annotations for MCP auth middleware 2026-03-18 22:52:14 +00:00
Viktor Barzin
48df739c82
add MCP SSE transport for direct Claude Code connection
Adds SSE endpoint at /mcp/sse so Claude Code can connect over HTTPS
without needing a local Python bridge script. Benefits:
- No local files or sandbox permission issues
- Works from any machine (OpenClaw, DevVM)
- No startup delay or stderr suppression hack
- Auth via Bearer token in request headers
2026-03-18 22:44:57 +00:00
Viktor Barzin
18e27d07d2
fix: remove slack notify from deploy pipeline to unblock manual triggers 2026-03-16 22:18:48 +00:00
Viktor Barzin
714fb366d0
fix: remove unused variable to pass ruff lint 2026-03-16 19:45:01 +00:00
Viktor Barzin
e47efee6b6
resilient memory sync: decouple push/pull, startup full resync, auth failure handling
- Decouple push and pull in _sync_once() so pull always runs even if push fails
- Add startup full resync to catch drift from other agents and schema changes
- Add periodic full resync every ~10 minutes for continuous drift correction
- Add auth failure detection (401/403) with graceful SQLite-only degradation
- Add /api/auth-check endpoint for lightweight key validation
- Add retry cap (5 attempts) on pending ops to prevent infinite queue buildup
- Add orphan reconciliation: push local-only records with content dedup
- Add memory_count MCP tool for sync diagnostics
- Add version-based SQLite schema migration (PRAGMA user_version)
- Fix API key in ~/.claude.json to match server
- Update README with sync resilience docs, test structure, project layout
- Add 30 new tests covering all new behaviors (155 total, all passing)
2026-03-16 18:37:59 +00:00
Viktor Barzin
a18b94d310 docs: expand README with search algorithm internals and secrets architecture
Add detailed Search Algorithm section covering FTS5/BM25 (SQLite) and
tsvector/ts_rank (PostgreSQL) backends, query construction, semantic
expansion at store time, and a comparison table. Also add Sensitive
Memory & Secrets section, Auto-Learn details, and Background Sync Engine
documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:24:02 +00:00
Viktor Barzin
673647710e
fix: use bitnami/kubectl:latest (1.32 has entrypoint issues) 2026-03-16 00:35:44 +00:00
Viktor Barzin
90759f5dfa
fix: change build-fallback event to deployment (prevents dual workflow on API trigger) 2026-03-16 00:15:45 +00:00
Viktor Barzin
1e100cefd1
fix: use Woodpecker repo ID in API URL, quote YAML commands
- Woodpecker API requires numeric repo IDs, not owner/name paths
- Quote kubectl commands to prevent YAML map parsing on empty vars
2026-03-16 00:06:26 +00:00
Viktor Barzin
7dc285a677
fix deploy.yml: accept push events, check IMAGE_TAG var 2026-03-15 23:58:09 +00:00
Viktor Barzin
da9b4716c2
fix last mypy error: asyncpg import-untyped ignore 2026-03-15 23:39:56 +00:00
Viktor Barzin
d370855abf
fix mypy across all source files, remove || true from CI
- Add type annotations to all FastAPI endpoints in api/app.py
- Fix bare list/dict generics in sync.py and app.py
- Fix no-any-return in vault_client.py and sync.py
- Remove mypy || true from GitHub Actions CI — mypy is now clean
2026-03-15 23:36:34 +00:00
Viktor Barzin
678d50654b
fix all mypy errors in mcp_server.py
- Add type annotations to all _sqlite_* private methods
- Type sqlite_conn as sqlite3.Connection | None with assert narrowing
- Fix _api_request return type (dict[str, Any])
- Add return type to _init_sqlite
- Fix tool_name type in handle_tools_call
- Import sqlite3 at module level
2026-03-15 23:33:05 +00:00
Viktor Barzin
df1f36a4f8
make mypy non-blocking in CI (pre-existing errors) 2026-03-15 23:30:50 +00:00
Viktor Barzin
bfc20d2ce9
move Docker builds to GitHub Actions, Woodpecker becomes deploy-only
- GHA ci.yml: add build + deploy jobs (push to DockerHub, trigger Woodpecker)
- Drop test matrix to single Python 3.12, preserve mypy step
- Build/deploy gated on push to main (PRs still run tests only)
- Woodpecker: deploy.yml (manual event, kubectl set image + slack notify)
- Old pipeline preserved as build-fallback.yml (manual trigger)
2026-03-15 23:25:36 +00:00
Viktor Barzin
44e1b84a5a
fix CI deploy: use kubectl set image instead of rollout restart
rollout restart re-pulls the same tag without updating it. Use set image
with CI_PIPELINE_NUMBER to deploy the actual new build.
2026-03-15 22:51:20 +00:00
Viktor Barzin
2fbca35e77
fix: remove extraneous f-string prefixes to pass ruff lint 2026-03-15 22:50:57 +00:00
Viktor Barzin
ba9d31f479
fix pre-existing test failures blocking CI
- test_sync: URL-decode before asserting since param (percent-encoded +)
- test_api: already fixed in previous commit (included for completeness)
2026-03-15 22:44:16 +00:00
Viktor Barzin
dbbacd75c2
fix delete tests to match idempotent endpoint behavior
The delete endpoint intentionally returns 200 (not 404) when a memory
is already deleted, to prevent retry loops in old clients. Tests were
asserting 404 incorrectly.
2026-03-15 22:41:10 +00:00
Viktor Barzin
17206cd855
improve search relevance: hybrid scoring + AND-then-OR matching
- Blend BM25/ts_rank relevance with importance instead of sorting by one
  dimension only. Default mode: 40% relevance + 60% importance. Relevance
  mode: 70% relevance + 30% importance.
- Try AND-match first for precise results, fall back to OR-match when too
  few results are found. Prevents single-word matches from flooding results.
- Applied to both SQLite (local) and PostgreSQL (API) search paths.
2026-03-15 22:36:35 +00:00
Viktor Barzin
5a73dff622
add 800-char memory limit and optimize for focused recall
- Add MAX_MEMORY_CHARS=800 Pydantic validation on MemoryStore.content
- Update auto-learn judge prompts: "ONE topic per event", 100-500 chars,
  include the WHY not just the WHAT
- Split 9 mega-memories (800-2400ch) into 70 focused memories (100-500ch)
  via migration script

Before: median 331ch, 11 memories >800ch, recall wastes 84% of returned tokens
After: median 213ch, 2 memories >800ch (dense single-topic refs), recall returns
only the relevant knowledge

Trade-off research: PostgreSQL ts_rank gives the same score regardless of
document size, so a 2400-char memory with 12 topics gets recalled for any
of its 12 topics but wastes context with the other 11. Focused memories
(100-500ch) give higher signal-to-noise per recall.
2026-03-15 15:51:18 +00:00
Viktor Barzin
6aa4d31170
fix: make DELETE idempotent — return 200 for already-deleted memories
Old sync clients without 404 handling get stuck in infinite retry
loops when trying to delete an already-deleted memory. Making the
endpoint idempotent (returning success regardless) fixes this for
all existing clients without requiring client upgrades.
2026-03-15 15:32:41 +00:00
Viktor Barzin
b6869ef496
ci: trigger rebuild with URL-decode fix 2026-03-15 15:29:08 +00:00
Viktor Barzin
e22a8f743a
fix: 404 retry loop on delete and URL-encode since param
Three fixes for high 4xx alert rate:
1. Catch urllib.error.HTTPError (not just RuntimeError) for 404 on
   DELETE in pending_ops — was causing infinite retry loop for
   already-deleted memories
2. try_sync_delete: treat 404 as success instead of enqueuing
   a retry that will also 404 forever
3. URL-encode the `since` query param to prevent `+` in timezone
   offset being decoded to a space (the asyncpg-string-timestamp
   pattern applied to the sync client)
2026-03-15 15:28:19 +00:00
Viktor Barzin
4456922294
add fallback chain for judge: claude CLI → ollama → heuristic
- claude CLI: run from /tmp to avoid internet-mode-used marker prompts
- ollama: try small local models (qwen2.5:3b, llama3.2:3b, etc.)
- heuristic: pattern matching for corrections, preferences, decisions
- better JSON extraction: handles markdown fences and surrounding text
2026-03-15 11:15:14 +00:00
Viktor Barzin
a8679d6cfb
enhance auto-learn hook: multi-turn extraction, dedup, and auto-memory files
- Deep extraction every 5 turns: reads last 5 exchanges for debugging
  insights, workarounds, architectural patterns, and operational knowledge
- Single-turn extraction on every other turn (cheap, corrections/prefs only)
- State tracking per session: turn counter, content hashes for dedup
- Writes to both memory API/SQLite AND auto-memory markdown files
- Expanded judge prompt: now catches debugging (error→cause→fix),
  workarounds, and operational knowledge — not just corrections/facts
- Auto-cleanup of state files older than 24 hours
2026-03-15 10:59:15 +00:00
Viktor Barzin
9b618711ee
fix: handle URL-decoded '+' in timezone offset for sync endpoint
The '+' in '+00:00' timezone offsets gets URL-decoded to a space,
causing datetime.fromisoformat() to fail with ValueError. Replace
spaces back to '+' before parsing.
2026-03-15 02:33:52 +00:00