Create shared-tool-adapter to convert @openchatlab/tools ToolDefinition → AgentTool.
Delete Electron-specific get-member-stats.ts and get-time-stats.ts,
replaced by shared definitions via adaptSharedTool().
Make ToolExecutionContext.db optional to support Electron (no DatabaseAdapter).
Replace direct @openchatlab/core function calls with dataProvider abstraction:
- member-stats, time-stats, search, recent-messages, sql, schema
- Handlers are now async, using context.dataProvider for data access
- Server consumers inject CoreDataProvider alongside db for backward compat
Add ToolDataProvider implementations for both platforms:
- CoreDataProvider: wraps @openchatlab/core sync queries for Server/MCP
- WorkerDataProvider: wraps workerManager async IPC for Electron
- Convert 20 tool definition files to import Type/AgentTool from
@openchatlab/node-runtime instead of direct pi-* packages
- Migrate compression, summary, RAG, LLM config, and IPC modules
to use node-runtime re-exports (completeSimple, streamSimple, etc.)
- Remove @mariozechner/pi-agent-core and @mariozechner/pi-ai from
root package.json — now only node-runtime depends on them directly
- Electron is now fully decoupled from pi-* direct imports
- Create runAgentCore() in node-runtime/src/ai/agent/ encapsulating
PiAgentCore construction, history conversion, event subscription,
abort forwarding, tool round management, and usage collection
- Re-export pi-agent-core/pi-ai types (AgentTool, Type, Model, etc.)
and functions (completeSimple, streamSimple) from node-runtime
- Refactor Server agent.ts to use runAgentCore, removing all direct
pi-agent-core/pi-ai imports (~335→~210 lines)
- Remove pi-agent-core and pi-ai from Server package.json dependencies
- Refactor Electron agent/index.ts and event-handler.ts to use
runAgentCore with DI hooks (streamFn, onConvertToLlm, onDebugContext)
- Document remaining Electron pi-ai usage points for future migration
- Replace local formatToolResultAsText, truncateFormattedMessages,
anonymizeMessageNames with imports from @openchatlab/node-runtime
- Replace local createActivateSkillTool with shared version via DI
(inject Electron's getSkillConfig and CORE_TOOL_NAMES)
- Retain Electron-specific translateTool (i18n) and getAllTools (tool registry)
- Net reduction of ~170 lines of duplicated code
- Create remote-api.ts with platform-agnostic validateApiKey (completeSimple
test call) and fetchRemoteModels (OpenAI/Gemini model listing endpoints)
- Add POST /_web/ai/llm/validate-key and POST /_web/ai/llm/remote-models
HTTP endpoints
- Update web shim to call new endpoints instead of returning stubs
- Create custom-store.ts with platform-agnostic CRUD for custom-providers.json
and custom-models.json (parameterized by aiDataDir)
- Add 6 HTTP endpoints: POST/PUT/DELETE for custom providers and models
- Update web shim to call new endpoints instead of returning WEB_STUB
- Add compressionConfig to SSE agent/stream request body with sensible defaults
- Pass compressionConfig from route handler to runServerAgent
- Update frontend runStream to forward compressionConfig to the SSE endpoint
- Extend ToolResult with optional rawMessages field for structured message data
- Update search and recent-messages tools to return rawMessages from query results
- Add preprocessing wrapper in server tool-adapter: auto-apply data cleaning,
denoising, desensitization, compact formatting, and token-aware truncation
when tools return rawMessages
Move compression logic from Electron to shared package with pluggable
LLM adapter interface. Integrate into server agent with pre-run
compression support. Electron side becomes a thin compatibility wrapper.
Add CRUD HTTP endpoints for LLM configurations (add/update/delete config,
set default assistant slot, set fast model slot).
Update web shim to call these endpoints instead of returning stubs.
Extract SkillManager from Electron to @openchatlab/node-runtime.
Create shared createActivateSkillTool factory.
Integrate skill menu into server agent system prompt and SSE route.
Extract migration logic from loadConfigStore into a shared Migration Runner
system (packages/config/src/migrations/). Runs at app startup in both
Electron and CLI, enabling CLI to handle encrypted key migration independently.
- Extract AIConversationManager to @openchatlab/node-runtime for Electron + Server reuse
- Add /_web/ai/conversations/* HTTP endpoints for conversation/message CRUD
- Create FetchAIAdapter to replace WebAIAdapter stub
- Implement server-side Agent with SSE streaming (/_web/ai/agent/stream)
- Add agentApi SSE client shim in frontend registry
- Migrate API key storage from encrypted llm-config.json to auth-profiles.json
- Add @openchatlab/config auth-profiles module for credential management
Move platform-agnostic NLP types, stopwords, POS tag definitions, and
text utilities into @openchatlab/core. Move segmenter engine, word
frequency computation, and dict manager into @openchatlab/node-runtime.
Electron nlp/ becomes a thin re-export layer; server nlp.ts shrinks
from ~450 to ~85 lines retaining only HTTP route definitions. Both
platforms now share the same NLP implementation via node-runtime.
Also fixes createJiebaNlpProvider to use shared getJieba() with disk
dict loading (was using empty Jieba instance causing degraded POS tags
in language preference analysis), adds ensureDefaultDict at server
startup, and adds /_web/nlp/* HTTP routes with window.nlpApi shim.
Split PathProvider into dual-root architecture: systemDir (~/.chatlab/)
for config/logs/cache/ai/settings, and userDataDir (~/.chatlab/data/ by
default) for chat databases. Both Electron and CLI share the same
config.toml for user_data_dir resolution.
- Refactor PathProvider interface (getSystemDir + getUserDataDir)
- Update config schema (data.dir → data.user_data_dir)
- Add writeConfigField() for config.toml manipulation
- Fix needsUnifiedDirMigration() to detect old Electron data even when
user_data_dir was already set by CLI (compare paths instead of
checking existence)
- Add POST /_web/demo/import backend endpoint: downloads demo data from
chatlab.fun and imports using streamImport with SSE progress
- Refactor DemoImportButton.vue to support both Electron (IPC) and
Web (SSE fetch) modes with shared result handling
- Add session index endpoints (generate-index, clear-index) with writable db
- Add window.sessionApi shims for web mode (getStats, generate, getSessions, etc.)
- Fix CORS: add Vite dev proxy for chatlab.fun, centralize CHATLAB_SITE_BASE
- Guard DemoImportButton: show fallback message in web mode
- Use tsx watch for dev:serve hot-reloading
- Reorganize dev scripts: dev:app (web frontend), dev:serve (backend), dev:web (both)
- dev: Electron only (renderer port 3400, unchanged)
- dev:app: Web frontend only (port 3100)
- dev:serve: Backend only (chatlab serve, default port 3110)
- dev:web: Web frontend + backend auto-start (3100 + 3110)
- Revert electron.vite.config.ts to original (no extra plugins)
- chatlabServePlugin gated by CHATLAB_AUTO_SERVE env var
- Fix streamImport error handling: close db before deleting files on failure
- Add POST /_web/sessions/:id/query endpoint for parameterized readonly SQL
- Expose PreparedStatement.readonly property in DatabaseAdapter interface
- Fix Fastify error handler to respect statusCode on non-ApiError errors
- Implement FetchAdapter.pluginQuery via HTTP endpoint
- Add web-api-shim.ts: window.chatApi/aiApi polyfills for chart-* and
MessageList (pluginQuery, pluginCompute, getMemberActivity,
getAvailableYears, message browsing methods)
- Fix Sidebar: navigate to home after deleting the currently viewed session
Add IS_ELECTRON guards around window.api.setThemeSource,
window.api.app.getOpenAtLogin, and window.api.app.setOpenAtLogin
to prevent crashes when opening settings in web mode.
Add chatlabServePlugin to vite.web.config.mts that automatically spawns
chatlab serve on port 3400 when Vite dev server starts, so the web UI
works without manually starting the backend in a separate terminal.
Register @fastify/multipart in startHttpServer before route registration
instead of inside registerWebRoutes, fixing 500 errors on /_web/ routes
caused by async plugin registration within route functions.
Add /_web/import (multipart upload + SSE progress stream),
/_web/detect-format, /_web/scan-multi-chat, and /_web/supported-formats
endpoints. Register @fastify/multipart for file upload handling.
Files are saved to temp directory for parsing then cleaned up.
Replace ChatLab-only reader with @openchatlab/parser in the server
import pipeline. Add streamImport() function for full-format streaming
parse + database write. Update CLI import command to use new pipeline
and add 'chatlab formats' command to list supported formats.
Move all 14+ format parsers (QQ, WeChat, Telegram, WhatsApp, LINE,
Discord, Instagram, etc.) from electron/main/parser/ to packages/parser/
as a standalone shared package. Electron's parser module becomes a thin
re-export wrapper. This enables the server to use the same parser for
CLI serve Web import functionality.
Phase 3A.3: Enable chatlab serve to host the Web SPA frontend
alongside the HTTP API, creating a complete local web experience.
- Add --web [dir] option to chatlab serve (defaults to dist-web/)
- Use @fastify/static for efficient static file serving
- SPA history fallback via setNotFoundHandler -> index.html
- Skip web hosting gracefully if dist-web/ doesn't exist
- Add webRoot option to HttpServerOptions / startHttpServer
Phase 3A.2: Create vite.web.config.mts for building the CLI serve
Web SPA independently from the Electron build pipeline.
- __IS_ELECTRON__ = false, __IS_BROWSER_STANDALONE__ = false
- Output to dist-web/ (gitignored)
- Dev proxy: /_web and /api -> localhost:3400 (chatlab serve)
- Resolve aliases for @electron/shared and @electron/preload
(type-only imports, erased at build time)
- npm scripts: dev:web, build:web
- Same manualChunks strategy as Electron renderer build
Phase 3A.1: Create the HTTP bridge between the Vue SPA frontend
and chatlab serve backend for the CLI serve Web version.
Server side (packages/server):
- Add /_web/ route group (routes/web.ts) with no auth requirement,
covering: sessions CRUD, time range, member-activity/hourly/daily/
weekday/message-type stats, members CRUD+pagination+merge+history,
SQL Lab execute+schema.
- Skip auth hook for /_web/ prefix (same-origin internal API).
- Add getTimeRange() to @openchatlab/core basic queries.
Client side (src/adapters):
- FetchAdapter wrapping all /_web/ endpoints with proper HTTP verbs,
TimeFilter query param serialization, and error handling.
- Unimplemented analysis methods (catchphrase, mention, cluster, etc.)
throw descriptive errors for progressive enhancement.
- Wire FetchAdapter into adapter factory (non-Electron, non-browser).
Phase 3.0.3: Systematically replace all data-query window.chatApi
calls across 24 files with getAdapter() from the new adapter layer.
Replaced categories:
- Session CRUD (getSessions, getSession, deleteSession, renameSession)
- Statistics (getMemberActivity, getHourlyActivity, getDailyActivity,
getWeekdayActivity, getMessageTypeDistribution, getAvailableYears)
- Member management (getMembers, getMembersPaginated, updateMemberAliases,
mergeMembers, deleteMember, getMemberNameHistory)
- Social analysis (getMentionAnalysis, getLaughAnalysis,
getCatchphraseAnalysis, getLanguagePreferenceAnalysis,
getRelationshipStats)
- SQL Lab (executeSQL, getSchema)
Remaining window.chatApi calls are Electron-specific operations
(import, migration, file dialog, demo, export) that will be handled
separately with IS_ELECTRON guards in future phases.
Also adds initAdapter() call in App.vue onMounted to bootstrap the
adapter before any data loading occurs.
Implement self-contained ChatLab format reader (JSON and JSONL) and
database importer in packages/server/src/import/. Supports full import
(new session) and incremental import (dedup via platform_message_id or
content hash). Includes FTS index creation and member upsert.
CLI: chatlab import <file> [--session-id <id>] [--dry-run]
Implement 'chatlab serve' CLI command with --port, --host, --token options.
Auto-generates and persists Bearer token on first run. Supports graceful
shutdown via SIGINT/SIGTERM. Verified: auth rejection, status endpoint,
session listing all working.
queryMessages supports keyword, time range, senderId filtering with
full pagination (total, page, totalPages). getMembersDetailed returns
accountName and groupNickname fields for HTTP API export compatibility.
Add rebuild-native.sh script that compiles better-sqlite3 in an isolated
temp directory for system Node.js, storing the binary at packages/server/native/.
DatabaseManager and openBetterSqliteDatabase now accept nativeBinding option.
CLI auto-detects non-Electron environment and uses the isolated binary.
Phase 2.2: add chatlab mcp command that starts an MCP stdio server.
Registers 8 tools from @openchatlab/tools and 3 resources (sessions
list, session meta, session schema). Compatible with Claude Desktop
and Cursor MCP client configuration.
The root project's electron-rebuild compiles better-sqlite3 for
Electron's Node ABI, making it incompatible with system Node.js.
Add a convenience script to rebuild for the current Node version.
Phase 1.7: add DatabaseManager to @openchatlab/node-runtime that handles
opening, caching, listing, and closing chat session databases using the
DatabaseAdapter and PathProvider interfaces from core.