Releases: mx-space/core
v13.7.0
TL;DR
In-app admin dashboard upgrades now resolve through the R2 latest.json manifest instead of the archived mx-space/mx-admin GitHub Releases, so the upgrade button actually downloads the current admin again.
Highlights
The admin updater no longer talks to api.github.com. UpdateService reads latest.json from ADMIN_UPDATE.s3BaseUrl (default https://admin-r2.innei.dev, the bucket both release.yml and admin-release.yml publish to), downloads the resolved admin-<version>.zip directly, and verifies the manifest's sha256 before installing. Self-hosted operators can point at their own bucket by setting ADMIN_UPDATE_S3_BASE_URL or --admin_update_s3_base_url.
The cross-version gate in GET /update/upgrade/dashboard is relaxed: minor and patch upgrades no longer require force=true. Only true major-version jumps are still blocked behind the flag. This matches the new reality where admin ships its own 8.x semver track decoupled from core's 13.x, and admin's in-repo monorepo cadence produces frequent patch/minor releases.
The release.yml workflow now emits sha256 in latest.json alongside the existing fields, matching admin-release.yml's schema. Both publish paths populate the same manifest contract that the new updater expects.
Changes
Features
- Admin in-app updater consumes R2
latest.json(with sha256 verification) instead of GitHub Releases. (31b6e33)
Upgrade Notes
No action required for users on the default deployment — the new manifest URL ships baked in. Operators running self-hosted admin buckets should set ADMIN_UPDATE_S3_BASE_URL (or --admin_update_s3_base_url) to their bucket root before this release lands.
Full Changelog: v13.6.0...v13.7.0
v13.6.0
TL;DR
Admin write surface gains mx-editor LiteXML authoring, and Lexical write APIs now require content and text to be submitted together.
Highlights
The admin write panel now accepts LiteXML envelopes (<mxpost>, <mxnote>) as a first-class authoring format. Content is parsed into Lexical JSON on submit, so the server-side storage shape is unchanged — operators see the same lexical content rows, with a richer authoring loop on the admin side via @haklex/rich-litexml.
Server-side validation for lexical writes is tightened: content (Lexical JSON) and text (plain-text projection) must now be sent as a pair. Creates require both; partial updates accept neither or both, never one alone. This prevents stale text from drifting out of sync with content after a write, which previously could happen when callers patched only one side.
Upgrade Notes
If you have any external integrations that POST/PATCH lexical posts, notes, or pages directly to the server, update them to submit content and text together. The official @mx-space/api-client v5.3.1 (published alongside this release) and admin are already aligned. Clients sending only content (or only text) for contentFormat: lexical writes will now receive a 400 VALIDATION_FAILED.
Changes
Features
- Admin rich editor: author posts in LiteXML (
<mxpost>,<mxnote>) — parsed to Lexical on submit (#2743) - Lexical write validation: enforce paired
content+textsubmission across post / note / page / draft (#2743)
Full Changelog: v13.5.2...v13.6.0
v13.5.2
TL;DR
Admin rich editor now accepts a dropped .gpx file and inserts a ready-to-render map node, with stop-detection parameters tunable from the insert dialog.
Highlights
The map extension previously required clicking through a slash-menu picker to add a track; now the editor catches drag-and-drop and paste of .gpx files directly, parses the GPS points client-side, compresses them via the existing stop-detection pipeline, uploads the resulting JSON track, and inserts the map node — all behind a single toast. The InsertMapDialog was also rewritten so the upload only happens when you click Insert, not the moment you pick a file, and exposes the two stop-detection knobs (cluster radius in metres and minimum dwell in minutes) that used to be hard-coded constants.
The editor surface in Write grows to fill the visible viewport, so the drop target is large enough to land a file on without precision-aiming the toolbar strip.
Changes
Features
- Drag a
.gpxfile onto the rich editor to insert a map node — same compression and stop-detection pipeline as the existing dialog. (9174971)
Full Changelog: v13.5.1...v13.5.2
v13.5.1
TL;DR
Patch release with admin map dialog upload fix and content list toolbar polish.
Changes
- Map insert dialog now defers the GPX upload until you click Insert, parses the file locally for preview, drops the lossless toggle (full track is always preserved), removes the noisy filename auto-fill for the title, and aligns the upload button height with the URL input. (8274418)
- Content list toolbar selection control moves to the leftmost slot so the select-all checkbox lines up with each row's checkbox, and timestamps in PostRow / NoteRow now stack with action icons in a single right-aligned vertical rail for clearer scanning. (c705547)
Full Changelog: v13.5.0...v13.5.1
v13.5.0
TL;DR
Embed Afilmory photo galleries directly inside posts from the admin editor, with a redesigned picker and live filters.
Highlights
The rich editor now ships a first-class Afilmory block. Authors paste a gallery base URL and either pick individual photos or describe a filter — tags, cameras, lenses, date range, and free-text search — that the renderer re-evaluates at view time. Single picks render as a polaroid card; multiple or filter sources render as a grid, masonry, or carousel inside the post.
The Insert-Afilmory dialog was rebuilt around a photo-first browser. Selection and filtering share one surface with no upfront mode toggle, a sticky search bar, a filter popover for facets and date range, and an inline active-filter chip row. When both a selection and a filter are present, the dialog now surfaces two CTAs so the submit intent is explicit rather than silently preferring the selection.
This release also stabilises the server build by reverting the kysely override pinned at 0.28.17 (the better-auth adapter still imports symbols the 0.29 line removed). Several runtime dependencies move forward by a major: marked 18, shiki 4, lexical 0.45, haklex 0.20, ejs 6, and happy-dom 20 in tests.
Changes
Features
- Embed an Afilmory gallery in any post: pick photos individually, or describe a filter the renderer evaluates live (tags, cameras, lenses, date range, search). Layout choices: grid, masonry, carousel. (28781df)
Bug Fixes
- Server build no longer fails on
@better-auth/kysely-adapterafter the kysely upgrade — the override is reverted to 0.28.17 until the adapter catches up. Admin tests are repaired alongside (i18n stub, getAnimations polyfill, markdown-render mock, badge data-testid forwarding). (b4aab3f)
Other
- Redesigned Insert-Afilmory dialog: photo-first grid browser, filter popover, dual CTAs when both selection and filter are set. (c4de8f2)
- Admin dashboard top section unified to a line style with deeper grid lines. (d7e1cc2)
- Major dependency bumps: marked 18, shiki 4, lexical 0.45, haklex 0.20, ejs 6, happy-dom 20. (4bf5255)
- Production bundle consolidated into the shared
vite.config.ts; the standalonebuild.config.tsis gone. (7729b0e)
Full Changelog: v13.4.0...v13.5.0
v13.4.0
TL;DR
Major AI subsystem rewrite on @earendil-works/pi-ai, plus a Map Lexical node, S3-backed uploads, and a redesigned admin comments inbox.
Breaking Changes
- AI agent conversations: the
ai_agent_conversationstable is dropped and recreated with a session-scoped shape —{ id, sessionId, model, providerId, messages, createdAt, updatedAt }. Legacy columns (refId,refType,title,reviewState,diffState,messageCount) and existing rows are not preserved. The article-scoped "conversations for this post" admin UX is removed. Migration: run the bundledmx-migratejob as a pre-deploy step with bothmx-corereplicas stopped or in cutover; the admin agent chat endpoint (/api/ai/agent/*) is unavailable for the cutover window (≤ 5 minutes). No data path is provided to recover historical conversations. - AI provider type collapsed to three values:
AIProviderTypenow exposes onlyOpenAICompatible,Anthropic,Generic. LegacyOpenAI/OpenRouterrows in the jsonb config are rewritten toopenai-compatibleautomatically. Migration: no action required for stored config; verify provider rows in the admin AI panel after deploy and confirm endpoints resolve to the expected piproviderId(openrouter.ai → openrouter, api.deepseek.com → deepseek, api.openai.com → openai, api.anthropic.com → anthropic). @mx-space/api-clientpublic surface trimmed:AiAgentSseEventis no longer exported from@mx-space/api-client. It moved to the new neutral@mx-space/aiworkspace package, consumed directly by the admin. Migration: public consumers (Shiroi / Yohaku) are unaffected because the type was admin-only; bump@mx-space/api-clientto 5.3.0 to pick up the trimmed type surface.
Highlights
The AI runtime is now a single PiRuntimeAdapter over @earendil-works/pi-ai, replacing the previous OpenAI / Anthropic / ai-sdk stack. Retries, provider routing, and gateway prompt caching are delegated to pi's hosted layer. Structured output validation moved off Zod to TypeBox with byte-pinned regression coverage against the historical decisions, and a new GET /api/ai/registry/models endpoint feeds the admin model picker with provider-aware suggestions. Public SSE wire bytes (/ai/summary, /ai/insights, /ai/translation) are preserved exactly, including the cache-hit synthetic done frame.
Translation tasks now stream through a Redis-backed leader / follower with cross-pod fan-out: a TaskQueueEmitter throttles progress, a RoomSubsService heart-beats per-admin subscriptions, and p-limit caps concurrent strategy calls while abort cancels orphans cleanly. Cost forwarding flows through every layer — strategy, executor, summary and insights — and recomputes parent group totals on each emit, so admin dashboards see live spend without polling.
Content gains a new Map Lexical node with admin authoring affordances, and the helper service now backs file uploads with S3 instead of local disk. The admin /comments surface was rewritten end to end: tabbed inbox, R3 row layout, threaded reply stream, and a fresh meta sidebar. The shared @mx-space/ai package decouples server SSE contracts from @mx-space/api-client, removing a long-standing reverse dependency from admin onto the public client lib.
Changes
Features
- Map Lexical node with admin authoring + S3-backed file uploads. (45b4a47)
- AI runtime migrated to
@earendil-works/pi-ai; legacy OpenAI / Anthropic / ai-sdk dependencies removed. (spec1 series) ai_agent_conversationsrewritten as session-scoped pi schema withstreamMessageaccumulator + abort persistence. (1189565, 676041b)- Admin agent chat SSE switched to JSON-frame protocol with monotonic
contentIndex, 15s heartbeats, and reply-end guards. (4f83662) GET /api/ai/registry/modelsreturns per-provider model metadata (context window, max tokens, cached-input pricing) with 5-minute SWR cache. (815de31)- AI provider type reduced to three values with jsonb config migration. (501ff20)
- Prompt schemas ported to TypeBox with byte-pinned regression fixture suite. (98c44f3, aa126ce)
- Translation task queue:
RoomSubsServiceRedis-backed subscriptions,TaskQueueEmitterthrottled progress,streamPusher+TaskStreamBuffer,p-limitconcurrency, abort cancels orphans. (spec2 series) - Cost forwarding through translation strategy, summary, and insights with parent-group recompute. (ceaa49c, bec39a0, c922764)
- AI agent conversation title generation + admin panel refactor. (ccddc78)
- Admin
/commentsredesign — inbox tabs, R3 rows, thread stream, meta sidebar. (9c2345b) - Admin URL-driven master-detail with iOS push nav stack. (a5b9dff)
@mx-space/aipackage extracted for sharedAiAgentSseEventcontracts. (108ccd9)
Bug Fixes
- Inline
Value.CheckincallWriterStreamingdone branch to prevent silent schema drift. (587903e) - Resolve stale conflict markers in migration journal. (19e2517)
Other
- Bump
@haklex/*to 0.20.0 (rich-headless + litexml). (8671d38) - Bump major deps: commander, ejs, inquirer, p-limit, testcontainers. (5375d20)
Upgrade Notes
- Run the pre-deploy migration with both replicas stopped.
ai_agent_conversationsis dropped and recreated; a hot replica reading the old schema during cutover will crash. Use themx-migrateDokploy job; the app boot guard (assertSchemaCurrent) refuses to start if the schema is behind. - Verify AI provider rows after deploy. Open the admin AI provider drawer and confirm each row's endpoint resolves to the expected pi
providerId. LegacyOpenAI/OpenRouterrows are rewritten automatically; if a row showsgenericunexpectedly, set the endpoint hostname explicitly. - S3 environment variables are required if the new file-upload pipeline is enabled — configure
S3_*env per the helper service before invoking uploads. Existing deployments without S3 configured retain the previous behaviour for unrelated paths but cannot accept new uploads. - Admin agent chat sessions are reset. Historical conversations are not migrated; instruct admin users that prior chat history is unavailable post-cutover. Title generation now happens client-side via
generateTitle()and is not persisted on the conversation row.
Full Changelog: v13.3.1...v13.4.0
v13.3.1
TL;DR
Patch release: the admin dashboard bundled with the server now reliably supersedes any previously downloaded copy after an upgrade.
Changes
- admin: the dashboard version baseline was raised to
8.2.0, above the lastmx-space/mx-adminGitHub release (8.1.0). The server picks the higher-versioned of the bundled dashboard and any copy previously downloaded into the data directory; with the in-repo dashboard having restarted at7.0.x, a stale8.1.0copy was being served instead of the freshly bundled build. The dashboard shipped with this server is now served without manually clearing the data directory. (69e7657)
Full Changelog: v13.3.0...v13.3.1
v13.3.0
TL;DR
The admin dashboard is now built into the server from source — no external download — alongside reader bans and URL-based enrichment in the SDK.
Highlights
The admin dashboard (formerly the separate mx-admin repo) now lives in this monorepo and is compiled from source as part of the server build. A release bundles the freshly built dashboard into out/admin, so a server install no longer downloads a prebuilt dashboard zip from GitHub. The dashboard is also published to a Cloudflare R2 update channel for future in-place upgrades. No operator action is required — it is served at the same /proxy/qaqdmin path as before.
Readers can now be banned, and the readers API supports role-filtered pagination, making moderation of a growing audience practical. Reader replies on comments now stay marked unread instead of inheriting the parent's read state, so fresh replies surface correctly.
The typed API client gained an EnrichmentController with resolveByUrl, exposing the server's URL enrichment (Open Graph / metadata) to frontends through the SDK.
Changes
Features
- Admin dashboard is built into the server from source and bundled at release time; also published to a Cloudflare R2 update channel for future upgrades. (#2740)
- Reader management: ban support and role-filtered pagination. (91acc5d)
- api-client: add
EnrichmentControllerwithresolveByUrl. (7557134)
Bug Fixes
- Reader replies stay unread instead of inheriting the parent comment's read state. (7332443)
Full Changelog: v13.2.0...v13.3.0
v13.2.0
TL;DR
Image placeholder hashes switch from blurhash to thumbhash across the stack, and AI translations now run through a writer → reviewer → editor pipeline for higher fidelity.
Breaking Changes
- Image placeholder format: every image carries a
thumbhash(lowercase, base64-encoded) field instead ofblurHash/blurhash. Theenrichment_captures.blurhashcolumn is dropped;images.blurHashkeys inside content JSONB are stripped;enrichment_cache.normalizedshedded its embedded*.blurhashpaths. Old hash strings are not migrated — legacy images fall back to the existingaccentcolor block as a placeholder until they are re-saved. Migration: stop both replicas before runningpnpm -C apps/core run migrate; bump@mx-space/api-clientto5.2.0+, admin to8.1.0+, and any custom frontend to readthumbhashinstead ofblurHash. Flushenrichment:cache:*in Redis after the migration completes to evict stale enriched payloads. (339ac4f, 88a0bca)
Highlights
Image placeholders are now generated and rendered with thumbhash. It is smaller on the wire (~24 base64 chars vs ~30), supports transparency, decodes roughly ten times faster on the client, and renders as a plain <img src="data:image/png;base64,..."> instead of a canvas mount — removing the need for react-blurhash everywhere downstream. The server-side encoder uses sharp to pre-resize to ≤100×100 before calling rgbaToThumbHash; client-side encoders in admin and frontend mirror the same pre-resize step.
AI translations now run through a writer → reviewer → editor pipeline. The writer produces an initial draft, the reviewer flags issues with structured feedback, and the editor applies fixes in a final pass. The result is noticeably better fidelity on long-form content with no extra cost to operators. See #2739 for the full design.
The 0015 schema migration runs as part of the release-phase migrator. It is destructive (DROP COLUMN blurhash) and authored under a maintenance-window assumption — operators should follow the deploy plan in the design spec to keep replicas stopped during the SQL run.
Changes
Features
- AI translation: writer → reviewer → editor pipeline for higher-fidelity long-form translations (#2739)
- Image pipeline: swap blurhash encoder for thumbhash across helper.image, image-meta, and OG capture services (88a0bca)
- Database: rename
enrichment_captures.blurhash→thumbhash; strip deadblurHashJSONB keys from content tables andenrichment_cache.normalized(339ac4f)
Upgrade Notes
- Enable a maintenance window — public site + admin.
- Stop both
mx-corereplicas in Dokploy. - Run
pnpm -C apps/core run migrate. The 0015 migration dropsenrichment_captures.blurhash, stripsblurHashkeys fromposts.images/notes.images/pages.images/drafts.images, and clearsenrichment_cache.normalized.thumbnailImage.blurhash+.captureImage.blurhash. - Flush stale enriched payloads in Redis:
redis-cli --scan --pattern 'enrichment:cache:*' | xargs -r redis-cli del
- Deploy the new
mx-coreimage. - Deploy admin-vue3
v8.1.0(already pinned viadashboard.versionin this release). - Deploy any custom frontend that previously read
blurHash/blurhash— it must readthumbhashand ideally render via a thumbhash decoder (e.g.thumbHashToDataURL). - Disable maintenance page and verify a sample post + link card renders a thumbhash placeholder on freshly-saved content, with accent fallback on legacy content.
Full design and rollout plan: docs/superpowers/specs/2026-05-28-thumbhash-migration-design.md.
Full Changelog: v13.1.2...v13.2.0
v13.1.2
TL;DR
Translation output now reads more naturally in casual registers, and the friend-link checker probes JS-rendered landing pages via the shared browser pool.
Highlights
The translation pipeline has been retuned around a single goal: write the target text the way a native author of that language would, not as a literal mirror of the source. The change improves sentence-level cadence and register matching — zh→en Chinglish is noticeably reduced, and zh→ja stays consistently in casual register instead of drifting between 敬体 and 常体. Output token cost is unchanged; system prompt is roughly 1.2k tokens larger per request, negligible on most provider pricing.
The link-health probe and enrichment open-graph capture now share a single Chromium-backed browser pool. JS-rendered landing pages and basic anti-bot challenges that previously looked dead to the friend-link checker now return correct status codes.
Note on versioning: v13.1.1 was tagged but its CI install failed on a stale semver override / lockfile mismatch — no artifacts (Docker, Release zip) were ever published for that tag. v13.1.2 ships the same product changes plus the lockfile alignment fix.
Changes
Other
- Translation prompts rewritten with a language-agnostic philosophy emphasising native-idiom output over surface-syntax mirroring. Adds register, idiom, and concept-level calque checks; tightens the scope of "preserve structure exactly" to Markdown/HTML/JSX layers only. (9c0cc84)
- Friend-link checker and open-graph enrichment now share a single agent-browser processor (
processors/agent-browser/). JS-rendered and anti-bot-gated pages are no longer reported as dead. (#2738) - Align
semveroverride with the resolved lockfile and exempt@babel/*fromminimumReleaseAge, so fresh upstream patch releases no longer block CI install.
Full Changelog: v13.1.0...v13.1.2