ReAct loop was terminating early when the model returned:
[Agent ran through N steps...]
{"thought": "...", "action": {...}}
The extract_json function only checked the string start or code fences.
Now scans for { or [ at non-word positions and uses depth-counting
to strip trailing text, allowing JSON buried anywhere in the response.
service.rs: Replace per-message Lua+DB seq with simple INCR, only
reconcile DB every 1000 messages (99.9% queries eliminated).
storage.rs: Replace N+1 GET loop with single MGET for both
get_user_sessions and get_workspace_sessions (N+1 → 2 roundtrips).
TypingStart/TypingStop actions are intercepted in ws_universal.rs so
this match arm is never reached, but we need a safe fallback instead
of std::hint::unreachable_unchecked().
RoomConnectionManager now holds a cache field and typing_inner broadcast
map. broadcast_typing() persists start/stop to Redis (SETEX 10s / DEL)
and broadcasts via tokio channel. ws_universal.rs handles TypingStart/
TypingStop actions and streams typing events to WS clients.
Add TypingEvent struct in queue::types for broadcast-based typing
indicators, and TypingStart/TypingStop variants in RoomEventType for
WebSocket event dispatch.
- Delete admin metrics dashboard page (admin/metrics/page.tsx)
- Delete LineChart component (used only by metrics)
- Remove "指标监控" nav link from Sidebar
- Remove getMetrics/exportMetricsCsv from admin-rpc.ts client
- Remove /api/admin/metrics and /api/admin/metrics/export HTTP routes
from adminrpc (was also leaking metrics via HTTP)
- Remove metrics RPC methods (get_metrics, export_metrics_csv) from
adminrpc gRPC server and their helper functions
- Remove spawn_redis_metrics_flusher from app/main.rs
- Remove redis_metrics module from observability crate
- Remove redis/deadpool-redis deps from observability Cargo.toml
Both init_tracing_subscriber() and init_otlp() were calling try_init()
on the global tracing dispatcher, causing "global default trace dispatcher
has already been set" at runtime when APP_OTEL_ENABLED=true.
Fix: simplify the API so init_tracing_subscriber() never installs the
subscriber — it either calls try_init() immediately (non-OTLP mode) or
returns without installing (OTLP mode, defer=true). init_otlp() now
builds the complete subscriber stack (registry + env_filter + fmt_layer +
otel_layer) and calls try_init() once.
init_tracing_subscriber() signature: (level, defer) → ()
init_otlp() signature: (endpoint, service_name, _, log_level) → Result
The fmt layer is replicated inside init_otlp() for the OTLP path.
Users who accepted a project invitation could not see that project
on their /user/{username} page because get_user_projects only queried
projects where created_by == user_uid, ignoring project_members entries.
Now unions created_projects and member_projects with privacy filtering.
Column::Project was used twice instead of Column::User in the query,
causing the filter to be project_id = X AND project_id = user_uid
which never matches.
- session_manager/manager.rs: remove slog::Logger field, update new()
and with_config() to remove log parameter
- email/lib.rs: remove slog::Logger from AppEmail::init()
- rpc/admin/server.rs: remove slog::Logger from serve() and spawn(),
replace with tracing::info!/error!
- Remove all use slog::* imports and log: slog::Logger fields
- ssh/handle.rs: replace slog macro chains with tracing::{info!, warn!,
error!, debug!}; remove log field from GitSshHandle
- ssh/authz.rs, ssh/mod.rs, ssh/server.rs: remove slog Logger fields
- http/: auth.rs, handler.rs, mod.rs, routes.rs: remove slog usage
- hook/: pool worker, sync modules, webhook_dispatch.rs: remove slog
- Remove all use slog::* imports and log: slog::Logger fields
- Replace slog macros with tracing::{info!, warn!, error!, debug!}
- metrics.rs: upgrade metrics 0.21→0.22, remove register_*! macros,
use functional API: metrics::gauge!(), metrics::counter!(),
metrics::histogram!(), metrics::describe_gauge!() etc.
- RoomMetrics: all fields now use functional metrics API, dynamic
room_id labels passed as owned String to avoid lifetime issues
- RoomService: remove pub log: slog::Logger field
- connection.rs: remove log from subscribe_room_events,
subscribe_project_room_events, subscribe_task_events_fn
- room-context: dedup by id not seq (streaming seq=0); single atomic
setStreamingContent with delta detection; preserve reactions from WS
- MessageBubble: fix avatar lookup (members before IIFE); handleReaction
deps (no message.reactions); add reactions to wsMessageToUiMessage
- MessageInput: memoize mentionItems; fix upload path with VITE_API_BASE_URL
- IMEditor: warn on upload failure instead of silent swallow
- RoomSettingsPanel: sync form on room switch; loadModels before useEffect
- DiscordChatPanel: extract inline callbacks to useCallback stable refs
- Rewrite DiscordChannelSidebar with @dnd-kit drag-and-drop:
rooms are sortable within categories; dragging onto a different
category header assigns the room to that category
- Add inline 'Add Category' button: Enter/Esc to confirm/cancel
- Wire category create/move handlers in room.tsx via RoomContext
- Fix onAiStreamChunk to accumulate content properly and avoid
redundant re-renders during AI streaming (dedup guard)
- No backend changes needed: category CRUD and room category update
endpoints were already wired