- supervisor loop keeps the OBv11 WebSocket alive, reconnecting with
exponential backoff (1s→30s) when the connection drops; Start no
longer fails permanently when NapCat is temporarily unreachable
- Send enforces MinGap between messages (from SendOptions or the
adapter's send_interval_ms setting, now wired from the admin panel)
- parseMessage no longer panics on events missing sender/time fields
- tests: reconnect after drop, rate limit enforcement, malformed events,
send while disconnected
- orchestrator: Tracer interface with spans for intent analysis, persona,
subsession dispatch, context build, LLM streaming and each tool call;
traces finish when the stream completes — the admin Trace waterfall
finally receives data
- background: Thinker generates real LLM thoughts (post-chat + silence
triggers, recent history as context, empty/overlong outputs discarded);
thoughts broadcast to web clients as 'thinking' WS messages; pipeline
postChatHook triggers post-chat thinking after each round
- memory: ApplyDecayAll + ListUserIDs with a 6h decay/purge cron in main
(was implemented but never scheduled)
- repository: optional pgvector embedding column (migration with
CREATE EXTENSION guarded by main), SearchByVector cosine search,
vector-enabled insert path
- service: SetEmbedder — memories are embedded on Add; Search merges
vector hits with n-gram keyword matches (vector first, deduped)
- embedder was already implemented (OpenAI-compatible API + n-gram FNV
fallback) but never instantiated; main now wires it from
EMBEDDING_API_URL/EMBEDDING_MODEL config
- store: new chat_sessions/chat_messages tables with CRUD, ILIKE message
search, and auto-titling from the first user message
- context: optional Persister on the sliding-window store — messages are
saved asynchronously on Add, sessions not in memory are restored from
the database on Get
- orchestrator: ProcessInput/FinishRound now carry userID so persisted
messages have an owner
- api: real session handlers (list/create/delete), message history and
clear endpoints, working message search and three-format export
(markdown/txt/json), fixing the short-ID panic in export
- main: migration, store wiring, ctxStore persister adapter
Tests: chat store handler lifecycle (fake store), context persister
save/restore, stub auth login flow updated.
The bare 'cyrene' .gitignore pattern matched the cmd/cyrene directory,
so trace_adapter.go was never committed and a fresh clone failed to
build (undefined: traceAdapter). Scope the pattern to the root binary
and add the missing file.
The WS hub and platform bridge now call Pipeline.FinishRound when the
response stream completes, recording the assistant reply and invoking
the LLM memory extractor asynchronously.
- llm: ChatStreamWithTools sends OpenAI-format tools; streamed tool_call
deltas are reassembled by index into complete ToolCalls on the Done token
- model: LLMMessage gains ToolCallID/ToolCalls; StreamToken gains ToolCalls
- orchestrator: new ToolExecutor interface and streamWithTools loop —
execute requested tools, feed results back, up to 3 rounds, final round
forces a plain text answer; normal chats still stream token by token.
Also adds Pipeline.FinishRound (assistant reply recording + post-round
hook), wired to transports in the next commit.
- main: inject the plugin registry via toolExecAdapter
Includes an httptest-based test covering delta reassembly, tool
execution, and result feedback.
Frontend sent remote_url/bot_token/webhook_url while the backend reads
ws_url/token/listen_addr, so QQ adapters could never connect. Also add
the missing bot_qq field and wire the webhook adapter's APIKey.
- POST /api/v1/admin/restart: admin-only restart (forks new process)
- Fix: MemUserStore now uses cfg.AdminUsername instead of hardcoded 'admin'
- RestartFunc injected by main.go for clean process fork + exit
- Context merged into single system message with clear delimiters
- Prefix: 「以下是系统上下文,不是用户说的话」
- Memory → 【相关记忆】, IoT → 【设备状态】
- Filter empty memories, skip raw intent data
- Persona rule: context markers are internal data, not user input
- Persona injector: concise natural language rules, not raw YAML
- Context builder: skip empty memory, skip raw intent data
- Prompt reduced from 2000+ tokens to ~500 tokens
- LLM response quality improved (natural tone, no confusion)
- llm.Adapter: SetCallLogger hook for recording every API call
- llm.CallLogger: ring-buffer with Recent+Stats for admin API
- main.go: wire DeepSeek adapter to CallLogger
- Fix: single CallLogger instance for both recording and display
- Metrics: model name, duration, success/fail, tokens
- All 16 packages pass
- memory/extractor: async LLM-based memory extraction from chat history
- Parses JSON lines from LLM response as memory entries
- Wire into orchestrator pipeline via PostProcess hook
- Non-blocking: runs in goroutine with 30s timeout
- Automatic dedup via existing keyword overlap logic
- BridgeConfigStore: file-backed JSON config (configs/bridge.json)
- Auto-creates config file only when user saves via admin panel
- Hot-reload: watches config file every 5s, reloads adapters live
- Admin API: GET/PUT/DELETE /api/v1/admin/bridge/config/:name
- Frontend: full CRUD page with platform type selector + toggle
- Removed bridge env vars from .env.example
- Backend: trace.Logger ring-buffer for request chain events
- Backend: perf.Monitor with runtime.MemStats sampling every 10s
- Backend: /api/v1/admin/trace and /api/v1/admin/perf endpoints
- Frontend: TracePage — timeline with hop icons and status dots
- Frontend: PerformancePage — memory/GC bar charts + live stats
- Admin nav expanded to 11 items