365 Commits
Author SHA1 Message Date
diguaandClaude Opus 4.8 7316a09823 fix(ai): fix stale overview and analysis caches after incremental import
Overview cache had no self-invalidation: once written it would return
stale lastMessageTs forever if the postImportHook failed silently.
Analysis query cache (17 chart/stats queries) was never cleared on
import, so charts showed stale data until the user triggered a manual
invalidate.

Add MAX(message.id) fingerprint to OverviewCache and introduce
getValidatedOverviewCache(), which recomputes automatically when new
messages are inserted. Update resolveOverview() in the Electron worker
to use this instead of bare getCache().

In both postImportHooks (incrementalImport, streamImport) also call
deleteSessionCache against the query cache directory so chart data is
fresh after every import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:36:46 +08:00
diguaandClaude Sonnet 4.6 76acbabcc2 fix(ai): preserve tool-only turns in history replay and fix export regressions
- Include assistant messages with tool content blocks in getHistoryForAgent
  even when text content is empty, so multi-turn tool results are not lost
- Append Date.now() timestamp to export filenames to prevent silent overwrites
- Pass 600s timeout to exportFilterResultToFile worker call to match the
  actual upper bound of large-session exports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 22:18:20 +08:00
diguaandCursor 39700b95b7 feat(ai): display cache hit/miss token usage in chat status bar
Extend TokenUsage types across the full data pipeline to include
cacheReadTokens and cacheWriteTokens. The ChatStatusBar tooltip now
shows cache hit information when available. This is a generic
implementation that works with any API provider reporting cache metrics,
not limited to DeepSeek.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 22:18:20 +08:00
diguaandCursor 2b6aed52c9 fix(ai): replay reasoning_content for DeepSeek tool-call turns
DeepSeek API requires reasoning_content to be retransmitted on assistant
messages that precede tool results. Add ReplayOptions to history replay
so thinking blocks are included as ThinkingContent when the model uses
DeepSeek-format thinking, preventing 400 errors in multi-turn tool-call
conversations.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 22:18:20 +08:00
digua 42649d835d fix(ai): count replayed tool results in compression token accounting 2026-06-10 22:18:20 +08:00
diguaandClaude Fable 5 9c7778f7e3 fix(ai): keep rawMessages out of LLM-facing tool result text
Message-retrieval tools mirror rawMessages inside data, and the
preprocessing pipeline rendered that array into the tool result text as
"rawMessages: [object Object], [object Object], ..." — roughly 1.7KB of
junk per call (~400-500 wasted tokens) that also polluted the truncated
result text persisted for history replay.

- formatToolResultAsText skips the rawMessages key (raw message objects
  are already rendered via the formatted messages list)
- CLI tool adapter strips rawMessages from extraDetails before invoking
  the pipeline, matching the existing desktop adapter behavior

Regression tests fail without the fix at both the format layer and the
CLI adapter layer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 22:18:20 +08:00
diguaandClaude Fable 5 ad96dca7ac fix(ai): persist and replay tool calls in chat history
Multi-turn agent conversations rebuilt history as plain text, dropping
all tool calls and results. Later turns could not see what the model
had actually retrieved, so it imitated tool-free answers and
hallucinated chat records instead of re-querying (worse after
incremental imports and in long conversations).

- Persist provider-issued toolCallId, truncated result text and isError
  on tool content blocks (written by the shared frontend store from
  stream chunks; pi-agent-core events already carry these fields)
- Replay persisted tool blocks as real toolCall/toolResult message
  pairs in toPiHistoryMessages: stable ids keep requests byte-identical
  across turns (prompt cache friendly), unfinished blocks are skipped
  so no toolCall is left unanswered, errors map to isError, results are
  capped at 4000 chars, runtime-injected underscore params are stripped
- Legacy messages without persisted tool data fall back to the old
  plain-text replay; no migration needed

