The MCP entry was loading better-sqlite3 from node_modules which may
have been compiled for a different Node.js ABI. Now passes the
pre-built native binding from apps/cli/native/ (maintained by
ensure-native.mjs), matching how other CLI commands handle this.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Expand MCP_TOOL_REGISTRY from 8 to 33 tools by including all
AGENT_TOOL_REGISTRY tools (analytics, deep search, conversation, etc.)
- Add format=text|json parameter to all MCP tools (default: text for
most, json for SQL tools) for token-efficient LLM consumption
- Create format.ts: message filtering, consecutive-sender merging,
content truncation, and compact text output
- Inspired by community chatlab-mcp-server project
Co-authored-by: Cursor <cursoragent@cursor.com>
Move MCP Server logic (tool registration, resource registration, schema
conversion) from apps/cli/src/mcp.ts to packages/mcp-server so both CLI
and Desktop can share the same MCP implementation.
- New package: chatlab-mcp (packages/mcp-server) with McpDatabaseManager
interface abstraction
- CLI mcp.ts slimmed to thin entry (~37 lines) calling startMcpServer()
- Desktop helper prototype at apps/desktop/helper/mcp.ts for dev-time
MCP without GUI
- All 8 MCP tools and 3 resources unchanged
Co-authored-by: Cursor <cursoragent@cursor.com>
Add @electron/rebuild devDependency and postinstall script to
apps/desktop so that better-sqlite3 is automatically recompiled
against Electron's Node.js headers after every pnpm install,
preventing NODE_MODULE_VERSION mismatch at runtime.
- Add backendPersist Pinia plugin for declarative preferences.json persistence
- Move Preferences types to @openchatlab/shared-types (single source of truth)
- Migrate sessionGapThreshold from localStorage to config.toml [ui]
- Migrate chatlab_filter_history from localStorage to preferences.json
- Fix stale localStorage locale read in i18n/index.ts
- Simplify usePreferencesSync (172 → 75 lines) by delegating to plugin
Adding a new backend-synced preference now only requires 3 changes:
1. shared-types interface 2. node-runtime DEFAULTS 3. store backendPersist.pick
When a desktop app user installs the CLI, automatically detect existing
Electron data directory (including custom paths via storage.json),
repoint config.toml, and migrate system data (ai, settings, cache, etc.)
to ~/.chatlab/. Block CLI execution with actionable instructions when
Electron was used but databases cannot be located.
- Remove "type": "module" from apps/cli/package.json to avoid
ESM/CJS interop issues with workspace packages in tsx dev mode
- Configure tsup to output .mjs files (always treated as ESM by Node.js)
- Rename bin entry from chatlab.js to chatlab.mjs
- Change chatlab web default port to 3100 (serve keeps 3110)
- Fix vite plugin: use pnpm instead of npx for tsx invocation
All command descriptions, console output, option labels, comments
and error messages are now in English, consistent with AGENTS.md
rules and npm package positioning.
- Rename package to `chatlab-cli`, version 0.1.0
- Add tsup config with ESM output, bundle @openchatlab/* + stream-json
- Update bin/chatlab.js from stub to actual entry point
- Unify version reading via src/version.ts (CLI/MCP/HTTP API)
- Add publishing fields: type, files, engines, publishConfig, keywords
- Move @openchatlab/* to devDependencies (bundled into dist/)
- Add better-sqlite3 to dependencies, @node-rs/jieba to optionalDependencies
- Fix --web default path for bundled dist/ directory structure
Move the CLI application from packages/server/ to apps/cli/ to align
with the monorepo convention (apps/ for deployable applications,
packages/ for shared libraries). Rename package to @openchatlab/chatlab.
Also fix a bug in ensureToken() that used raw string manipulation to
write config.toml, causing duplicate token fields that corrupted the
TOML file. This led to config parse failures and user_data_dir being
overwritten with defaults. Now uses writeConfigField() for safe writes.
Worker thread crashed in packaged builds with "Cannot find module 'electron'"
because streamImport.ts transitively imported paths.ts (which requires electron)
through merger/tempCache.ts. Replaced with worker-local implementations that
receive tempDir via workerData, matching the existing dbDir/cacheDir pattern.
The extraction of Electron code to apps/desktop/ changed the package name
from "ChatLab" to "@openchatlab/desktop", which caused app.getPath('userData')
to return a wrong path in dev mode. This made the migration system unable to
find existing databases, resulting in a fresh-app experience for upgrading users.
- Add productName "ChatLab" to desktop package.json to ensure correct userData path
- Add verifyDataPath() safety net: auto-correct config.toml when configured dir
has no databases but old Electron path does
- Pin electron to 35.7.5 (electron-builder requires fixed version)
- Remove redundant @electron/rebuild (built into electron-builder)
- Use electron-builder install-app-deps for postinstall
Adds a zero-dependency Node.js script with arrow-key selection
for choosing between Desktop (Electron), Web (CLI serve + frontend),
Server Only (API backend), and Frontend-only dev modes.
Remembers last selection in node_modules/.cache/dev-mode.
Move Electron-specific code (main, preload, shared) and configs
(electron.vite, electron-builder) into apps/desktop/ to prepare
for multi-platform support (Web Browser standalone).
- Create apps/desktop/ with dedicated package.json and tsconfig
- Split Electron deps (electron, electron-updater, better-sqlite3,
etc.) from shared frontend deps in root package.json
- Declare better-sqlite3 and @node-rs/jieba in packages/node-runtime
- Add stream-json to packages/parser where it belongs
- Update all path aliases (@electron/* → apps/desktop/*) in tsconfig,
vite configs, and relative imports in preload/main
- Keep slim root tsconfig.node.json as shared base for packages
- Update CI/CD release workflow for new directory layout
- Fix pre-existing type errors (pi-ai Model generic, SQLLabTab
LLMChatStreamChunk, filter.test.ts empty methods)
These are frontend-only Vue/ECharts components that depend on app internals (@/),
not cross-platform shared packages. Moving them to src/features/charts-* keeps
packages/ purely for reusable Node.js libraries.
The query barrel file was missing re-exports for getSessionsByTimeRange,
getRecentChatSessions, loadSessionMessages, getPrivateChatMemberAvatar,
getExportSessionData, and searchMessagesWithFtsAsync.
- Extract getPrivateChatMemberAvatar to @openchatlab/core
- Extract getExportSessionData (meta/members/messages SQL) to @openchatlab/core
- Extract loadSessionMessages for summary generation to @openchatlab/core
- Replace inline SQL in summary buildDeps with shared core query functions
- Extract createCompressionLlmAdapter factory to @openchatlab/node-runtime
- Simplify server and electron compression adapter creation via shared factory
- Extract formatAIError to @openchatlab/node-runtime, eliminate duplicate in Electron and Server
- Extract stripAvatarFields to @openchatlab/core
- Extract FTS message search SQL to core (searchMessagesWithFtsAsync)
- Remove redundant sync searchMessagesWithLike from Electron worker
- Remove dead code: electron/main/ai/agent/content-parser.ts (duplicate of @openchatlab/core)
- Extract getSessionsByTimeRange and getRecentChatSessions to @openchatlab/core
- Route IPC session:getByTimeRange and session:getRecent through Worker instead of inline SQL
- Eliminate duplicate getSessionSummaries in workerManager.ts, delegate to core via Worker
- All distribution targets share root package.json version (lock-step)
- Commit scope: free-form for shared changes, platform scope (electron/
cli/web) only when the change is platform-specific
Inject __APP_VERSION__ from package.json via Vite define, read it in
WebPlatformAdapter.getVersion(), and call getVersion() on all platforms
(not just Electron) in Sidebar onMounted.
tsx watch's symlink-based file watching doesn't trigger on changes to
pnpm workspace packages (tsx#221). Add --include flags for packages/core
and packages/node-runtime source directories to both dev:serve script
and vite.web.config.mts auto-serve plugin. Extract long command to
scripts/dev-serve.sh for cleaner package.json.
- Extract runSimpleLlmStream to @openchatlab/node-runtime for shared
Electron/Server streaming with thinking_start/delta/end event handling
- Simplify Electron IPC ai.ts by delegating to shared stream function
- Add thinking/thinkingDone fields to LlmStreamChunk and preload types
- Add collapsible thinking process display in SQL LAB with auto-scroll
- Implement Web chatStream via SSE with proper error propagation
- Add /_web/cache/save-to-downloads and show-in-folder server routes
- Add cacheApi shim in Web registry for server-side file operations
- Increase SQL LAB maxTokens to 4096 to accommodate reasoning models
- Detect finishReason:length and show token limit error message
- Add i18n keys for thinking/thinkingProcess/errorTokenLimit (4 locales)
- Replace mapCoreEventToStream with shared AgentEventHandler for rich
status events (contextTokens, totalUsage, updatedAt)
- Add formatAIError for user-friendly error messages (429/503/quota)
- Compute maxToolResultTokens dynamically from model contextWindow
- Add steerMessage (answerWithoutTools) and onConvertToLlm callback
- Forward ownerInfo/mentionedMembers from web frontend to server
Move AI-related translations (tools, agent prompts, LLM, summary) from
Electron-specific locale files into @openchatlab/node-runtime, enabling
single-source maintenance across Electron and Server.
- Add packages/node-runtime/src/ai/i18n/ with createAiTranslate() and
bundled locale data (zh-CN, en-US, ja-JP, zh-TW)
- Electron locale files now compose: Electron-specific keys + shared AI
translations from node-runtime
- Server agent.ts switches to shared buildSystemPrompt with evidence
policy, data snapshot injection, ownerInfo and mentionedMembers support
- Server route fetches dataSnapshot from getChatOverview before agent run
- Extract JSON cache CRUD + overview/members compute to node-runtime/cache
- Functions now accept DatabaseAdapter instead of better-sqlite3 Database
- Electron sessionCache.ts becomes pure re-export
- Updated all Electron callers to wrap DB with BetterSqliteAdapter
- Extract migration definitions (v1-v4) to shared getChatDbMigrations() with DI for tokenizer
- DatabaseManager.openWritable() auto-runs pending migrations
- Electron migrations.ts becomes thin wrapper delegating to shared definitions
After streaming or incremental import, automatically generate the
session index (chat_session table) so the frontend does not show
the "generate index" prompt when entering a conversation.
- Replace buffered import with high-performance streaming pipeline
- Add POST /_web/sessions/:id/import/incremental (SSE)
- Add POST /_web/sessions/:id/import/incremental/analyze
- Add POST /_web/import/analyze (dry-run)
- CLI import now reports diagnostics from StreamingImporter
Platform-agnostic temp DB management for merge operations using
DatabaseAdapter. Includes TempDbWriter (streaming write), TempDbReader
(streaming read + MergerDataSource adapter), exportSessionToJson,
and cleanup helpers.
Platform-agnostic incremental import with dual-path dedup
(platformMessageId + content hash), member upsert, meta patching,
FTS sync, and injectable dependencies (IncrementalImportDeps).
Platform-agnostic streaming import with batched transactions, deferred
indexes, nickname history tracking, FTS indexing, and format fallback.
All Electron-specific dependencies replaced with injectable interfaces
(StreamImportDeps, ImportLogger, ImportProgressCallback).
encryptApiKey/decryptApiKey and device key management have zero
importers — legacy functionality already handled by
@openchatlab/config/migrations/crypto-legacy. Removes ~278 lines.
Move the entire RAG module (~2458 lines) from electron/main/ai/rag/ to
@openchatlab/node-runtime. Includes chunking, embedding, vector store,
and semantic pipeline.
Key changes:
- Config operations use injected aiDataDir instead of getAiDataDir()
- Logger uses injectable RagLogger interface
- LLM config provided via getLLMConfig/getAssistantConfig callbacks
- Deduplicated cosineSimilarity into shared utils.ts
- Electron rag/index.ts is now a thin wrapper (~128 lines)
Replace ~120 lines of inline SQL in server stream-import.ts and ~60 lines
in importer.ts fullImport with calls to shared writeParseResultToDb from
@openchatlab/node-runtime. Server imports now gain name history tracking
that was previously Electron-only.
Move performance logging from electron/main/worker/core/perfLogger.ts to
@openchatlab/node-runtime. Log directory is now caller-provided instead
of derived from Electron paths. Electron wrapper provides the path.
Reduces Electron perfLogger from ~181 lines to ~28 lines.
Move core FTS operations (create, build, rebuild, insert, search) to
@openchatlab/node-runtime working with DatabaseAdapter. Electron fts.ts
becomes a thin adapter that adds session-level DB connection management.
Reduces Electron fts.ts from ~207 lines to ~90 lines of pure wrappers.
Move pure SQL session CRUD operations (owner_id update, rename) to the
shared core package as DatabaseAdapter functions. Electron wrappers now
delegate to core, keeping only connection management and error handling.
Move the core data writing logic (meta/member/message insertion with
member_name_history tracking) from electron/main/database/core.ts to
@openchatlab/node-runtime. Electron importData() now delegates to the
shared function via BetterSqliteAdapter, reducing ~130 lines to ~10.
This enables both Electron and Server to share the same import logic
including name history tracking.
Replace Electron's independent migration runner with core's runMigrations
engine. Migration scripts stay in Electron layer (they depend on NLP
tokenizer and i18n), but execution logic (version check, transaction,
version bump) is now delegated to the shared framework.
- Re-export CURRENT_SCHEMA_VERSION from @openchatlab/core
- Wrap better-sqlite3 Database in BetterSqliteAdapter for core compat
- Convert migration up() signatures to accept DatabaseAdapter
- Remove ~100 lines of duplicated migration infrastructure
Remove ~76 lines of duplicated CREATE TABLE/INDEX/FTS SQL from
database/core.ts createDatabase(), delegating to CHAT_DB_SCHEMA and
FTS_TABLE_SCHEMA from @openchatlab/core — the single source of truth.
The generateMessageKey function used crypto.createHash (Node.js only),
causing Vite browser externalization error. Replaced with pure-JS FNV-1a
64-bit hash that works in both Node.js and browser environments.
Co-authored-by: Cursor <cursoragent@cursor.com>
Platform-agnostic skill CRUD manager with dependency injection for file
system ops, builtin skill resources, and content hashing. Supports init,
CRUD, builtin import/reimport, cloud markdown import, and AI skill menu
generation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Platform-agnostic AssistantManager with dependency injection for file
system ops, builtin configs, and ID generation. Supports init, CRUD,
builtin import/reimport/reset, and cloud markdown import.
Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce ConfigStorage abstraction and LLMConfigStore class with
dependency injection for i18n, UUID generation, and auth profile hooks.
All CRUD operations (add/update/delete/slots) are platform-agnostic.
Electron can implement ConfigStorage with fs + getAiDataDir.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move platform ID collision detection, normalization, conflict detection,
member merge, and message dedup-sort from electron/main/merger to a
shared package with zero Node.js dependencies.
Also fix pre-existing unused-variable TS errors in summary test.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move session summary generation (573 lines) to shared package with
SummaryDeps injection for DB/LLM/i18n. Pure algorithms (isValidMessage,
filterValidMessages, splitIntoSegments, prompt builders) are now shared.
Electron summary/index.ts implements SummaryDeps and delegates.
Added 14 unit tests covering message filtering, segmentation,
summary generation, and eligibility checks.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move AgentEventHandler, TokenUsage, AgentRuntimeStatus, AgentStreamChunk
to packages/node-runtime/src/ai/agent/event-handler.ts.
Token estimation and event-to-chunk mapping are now shared.
Electron agent/types.ts re-exports from node-runtime.
Added 8 unit tests covering token estimation, tool tracking, usage cloning.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move buildSystemPrompt from electron/main/ai/agent/ to shared package.
i18n t() is now injected via BuildSystemPromptOptions parameter.
Types OwnerInfo, MentionedMember, SkillContext are shared.
Electron prompt-builder.ts injects its own t() and delegates.
Added 8 unit tests with mock translation function.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move mergeMembers, deleteMember, updateMemberAliases, ensureAliasesColumn,
ensureAvatarColumn to packages/core/src/query/member-ops.ts.
All functions accept DatabaseAdapter. Electron basic.ts now wraps raw
Database with BetterSqliteAdapter and delegates to core.
Added 8 unit tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move filterMessagesWithContext and getMultipleSessionsMessages to
packages/core/src/query/filter.ts. Functions now accept DatabaseAdapter
instead of raw better-sqlite3 Database. Types FilterMessage, ContextBlock,
PaginationInfo, FilterResultWithPagination moved to core.
Electron filter.ts is now a thin wrapper that opens DB and delegates.
Added 6 unit tests with mock DatabaseAdapter.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move tokenizeForFts and tokenizeQueryForFts from electron/main/nlp/ to
packages/node-runtime/src/nlp/fts-tokenizer.ts. Uses the already-shared
getJieba() internally. Electron ftsTokenizer.ts now re-exports.
Added 9 unit tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move extractThinkingContent and stripToolCallTags (pure string algorithms
with zero deps) from electron/main/ai/agent/ to packages/core/src/ai/.
Electron agent now imports from @openchatlab/core.
Added 12 unit tests covering all parsing paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
All analysis queries have been migrated to @openchatlab/core and are
consumed via Worker query modules and Server routes. This file had
zero imports and was fully superseded.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move remote LLM API operations into shared module with optional
headers and logger injection. Electron wraps with UA headers and
aiLogger; server re-exports directly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move URL normalization helpers and PiModel construction into a shared
llm-builder module. Electron and server both delegate to the core
builder, with Electron adding optional headers and provider validation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Eliminate duplicate buildMemberIdMap in server importer and
stream-import. Core function uses DatabaseAdapter interface.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract canonical generateMessageKey (SHA-256 + base64url) into core.
Electron tempDb, merger, and server importer now share the same
algorithm, eliminating the server's divergent hex-truncated variant.
Co-authored-by: Cursor <cursoragent@cursor.com>
Split CHAT_DB_SCHEMA into CHAT_DB_TABLES and CHAT_DB_INDEXES constants.
Electron import utils now uses core definitions, eliminating schema drift.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align searchSessions and getSessionMessages in @openchatlab/core with
Electron's richer return shape and FTS support:
- Add senderId, senderPlatformId to SessionPreviewMessage type and SQL
- Add optional ftsMatchExpression param to searchSessions for FTS path
- Electron aiTools.ts: delegates to core with FTS tokenization at edge
- Electron types.ts: re-export SessionSearchResultItem/SessionMessagesResult from core
- workerManager.ts: remove duplicate type definitions, re-export from session module
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract duplicated member list/pagination logic from electron/main/worker/query/basic.ts
and packages/server/src/http/routes/web.ts into shared core functions.
- Add getMembersPaginated with search, sort, pagination to core
- Extend MemberWithAliases type with avatar field
- Extend getMembersWithAliases with avatar column detection
- Electron basic.ts: getMemberNameHistory, getMembers, getMembersPaginated delegate to core
- Server web.ts: /_web/members and /_web/members/paginated both delegate to core
- Retain Electron-only schema migrations and member DML operations
- Add 14 unit tests covering aliases/avatar presence, pagination, search, sort, system filter
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract duplicated 12-column message SELECT, types, and row mapper into
@openchatlab/core/query/message-sql.ts as single source of truth.
Migrate Electron worker, CLI Web FetchMessageAdapter, filter.ts, and
core hydrateMessagesByIds to use shared FULL_MSG_SELECT + mapMessageRow.
Remove DbMessageRow, sanitizeMessageRow, and all inline SQL duplicates.
- Fix subscription sync not showing +xx: trigger foreground pull after
adding sessions instead of relying on background scheduler
- Fix negative message count (+-N): use messageCount directly as it
already represents net new writes after dedup
- Eliminate duplicated fullImportFile: delegate to importData() to avoid
missing fields (avatar, roles, reply_to_message_id, group_avatar)
- Fix member avatar not displaying in CLI Web: SQL alias mismatch
(avatar vs senderAvatar)
- Add skipInitialPull param to scheduler to avoid race with foreground pull
- Add @openchatlab/sync to server package.json for proper tsx watch support
Extract platform-agnostic sync logic (config, data source, discovery,
pull engine, scheduler) into @openchatlab/sync. Implement server-side
adapters (NodeFetcher, DirectImporter, NoopNotifier) and HTTP routes
(/_web/automation/*) for the CLI web UI. Adapt Pinia store with
dual-transport (IPC for Electron, HTTP for Web).
Extract 10 query functions from Electron worker to @openchatlab/core:
- message-queries: getMessageContext, getSearchMessageContext,
getConversationBetween, getMemberNameHistory, getMembersWithAliases,
executeParameterizedSql
- session-queries: getChatOverview, searchSessions, getSessionMessages,
getSessionSummaries
- filters: hasTable, hasColumn utilities
CoreDataProvider now delegates to core functions instead of throwing
NotImplementedError. All 27 Server Agent tools are functional.
Also inject segmentText (batchSegmentWithFrequency) into server
ToolExecutionContext so keyword_frequency tool works properly.
- Rename TOOL_REGISTRY to MCP_TOOL_REGISTRY (MCP Server only)
- Create AGENT_TOOL_REGISTRY with full Electron-aligned tool set (27 tools)
- MCP-only tools (list_sessions, get_session_info, search_keyword) excluded
from agent registry
- Update ai.ts to use AGENT_TOOL_REGISTRY, mcp.ts to use MCP_TOOL_REGISTRY
- Add truncation strategies for newly registered agent tools
Note: Most newly registered agent tools require CoreDataProvider method
implementations (currently NotImplementedError). See follow-up task doc.
Remove chatlab_ prefix from all tool definitions in @openchatlab/tools,
aligning with Electron-style names (get_*, search_*, etc.). The chatlab_
prefix is now only added at the MCP registration layer via MCP_TOOL_PREFIX
constant, keeping MCP namespace isolation without polluting internal names.
This ensures the AI agent uses identical tool names in both Electron and
CLI Web environments, fixing skill system compatibility and tool catalog
consistency.
Move the file-based AI logging implementation from Electron-specific
code to a shared AiLogger class in @openchatlab/node-runtime. Both
Electron and Server/CLI now use the same logger writing to
~/.chatlab/logs/ai/. Server-side agent, tool adapter, and compression
now produce the same structured log files as Electron.
Delete tokenizer/index.ts, skills/parser.ts, and assistant/parser.ts
which were single-line re-exports from @openchatlab/node-runtime.
All consumers now import directly from the shared package.
Inline createCompressionLlmAdapter and logger into the sole caller
(ipc/ai.ts) and import checkAndCompress/manualCompress directly from
@openchatlab/node-runtime. The "old API compatibility" layer was
unnecessary since there is only one internal consumer.
The 4-file preprocessor wrapper directory was entirely re-exports from
@openchatlab/node-runtime after the pipeline unification. Replace all
consumers with direct imports and add logger injection support to
PreprocessingPipelineOptions so Electron retains aiLogger integration.
Unify the duplicated preprocessing logic between Electron (wrapWithPreprocessing)
and Server (applyPreprocessing) into a single applyPreprocessingPipeline() function
in @openchatlab/node-runtime. Both platforms now delegate to the same implementation,
eliminating ~50 lines of redundant code and fixing a redundant token counting bug
in the Server version.
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