Commit Graph
539 Commits
Author SHA1 Message Date
viettranx eec083c91a docs: add ErrorBoundary key + route params rules to CLAUDE.md 2026-03-19 22:39:41 +07:00
viettranx 886ef64aec feat(ui): delete chat conversation from sidebar
- Add deleteSession() to useChatSessions hook
- Trash icon on session rows (hover-reveal, always visible on mobile)
- Simple confirm dialog (no typing required)
- If active session deleted, switch to next or create new
- i18n strings for en/vi/zh
2026-03-19 22:39:34 +07:00
viettranx 45295bc75a fix(ui): eliminate full-page flash on chat session switch
Root cause: ErrorBoundary in AppLayout used key={location.pathname},
causing React to unmount/remount the entire page on every URL change.

- Add stableErrorBoundaryKey() that strips dynamic segments from pathname
- Merge /chat and /chat/:sessionKey into single /chat/:sessionKey? route
- Derive sessionKey from useParams() instead of duplicating into useState
  (dual state caused race condition: setState + navigate → B→A→B bounce)
- Move session reset from render-time setState to useEffect
- Only show loading spinner when messages array is empty
2026-03-19 22:38:06 +07:00
viettranx b80ee0ae43 feat(sandbox): hint LLM when tool/binary missing in sandbox container
Detect exit code 127 and "command not found" patterns in sandbox
execution paths and append a [SANDBOX] hint to the error output,
so the model can inform the user about sandbox environment limitations.