Verified on a real CLI web session: 25+ replayed tool pairs accepted by
the provider across turns with zero API errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 22:18:20 +08:00
digua 6ec736c05a feat(export): simplify message export and support multiple formats 2026-06-10 22:18:20 +08:00
diguaandClaude Sonnet 4.6 56ad4a9578 feat(ai): prefer high-level tools over raw SQL when rendering charts
Add optional `rows` parameter to render_chart so AI can pass pre-fetched
data arrays (e.g. from get_time_stats, member_stats) directly instead of
rewriting SQL from scratch. Update chart capability prompts and all locale
files to guide AI to check high-level tools first before falling back to SQL.
Remove debug console.debug log from agent-core thinkingLevel resolution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 00:15:25 +08:00
digua 1290ae6569 feat(ai): add chart auto mode preference 2026-06-10 00:15:25 +08:00
digua 84a3b13017 feat(ai): improve analysis and chart behavior 2026-06-10 00:15:25 +08:00
digua 6594e586d3 feat(ai): guide planner with startup context 2026-06-10 00:15:25 +08:00
digua 718735a748 feat(ai): build extended data snapshots 2026-06-10 00:15:25 +08:00
digua 383af0b13a feat(ai): enrich session data snapshot prompt 2026-06-10 00:15:25 +08:00
digua 0801ccf940 feat(ai): stream structured analysis plans 2026-06-10 00:15:25 +08:00
digua 9ad012680d feat(ai): integrate chart planning support 2026-06-10 00:15:25 +08:00
digua 6f54dcc62b feat(cli): expose full agent stream events 2026-06-10 00:15:25 +08:00
digua 2d413f4202 feat(ai): render analysis plan blocks 2026-06-10 00:15:25 +08:00
digua b61974136e feat(ai): emit analysis plan guidance 2026-06-10 00:15:25 +08:00
digua 645f0c6789 feat(ai): add analysis planner core 2026-06-10 00:15:25 +08:00
digua 8ca3f0be8b feat(ai): add llm fallback for shadow routing 2026-06-10 00:15:25 +08:00
digua 712d5212c2 feat(ai): log shadow agent route decisions 2026-06-10 00:15:25 +08:00
digua bd19512f92 test(ai): add agent routing evaluation set 2026-06-10 00:15:25 +08:00
diguaandClaude Opus 4.8 7af7fb1f10 fix: persist thinking level selection across restarts
thinkingLevels was excluded from the mergeDefaults whitelist in
PreferencesManager, so user choices were silently dropped on every
save/load cycle and never written to preferences.json.

Add the field to the Preferences interface, DEFAULTS, and mergeDefaults
so the existing backendPersist → savePreferences pipeline can write and
restore it correctly. Adds regression tests covering the save/load
round-trip, multi-save merge, and legacy-file backward compatibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:15:25 +08:00
Yui d70ab042ce fix(import): normalize jsonl timestamps for incremental import 2026-06-09 09:54:50 +08:00
diguaandClaude Sonnet 4.6 e7561e224e fix: write data directory compatibility gate on migration and import
Harden the startup migration path: fail fast and show an error dialog if
schema migration cannot complete. Repair the gate retroactively for
databases that were already migrated before the gate system existed.

