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.
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.
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
- 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>
- 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)
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.
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
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.
- 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
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"
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.
- Split EnsureUserFilesFunc into EnsureUserProfileFunc (profile + workspace)
and SeedUserFilesFunc (context file seeding) for single-responsibility
- Merge userWorkspaces + userFilesSeeded sync.Maps into unified userSetups
struct to prevent desync between workspace and seeding state
- Add skipIfAnyExist param to SeedUserFiles to encapsulate the
"seed only for brand-new users" logic within the bootstrap package
- Extract getOrCreateUserSetup helper for clean per-user initialization
- Add bootstrap state tests covering all 4 system prompt branches
- Keep legacy EnsureUserFilesFunc as fallback for backward compatibility
- Separate file seeding from workspace resolution so agents without
workspace still get BOOTSTRAP.md and USER.md seeded
- Always seed context files for existing profiles that have zero files
(handles EnsureUserProfile pre-creation via HTTP API)
- Add persistent "USER PROFILE INCOMPLETE" nudge in system prompt when
BOOTSTRAP.md is cleaned up but USER.md remains blank
- Move bootstrap auto-cleanup nudge before session flush so the reminder
is persisted to history
- Add userFilesSeeded sync.Map to avoid redundant seeding calls
- Capture workspace from seeding call to eliminate double DB roundtrip
- Remove USER_PREDEFINED.md from summoning file list (web + desktop)
- Soften canvas-dots pattern (smaller dots, wider spacing)
- Require at least 1 member when creating a team
- Prevent removing the last non-lead member from team
Browse, upload, download, move, and delete workspace files from the
desktop app. File tree with drag-and-drop move (@dnd-kit/core), syntax
highlighting (react-syntax-highlighter), markdown/CSV/image viewers,
upload dialog with drag-drop + validation, and SSE storage size streaming.
The case expression `taskActionFlags.Progressed || ...` always evaluates
to `true/false`, never matching the switch value. Merge it into the
default branch so non-terminal actions consistently auto-complete.
PR #511 removed WithMediaImages context in file-ref mode, breaking
read_image when LLM omits the path param. Restore base64 context as
fallback (costs Go memory, not LLM tokens). Also improve error messages
to distinguish missing provider config vs provider failures.
The Makefile `net` target creates goclaw-net via `docker network create`
without the `com.docker.compose.network` label. Docker Compose then rejects
the network on macOS Docker Desktop. Compose already manages this network
automatically with correct labels.
Closes#488
* fix(vision): file-ref mode + media pipeline fixes for image visibility
Replace inline base64 image loading with file path references when
read_image provider is configured. LLM calls read_image(path=...)
instead of receiving 25K-250K tokens of base64 per image.
Changes:
- Agent loop: skip base64 context storage in file-ref mode, only
load historical images for inline fallback
- New enrichImagePaths() enriches ALL user messages with file paths
(not just current turn) so historical images are accessible
- System prompt: imperative tool summaries ("REQUIRED when you see
<media:image> tags") + dedicated "Media Files" section
- Slack: store mediaPaths in pending history + CollectMedia on mention
- Feishu: add CollectMedia for group context history media
- RC-1 fix: save enriched content (with media IDs/paths) to DB
instead of raw req.Message
Closes#509
* fix(vision): resolve Claude review — Feishu early media + skip loadImages in file-ref mode
- Feishu: download media BEFORE mention gate (step 4) and store file
paths in pending history via Media field. Reuse early-resolved
MediaInfo at step 10 to avoid double-download. CollectMedia now
returns actual paths instead of empty.
- Agent loop: guard loadImages() behind !deferToReadImageTool so
file-ref mode avoids unnecessary disk I/O + base64 encoding.
ws-client.ts treated any UNAUTHORIZED WebSocket response as session
invalidation, triggering full logout. This caused clicking the TTS tab
to log users out because config.get requires owner role while the route
only required admin.
- Remove UNAUTHORIZED from onAuthFailure trigger in handleResponse;
only TENANT_ACCESS_REVOKED forces logout now
- Change TTS route guard from RequireAdmin to RequireOwner (matches
backend requireOwner on config.get/config.patch)
- Gate TTS sidebar item behind isOwner so non-owners don't see it
Closes#501
* fix(feishu): increase reply context max length from 500 to 2000
500 bytes is too short for CJK/Unicode text (each accented char = 2-3
bytes, so ~250 real characters). Increase to 2000 so reply context is
not aggressively truncated.
* fix(teams): handle NULL data column in task events queries
ListTaskEvents and ListTeamEvents crash with "unsupported Scan,
storing driver.Value type <nil> into type *json.RawMessage" when the
data column is NULL. Use COALESCE(data, '{}') to return an empty JSON
object instead.
---------
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
- toFileUrl: normalize backslashes, detect Windows drive-letter paths
- resolveFileUrl: strip MEDIA: prefix from tool results, Windows path support
- handleServe: skip prepending "/" for Windows drive-letter URL paths
Leader-created tasks copied media files to workspace and stored paths
in task metadata, but never inserted into team_task_attachments table.
Members got auto-attached via WorkspaceInterceptor context, but leaders
don't run inside a task context. Now explicitly call AttachFileToTask
after CreateTask succeeds. Dedup handled by ON CONFLICT DO NOTHING.
- Add TeamSettingsModal: editable name/description, member management
(add/remove with minimum 1 member guard), notification toggles (5 events
+ direct/leader mode)
- Extend backend teams.update to support name/description fields
- Add gear button + "+" info button to team board header
- Task detail modal: fetch full detail with attachments on open,
render attachment list with download links (resolved against local gateway)
- Chat view TaskPanel: clicking active tasks opens TaskDetailModal
- KanbanCard: show comment and attachment counts
- Extract 14 shared SVG icons to Icons.tsx, replace all inline SVGs
in team components (0 remaining)
- Extract TERMINAL_STATUSES and TeamNotifyConfig to shared types
- i18n: en/vi/zh settings, members, notification, attachment keys
Non-owner tenant switch set only tenant_hint in localStorage, but WS
Path 1 non-owner and HTTP client only read tenant_id — causing silent
fallback to MasterTenantID.
Frontend: always set TENANT_ID on switch; keep TENANT_HINT for pairing compat.
Backend: WS Path 1 non-owner now falls back to TenantHint before deprecated TenantScope.
When users send media in a group without mentioning the bot, store
Telegram file_ids as lightweight MediaRef in history entries (no
download). When the bot is mentioned, resolve refs by downloading
media and including them in the LLM context.
Safeguards: 5 MB file size cap, max 15 refs per mention, 30s batch
timeout. Mirrors existing CollectMedia pattern from Discord/Zalo.
- fix Rules of Hooks violation in chatgpt-oauth-routing-section
- add stale-while-revalidate with atomic dedup for RouteEligibility
- move raw SQL from HTTP handler to TracingStore.ListCodexPoolSpans
- persist round-robin state in Registry shared counter
- extract duplicated frontend helpers to agent-display-utils
- split oversized frontend files (964→214 lines max)
- add GIN indexes for spans.metadata and sessions.metadata
- fix tenant-aware provider lookup in handleQuota
- separate empty-role vs error handling in resolveTenantHint
- scope pool validation to chatgpt_oauth providers only
- wrap buildEntries in useCallback for stable useMemo deps
- document OAuth concurrent auth limitation and RoleAdmin breaking change
* feat(auth): support named chatgpt oauth providers
- add provider-scoped ChatGPT OAuth routes and CLI support
- persist refresh tokens per provider and reject provider-type collisions
- wire provider OAuth setup flows in the dashboard and setup UI
Refs #448
* feat(agent): add chatgpt oauth account routing
- add agent other_config routing for manual and round-robin selection
- reuse routed provider resolution across resolver and pending loaders
- add router, parser, and agent advanced dialog coverage for multi-account use
Refs #448
* docs(api): describe chatgpt oauth routing
- document named-provider ChatGPT OAuth auth routes
- describe agent-side account routing and round-robin behavior
- update OpenAPI agent config schema and provider type enum
Refs #448
* fix(store): add missing agent key context helpers
* feat(ui): clarify chatgpt oauth account setup and routing
* docs(providers): align chatgpt oauth alias examples
* feat(agent): add codex pool activity dashboard
* fix(providers): harden codex oauth alias setup
* feat(codex-pool): improve routing dashboard UX
- redesign the Codex/OpenAI pool page around saved-pool checkpoints and live evidence
- add clearer selection, attention, and recent-proof states for pool members
- make the lower panels fill the remaining desktop viewport while staying responsive
* fix(store): resolve context helper merge duplication
* feat(oauth): add codex pool quota and observation APIs
- add quota inspection and observation endpoints for ChatGPT Subscription (OAuth) providers
- teach codex routing to surface pool activity, observation metadata, and quota-aware readiness
- extend tests and HTTP docs/OpenAPI for the new pool monitoring flows
* feat(web): add codex pool quota monitor and controls
- add provider quota fetching, readiness badges, and live routing evidence on the account pool page
- redesign pool setup and activity panels for multi-account management with localized copy updates
- keep the live monitor internally scrollable and compact the account cards for better viewport fit
* fix(web): clarify pool routing labels
- rename the recent request badge from Direct to Selected
- restore compact quota bars in the live pool cards
* feat(codex-pool): add runtime health dashboard
- derive per-provider success and failure health from routed Codex traces
- surface routing, quota, and recent request evidence in the pool UI
- align provider alias guidance and owner access with the dashboard role model
* docs(auth): document tenant scoping and key roles
* fix(auth): harden tenant and codex pool access control
* fix(providers): align codex pool runtime defaults
* feat(ui): tighten codex pool responsive layout
* feat(chatgpt-oauth): refine codex pool management UX
* feat(chatgpt-oauth): surface quota bars on provider pages
- add compact quota bars to Codex provider rows and provider detail
- fetch quota only for ready visible provider rows and ready detail aliases
- fix managed-member detail visibility and tighten provider locale copy
* feat(telegram): add yield mention mode for multi-bot group support
- Add "yield" mention_mode: bot responds to all messages unless another
bot is explicitly @mentioned, enabling shared groups where bots coexist
- Skip messages from other bots (user.IsBot) in yield mode to prevent
infinite cross-bot loops
- Enter mention gate for yield mode even when require_mention is false
- Add display_name to channel instance update allowlist
- Change default dev postgres port to 5444 to avoid conflicts
* fix(telegram): address yield mode review — pairing guard, mention check, naming
- Add pairing guard to bot-skip path in yield mode (prevent history
recording in unpaired groups — matches existing security pattern)
- Check detectMention before skipping other bots' messages (allow
cross-bot @commands like "@my_bot help" from another bot)
- Rename hasOtherBotMention → hasOtherMention (function yields on any
@mention, not just bots — name now matches behavior)
- Remove dead MentionMode field from SlackConfig (no Slack handler uses it)
- Add mention_mode validation with warning log for unknown values
* fix(ui): swap mention_mode and require_mention field order
* fix(i18n): add translations for require_mention disabled hint
* fix(ui): apply mention_mode disabled hint to group override tab
* fix(i18n): improve mention_mode UI labels for clarity
Replace developer jargon (Strict/Yield) with user-friendly labels:
- "Mention Mode" → "Group Response Behavior"
- "Strict" → "Default (follow @mention setting)"
- "Yield" → "Multi-bot (respond unless another bot is @mentioned)"
---------
Co-authored-by: viettranx <viettranx@gmail.com>
- executeProgress: early-exit on terminal tasks using pre-fetched status,
check TaskActionFlags before re-querying DB, auto re-assign on stale recovery
- executeComplete: handle already-completed/failed/cancelled gracefully,
auto re-assign pending tasks reset by stale recovery ticker
- Increase taskLockDuration 30min→60min, heartbeat 5min (was 10min)
for 12x safety margin against stale recovery race conditions
- Add diagnostic logging in UpdateTaskProgress when 0 rows affected