diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 462bccc2..82b92379 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,3 +73,34 @@ jobs: - name: Verify packaged artifact run: npm run test:packaged + + dashboard-e2e-smoke: + name: Packaged Dashboard E2E Smoke + needs: build-and-test + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install Playwright Chromium + run: npx playwright install --with-deps chromium + + - name: Build + run: npm run build + + - name: Build dashboard + run: cd dashboard && npm ci && npx vite build + + - name: Run packaged dashboard e2e smoke + run: npm run test:e2e-dashboard diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 1439dc92..e8f31112 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -37,6 +37,12 @@ jobs: - name: Smoke test packaged artifact run: npm run test:packaged + - name: Install Playwright Chromium + run: npx playwright install --with-deps chromium + + - name: Smoke test packaged dashboard + run: npm run test:e2e-dashboard + - name: Verify package contents run: npm pack --dry-run diff --git a/CHANGELOG-4.0.1.md b/CHANGELOG-4.0.1.md index fed1d894..86283ec5 100644 --- a/CHANGELOG-4.0.1.md +++ b/CHANGELOG-4.0.1.md @@ -14,7 +14,7 @@ This release addresses dashboard access, security, data integrity, release-readi - Vector persistence, vector isolation, hook state isolation, and session tracking are corrected - Clean npm consumer installs no longer inherit the stale Xenova ONNX dependency chain -Published v4.0.1 had 445 tests. The current v4.0.2 release candidate has 452 tests. No breaking changes. +Published v4.0.1 had 445 tests. The current v4.0.2 release candidate has 463 tests plus post-publication security hardening around import trust, hook injection boundaries, remote HTTP bind safety, and local file permissions. No breaking changes. --- diff --git a/CHANGELOG-4.0.2.md b/CHANGELOG-4.0.2.md index 2698311e..194ab873 100644 --- a/CHANGELOG-4.0.2.md +++ b/CHANGELOG-4.0.2.md @@ -1,6 +1,6 @@ # MeMesh v4.0.2 — Release-Readiness Fixes -**Release Date:** 2026-04-23 +**Release Date:** 2026-04-24 **Type:** Patch release candidate after npm v4.0.1 publication --- @@ -18,21 +18,29 @@ v4.0.1 is already published on npm, so the follow-up reliability, install, and b - **Clean consumer install audit:** replaced stale `@xenova/transformers` with maintained `@huggingface/transformers`, removing the vulnerable `onnxruntime-web -> onnx-proto -> protobufjs@6` chain from clean installs. - **Capability reporting:** Level 0/no-LLM mode reports `onnx` when the local Transformers.js provider is available. - **Dashboard browser smoke:** `/favicon.ico` returns 204 so packaged dashboard browser smoke is console-clean. +- **Packaged dashboard e2e smoke:** added `npm run test:e2e-dashboard`, which packs the tarball, serves the packaged dashboard, verifies Browse/Search/Settings, confirms locale switches in-place, and fails on page/console errors. +- **Dashboard i18n UX:** all 11 locales have translation key parity, and language changes apply immediately without a full-page reload. +- **Imported memory trust boundary:** imported entities are now marked `trust: untrusted` with import provenance, so shared bundles remain searchable but are excluded from automatic Claude hook injection until reviewed. +- **Hook prompt-injection hardening:** session-start and pre-edit hooks wrap recalled memories as reference data and skip untrusted/imported entities during automatic injection. +- **HTTP bind safety:** `memesh serve` refuses non-loopback hosts unless `--allow-remote` or `MEMESH_HTTP_ALLOW_REMOTE=true` is explicitly set. +- **Private local artifacts:** config, hook throttle state, and session recall-tracking files are chmod-hardened after write (`0700` dirs, `0600` files). ## Documentation -- README test count updated to 452 tests. +- README test count updated to 463 tests. - `CHANGELOG.md` now distinguishes published v4.0.1 from v4.0.2 follow-up fixes. - `docs/plans/README.md` marks historical plans as archived context, not active backlog. - Obsidian project notes were updated outside the repo to mark stale package facts as historical and note that v4.0.2 follow-up fixes are not published until a new npm release is cut. +- API reference now documents imported-memory auto-context behavior and remote HTTP bind opt-in. ## Verification -- `npm test` — 29 files, 452 tests passed. +- `npm test` — 30 files, 463 tests passed. - `npm run typecheck` — passed. - `npm run build` — passed. - `npm audit --omit=dev --json` — 0 vulnerabilities. - `npm run test:packaged` — passed. - Clean-machine packed install smoke — fresh temp app install, CLI `remember`, CLI `recall`, and clean consumer `npm audit --omit=dev` passed. -- Packaged dashboard browser smoke — `/dashboard` rendered from the packed install with 0 Playwright console warnings/errors. +- Packaged dashboard browser smoke — `/dashboard` rendered from the packed install with 0 Playwright console warnings/errors; English -> Traditional Chinese -> English language switching updated in-place. +- Packaged dashboard e2e smoke — passed via `npm run test:e2e-dashboard`. - npm registry verification — npm latest is still v4.0.1 at published gitHead `c936c2548ff886b884c4ba40c83a080b467b4e17`; v4.0.2 is not published yet. diff --git a/CHANGELOG.md b/CHANGELOG.md index a78ecfc5..b14477eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to MeMesh are documented here. -## [4.0.2] — 2026-04-23 +## [4.0.2] — 2026-04-24 ### Fixed - **sqlite-vec Vector Persistence** — Fixed vector writes by binding vec0 row IDs as `BigInt`, replacing vectors via delete+insert, and using byte-offset-safe embedding blobs. CLI `remember` now flushes queued embeddings before closing the database. @@ -11,11 +11,17 @@ All notable changes to MeMesh are documented here. - **Clean Consumer Install Audit** — Replaced stale `@xenova/transformers` with maintained `@huggingface/transformers`, removing the vulnerable `onnxruntime-web -> onnx-proto -> protobufjs@6` dependency chain for clean npm consumers. - **Embedding Capability Reporting** — Level 0/no-LLM mode now reports `onnx` when the local Transformers.js provider is available, matching the actual runtime embedding fallback. - **Dashboard Browser Smoke** — Added a no-content favicon response so packaged dashboard browser smoke tests stay console-clean. +- **Packaged Dashboard E2E Smoke** — Added a Playwright-based `npm run test:e2e-dashboard` flow that packs the tarball, serves the packaged dashboard, verifies Browse/Search/Settings, checks instant locale switching without reload, and fails on page/console errors. +- **Dashboard i18n UX** — All 11 locales now have translation key parity, and language changes apply immediately without a full-page reload. +- **Imported Memory Trust Boundary** — Imported memories are now marked `trust: untrusted` with import provenance, so team/shared bundles stay searchable but are excluded from automatic Claude hook injection until reviewed. +- **Hook Context Guardrails** — Session-start and pre-edit hooks now wrap recalled memories as reference data rather than raw instructions, and they skip untrusted/imported entities during automatic injection. +- **HTTP Remote Bind Guard** — `memesh serve` now refuses non-loopback hosts unless you pass `--allow-remote` or set `MEMESH_HTTP_ALLOW_REMOTE=true`, preventing accidental unauthenticated LAN exposure. +- **Private Local Artifact Permissions** — Config, hook throttle state, and session recall-tracking files are now chmod-hardened after write (`0700` dirs, `0600` files) instead of relying on creation mode alone. ### Changed - Added `docs/plans/README.md` to mark historical plans as archived context, not active backlog. -- 452 tests passing across 29 test files. -- Verified clean-machine packed install, clean consumer audit, packaged CLI smoke, packaged dashboard browser smoke, and npm registry publication status. +- 463 tests passing across 30 test files. +- Verified clean-machine packed install, clean consumer audit, packaged CLI smoke, packaged dashboard browser/i18n smoke, packaged dashboard e2e smoke, and npm registry publication status. ## [4.0.1] — 2026-04-21 diff --git a/README.md b/README.md index 9b2fe6d0..cdcb243c 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ You don't need to manually remember everything. MeMesh has **5 hooks** that capt | **Graph** | Interactive force-directed knowledge graph with type filters, search, ego mode, recency heatmap | | **Lessons** | Structured lessons from past failures (error, root cause, fix, prevention) | | **Manage** | Archive and restore entities | -| **Settings** | LLM provider config, language selector | +| **Settings** | LLM provider config, instant language selector | --- @@ -178,6 +178,7 @@ You don't need to manually remember everything. MeMesh has **5 hooks** that capt **⚠️ Conflict Detection** — If you have two memories that contradict each other, MeMesh warns you. **📦 Team Sharing** — `memesh export > team-knowledge.json` → share with your team → `memesh import team-knowledge.json` +Imported bundles stay searchable, but MeMesh does not auto-inject imported memories into Claude hooks until you review or re-store them locally. --- @@ -259,7 +260,8 @@ Core is framework-agnostic. Same logic runs from terminal, HTTP, or MCP. ```bash git clone https://github.com/PCIRCLE-AI/memesh-llm-memory cd memesh-llm-memory && npm install && npm run build -npm test # 452 tests +npm test # 463 tests +npm run test:e2e-dashboard ``` Dashboard: `cd dashboard && npm install && npm run dev` diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index 6d475f01..ec0a7ef1 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -9,9 +9,7 @@ import { GraphTab } from './components/GraphTab'; import { LessonsTab } from './components/LessonsTab'; import { FeedbackWidget } from './components/FeedbackWidget'; import { api, type HealthData } from './lib/api'; -import { initLocale, t } from './lib/i18n'; - -initLocale(); +import { initLocale, t, type Locale } from './lib/i18n'; const TAB_KEYS = ['Search', 'Browse', 'Analytics', 'Graph', 'Lessons', 'Manage', 'Settings'] as const; type Tab = typeof TAB_KEYS[number]; @@ -27,6 +25,7 @@ const TAB_I18N_KEYS: Record = { }; export function App() { + const [locale, setLocale] = useState(() => initLocale()); const [tab, setTab] = useState('Browse'); const [health, setHealth] = useState(null); const [error, setError] = useState(''); @@ -51,7 +50,9 @@ export function App() {
{tab === 'Graph' && }
{tab === 'Lessons' && }
{tab === 'Manage' && }
-
{tab === 'Settings' && }
+
+ {tab === 'Settings' && } +
diff --git a/dashboard/src/components/SettingsTab.tsx b/dashboard/src/components/SettingsTab.tsx index e28f3c05..2d1e532f 100644 --- a/dashboard/src/components/SettingsTab.tsx +++ b/dashboard/src/components/SettingsTab.tsx @@ -1,13 +1,18 @@ import { useState, useEffect } from 'preact/hooks'; import { api, type ConfigData } from '../lib/api'; -import { t, getLocale, setLocale, getLocales, type Locale } from '../lib/i18n'; +import { t, setLocale, getLocales, type Locale } from '../lib/i18n'; + +interface SettingsTabProps { + locale: Locale; + onLocaleChange: (locale: Locale) => void; +} function capitalize(s: string): string { if (!s) return s; return s.charAt(0).toUpperCase() + s.slice(1); } -export function SettingsTab() { +export function SettingsTab({ locale, onLocaleChange }: SettingsTabProps) { const [config, setConfig] = useState(null); const [provider, setProvider] = useState(''); const [apiKey, setApiKey] = useState(''); @@ -71,59 +76,68 @@ export function SettingsTab() { {/* LLM Config */}
{t('settings.llmProvider')}
-
- {([['anthropic', 'Anthropic (Claude)'], ['openai', 'OpenAI'], ['ollama', 'Ollama (Local)']] as const).map(([val, label]) => ( -
+ )} - {provider && provider !== 'ollama' && ( -
- +
+ setApiKey((e.target as HTMLInputElement).value)} + type="text" + placeholder={provider === 'anthropic' ? 'claude-haiku-4-5' : provider === 'openai' ? 'gpt-4o-mini' : 'llama3.2'} + value={model} + onInput={(e) => setModel((e.target as HTMLInputElement).value)} />
- )} - -
- - setModel((e.target as HTMLInputElement).value)} - /> -
-
- - {msg && {msg}} -
+
+ + {msg && {msg}} +
+
{/* Language */}
{t('settings.language')}