Add scripts/changelog-to-markdown.mjs that renders changelogs/{cn,en,tw,ja}.json
into readable Markdown files. Runs idempotently; output passes Prettier without
changes.
Wire the script into the sync-changelog skill (step 4, before the release commit)
and update commit_release_changelogs.sh to include the four .md files in the
release: v<version> commit, with a preflight guard that errors if the md files
are missing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enable esbuild minification for main/preload (was renderer-only by default),
reducing main process bundle by ~55%. Extract cl100k_base BPE rank table out
of the synchronous startup path via dynamic import — it now lands in its own
chunk and is loaded on first agent run instead of at app startup.
Also fix Windows CI step to use PowerShell syntax for version injection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In edit mode the API key is intentionally not returned to the client,
causing both buttons to be disabled. Now the backend resolves the stored
key by configId when the client sends an empty key, and the frontend
passes configId for the reuse case so both buttons work without exposing
the key.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrate conversation/message CRUD and debug queries from IPC to HTTP
by having ElectronAIAdapter extend FetchAIAdapter. Remove 18 dead
handlers (14 CRUD + 3 debug + 1 compressContext) from ipc/ai.ts and
slim down preload AiApi to IPC-only methods (filtering, export, tool
testing, desensitize rules, native shell).
Co-authored-by: Cursor <cursoragent@cursor.com>
Unify the merge workflow (parse/conflicts/execute/clear/export-for-merge)
across CLI, Electron, and web-serve into a single set of shared routes in
@openchatlab/http-routes.
Key changes:
- Move MergeSessionCache from CLI to node-runtime shared package
- Add dual-mode parse route (multipart upload for web, JSON filePath for Electron)
- Integrate merge routes into Electron internal API server with @fastify/multipart
- Create installMergeShims() with filePath-to-handle mapping for frontend
- Remove Electron merge IPC handlers, merger module, and preload bridges (~790 lines deleted)
- Address CodeX review: CHAT_DB_TABLES in openDatabase, no chatApi patching,
outputName sanitization, resp.ok checks, @fastify/multipart in desktop deps
Co-authored-by: Cursor <cursoragent@cursor.com>
Both shims were installed only in web-serve mode. Chart modules
already use useDataService() directly and NLP components use
HTTP helpers, so the window.chatApi.pluginQuery/pluginCompute/
getMemberActivity/getAvailableYears and window.nlpApi shims
have zero remaining callers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove business IPC handlers/preload APIs that are now served by
Internal HTTP Server: preferences, NLP, data queries, message search,
analytics, members, SQL lab. Migrate chart modules from window.*Api
direct calls to useDataService() and NLP components to HTTP.
Make Internal Server a hard dependency, delete ElectronDataAdapter/
MessageAdapter/PreferencesAdapter and IPC fallback path. Retain IPC
only for native capabilities: file import, AI/LLM, session-index
summaries, agent streaming.
Co-authored-by: Cursor <cursoragent@cursor.com>
Improve auth hook security: restructure URL classification to fix
/_web/* bypass when requireAuth is active, use HMAC-SHA256 for constant-
time token comparison, remove dead webModeEnabled code.
Improve frontend auth flow: extract initializeApp() with watch-based
re-init after login, add getToken callback for real-time store reads,
unify SSE 401 handling via fetchWithAuth, switch default storage to
sessionStorage, add router guard consuming meta.public.
Add "Remember this device" checkbox on login page with dual
localStorage/sessionStorage strategy and safety warning.
Add 23 unit tests for auth hook matrix and http client.
Co-authored-by: Cursor <cursoragent@cursor.com>
Complete the frontend side of the --require-auth feature:
- Add configureHttpClient() with token injection and 401 handling
- Auto-inject Authorization header in all HTTP helpers and SSE
- Replace 32+ direct fetch('/_web/...') calls with fetchWithAuth
- Add login page for Server mode (token input + validation)
- Add auth store for token persistence (localStorage)
- Add logout button in Settings > About (Server mode only)
- Add i18n translations for login flow (zh-CN/en-US/zh-TW/ja-JP)
- Fix login redirect loop caused by concurrent 401 responses
Co-authored-by: Cursor <cursoragent@cursor.com>
When enabled, all /_web/* routes require Bearer token authentication,
same as /api/v1/*. This hardens security for server/headless/public
deployments where the same-origin assumption doesn't hold.
- Add `require_auth` field to config schema (default: false)
- Add `setRequireAuth()` to auth hook, conditionally enforce auth on /_web/*
- Support `--require-auth` in both `chatlab start` and `--daemon` mode
- Read from config.api.require_auth or CLI flag override
Co-authored-by: Cursor <cursoragent@cursor.com>
在 Web 生产模式(chatlab start)下,浏览器无法直连 chatlab.fun(CORS 限制),
导致首页底部社交链接(如小红书)及云端助手/技能预设拉取失败。
Vite dev proxy 已有对应配置,但生产 Fastify server 缺少该代理,现补全。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the user selects 'auto' thinking level for an effort-based reasoning
model (Kimi, Doubao) that has thinkingLevelMap but no thinkingFormat,
the previous code fell through to `return undefined`, causing pi-agent-core
to treat it as 'off' and send reasoning_effort:'none' to the API —
silently disabling thinking.
pi-ai's clampThinkingLevel only covers EXTENDED_THINKING_LEVELS (no 'auto'),
so 'auto' cannot be forwarded verbatim. Return 'high' instead, matching
the behavior for thinkingFormat models, to at least enable reasoning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Custom models auto-saved by ensureCustomProvidersAndModels carry only
capabilities:['chat'], so inferReasoning was returning reasoning=false
even for models like qwen3:8b whose names match the reasoning heuristic.
The UI still showed thinking levels via getSupportedThinkingLevels, but
selections were silently ignored by the backend.
Fix: for non-builtin (builtin=false) model definitions, also apply the
name/provider heuristic as a fallback alongside the capabilities check.
Builtin catalog models remain authoritative.
Adds a regression test covering the exact findModelFn shape produced by
ensureCustomProvidersAndModels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The supportedThinkingLevels, modelContextWindow, and model name display
were reading from config.model (static config value) instead of the
slot's modelId (actual user-selected model). This caused the thinking
selector to show/hide incorrectly when users switched models.
Also fix HTTP utils sending Content-Type: application/json with empty
body when body is null (e.g. setFastModel(null)), which caused Fastify
to return 400.
Co-authored-by: Cursor <cursoragent@cursor.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>
- Add 'default' option (no reasoning params sent, model uses native behavior)
- Add 'auto' option for models that natively support it (deepseek_hybrid, kimi, doubao)
- Expand model recognition with regex coverage for 15+ model families
- Add Gemini 2.5+ thinking level support via native Google provider
- Simplify deepseek_hybrid options to ['default', 'off', 'auto'] aligned with Cherry Studio
- Use primary theme color for thinking selector instead of hardcoded blue
- Update docs with thinking control architecture section
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>
Split the monolithic MessageView into two focused modules:
- TypeAnalysisView: type distribution, text depth analysis, length distributions
- TimeAnalysisView: hourly/weekday/monthly/yearly distributions, member trend, heatmap, calendar
Each tab has a redesigned narrative profile card with rich insights:
- TypeProfileCard: date range, total messages, non-text breakdown pills, 4 metric cards
- TimeProfileCard: active days narrative, time personality labels (multi-dimensional), 4 metric cards
Also: reduce responseLatency font size in RelationshipView for better readability.
Co-authored-by: Cursor <cursoragent@cursor.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>
Warn users that regeneration will clear all existing AI summaries.
Uses the same UModal pattern as other confirmation dialogs in settings.
Co-authored-by: Cursor <cursoragent@cursor.com>
- insertMessageAfter: update active_message_id when appending to tail
so the new reply is on the active path after reload
- editAndOverwriteAll: add hasConfig + abort checks before
deleteMessagesFrom to prevent data loss when LLM is unconfigured or
a concurrent request starts
- editCurrentRoundOnly: defer all DB mutations until after stream
completes; on abort/error only in-memory state is restored
- sync in-memory parentId after relinking so follow-up edits send the
correct historyLeafMessageId
Co-authored-by: Cursor <cursoragent@cursor.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>
- Move quick-start.md to usage/ across cn/en/tw
- Switch locale index pages from home to doc layout
- Add Baidu analytics script and adjust nav doc links
Co-Authored-By: Claude Opus 4.8 <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>
Previously clicking stop would only take effect after the current LLM
request finished. Now the UI state updates instantly while the in-flight
request completes silently in the background.
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>