Implement legacy STT bridge adapter to support existing per-channel STTProxyURL database configuration. Wraps arbitrary proxy endpoints and auto-registers them via Manager at boot. Includes integration tests for URL validation, authentication, and error handling.
Add Transcribe() method to Manager with STT provider dispatch and channel-specific overrides. Implements channelSTTOverrides map, WithChannel context propagation, RegisterChannelSTT registration, and resolveSTTChain fallback logic. Includes manager-level unit tests.
Implement STT proxy provider that wraps arbitrary HTTP-based STT services. Ported 12 test cases from legacy Telegram STT integration to validate multipart/form-data submission, language/model overrides, and API error handling.
Implement native ElevenLabs Scribe STT provider with POST /v1/speech-to-text endpoint. Supports 20MB audio cap, multipart form submission, configurable model and language. Includes comprehensive unit tests for happy path, audio size validation, API errors, and edge cases.
Add STTInput, STTOptions, and TranscriptResult types to support native STT providers (ElevenLabs Scribe). Includes audio_duration_secs JSON tag for transcription metadata.
Update architecture overview with streaming TTS provider layer. Expand tools
system docs with voice/model resolution. Update HTTP and WebSocket RPC
documentation for voice endpoints. Record Phase 02 completion in changelog.
Update CLAUDE.md with voice picker and streaming TTS patterns.
Add VoicePicker with live search and preview button. Integrate into prompt
settings section. Add voice API hooks (useVoices, useRefreshVoices). Localize
UI strings for en/vi/zh. Fix snake_case JSON field mapping (voice_id, preview_url).
Add StreamingTTSProvider interface for audio streaming support. Implement voice
caching with TTL+LRU. Add ElevenLabs model validation and voice list retrieval.
Update module map to list internal/audio/ (unified manager, TTS active,
STT/Music/SFX stubbed/partial) and clarify internal/tts/ as a
24-symbol backward-compat alias layer. Add changelog entry for Phase 1.
Add optional Audio *AudioConfig pointer field on Config with STT and
Music sub-structs. Nil-safe — absent in JSON5 decodes as nil, no
breaking change. cfg.Tts retained unchanged for backward compat.
setupAudioExtras stub wired for Phase 3/4 STT/Music provider
registration.
internal/tts becomes a thin backward-compat alias over internal/audio:
15 type aliases, 6 constants, 5 constructors, 5 compile-time signature
guards. All pre-refactor callers compile unchanged. alias_test.go
enforces symbol coverage and type identity. Old per-provider files
(manager, types, elevenlabs, openai, edge, minimax) are removed in
the same commit to keep history bisectable.
MCP servers with require_user_credentials (e.g. Notion) were defined
but never loaded into the agent's tool registry. Three gaps:
1. getUserMCPTools was defined but never called — add call in
makeBuildFilteredTools before FilterTools runs each iteration.
2. hasMCPTools stayed false when only user-credential servers existed,
so agentToolPolicyWithMCP never injected "group:mcp" into alsoAllow.
Now set true when mcpUserCredSrvs is non-empty.
3. Per-user BridgeTools were registered in the registry but never added
to the "mcp" tool group, so expandSpec("group:mcp") returned empty.
Add MergeToolGroup helper for additive group updates.
Also add debug log when getUserMCPTools skips due to empty userID.
Stale recovery sweeps traces by `start_time < NOW() - threshold`, which
measures trace age rather than inactivity. Any threshold low enough to
be useful (2-10 min) kills legitimate long-running agent runs: research
chains, large code generation, extended shell commands routinely exceed
10 minutes.
Disabled in Start() — function kept in place for easy re-enable once a
`last_span_at` column is added so recovery can gate on "no activity for
N minutes" instead of "started > N min ago".
Trade-off: zombie traces from gateway crashes may remain `running` in
DB. Accepted: primary abort path (router 2-phase + trace.status WS
event) handles the common case; safety-net gap preferred over false
kills of healthy runs.
Integration test RecoverStaleNow() still works (manual trigger, not
loop-dependent) so coverage of the recovery function itself is
preserved for when it's re-enabled.
matchesBinaryDeny used unanchored regex on joined args, causing `-v` pattern
to false-positive on `--version`. Split deny_verbose into matchesBinaryVerbose
with start-anchored per-arg matching: `-v` blocks `-v`, `-vv`, `-v=1` but not
`--version`. deny_args keeps joined matching for multi-token patterns.
v3 pipeline's parallel path (makeExecuteToolRaw) skipped the tool.call
WebSocket event, so web UI and desktop UI silently dropped tool cards
during real-time streaming. Only page refresh (which reloads history)
revealed the tool calls. Both UIs rely on tool.call to create entries
that later tool.result events can update.
Fix: mirror the sequential path's emission in makeExecuteToolRaw.
Bus.Broadcast is RWMutex-guarded, safe to call from parallel goroutines.
Add tests at two layers to prevent regression:
- Pipeline layer (stages_test.go): guards the dispatch contract —
multiple tool calls route through ExecuteToolRaw + ProcessToolResult
rather than ExecuteToolCall. Previously the parallel path had zero
test coverage, which is why this bug escaped.
- Agent layer (loop_pipeline_tool_callbacks_test.go): guards the
emission contract — both sequential and parallel wrappers emit
tool.call with correct payload and routing context. Mutation-verified.
- AddError() now skips broadcast after Finish() to prevent cancelled
goroutines from emitting error events to UI after user stops enrichment
- batchSummarize skips AddError when context is cancelled (expected on stop)
- Rescan always re-enqueues unenriched docs alongside new/updated files,
worker-level dedup prevents double-processing
* fix: handle ignored errors, unsafe type assertions, missing panic recovery
- Cron scheduler (PG + SQLite): check all ExecContext errors in
recomputeStaleJobs, run log insert, job delete, and post-run update.
Previously these errors were silently discarded, which could leave
job state inconsistent without any log trace.
- Discord: use comma-ok type assertions on sync.Map placeholder loads
to prevent potential panics from bare type assertions.
- Slack: use comma-ok type assertions in sweepMaps for dedup and
thread participation eviction to prevent potential panics.
- Feishu: add safego.Recover to WebSocket goroutine so a panic in
the WS client doesn't silently kill the goroutine.
- Agent export: add tenant owner/admin permission check to canExport.
Previously only agent owner and system owner could export — tenant
admins were incorrectly denied.
- Channel health: use errors.Is/errors.As for context.DeadlineExceeded,
net.DNSError, and net.OpError before falling back to string matching.
DNS NXDOMAIN is now correctly classified as non-retryable.
* fix(review): revert export to system-only + add missing rows.Err check
- Revert canExport tenant role check — export/import is restricted to
agent owner and system owner by design
- Add rows.Err() check after recomputeStaleJobs loop in PG cron
(parity with SQLite implementation)
---------
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Co-authored-by: viettranx <viettranx@gmail.com>
* fix(facebook): preserve fb_mode metadata in outbound routing + admin reply detection
Two issues fixed:
1. Messenger auto-reply never delivered because fb_mode metadata was
stripped during outbound message construction. The routing whitelist
in gateway_consumer_normal.go and channels/events.go only copied
thread_id/local_key/group_id — facebook-specific keys (fb_mode,
sender_id, page_id, reply_to_comment_id) were dropped, causing
facebook.Send() to fall into the comment path and fail with
"reply_to_comment_id missing".
2. Added admin reply detection: before sending a bot reply, check via
Graph API if the last page message in the conversation was sent by
an admin (human) rather than the bot itself. Skips bot reply when
admin already responded, preventing duplicate messages.
Uses timestamp comparison with bot's own send history to distinguish
bot-sent vs admin-sent page messages (both have from.id = page_id).
* chore: exclude compiled binary from git
* fix(facebook): ignore bot echoes in messenger cooldown
* fix(facebook): add memory cleanup for admin reply maps and reduce echo window
- Add adminReplied and botSentAt eviction to runDedupCleaner to prevent
unbounded memory growth on high-traffic pages
- Reduce botEchoWindow from 60s to 15s to avoid misclassifying real admin
replies as bot echoes
- Restore doc comments on routingMetaKeys and copyRoutingMeta
- Add cross-reference comment between consumer and events routing key lists
- Simplify admin-reply skip log (remove redundant map re-read)
---------
Co-authored-by: khanhtran <>
Co-authored-by: Plateau Nguyen <nguyennlt.ncc@gmail.com>
Co-authored-by: viettranx <viettranx@gmail.com>
release.yaml is tag-triggered (v[0-9]+.[0-9]+.[0-9]+), not
branch-triggered. There is no go-semantic-release — releases
are manual tag pushes. Merging to main only triggers CI.
SSE progress hook crashed React when backend returned nested error
object from writeError ({"error": {"code": ..., "message": ...}})
instead of flat string. Now parses both formats correctly.
PolicyEngine.IsOwner lacked the "system" fallback that isHTTPOwnerID
already had — when owner_ids is empty, "system" user was rejected from
all backup/restore endpoints. Added consistent fallback logic.
Also added slog.Warn to all silent owner-check rejections across
backup, restore, tenant backup/restore, and S3 handlers.
- Add bgalert package: classify provider errors (auth, billing,
model_not_found), store alert in system_configs, broadcast WS event
- Wire AlertDeps into consolidation (episodic, semantic, dreaming)
and vault enrich workers to report failures after retry exhaustion
- Auto-clear alert when admin changes provider-related system configs
- Add BackgroundErrorBanner component with dismiss + "Fix in Settings"
- Lift settings modal state to AppLayout so banner can open it
- Add EventBackgroundError to admin-only WS event filter
- Add i18n translations (en/vi/zh) for alert messages and reasons
- Replace char-based heuristic (chars/4) with tiktoken BPE for accurate
token counting, especially for non-ASCII content (Vietnamese/Chinese)
- Add pruningEstimator wrapper with tiktoken/fallback dual-path
- Raise default soft trim budget from 3K to 6K chars (3K head + 3K tail)
- Media tools (read_image, read_document, read_audio, read_video) get
higher soft trim budget (8K: 4K head + 4K tail) and skip hard clear
entirely — their vision/audio descriptions are irreplaceable
- Add per-result context guard (Pass 0): force-trim any single tool
result exceeding 30% of context window
Both v3 mid-loop compaction and v2 background summarization were
dropping MediaRefs when summarizing old messages, making previously
shared images/documents permanently inaccessible to the agent.
Now collect up to 30 most recent MediaRefs from compacted messages
and attach them to the summary/first-kept message so they survive
the compaction cycle.
Extract shouldSkipEnrichment() replacing 4 scattered goclaw_gen_ checks.
Filter also skips UUID, hex hash, digit-only, short, and known junk filenames.
Fix cancelFuncs never being populated — Stop() was a no-op leaving UI stuck.
When share_memory=true, memory_search tool used MemoryUserID(ctx)=""
(cross-user) but auto-inject still passed raw userID, limiting episodic
L0 injection to the current user only. Now uses store.MemoryUserID(ctx)
so shared-memory agents get cross-user episodic summaries in auto-inject.
Group-scoped agents could read sessions from other groups via session
tools (sessions_list, sessions_history, session_status, sessions_send)
because they only checked agent_key, not group context. This caused
cron jobs to leak data from unrelated groups into reports.
Add isSessionInScope() guard to all 4 session tools with colon-bounded
chatID matching. New share_sessions setting (default false) controls
cross-group visibility, following the same pattern as share_memory and
share_knowledge_graph. Web UI toggle and i18n strings included.
63 test cases covering guild/DM/group users, realistic Zalo IDs,
boundary exactness, multi-colon chatIDs, and the exact bug scenario.
- Sigma.js graph: restore doc_type coloring (revert Louvain community detection)
- Fix animation flash after FA2 layout finishes by removing post-processing
camera reset and redundant noverlap/compactOrphans in stopLayout
- Fix vault tree "Load more" state bug when filtering by doc_type:
add treeVersion counter to force re-mount and reset auto-expand state
- Fix meta map loss: loadRoot now merges instead of replacing, preserving
subtree entries from previous loadSubtree calls
- Add compact graph DTO endpoints and hooks for KG and vault graphs
- Add semantic zoom tiers, adaptive FA2 settings, and node sizing