Commit Graph
819 Commits
Author SHA1 Message Date
Luan VuandLuvu182 97f4942943 fix(channels): add display_name to session metadata for Feishu, Zalo, Slack (#536)
Sessions page shows raw IDs instead of sender names because these channels
didn't include display_name in their message metadata. The gateway's
extractSessionMetadata() already looks for this key but it was missing
from Feishu, Zalo Personal, and Slack message payloads.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-29 11:25:05 +07:00
viettranx a506b297cf fix(i18n): update maxHistoryShare tooltip from 0.75 to 0.85 default 2026-03-29 10:18:27 +07:00
viettranx 3695353de7 refactor(providers): extract Novita defaults into constants and add to desktop UI
DRY: replace hardcoded Novita API base/model across 3 files with
store.NovitaDefaultAPIBase and store.NovitaDefaultModel constants.
Add Novita to desktop frontend provider list.
2026-03-29 10:16:28 +07:00
Alex bd1411028b feat(providers): add Novita AI as new LLM provider (#444)
Add Novita AI as a new LLM provider with OpenAI-compatible API endpoint.

- Add ProviderNovita constant and valid type entry
- Add Novita to ProvidersConfig with APIBaseForType and HasAnyProvider
- Register Novita provider in gateway startup (config and DB paths)
- Add Novita case in HTTP handler's registerInMemory
- Add Novita to UI provider selection dropdown

Default model: moonshotai/kimi-k2.5
Default endpoint: https://api.novita.ai/openai
2026-03-29 10:15:14 +07:00
viettranx 0c6cd02285 refactor(compaction): remove minMessages guard, use token-only threshold
Message count-based compaction trigger (default 200) was a legacy safety
net from when token estimation was unreliable. With calibrated overhead
estimation using actual LLM PromptTokens, the token threshold alone is
sufficient — matching industry standard (Claude Code, Anthropic API,
LangChain all use token-only).

Also fixes EstimateTokens vs EstimateHistoryTokens inconsistency in
end-of-run overhead calculation (loop_history.go), and updates stale
CLAUDE.md docs from 75% to 85%.
2026-03-29 10:14:52 +07:00
viettranx db03bf92e0 feat(web): add KG extraction settings to system settings modal and tighten code block spacing
- Knowledge Graph section: provider/model picker + min confidence threshold
- Saves via builtin tools API (knowledge_graph_search settings)
- Reduce code block vertical margin and padding in markdown renderer
2026-03-29 09:24:06 +07:00
viettranx 028ea8a7d8 fix(security): elevate management HTTP routes from Operator to Admin role
Management endpoints (MCP, Agents, Skills, Channels, Builtin Tools)
previously used auto-detect auth which allowed Operator-level users
to perform CRUD operations. Now consistent with WS policy: Admin+
required for writes, Viewer+ for reads.

Also moves WS skills.update to isAdminMethod and improves 403 error
messages to include required role info.
2026-03-29 09:02:48 +07:00
viettranx 8b75e095e9 feat(cancel): add run.cancelled event and fix stop across web UI, desktop, and channels
- Add distinct `run.cancelled` agent event to differentiate user-initiated
  cancellation from real errors (previously both emitted `run.failed`)
- Fix desktop stop button: wire onStop callback to InputBar, add abort()
  function in useChat hook, restore isRunning state on session switch
- Fix web UI: handle run.cancelled without showing error message, preserve
  partial streamed content on cancel
- Fix hanging chat.send promise: send {cancelled: true} OK response instead
  of silently dropping (was hanging 600s until timeout)
- Fix channel stream cleanup: handle run.cancelled for stream stop + run
  context deletion (prevents memory leak) + reaction status clear
- Fix child trace status: cancelled subagent runs now correctly get
  TraceStatusCancelled instead of TraceStatusError
- Add run.cancelled to all event consumers: query invalidation, session
  detail refresh, trace dialog refresh, gateway activity clearing
2026-03-29 09:00:06 +07:00
viettranx 635870acd2 fix(agent): prevent premature compaction by separating overhead from history tokens
Root cause: compaction threshold compared total prompt tokens (including
system prompt, tool definitions, context files) against budget. Sessions
with only 50-60k of conversation could trigger compaction when overhead
pushed total to 120k+.

Changes:
- Enable context pruning by default for all providers (was Anthropic-only)
- Add EstimateHistoryTokens() that excludes system messages from count
- Fix EstimateTokens() to include ToolCall argument sizes
- Calibrate overhead on first LLM response, subtract from threshold checks
- Mid-loop: prune old tool results first (at 70% budget), compact only if
  still over budget after pruning
- maybeSummarize: subtract overhead from calibration base
- Remove applyContextPruningDefaults() (Anthropic-only auto-enable)
- Simplify context pruning UI: show only Keep Last Assistants by default,
  hide advanced params behind collapsible, remove Mode selector
2026-03-29 07:50:00 +07:00
viettranx f8278cfaff fix(mcp): strip placeholder values from optional tool args and guide LLMs on optional params
LLMs often send empty strings, "null", "SHOULD_NOT_BE_HERE" etc. for optional
MCP tool parameters instead of omitting them, causing servers to reject invalid
values (e.g. UUID validation fails on "").

- Add stripEmptyOptionalArgs() to clean optional args before MCP calls
- Detect known placeholders (null/none/nil) and all-caps patterns
- Add system prompt guidance for optional parameter handling
- Empty string "" is intentionally NOT stripped (may be valid for text fields)
2026-03-29 07:48:19 +07:00
viettranx 243656a7b6 feat(mcp): add reconnect button to evict stale MCP connections
When MCP servers update their tools externally, the gateway keeps
stale cached connections. Add POST /v1/mcp/servers/{id}/reconnect
endpoint that evicts the pooled connection, forcing re-discovery
on next agent use. Exposed via reconnect button on both web and
desktop MCP management pages.
2026-03-29 07:15:03 +07:00
viettranx 985d2fa50d fix(heartbeat): log provider override and include in trace tags 2026-03-28 22:55:11 +07:00
viettranx 5c8e0b7af2 fix(heartbeat): apply provider override from heartbeat config
Heartbeat's ProviderID was stored in DB but never used during execution —
the agent's default provider always won. Add ProviderOverride to RunRequest,
resolve hb.ProviderID in the ticker, and use the override in the agent loop.
2026-03-28 22:48:27 +07:00
viettranx 175e05287a fix(cache): heartbeat/cron used UUID in session keys causing stale cache after agent update
Session keys must use agentKey (human-readable, e.g. "default") not UUID.
Cache invalidation via InvalidateAgent(agentKey) only matches agentKey suffix,
so UUID-keyed entries were never cleared — causing stale provider/model after
agent updates from the web UI.

- heartbeat: use agentKey (already resolved) instead of UUID for session key
  and HasActiveSessionsForAgent check
- cron: resolve agentKey from UUID via agentStore before building session key
- agents_update: also invalidate by UUID as safety net for legacy entries
- startup migration: auto-fix existing UUID-based session keys in DB
- tests: 7 new tests covering session key format and cache invalidation
2026-03-28 19:02:53 +07:00
viettranx 3d84e72934 fix(build): filter git describe to exclude desktop lite-v* tags
Add --match "v[0-9]*" so VERSION only picks up server release tags,
preventing lite-v* desktop tags from being used as the build version.
2026-03-28 18:58:39 +07:00
viettranx c70e50c96a fix(tools): harden session tools — phantom sessions, JSON injection, fail-closed checks
- session_status: GetOrCreate → Get to prevent phantom session creation
- sessions_history: replace string concat with json.Marshal (2 paths) to prevent JSON injection
- sessions_send: use s.Label from List() directly (eliminates N+1 Get calls), add self-send protection, use json.Marshal for response
- All tools: fail-closed agentID check (error when empty instead of skipping validation)
- Add 22 unit tests covering all session tools
2026-03-28 18:25:03 +07:00
viettranx 90b7396d74 fix(bus,cron): panic recovery + deadlock fix + unit tests for 9 packages
Bug fixes:
- bus: add panic recovery in Broadcast() — panicking subscriber no longer
  crashes entire event bus goroutine
- cron: fix deadlock in RunJob() — recordRun called while holding mutex,
  extracted recordRunLocked for callers already holding lock
- cron: add run log recording to executeJobByID — automatic scheduler
  was not populating run log, only manual RunJob did

New tests (~170 cases across 9 packages):
- crypto: roundtrip, key derivation (hex/b64/raw), nonce uniqueness,
  backward compat, wrong key, corruption
- permissions: role hierarchy, RoleFromScopes, CanAccess, scope precedence
- bus: pub/sub delivery, buffer full, panic recovery, concurrent safety
- providers/retry: IsRetryableError, backoff, jitter, Retry-After, context
  cancellation, hook callback
- sessions: idempotency, concurrent writes, defensive copy, save/load
  roundtrip, metadata accumulation
- scheduler: draining, drop policies, adaptive throttle, stale completion,
  lane concurrency, debounce, interrupt mode
- config: JSON5 parsing, env overrides, FlexibleStringSlice, owner IDs
- cron: schedule validation, computeNextRun, CRUD, job execution, failure
  tracking, persistence roundtrip
- agent: history sanitization edge cases — all-tool history, partial results,
  cross-turn dedup, orphaned tools, 1000-msg performance
2026-03-28 18:15:46 +07:00
viettranx a7800604b6 fix(web): streaming broken on first chat send due to expectingRunRef race
When sending the first message (no session key), handleSend calls
navigate() then send(). The navigation triggers a session-change effect
that resets expectingRunRef=false before run.started arrives, causing
all streaming events to be silently dropped.

Only reset expectingRunRef when switching between existing sessions,
not on the "" → new-key transition that is part of the send flow.
2026-03-28 18:14:08 +07:00
viettranx 2d344e4f35 fix(tools): block message tool self-send to prevent duplicate media delivery
When a team member creates an image and completes a task, the announce
flow already carries media to the leader agent, which dispatches it to
the channel via ForwardMedia. If the leader also calls the message tool
to send the same image to its own chat, the image appears twice.

Block message tool calls where channel+target match the agent's own
context, returning an error that guides the LLM to include content in
its normal response instead.
2026-03-28 17:35:51 +07:00
viettranx 78253f1841 refactor: decompose consumer, agent loop, team tools, and MCP manager
6-phase pure refactor — zero behavior change, all tests pass.

Phase 1: DRY event payloads + metadata keys
- BuildTaskEventPayload() helper with functional options replaces 24 inline constructions
- 26 metadata string constants replace ~66 magic strings across dispatch/consumer

Phase 2: Consumer handler decomposition
- ConsumerDeps struct collapses 11-13 positional params to 3 per handler
- Extract startTaskLockRenewal() and resolveTeamTaskOutcome()

Phase 3: loop.go extraction (978→686 LOC)
- enrichInputMedia(): media persistence, ref collection, tag enrichment
- injectTeamTaskReminders(): leader/member task context injection
- buildFilteredTools(): policy, bootstrap, channel, iteration filtering
- collectRefsByKind() helper eliminates 3x copy-paste

Phase 4: team_tasks_mutations split (630→318 LOC)
- executeCreate extracted to team_tasks_create.go
- resolveTeamAndTask() helper used by 13 execute* functions

Phase 5: MCP LoadForAgent split (~90→~15 LOC body)
- resolveServerCredentials() + connectAndFilter() helpers

Phase 6: Comments + DI
- 5 inaccurate comments fixed
- ToolExecutor interface for dependency inversion (Loop.tools field)
- FilterTools accepts ToolExecutor instead of concrete *Registry
2026-03-28 17:25:59 +07:00
viettranx 4b6ed8cfce fix(agent): broken cache invalidation + WS handler parity gaps
Root cause: InvalidateAgent() and Remove() used plain agentKey
for cache deletion, but Get() stores with tenant-scoped key
"tenantID:agentKey" — invalidation never matched, so old
provider/model persisted up to 10min (TTL).

Also fixed:
- WS agents.update missing provider, status, context_window,
  max_tool_iterations, frontmatter, is_default, budget fields
- WS agents.create ignoring user provider/model (hardcoded defaults)
- WS channel_instances.update bypassing column allowlist (security)
- HTTP agentAllowedFields missing sandbox_config, context_pruning
2026-03-28 16:55:59 +07:00
viettranx 040ced0a29 fix(desktop): allow scrolling long text in chat input
Change textarea overflow from hidden to auto so users can scroll
through long messages. Align attach/send buttons to bottom.
2026-03-28 15:08:49 +07:00
viettranx 40e32cf239 fix(agent): uniqueness-aware loop detector + auto-fail killed team tasks (#506)
Read-only streak detector was a dumb counter that killed agents exploring
unique files (12 reads = kill regardless of uniqueness). Team tasks were
then falsely auto-completed with the kill message as "result."

Changes:
- Add uniqueness ratio to read-only streak: exploration mode (>60% unique)
  uses relaxed thresholds (warn=24, kill=36) vs stuck mode (warn=8, kill=12)
- Classify team_tasks by action: list/get/search=read-only, progress=neutral,
  create/complete/cancel/comment/etc.=mutating
- Add LoopKilled flag to RunResult, propagated through all 3 kill paths
- Consumer routes LoopKilled to FailTask (not CompleteTask) with clear signal
- 40 tests covering uniqueness detection, team_tasks classification,
  exact #506 trace replay, boundary values, and edge cases
2026-03-28 14:53:43 +07:00
Duc Nguyen 8b5a05a4b7 fix(web): sign media_refs paths in session history delivery (#519) (#520)
File downloads in web chat returned 401 when loading session history
because MediaRefs paths were not signed with ?ft= HMAC tokens.

- Add SignMediaPath() to clean legacy corrupted paths (stacked
  /v1/files/ prefixes, stale ?ft= tokens) and produce fresh signed URLs
- Sign MediaRefs in chat.history and sessions.get WS handlers
- Strip ?ft= from filename display in frontend
- Add path traversal defense-in-depth check
- Add unit tests for SignMediaPath legacy data healing

Closes #519
2026-03-28 13:33:25 +07:00
therichardngai-code 6bfad07ed8 fix(docker): restore base capabilities in sandbox overlay (#523)
Sandbox overlay's cap_add replaces (not merges) the base compose, dropping
SETUID, SETGID, CHOWN. This causes credential copy to fail with Permission
denied when combining sandbox + claude-cli overlays.

Changes:
- Re-include base capabilities in sandbox overlay's cap_add
- Use umask 077 for atomic permission-safe credential copy
- Add ENABLE_CLAUDE_CLI build arg to pre-install Claude CLI in image
- Add runtime warning when credentials mounted but CLI binary missing
- Add WITH_CLAUDE_CLI to Makefile for overlay consistency
- Add security warning comment for sandbox overlay attack surface
2026-03-28 13:17:08 +07:00
viettranx 0cfe5800ca feat(web): pre-fill "system" user ID on login form with hint note
Default User ID to "system" and add i18n hint text explaining it
grants full system access, reducing friction for admin login.
2026-03-28 12:57:52 +07:00
viettranx 06ee648392 fix(kg): add 3-tier fallback for traversal + cap output limits
KG traversal only followed outgoing edges, returning empty results for
entities with only incoming relations (e.g. Kuwait had 1 incoming but 0
outgoing). Add 3-tier fallback: (1) outgoing deep traversal, (2) direct
connections via ListRelations (bidirectional, cap 10), (3) search query
fallback. Also cap traversal results at 20 and search relations at 5 per
entity with hints for the model to query further.
2026-03-28 12:19:52 +07:00
viettranx 407b2a3ed3 feat(desktop): add DB reset in About tab + complete onboarding i18n
About tab:
- Show clickable database path that opens data folder
- Add "Reset Database" button with RESET confirmation modal
- Reset deletes DB files and restarts app via RestartApp()
- Add GetDataDir/ResetDatabase Wails bindings

Onboarding i18n:
- Replace all hardcoded English strings in ProviderStep,
  ModelVerifyStep, and AgentStep with t() calls
- Add missing i18n keys to en/vi/zh common.json and desktop.json
2026-03-28 12:03:12 +07:00
viettranx ea60397754 fix(desktop): fix media chain settings not editable on reopen + add drag-drop
- Fix model Combobox permanently disabled on reopen: load models for
  existing chain entries once providers list is fetched
- Add drag-and-drop reordering via @dnd-kit/sortable (matching web UI)
- Use stable UUIDs as React keys instead of array indices
- Show model display names (e.g. "Gemini 2.0 Flash") instead of raw IDs
- Disable Save button when any entry is missing provider or model
- Extract MediaProviderChainForm into its own file
2026-03-28 09:19:00 +07:00
viettranx b088144fd9 fix(sqlite): bootstrap not running on first chat due to per-connection PRAGMA gap
Root cause: pool.go applied busy_timeout PRAGMA via db.Exec() which only
affects one connection in the pool. Other connections had no busy_timeout,
causing immediate SQLITE_BUSY errors during concurrent startup operations
(agent creation, WebSocket connect, health checks). This silently aborted
context file seeding — BOOTSTRAP.md, USER.md, AGENTS.md all missing from
system prompt on first interaction.

Fix (3 layers):
1. pragmaConnector: wraps sql.Driver to apply PRAGMAs (busy_timeout, WAL,
   etc.) on every new connection, not just one. All SQLite queries benefit.
2. CacheInvalidateFunc: clears ContextFileInterceptor cache after seeding
   so LoadContextFiles sees newly seeded files on the first turn.
3. fallbackBootstrap: if DB seed still fails, injects embedded templates
   in-memory so the first turn still gets onboarding. Clears after use.
2026-03-28 08:31:18 +07:00
viettranx 8beb72039c feat(desktop): add language switcher to onboarding setup screen
Add centered language toggle (EN/VI/中文) at top of setup wizard.
Default language changed from English to Vietnamese.
2026-03-27 23:53:17 +07:00
viettranx b373561f72 fix(desktop): fetch up to 100 releases to find lite-v* tags
GitHub API returns 30 releases by default. With 30+ v2.x releases,
lite-v* tags were pushed out of page 1 causing "No desktop release
found" error.
2026-03-27 23:44:59 +07:00
viettranx e1b8217e45 fix(desktop): keep PowerShell window open on install errors
Add Exit-WithPause helper that waits for keypress before closing the
window, so users can read error messages when fetch/download fails.
2026-03-27 23:37:06 +07:00
viettranx d9087fd0b7 fix(security): harden MCP bridge tenant_id propagation
- Guard uuid.Nil before converting to string in loop.go to avoid
  sending zero UUID as X-Tenant-ID header when no tenant is set
- Return (ok, tenantVerified) from VerifyBridgeContext so callers
  only inject tenant_id when HMAC actually covers it (level 1)
- Add unit tests for SignBridgeContext and VerifyBridgeContext
  including security test for tenant injection via HMAC fallback
2026-03-27 23:23:14 +07:00
mikedoandadmin 6551c2d1e2 fix: propagate tenant_id through MCP bridge for memory indexing (#512)
Problem:
Memory documents saved via write_file → MemoryInterceptor are stored
in memory_documents (PutDocument succeeds via fail-open tenant fallback),
but IndexDocument fails with "tenant_id required" because the MCP bridge
chain never propagates tenant_id. This results in 0 memory_chunks,
making memory_search return empty for all agents using claude_cli provider.

Root cause:
- BridgeContext struct lacks TenantID field
- writeMCPConfigInternal never sets X-Tenant-ID header
- bridgeContextMiddleware never extracts/injects tenant_id into context
- PutDocument uses tenantIDForInsert() (fail-open, defaults to master)
- IndexDocument uses scopeClause() → requireTenantID() (fail-closed)

Fix:
- Add OptTenantID constant and BridgeContext.TenantID field
- Pass tenant_id from agent loop context → provider opts → MCP headers
- Add X-Tenant-ID to bridge request headers (HMAC-signed)
- Extract X-Tenant-ID in bridgeContextMiddleware → store.WithTenantID()
- Backward-compatible HMAC verification with 3 fallback levels
  (with tenant, without tenant, without workspace+tenant)

Affected: all agents using claude_cli provider type.
Also fixes: "KG embeddings backfill failed: tenant_id required"

Co-authored-by: admin <admin@admins-MacBook-Pro.local>
2026-03-27 23:20:07 +07:00
viettranx f4f050bcd4 fix(desktop): increase media thumbnail height from 160px to 288px
Change from h-40 object-cover (fixed crop) to max-h-72 object-contain
so images display at proper aspect ratio without cropping.
2026-03-27 23:00:16 +07:00
viettranx 0b8366bfd0 fix(agent): raise default compaction threshold from 75% to 85%
Compaction was triggering too early because prompt tokens include
system prompt + context files + tools, not just history. Raising
the threshold reduces unnecessary compaction cycles.
2026-03-27 22:55:59 +07:00
viettranx ee9f5b8aaf feat(team): share leader memory with team members (read-only)
Team members dispatched via team tasks can now read the leader's memory
files (MEMORY.md, memory/*.md) as a fallback when they have no own
memory. Memory writes are blocked for members — only the leader can
save memory files. This follows the existing team_workspace context
propagation pattern: dispatch metadata → RunRequest → context → tools.

Affected tools: read_file (interceptor), memory_get, memory_search,
list_files (interceptor). All include leader fallback with per-user →
global scope cascade.
2026-03-27 22:45:40 +07:00
viettranx e9342533e0 refactor(tools): extract TeamToolBackend interface for team task testability
Extract TeamToolBackend interface from concrete *TeamToolManager to enable
unit testing of the 17 team task action handlers without DB or message bus.

- Add TeamToolBackend interface (19 methods) in team_tool_backend.go
- Add exported wrappers on TeamToolManager (cache, helpers, dispatch, manager)
- Migrate 133 call sites across 5 action handler files (pure casing rename)
- Change TeamTasksTool.manager field from *TeamToolManager to TeamToolBackend
- Add mock backend infrastructure (baseNoopTeamStore + mockTaskStore + mockBackend)
- Add 94 unit subtests across 6 test files covering all actions:
  lifecycle (claim/complete/cancel/review/approve/reject),
  create validation, blocker escalation, mutations (comment/progress/attach/update),
  followup (ask_user/clear_ask_user/retry), read actions (notification/policy/list/search/get)

WorkspaceInterceptor and PostTurnProcessor remain on concrete *TeamToolManager.
No behavioral changes — all existing logic preserved.
2026-03-27 22:26:36 +07:00
Thieu Nguyen c0dfea35ea fix(ui): show env var input section for manual CLI credentials (#514)
When adding a CLI credential with "Manual (no preset)" mode, the env var
input section was hidden because it only rendered when a preset was
selected. Add a dynamic key-value form allowing users to define custom
environment variables for non-preset CLI tools.

Closes #477
2026-03-27 21:50:31 +07:00
Luan VuandLuvu182 be9044ca8e fix(zalo): update API version, batch group fetch, fix WS decrypt (#516)
- Bump DefaultAPIVersion from 665 to 671 to match current Zalo web client
- Batch getmg-v2 requests (50 groups/batch) — Zalo rejects large payloads,
  causing error 114 for accounts with many groups (e.g. 250+)
- Fix encryptType=1 WebSocket event decryption: add gzip decompression
  after base64 decode (matches zca-js pako.inflate behavior)
- Split contacts picker into Friends/Groups tabs for better usability

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-27 21:50:01 +07:00
viettranx e297fb63b8 ci(release): add pnpm/node cache to desktop release workflow
Replace corepack-only pnpm setup with pnpm/action-setup@v4 + actions/setup-node@v4
with cache enabled. Saves ~30-60s per build by caching pnpm store.
2026-03-27 21:06:53 +07:00
viettranx 7f796b0120 fix(agent): channel-aware tool filtering, group chat title in system prompt
- Add filteredToolNamesForChannel() so system prompt Tooling section
  excludes channel-specific tools for wrong channels (e.g. list_group_members
  hidden for Telegram, create_forum_topic hidden for Feishu)
- Add ChannelAware interface to CreateForumTopicTool (Telegram-only)
- Add coreToolSummaries for list_group_members and create_forum_topic
  (were showing as "(custom tool)")
- Inject group chat title into system prompt identity line
  (e.g. "telegram (group chat \"My Group\")" instead of "a group chat")
- Sanitize ChatTitle to prevent prompt injection (strip quotes/newlines,
  truncate to 100 chars)
2026-03-27 21:03:34 +07:00
viettranx 8b08dec04c fix: address code review findings (critical + high + medium)
Critical:
- retryOnBusy: return lastErr after exhausted retries instead of nil

High:
- Remove debug console.log from use-paired-devices.ts

Medium:
- ChannelGeneralTab: sync state from instance prop via useEffect
- i18n: add MsgCannotRemoveLastWriter key + translations (en/vi/zh)
- use-channel-crud: refetch after delete instead of optimistic remove
2026-03-27 20:53:05 +07:00
viettranx 5f5b0c45bd fix(telegram): inject tenant_id into command handler context
All Telegram bot commands (/writers, /addwriter, /removewriter, /reset,
/tasks, /task_detail) and callback queries were missing tenant_id in
context, causing "tenant_id required" errors from scopeClause() or
silent fallback to MasterTenantID breaking multi-tenant isolation.
2026-03-27 20:44:27 +07:00
viettranx 0b3be862c9 fix(desktop): fix pairing time scan, i18n keys, convert advanced to tab
SQLite pairing:
- Fix rows.Scan failing on Go time.Time strings with monotonic suffix
- Scan timestamps as strings, parse with parseTimeToMillis() that strips m=+
- Use time.Now().Round(0) for all SQLite time comparisons
- Wire SetOnRequest for SQLite store via interface assertion (not PG-only)

Desktop UI:
- Fix ChannelManagersTab i18n keys: managers.xxx → detail.managers.xxx
- Fix ChannelAdvancedDialog i18n keys and save logic (wrap in config)
- Convert Advanced from modal dialog to inline tab (4th tab)
- Add "advanced" tab label to all 3 locale files
2026-03-27 20:44:12 +07:00
viettranx 2ba883aff7 fix(pairing): broadcast pairing events for SQLite stores, not just PG
SetOnRequest was only wired for PGPairingStore via concrete type assertion.
Use interface-based assertion so SQLitePairingStore also gets the callback.
This fixes desktop app not receiving real-time pairing request notifications.
2026-03-27 20:27:25 +07:00
viettranx 0a8ca13d08 refactor(http): standardize error responses with writeError helper
- Create response_helpers.go with writeError(w, status, code, msg) for structured errors
- Move writeJSON to response_helpers.go for co-location
- Migrate agents.go, channel_instances.go, api_keys.go to writeError with protocol error codes
- Replace raw err.Error() with i18n messages + slog.Error for debugging
- Fix channel creation: map camelCase input to snake_case for backend HTTP API
- Fix PairedDevicesSection reload button: use RefreshButton component
2026-03-27 20:15:26 +07:00
viettranx 6ea9b4d762 feat(desktop): add channel management, paired devices, and multiple fixes
Channel Management:
- Add Channels settings tab with full CRUD for Telegram/Discord (Lite: max 1 each)
- Channel detail panel with tabs: General, Credentials, Managers
- Advanced settings dialog (network, limits, streaming, behavior, access control)
- Schema-driven field renderer with Combobox selects, Switch toggles
- Paired Devices section: approve/deny pending, revoke paired, WS event auto-refresh
- Pairing notification badge in sidebar footer with pending count
- i18n support (en/vi/zh) for all channel strings

Bug Fixes:
- Fix Vietnamese slug generation (NFD normalize + đ/Đ handling) in lib/slug.ts
- Make agent key field editable instead of read-only
- Fix trace detail input/output: use MarkdownRenderer with copy button instead of forced-dark CodePreview
- Fix API error parsing to handle both {error: "string"} and {error: {message}} formats
- Add Accept-Language header to desktop API client for i18n error messages
- Wrap raw err.Error() with i18n messages in HTTP handlers (agents, channel_instances)
- Increase SQLite MaxOpenConns from 2 to 4 to reduce SQLITE_BUSY contention
- Add retryOnBusy wrapper for context file seeding writes
- Update EditionCompareModal: channels moved from "false" to "1 Telegram + 1 Discord"
2026-03-27 19:52:28 +07:00
viettranx 2bc0f5f14a fix(telegram): prevent duplicate messages in groups when streaming disabled
When group_stream=false (default), channels implementing StreamingChannel
still processed chunk events, creating stream messages alongside block
replies — causing each intermediate message to appear twice.

Gate the streaming handler on rc.Streaming so it only activates when
streaming is actually enabled for the run.
2026-03-27 19:49:33 +07:00