Only applies to sandbox execution — host paths are untouched.
2026-03-19 22:17:29 +07:00
viettranx 711395f81f fix(ui): chat polish — floating bar, dotted bg, tool icon, flash fix
- Chat input bar: floating with backdrop blur and rounded corners
- Chat thread: dotted background pattern matching kanban board
- Tool call cards: blue Wrench icon for completed (was green Check)
- Memo wrap ChatSidebar, SessionSwitcher, ChatThread to prevent re-renders
- Keep old messages visible during session switch to prevent flash
2026-03-19 21:47:46 +07:00
viettranx 8409db5890 feat(ui): adopt canonical WS session key format + channel filter
- buildNewSessionKey uses agent:{id}:ws:direct:{uuid} format
- sessions.list sends channel:"ws" to filter WS-only sessions
- isOwnSession supports both new and legacy key formats
- Read-only bar now floating with rounded corners
2026-03-19 21:47:35 +07:00
viettranx 6b747d7571 fix(sessions): auto-migrate legacy ws-{userId}-{ts} keys to ws:direct:{ts}
Runs idempotent regex migration at PGSessionStore init to convert
legacy WS session keys to canonical format. Handles multi-hyphen
userIDs (UUIDs) by matching last segment as timestamp.
2026-03-19 21:47:26 +07:00
viettranx 642797f079 feat(sessions): canonical WS session key format + channel/user filtering
- Add BuildWSSessionKey() and IsWSSession() helpers for ws:direct:{convId} format
- Change ChatID to userId (not clientID) for consistent team/workspace isolation
- Add Channel and UserID fields to SessionListOpts with dynamic WHERE builder
- Extract shared buildSessionFilter() for ListPaged and ListPagedRich
- Chat sidebar sends channel:"ws" to filter WS-only sessions per user
- Sessions admin page remains unfiltered (no channel param)
2026-03-19 21:47:15 +07:00
badgerbeesandviettranx 3f9401257c feat(telegram): networking isolation and sticky IPv4 fallback (#278)
* fix(telegram): isolated transport, sticky fallback, and overall timeouts

* fix(telegram): improve networking isolation for high-concurrency

- Tune MaxIdleConnsPerHost to 64 (default 2 starves concurrent sends)
- Fix data race in enableIPv4Only using sync.Once
- Add force_ipv4 config option for explicit IPv4-only mode
- Narrow auto-detect heuristic to "unreachable" only (avoid false-positive on timeouts)
- Use bot username instead of token prefix in logs
- Extract applyIPv4Dialer helper for reuse between config and runtime paths

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-19 20:45:49 +07:00
viettranx 1452d9e8f3 docs: add solution design guideline to CLAUDE.md
Require root cause analysis and production scenario thinking
(high concurrency, multi-tenant, failure cascades) before designing
fixes or features. Prefer explicit config over runtime heuristics.
2026-03-19 20:39:44 +07:00
viettranx 15916823e0 feat(ui): integrate chat redesign + file preview modal
- ChatThread: merge consecutive tool-only messages, use ActiveRunZone
- ChatPage: add ChatTopBar, DropZone, pass new hook state
- MessageBubble: compact tool-only layout, block reply support
- ToolCallCard: compact mode with smaller padding
- AgentSelector: show emoji from other_config
- SessionSwitcher: friendly labels (Chat xxx instead of raw keys)
- FileBlock: click opens rich content modal with download button
- MarkdownRenderer: intercept /v1/files/ links as file chips with
  inline download icon, modal preview for md/code/audio/video/image
2026-03-19 19:01:51 +07:00
viettranx a9ec6302ca feat(ui): redesign chat with real-time event handling
Add 8 new chat components and extend use-chat-messages hook to handle
block.reply, activity, run.retrying, and team.task.* events.

New components: ActiveRunZone, ActivityIndicator, BlockReplyBubble,
SystemNotification, TeamActivityPanel, MediaGallery, ChatTopBar, DropZone.
2026-03-19 19:01:37 +07:00
viettranx b1b7bfbe69 fix(ws): detach agent runs from HTTP request context
Agent runs were tied to the HTTP request context, so navigating away
cancelled the LLM stream. Now uses context.WithoutCancel() to survive
page navigation while preserving explicit chat.abort support.
2026-03-19 19:01:19 +07:00
viettranx 78ffc57a8e fix(ws): propagate origin_session_key for team task announces
Team task announces rebuilt session keys via BuildScopedSessionKey(),
producing different format than WS sessions (ws:direct:uuid vs
ws-userId-ts). Now stores and forwards origin_session_key through
task metadata and dispatch, matching subagent announce pattern.

Also adds "ws" to InternalChannels to suppress spurious warnings.
2026-03-19 19:01:10 +07:00
2c1efa29ec fix(memory): prevent silent data loss on memory file overwrites (#279)
* fix(memory): auto-backup + overwrite warning for memory files

When an agent writes to a memory file that already has content:
1. Old content is backed up to memory/.prev/{filename} (best-effort)
2. Write succeeds normally (no blocking)
3. Tool result includes warning with previous content (truncated to
   4000 chars) so the agent can notice and merge if needed

Backup files are hidden from list_files and not indexed/embedded.
No database migration needed - uses existing PutDocument.

* fix(memory): support append mode + fix UTF-8 truncation in overwrite warning

- Add appendMode parameter to MemoryInterceptor.WriteFile() so
  write_file(append=true) actually appends to memory files instead
  of silently replacing content. Uses "\n\n---\n\n" separator.
- Fix UTF-8 truncation in overwrite warning: use []rune instead of
  byte slicing to avoid cutting multi-byte characters.
- Fix char count in warning message: use len([]rune()) not len().
- Remove .prev/ backup mechanism (prevBackupPath, isPrevPath, backup
  writes, ListFiles filter) in favor of simpler approach: append
  mode prevents data loss, warning enables self-correction.

---------

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-19 17:42:03 +07:00
bb783b952c feat(feishu): add list_group_members tool + native @mention support (#280)
* feat(feishu): add list_group_members tool + native @mention support

Add agent tool to list all members in a Lark group chat via
GET /open-apis/im/v1/chats/{chat_id}/members with auto-pagination.

- New tool `list_group_members`: returns member IDs + display names
- Native @mention conversion: @ou_xxx in outbound messages becomes
  real Lark mentions (post: "at" element, card: <at> markdown tag)
- Auto-sync discovered members into ContactStore
- GroupMemberProvider interface on Channel for extensibility
- Add im:chat.members:read scope to channel setup UI

Requires Lark app scope: im:chat.members:read

* fix(feishu): URL-encode API params + channel-aware tool filtering

- URL-encode chatID (PathEscape) and pageToken (QueryEscape) in
  ListChatMembers to prevent path traversal and query corruption
- Add slog.Warn when ListGroupMembers fails for observability
- Add ChannelAware interface so tools can declare required channel types
- ListGroupMembersTool now declares RequiredChannelTypes=["feishu"]
- loop.go filters out channel-specific tools when channel type mismatches,
  so list_group_members only appears for Feishu conversations

---------

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-19 17:21:38 +07:00
viettranx ec24b982f3 fix(subagent): replace bare count with deterministic roster in announce messages
Subagent announce messages told the parent LLM "There are still X active
runs" — a bare number. The LLM had to reconstruct which subagents were
done vs running from conversation history, causing hallucination when
results arrived out of order.

Replace with a full roster listing each subagent by label and status,
including per-agent config limits. Remove dead CountRunningForParent()
and unused rosterFunc field on AnnounceQueue.
2026-03-19 16:33:54 +07:00
viettranx 941a9657ca docs: add heartbeat system documentation
Document the agent heartbeat check-in mechanism covering architecture,
configuration, ticker loop, execution flow, suppression, delivery,
RPC methods, agent tool, frontend integration, and heartbeat vs cron
comparison.
2026-03-19 14:34:10 +07:00
chungtran4078andChung Tran efd3d15c70 fix: agent add cron within invalid params (#277)
Co-authored-by: Chung Tran <chung.tran@diqit.io>
2026-03-19 14:15:46 +07:00
Luan VuandLuvu182 44197011f8 feat(feishu): include reply context when user replies to a message (#272)
When a Feishu/Lark user replies to an existing message, the agent now
sees the quoted message content via the GetMessage API, formatted as
[Replying to SenderName]...[/Replying] — matching the Telegram channel
behavior. Previously parent_id was parsed but the content was silently
discarded, so the agent had no idea what the user was replying to.

Requires im:message:readonly scope on the Lark app.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-19 14:03:09 +07:00
0dd4ebd6e6 fix(channels): collect contacts for DM and group-mentioned messages (#271)
* fix(channels): collect contacts for DM and group-mentioned messages

Previously, EnsureContact was only called inside the "not mentioned →
record history → return" branch, so contacts were never saved for
messages that the bot actually processed (DMs and group @mentions).
This caused the contacts list to show empty names ("—") for active
users across all channels.

Move contact collection to the main processing path (before
PublishInbound / HandleMessage) so every processed message upserts
the sender's display name. The existing ContactCollector 30-minute
cache prevents redundant DB writes.

Affected channels: Feishu, Telegram, Discord, Slack, Zalo, WhatsApp.

* fix(slack): use clean userID for EnsureContact cache key

The senderID param in HandleMessage is the compound "U123|DisplayName"
form, which produces a different cache key than the clean "U123" used
in the group-history path (handlers.go:178). Use userID (extracted
clean ID) to ensure cache dedup works correctly.

---------

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-19 14:02:19 +07:00
Luan VuandLuvu182 a38d972438 fix: agent update API missing is_default, budget, subagents fields (#270)
The HTTP allowlist (agentAllowedFields) was missing three fields that
the web UI sends when saving agent settings: is_default,
budget_monthly_cents, and subagents_config. These fields were silently
filtered out by filterAllowedKeys(), so saves appeared successful but
values never persisted to the database.

Also adds exclusive-default logic: when setting is_default=true on an
agent, any previously-default agent is automatically unset first.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-19 13:59:49 +07:00
Luan VuandLuvu182 73897b9481 refactor: replace magic numbers and hardcoded strings with named constants (#269)
Extract scattered magic numbers and hardcoded agent type strings into
centralized named constants to improve maintainability and prevent
silent inconsistencies when defaults need to change.

Changes:
- Add config/defaults.go: DefaultContextWindow, DefaultMaxTokens,
  DefaultMaxMessageChars, DefaultMaxIterations, DefaultTemperature,
  DefaultHistoryShare
- Add providers/defaults.go: DefaultHTTPTimeout, SSE/Stdio scanner
  buffer size constants
- Replace 6 hardcoded 200000 (context window) across agent/, http/,
  cmd/ with config.DefaultContextWindow
- Replace 4 hardcoded 32000/32_000 (max message chars) with constant
- Replace 3 hardcoded 20 (max iterations) with constant
- Replace 2 hardcoded 0.75 (history share) with constant
- Replace 3 hardcoded 300*time.Second (HTTP timeout) with constant
- Replace 5 hardcoded scanner buffer sizes with named constants
- Replace 10 hardcoded "predefined" string comparisons with
  store.AgentTypePredefined constant (already defined but unused)
- Use config.DefaultTemperature instead of hardcoded 0.7 in agent loop

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-19 13:59:32 +07:00
viettranx 2c530f67c3 fix(ui): agent delete dialog + channel managers cleanup + combobox iOS
- agents-page: ConfirmDeleteDialog with type-to-confirm, expanded warning
- channel-managers-tab: simplify add form (remove manual name/username
  fields, auto-fill from contact search), Loader2 spinner on refresh
- combobox: text-base md:text-sm to prevent iOS Safari auto-zoom
2026-03-19 13:35:57 +07:00
viettranx 8a63e38ecd feat(ui): redesign permissions tab with file writer support
- Add file_writer to CONFIG_TYPES with contextual descriptions
- Smart scope select: dynamic group scopes from existing rules + Combobox
  for custom group IDs (e.g. group:telegram:-100456)
- Auto-fill metadata (displayName/username) from contact selection
- Two-section display: File Writers grouped by scope, Config Permissions flat
- Add metadata field to ConfigPermission interface + grant()
- i18n: permission type descriptions + scope labels in 3 locales
2026-03-19 13:35:57 +07:00
viettranx 6595c0185a refactor: merge file writers into config permissions + agent hard delete
Migration 000023:
- Migrate group_file_writers data into agent_config_permissions (config_type='file_writer')
- Add FK CASCADE/SET NULL on 16 tables for agent hard delete
- Clean up soft-deleted zombie agents
- ALTER scope VARCHAR(100) → VARCHAR(255)
- Partial unique index on agent_key (allows reuse after delete)

Store layer:
- Remove GroupWriterCache, GroupFileWriterData, 5 AgentStore methods
- Add CheckFileWriterPermission (fail-open), ListFileWriters (cached)
- Add scope filter to ConfigPermissionStore.List()

Tools: Replace GroupWriterCache with ConfigPermissionStore in read_file,
write_file, edit, cron, context_file_interceptor

Agent loop: buildGroupWriterPrompt uses ListFileWriters + metadata parsing

Channels: Telegram commands rewritten to use configPermStore.Grant/Revoke/List
HTTP API: Writer endpoints rewired to configPermStore (same response shapes)

Hard delete: agents.go DELETE FROM instead of soft delete
2026-03-19 13:35:57 +07:00
viettranx 68a4ee39b4 feat: config permissions RPC + UI tab + contact search improvements
Backend:
- Add config.permissions.list/grant/revoke RPC methods
- Auto-fill granted_by from caller's context identity
- Fix Telegram contact display_name: FirstName + LastName (was FirstName only)
- Change contact search to prefix match (term%) for B-tree index usage

Frontend:
- Add Permissions tab to agent detail with inline add row layout
- Contact search combobox shows name, @username, sender_id, [channel]
- Reduce contact search debounce from 300ms to 150ms
- i18n keys for en/vi/zh
2026-03-19 13:35:57 +07:00
viettranx b6ff57ddab fix(ui): heartbeat logs crash + header/card improvements
- Fix HeartbeatLog interface field names (snake_case → camelCase) to match
  backend JSON tags — fixes white screen crash on logs dialog open
- Format duration with formatDuration(), show token usage per log entry
- Fix logs dialog padding to match standard pattern + close button overlap
- Show model override and last error on heartbeat card
- Replace status badge with colored dot in agent header
- Compact header badges on mobile (icon-only for evolving/static)
- Switch sidebar to tablet breakpoint (1024px) for earlier collapse
2026-03-19 13:35:57 +07:00
viettranx ef1a1463ac docs: add DB query reuse rule to post-implementation checklist 2026-03-19 13:35:57 +07:00
viettranx 4e9f155a4c feat(agent): adaptive tool timing with slow tool notification
Track per-tool execution time statistics in session metadata. When a tool
call exceeds its adaptive threshold (2x historical max, min 120s default),
send a direct outbound notification to the user.

- ToolTimingMap: parse/serialize/record/threshold from session metadata
- StartSlowTimer: fires once per tool call, auto-cancels on completion
- Team config: slow_tool toggle (default on, always direct, never leader)
- UI: toggle in team settings with i18n (en/vi/zh)
- Store: add GetSessionMetadata to session store interface
2026-03-19 13:35:57 +07:00
viettranx 0df619023c feat(tools): block binary in read_file, add workspace path to read_image
- read_file: reject binary files (images, audio, video, archives) with
  helpful error pointing to the correct specialized tool
- read_image: add optional `path` parameter to analyze workspace/generated
  images via vision API (with workspace restriction + denied path checks)
2026-03-19 13:35:57 +07:00
viettranx 1e2ca2df7c fix(agent): improve team lead delegation messaging + group chat reply hint
- Team lead: no completion language after delegating, no question phrasing
- Group chat: inject reply context hint (NO_REPLY when reply addresses others)
- Both v1 and v2 team lead sections updated
2026-03-19 13:35:57 +07:00
Goon 6085e724a2 Merge pull request #275 from nextlevelbuilder/fix/release-docker-build
fix(ci): inline Docker build into release workflow
2026-03-19 12:33:22 +07:00
Goon 391a263709 fix(ci): make discord notify resilient to docker failures + DRY env vars
- notify-discord uses always() so it fires even if docker jobs fail
- Move GHCR_IMAGE/DOCKERHUB_IMAGE to workflow-level env block
2026-03-19 12:29:22 +07:00
Goon a3a4d18e9a fix(ci): inline Docker build into release workflow
go-semantic-release creates tags via GITHUB_TOKEN, which doesn't
trigger other workflows (GitHub anti-loop policy). This caused
docker-publish.yaml to stop firing since v1.2.0.

Move Docker image builds (7 variants + web) directly into release.yaml
as parallel jobs alongside build-binaries.
2026-03-19 12:23:54 +07:00
Luan VuandLuvu182 f21bf81280 fix: null-safe access on changes array in events page (#274)
Go can serialize empty slices as null in JSON. The events page
accessed p.changes.length without null checking, causing a crash
when viewing team.updated or agent_link.updated events with null
changes arrays.

Fixes #192

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-19 12:22:18 +07:00
Luan VuandLuvu182 ba9b5a6be3 feat(ui): show required API scopes for Feishu/Lark channels (#268)
* feat(ui): show required API scopes for Feishu/Lark channels

Add a collapsible info panel listing the required Lark/Feishu API
permissions (scopes) on the channel create/edit dialog and config
detail tab. Includes reminder about Contact Range and app publishing.

* fix(feishu): annotate DM messages with sender name

Feishu DMs were missing the [From: ...] annotation, so the agent
couldn't identify who was messaging. Group messages already had this.
Align with Telegram channel which annotates both DM and group messages.

---------

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-19 08:47:48 +07:00
Duc Nguyenandviettranx 23d0b5eb0b fix(providers): auto-clamp max_tokens on model rejection (#267)
* fix(providers): auto-clamp max_tokens on model rejection + fix verify for reasoning models

When OpenAI-compat models reject max_tokens as too large (e.g. gpt-3.5-turbo
supports 4096 but we send 8192), parse the model's stated limit from the 400
error, clamp the value, and retry once. This fixes agent creation for models
with lower output token limits without hardcoding model names.

Also increase the provider verify endpoint's max_tokens from 1 to 50 so
reasoning models (gpt-5, o-series) have enough headroom for internal
reasoning during the check call.

Closes #248, closes #245

* refactor(providers): extract chat retry closure + fix clamp log key

- Extract duplicate retry closure into chatRequestFn() to follow DRY
- Fix slog logging wrong key: body["max_tokens"] was nil for reasoning
  models that use max_completion_tokens — now uses clampedLimit() helper
- Remove unnecessary _ = resp in provider verify endpoint

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-19 08:41:20 +07:00
viettranx 74cf7e52f5 fix(ui): heartbeat dialog tweaks — timezone select, interval width, layout
- Replace timezone text input with Select dropdown using shared IANA_TIMEZONES
- Extract IANA_TIMEZONES to constants.ts (reused by cron-section)
- Shrink interval input width (w-24 → w-[4.5rem]) for 3-digit fit
- Fix schedule section layout: flex-based with fixed-width time inputs
- Default isolatedSession to false
2026-03-19 08:39:13 +07:00
Duc Nguyenandviettranx 2cc9d68cdc fix(tts): config save, Edge provider, media dispatch + dark mode chat (#265)
* fix(tts): config save + Edge provider registration + dark mode chat bubbles

- Wrap TTS config payload in `raw` field for config.patch RPC (#229)
- Always register Edge TTS provider (free, no API key) instead of gating on `enabled` flag
- Fix low-contrast user message bubbles in dark mode chat

* fix(tts): skip duplicate media dispatch when temp file already delivered

When both the agent loop and the message tool dispatch the same TTS
temp file, the first dispatch succeeds and cleanup deletes it. Filter
out missing temp media files before sending to prevent "file not found"
errors and spurious error notifications on Telegram/Slack/Discord.

* feat(tts): include edge-tts in Docker image when Python enabled

Edge TTS is free (no API key) and serves as a universal TTS fallback.
Install it alongside Python in both ENABLE_PYTHON and ENABLE_FULL_SKILLS builds.

* chore(docker): expose build args from .env for compose builds

Pass ENABLE_OTEL, ENABLE_PYTHON, ENABLE_FULL_SKILLS as env-driven
build args so .env can control Docker build features without editing
docker-compose.yml directly.

* fix(tts): hot-reload TTS config on settings change via pub/sub

TTS providers were only registered at startup, so changing provider/API
key via the Web UI had no effect until container restart. Add a
tts-config-reload bus subscriber that rebuilds the TTS manager on
config changes, matching the pattern used by quota, cron, and web_fetch.
Always create a TtsTool at startup (even without providers) so the
reload subscriber can populate it when settings are first configured.

* fix(tts): protect TtsTool.UpdateManager with RWMutex to prevent data race

UpdateManager() can be called from the config reload goroutine while
Execute() reads t.manager concurrently from agent goroutines. Add
sync.RWMutex following the same pattern as WebFetchTool.UpdatePolicy().

Also update setupTTS doc comment which incorrectly stated it could
return nil — Edge TTS is now always registered.

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-19 08:21:06 +07:00
viettranx 545941eeb4 fix(ui): heartbeat dialog layout tweaks + skills sort order
- Move checklist section to bottom of heartbeat dialog
- Reduce checklist textarea height (rows 8, min-h 120/200px)
- Widen interval input (w-24)
- Skills list: sort ungranted skills first
2026-03-18 23:26:24 +07:00
viettranx 5b349db7eb feat(heartbeat): provider/model override + fix cache invalidation
- Add ProviderModelSelect to heartbeat config dialog (allowEmpty, verify button)
- Backend: accept providerName in HEARTBEAT.SET, resolve to UUID via GetProviderByName
- Add ModelOverride to RunRequest, used by Loop when set (cheaper model for heartbeat)
- Ticker passes heartbeat model override to agent RunRequest
- Fix: InvalidateCache after UpdateState so ListDue picks up new next_run_at immediately
- i18n: add sectionModel/modelHint keys (en/vi/zh)
2026-03-18 23:02:48 +07:00
Keith 74c9fdd66b fix: add database name to postgres healthcheck (#203)
When POSTGRES_USER differs from POSTGRES_DB, pg_isready defaults to
connecting to a database named after the user, causing healthcheck
to fail with "database does not exist".
2026-03-18 22:45:24 +07:00
Duc Nguyenandviettranx dc51018563 fix: subagent provider routing + api_base fallback (#262)
* fix(subagent): inherit parent agent's provider instead of alphabetical fallback

Subagents previously used a fixed provider (alphabetically first from the
registry, often "anthropic") regardless of which provider the parent agent
used. This caused invalid combos like anthropic/glm-5 when a zai-coding
agent spawned subagents.

- Pass provider registry to SubagentManager for runtime resolution
- Inject parent provider name into context (WithParentProvider)
- Resolve activeProvider from parent context before LLM call
- Fix trace spans to show actual resolved provider, not default

* fix(providers): api_base fallback from config/env for DB providers

DB providers with empty api_base now inherit from config/env vars
(e.g., GOCLAW_ANTHROPIC_BASE_URL). Prevents proxy API keys from being
sent to the real provider API endpoint.

- Add APIBaseForType() method on ProvidersConfig
- registerProvidersFromDB falls back to config when api_base is empty
- ProvidersHandler uses resolveAPIBase() for model listing
- Add api_base, display_name, settings to provider validation whitelist

* fix(tracing): pass resolved provider name to subagent span emitters

- emitSubagentSpanStart now accepts providerName param instead of
  reading sm.provider.Name() — ensures root subagent span reflects
  the inherited parent provider, not the fallback default
- registerInMemory now uses resolveAPIBase() so DB providers with
  empty api_base inherit the config/env fallback (same as startup path)

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-18 22:40:49 +07:00
Luan VuandLuvu182 b379c3ba30 fix(feishu): align WebSocket protocol with Lark SDK + fix incorrect UI labels (#263)
Backend — WSClient protocol fixes (larkws.go):
- Parse service_id from WS URL query params instead of hardcoding 0
- Update all 4 server config values from pong payload (PingInterval,
  ReconnectCount, ReconnectInterval, ReconnectNonce)
- Use server-configured reconnect params instead of hardcoded 120s wait
- Return HTTP 500 in ACK when event handler fails (enables Lark retry)
- Filter data frames by type header — only process "event" frames
- Report actual processing time in biz_rt header (was hardcoded "0")

Backend — event adapter (feishu.go):
- Return parse error from HandleEvent so ACK reflects failure status

UI — fix incorrect Feishu channel form labels:
- Remove "webhook only" from Lark Global domain label (WebSocket works
  on both Lark Global and Feishu China)
- Remove "Feishu only" from WebSocket option label
- Change default connection_mode from "webhook" to "websocket" (matches
  backend default)
- Add showWhen conditional field support to ChannelFields component
- Hide webhook_port, webhook_path, encrypt_key, verification_token when
  WebSocket mode is selected
- Update i18n labels in all 3 locales (en, vi, zh)

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-18 17:51:09 +07:00
viettranx 2504095dfe fix(agents): complete shell deny groups propagation chain
ShellDenyGroups was defined in SystemPromptConfig but lacked full propagation
through parser, Loop fields, context injection, and system prompt population.
Per-agent overrides from other_config JSONB had zero runtime effect.

Changes:
- agent_store.go: Add ParseShellDenyGroups() to extract overrides from JSONB
- loop_types.go: Add shellDenyGroups field to Loop and LoopConfig, wire in NewLoop
- resolver.go: Wire agent-parsed shell deny groups into LoopConfig
- loop.go: Inject shellDenyGroups into context via store.WithShellDenyGroups
- loop_history.go: Populate ShellDenyGroups in system prompt config
- message_test.go: Fix macOS symlink path normalization in test expectations

Fixes test failures on macOS where /var/folders symlinks to /private/var/folders.
2026-03-18 17:04:26 +07:00
viettranx b818de6d63 fix(heartbeat): delayed refresh after toggle/update, taller checklist textarea
- Wait 2s after toggle/update before refreshing config for accurate countdown
- Checklist textarea: min-h-[200px] mobile, min-h-[400px] desktop, resizable
2026-03-18 16:48:01 +07:00
viettranx 551184c5b2 fix(heartbeat): delayed refresh after toggle/update for accurate countdown
Wait 2s after toggle/update before refreshing config from backend to
ensure nextRunAt is computed and persisted. Prevents stale countdown
in header heartbeat button.
2026-03-18 16:43:41 +07:00
Nguyễn Hoàng Thức b7d8d52f5e fix(ui): dark mode white dropdown on language and timezone selects (#255)
* fix(ui): use color-scheme dark for select dropdowns in dark mode

Language and timezone selects rendered with white native dropdown in
dark mode because browser ignores CSS bg-transparent on <option>.
Adding dark:scheme-dark tells the browser to render the native select
dropdown using dark color scheme in dark mode.

* fix(ui): replace native select with Radix UI Select for dark mode fix

Native <select> dropdown ignores CSS styling — browser always renders
the popup with its default (light) color scheme, causing white box in
dark mode. Replace language and timezone selectors with Radix UI Select
which renders custom HTML/CSS dropdowns that fully respect dark mode.

- Use bg-popover/text-popover-foreground from ui/select.tsx (dark-aware)
- Remove unused handleLanguageChange handler
- Use Tailwind v4 canonical **:data-radix-select-icon:hidden to hide chevron
2026-03-18 16:38:33 +07:00
viettranx 2141852945 fix(ui): hide [System] nudge messages from web chat and session viewer
Bootstrap nudge messages (role=user, [System] prefix) are internal
prompts for the model, not meant for end users. Filter them from
chat-thread render and recognize both [System] and [System Message]
prefixes in session detail page.
2026-03-18 16:38:15 +07:00