Raise the gate after every import path (full import, incremental import,
stream import) and after opening any database that already carries a
current-segment schema. Centralise gate-raising inside DatabaseManager so
call sites cannot accidentally skip it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 10:14:42 +08:00
diguaandClaude Sonnet 4.6 1c7781d85b fix: establish runtime identity for data directory gates
Use the packaged app version (not Electron's dev-mode 0.0.0) across all
compatibility checks. Make DatabaseManager require a RuntimeIdentity so
every gate write carries a verified version and kind.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 10:14:42 +08:00
digua c518a4d968 feat(runtime): gate database access by data dir compatibility 2026-06-08 10:14:42 +08:00
digua c0885ba053 feat(runtime): add data directory compatibility gate 2026-06-08 10:14:42 +08:00
diguaandClaude Sonnet 4.6 62ad3c6248 fix: normalize legacy tool names and harden cli chat command
- Add normalizeBuiltinToolNames() to map old session-based tool names
  (search_sessions, get_session_messages, get_session_summaries) to
  current segment names; applied in assistant parser, agent-stream-runner,
  and desktop tool-filter so existing assistant configs keep working
- Propagate agent stream error chunks as thrown Errors in CLI chat command
- Pass assistantId through ResolvedAIChatTarget to agent stream
- Preserve legacy chat_session rows in v6 migration when segment table
  already exists (INSERT OR IGNORE + DROP); revert v3/v5 DDL to chat_session
- Rename session_id alias to segment_id in relationship query
- Add tests: assistant-parser normalization, tool-filter legacy names,
  agent-stream-runner legacy allowlist, REPL error resilience, migration paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 20:27:27 +08:00
digua c2295e317f refactor: align ai chat and segment identifiers 2026-06-07 20:27:27 +08:00
digua 31f3c610ce fix(ai): increase default tool rounds 2026-06-05 11:12:42 +08:00
digua 9b1f359699 fix(ai): register chart tool for electron auto skills 2026-06-05 11:12:42 +08:00
digua f825c0734b refactor(ai): deduplicate skill menu and manager 2026-06-05 11:12:42 +08:00
digua 296573ed5c refactor(ai): centralize chart runtime policy 2026-06-05 11:12:42 +08:00
CEQ151 15470444e5 feat(ai): improve chart runtime UX 2026-06-05 11:12:42 +08:00
CEQ151 782f67ba63 feat(ai): add slash-activated chart runtime with EChart init fix
- Add ChartSpec validation and chart data normalization (bar/line/pie/heatmap)
- Add render_chart analysis tool with SQL safety (readonly check, outer LIMIT wrapper, CTE write rejection)
- Add chart content block type and ChartBlockRenderer for inline chart rendering
- Add slash menu chart capability activation (single-use per message)
- Add chart payload transport through Electron and CLI Web tool adapters
- Add error block handling for render-only chart tool failures
- Add chart payload persistence through content_blocks JSON storage
- Fix EChart.vue init timing: defer initChart() to nextTick so container
  layout completes before ECharts reads width (was rendering 92px canvases)
- Add comprehensive test coverage: core, integration, agent flow, frontend, smoke
- Add i18n for render_chart and get_schema in 4 locales
- Add project handoff documentation
2026-06-05 11:12:42 +08:00
digua 8b2e6de479 fix: harden preferences and update notice checks 2026-06-04 14:37:12 +08:00
digua 2a44f8b69e fix(preferences): preserve legacy builtin masking overrides 2026-06-04 14:37:12 +08:00
digua 218c3c9ee9 feat(ai): group desensitize rules 2026-06-04 14:37:12 +08:00
digua 0c82a300e8 feat(ai): enable preprocess defaults 2026-06-04 14:37:12 +08:00
CEQ151andClaude Opus 4.8 185e88ba3e fix(import): refresh sidebar message count after incremental import
After incremental import via Electron IPC, the sidebar session list
showed stale message counts. Three contributing gaps:

1. The chat:incrementalImport IPC handler did not notify the renderer
   to refresh the session list (unlike the API route which calls
   notifySessionListChanged). Added win.webContents.send('api:importCompleted')
   to trigger the Sidebar's existing onImportCompleted listener.

2. The postImportHook in worker adapters was async (due to dynamic
   import()) but the caller did not await it, causing a race condition
   where loadSessions() could read stale overview cache before
   computeAndSetOverviewCache() finished. Converted to static import
   making the hook synchronous, and updated the type to void | Promise<void>
   with await at call sites in both incremental-importer and
   streaming-importer.

3. The @imported handlers on group-chat and private-chat pages only
   refreshed local analysis data without calling sessionStore.loadSessions().
   Added the call so the sidebar updates immediately on the client side.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 10:20:28 +08:00
digua 6e016837bc fix(data): harden directory and database migrations 2026-06-03 00:46:34 +08:00
digua 42837278fa fix(data): migrate legacy chat database columns before reads 2026-06-03 00:46:34 +08:00
digua e68ce77266 feat(data): support CLI web data directory migration 2026-06-03 00:46:34 +08:00
diguaandClaude Opus 4.8 2f8591fb8c perf(build): minify main process and lazy-load tiktoken rank table
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>
2026-06-03 00:46:34 +08:00
diguaandCursor ae90db63a5 feat(merge): migrate merge routes from IPC to shared HTTP package
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>
2026-06-02 15:41:21 +08:00
diguaandCursor 3ab162162b feat(ai): migrate AI CRUD routes to shared package and establish frontend service layer
Extract LLM config, assistant, skill, conversation and summary CRUD
routes from CLI-specific ai.ts into @openchatlab/http-routes, enabling
Electron Internal Server to serve AI endpoints via shared routes.

Frontend service layer:
- Create src/services/{llm,assistant,skill}/ with typed adapters
- Migrate all window.llmApi/assistantApi/skillApi calls to service layer
- Clean up Electron preload: remove CRUD bridges, keep only streaming IPC

Shared AI routes (packages/http-routes):
- ai-llm: config CRUD, provider registry, model catalog, custom store
- ai-assistants: CRUD, import/reimport, tool catalog
- ai-skills: CRUD, import/reimport, builtin catalog
- ai-conversations: conversation/message/debug CRUD
- ai-summaries: summary generation with LLM config injection
- Add requireAi fail-fast option to registerSharedRoutes

Backend improvements:
- Extract CustomProviderStore/CustomModelStore to node-runtime
- Fix authProfile persistence (onApiKeyCreated returns profileName)
- Reuse AI manager singletons in Electron (avoid dual instances)
- Prevent plaintext API key leakage in addConfig response
- Add authProfile regression tests

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 15:41:21 +08:00
diguaandClaude Sonnet 4.6 0a916e4b17 fix(types): use AIMessageRole type for message role params & strengthen conversation test assertion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:56:08 +08:00
diguaandClaude Sonnet 4.6 334544880f fix(ai): enable thinking for effort-based models when 'auto' is selected
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>
2026-05-31 10:28:29 +08:00