在 Web 生产模式(chatlab start)下,浏览器无法直连 chatlab.fun(CORS 限制),
导致首页底部社交链接(如小红书)及云端助手/技能预设拉取失败。
Vite dev proxy 已有对应配置,但生产 Fastify server 缺少该代理,现补全。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StatusBar model button: show model name instead of provider, click opens
settings directly (scrolls to default model section), remove dropdown
- SessionIndexSection: replace N serial pluginQuery calls with single batch
API (GET /_web/sessions/index-stats), add IntersectionObserver lazy loading
- Full-stack implementation: CLI route, Electron IPC/Worker, frontend adapters
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds a lightbulb button next to the model switcher in ChatStatusBar
that lets users choose the thinking intensity for each reasoning model,
with the choice remembered per model slot (configId:modelId).
Key changes:
- packages/core/src/ai/thinking.ts (new): per-model-type thinking level
table inspired by cherry-studio's MODEL_SUPPORTED_REASONING_EFFORT.
Covers Qwen/GLM (on/off), DeepSeek V4 (high/max), o-series
(low/medium/high), gpt-5.x (minimal–xhigh), Kimi/Doubao/Grok and a
default bucket. Exports getSupportedThinkingLevels, isReasoningModel,
getThinkingCompat.
- packages/node-runtime/src/ai/llm-builder.ts: inferReasoning now calls
getThinkingCompat() which sets compat.supportsReasoningEffort and
compat.thinkingLevelMap so pi-ai can inject the correct request param
(reasoning_effort for OpenAI/DeepSeek, enable_thinking for Qwen/GLM).
- packages/node-runtime/src/ai/agent/types.ts + core.ts: AgentCoreOptions
gains an optional thinkingLevel; core.ts:93 uses clampThinkingLevel()
(from pi-ai) to honour it safely.
- Full transparent pass-through: desktop Agent → IPC handler → preload
→ agentApi.runStream; CLI HTTP route → runServerAgent → runAgentCore.
- src/stores/prompt.ts: thinkingLevels Record<configId:modelId,
ThinkingLevel> persisted in backendPersist; getThinkingLevel/
setThinkingLevel helpers.
- src/stores/aiChat.ts: reads current model slot and passes remembered
level to both sendMessage and editCurrentRoundOnly runStream calls.
- ChatStatusBar.vue: UPopover thinking selector appears only for
reasoning models, shows only the levels that model supports, highlights
current choice, hides for non-reasoning models.
- 46 tests passing (26 new in thinking.test.ts + 20 in llm-builder).
Scope: openai-completions path only (Qwen, DeepSeek, OpenAI, xAI, Kimi,
Doubao, SiliconFlow, OpenRouter…). Anthropic/Gemini native thinking is
out of scope for this version.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the "Reasoning Model" and "Disable Thinking Mode" toggles from
the model config dialog. These switches were injecting `enable_thinking`
(a Qwen/DashScope-specific request parameter) into all providers,
causing 400 errors on OpenAI, Anthropic, Gemini, and other official
services.
Replace with automatic inference in buildPiModel:
- reasoning: true when the model's catalog capabilities include
'reasoning'; falls back to a model-name heuristic for unlisted
custom/self-hosted models (qwen3, qwq, deepseek-r, o1/o3, etc.).
- compat.thinkingFormat 'qwen': only set when reasoning is true AND
the model is from the qwen provider or the model id contains
"qwen"/"qwq". All other providers get compat: undefined and will
never receive enable_thinking.
This means official reasoning models (o3, deepseek-reasoner, etc.) now
automatically get thinkingLevel:'medium', and self-hosted Qwen3
models continue to work as before — without any manual toggle.
Behavior change: models with 'reasoning' in their catalog capabilities
now default to thinkingLevel:'medium' (previously always 'off' unless
the manual switch was enabled).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wraps each argument in double quotes when building the systemd unit's
ExecStart= line, so installs under paths containing spaces (e.g. home
directories with spaces, nvm paths) no longer cause systemd to
mis-tokenize the command and fail to start the service.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the message branching/tree system in favor of a simpler editing model:
- Default: edit a message and only regenerate the current round's AI response
- Optional: overwrite all subsequent messages when editing
Key changes:
- Backend: add updateMessageContent, deleteAndRelinkMessage, insertMessageAfter,
deleteMessagesFrom, forkConversation; remove createMessageBranch, switchMessageBranch
- Platform layer: add IPC/HTTP handlers for new methods across Electron and CLI
- Frontend store: refactor editMessageAndRegenerate with two modes
- UI: replace branch navigation with "overwrite subsequent" checkbox,
auto-resizing edit textarea
- Fix insertMessageAfter missing debug_context from pendingDebugContextMap
- Update tests to cover new editing methods
Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace 6-subcommand daemon group with simpler top-level design:
chatlab start --daemon install as system service (launchd/systemd)
chatlab stop uninstall service and disable auto-start
chatlab status show resident service status
- Drop temporary background process layer; daemon = system service only
- Store port/host meta in ~/.chatlab/daemon.json for status display
- Fix --no-open description: Web UI still runs, only browser is suppressed
- Update quick-start docs (cn/en/tw) to reflect new commands
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix apiKeySet reporting false positives for local no-key configs by
resolving the actual key via resolveApiKey and excluding placeholders
- Prevent editing a config from reusing an incompatible API key when
provider or connection mode has changed
- Pass summary strategy to single-session generation for consistency
Co-authored-by: Cursor <cursoragent@cursor.com>
Support two summary strategies (brief/standard) for AI-generated session
summaries. Users can choose their preferred detail level in Settings > AI >
Session Index. Standard (default) produces richer summaries covering people,
events, and key details.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Implement generateSummary/checkCanGenerateSummary in FetchSessionIndexAdapter
via existing HTTP endpoints instead of stubs that returned instantly
- Add checkCanGenerate service function in summary-service.ts
- Add POST /_web/sessions/:id/summaries/check-can-generate route
The page froze because stub methods returned synchronously, causing a tight
loop of hundreds of Vue reactive updates without any network delay.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add apiKeySet field to GET /_web/ai/llm/configs response
- Allow saving in edit mode without re-entering API key when key already set
- Skip API key validation on save when editing with existing key unchanged
- Show edit-mode placeholder hint on API Key input
- Add missing customModels/disableThinking/isReasoningModel to CLI route types
Co-authored-by: Cursor <cursoragent@cursor.com>
Restructure import components into dedicated import/ directory with new
ImportTabSelector, ApiImportCard, and AutoSyncCard components. Add demo
import, entry animation, and tutorial links to home page. Update i18n
translations for all locales. Add chatApi init guard in registry and
defaultPath to DataDirInfo preload interface.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace @mariozechner/pi-ai and pi-agent-core with @earendil-works scope
- Adapt to AgentTool.execute() params type change (Record → unknown)
- Migrate Agent constructor to use initialState for systemPrompt/tools/messages
- Update onPayload callback signature to include model parameter
- Filter finalMessages with isPiMessage() for new AgentMessage union type
- Use state.errorMessage instead of deprecated state.error
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add "type": "module" to all workspace package.json files
- Replace __dirname with import.meta.url-based resolveCliPath() helper
- Convert stream-json/stream-chain CJS named imports to default + destructure
for ESM compatibility
- Use createRequire() for @node-rs/jieba native module loading in ESM context
Co-authored-by: Cursor <cursoragent@cursor.com>
The unified directory migration (needsUnifiedDirMigration) would
re-trigger after a user changed their data directory, overwriting
config.toml back to the old Electron path.
Root cause: no persistent flag tracking whether the migration had
already completed. If old databases remained (cleanup failure or
timing), it would re-detect and re-run.
Fix:
- Add `electron_migration_done` flag to config.toml [data] section
- Set flag after migrateToUnifiedDirs succeeds and in setCustomDataDir
- Check flag at start of needsUnifiedDirMigration to skip if done
- Add "migrate to default" button when data is at non-default path
- Fix NLP directory path: move from userDataDir to systemDir (~/.chatlab/nlp)
Co-authored-by: Cursor <cursoragent@cursor.com>
CLI Web's Settings > Storage Management was completely non-functional
(stub cacheApi returning empty data). This adds real HTTP endpoints
and wires the frontend to use them.
Backend (cache.ts):
- GET /_web/cache/info — scan databases/ai/cache/logs directory sizes
- POST /_web/cache/clear — clear cache or logs directory (allowlist)
- POST /_web/cache/open-dir — open directory in system file manager
- GET /_web/cache/data-dir — return current data directory path
- GET /_web/cache/latest-import-log — find latest import log file
Frontend:
- Replace cacheApi stubs with real HTTP calls in registry.ts
- Hide data directory migration UI in CLI Web (IS_ELECTRON guard)
- Add "Open Root Dir" button for ~/.chatlab/ in header area
Co-authored-by: Cursor <cursoragent@cursor.com>
After pull-sync imports new messages, generate session index incrementally
instead of full regeneration. This preserves existing sessions and their
summaries, only processing newly imported (unindexed) messages.
Adds generateIncremental method across the full stack:
- core: already had generateIncrementalSessionIndex
- node-runtime: expose generateIncrementalIndex
- CLI: add POST /_web/sessions/:id/generate-incremental-index route
- Electron: add session:generateIncremental IPC handler
- Frontend: add generateIncremental to SessionIndexAdapter interface
- Show confirmation dialog with two choices: keep data or delete imported chats
- Backend supports deleteData param across CLI routes and Electron IPC
- DataSourceManager.removeSession now returns the removed session info
- Add i18n strings for the delete confirmation dialog (zh-CN/en-US/ja-JP/zh-TW)
- Add multi-retry (3 attempts, 2s/3s/5s delays) for cold-start empty responses
- Remove unused offset/end parameters from fetch URL construction
- Fix Accept header to application/json only (avoids content negotiation issues)
- Add PullProgress type and progress tracking in PullEngine
- Export progress via getProgress() for frontend polling
sessionExists() previously only checked file existence, causing pull sync
to enter incremental import path on invalid/empty DB files. Now validates
that the message table exists, and maps schema errors to needFullResync
as a safety fallback.
When a pullAllSessions was already running, the reloadTimer triggered
by adding sessions would be skipped by the source-level concurrency
lock, causing newly added sessions to wait until the next scheduled
interval (up to 60 minutes) before syncing.
Fix: trigger individual per-session pulls via triggerPull(sourceId,
sessionId) which bypasses the source-level lock, ensuring immediate
sync for newly added sessions regardless of concurrent pulls.
Also includes: connection pre-check on data source add, removal of
duplicate "browse remote sessions" button, hiding "other" session
types, and full resync fix for deleted local sessions.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared business logic from monolithic web.ts into reusable
services in @openchatlab/node-runtime (session, member, index, summary,
export), using SessionRuntimeAdapter to abstract platform differences.
Split web.ts into 12 domain-specific route modules under routes/web/.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add directory import support for multi-file formats (e.g. shuakami chunked-jsonl).
Users can select or drag-and-drop a directory via the "Import Folder" checkbox.
- Add `findEntryFileInDirectory()` to parser layer for format-agnostic entry file discovery
- Add `importDirectory` method to ImportAdapter interface
- Implement FetchImportAdapter.importDirectory with multi-file FormData upload
- Add `POST /_web/import-directory` CLI route with directory reconstruction
- Add `chat:importDirectory` IPC handler in Electron main process
- Enhance FileDropZone with `directory` prop and drag-and-drop directory support
- Refactor FetchImportAdapter to extract reusable `consumeSseStream` helper
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
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>