Commit Graph
662 Commits
Author SHA1 Message Date
Thieu Nguyen 05c8b1f1dc [In Progress] fix(ui): show toast error when package install/uninstall fails (#440)
* fix(ui): show toast error when package install/uninstall fails

HttpClient throws ApiError on HTTP 500 responses, but installPackage
and uninstallPackage hooks did not catch the exception. This caused
the error to propagate past the toast logic, so users saw no feedback
when a system package failed to install.

Wrap http.post calls in try/catch to always show toast with the server
error message. Update PackageSection handlers to check res.ok instead
of relying on try/catch for flow control.

* fix(ui): truncate verbose package error in toast notification

Extract last meaningful error line (e.g. "ERROR: ...") from package
manager output instead of dumping the full multi-line response into
the toast, which caused text overflow.

* fix(ui): wrap long text in toast instead of truncating

Add break-words and min-w-0 to toast content so long error messages
(URLs, package manager output) wrap properly within the toast element.
Remove shortError helper — show full error with proper wrapping.

* fix(ui): constrain toast container width to prevent overflow

Move max-w-sm from toast item to the fixed container so long text
(URLs, multi-line errors) is properly bounded. Add overflow-hidden
on toast items as a safety net.

* fix(pkg-helper): re-add packages cascade-removed by apk del

apk del removes orphaned dependencies, which may include other
user-installed packages if they were pulled in as transitive deps.
After uninstalling, check which persisted packages are still present
and re-install any that were accidentally removed.

* revert: remove cascade re-add logic from pkg-helper

apk del does not cascade-remove other world packages (verified via
Docker test). The re-add logic was based on incorrect assumption.
Root cause of the all-packages-deleted issue needs further investigation.
2026-03-24 21:15:22 +07:00
therichardngai-code 00db30c2d1 fix(agent): set TenantID on streaming events in runLoop emitRun (#445)
The emitRun wrapper in runLoop (loop.go) is missing TenantID, causing
all streaming events (chunk, thinking, tool.call) to be emitted with
TenantID=Nil. The event filter (event_filter.go:43-44) blocks unscoped
events for non-admin clients, so tenant-bound API key users receive no
streaming — only run.started and run.completed (emitted from Run() in
loop_run.go which already sets TenantID).

Fix: add event.TenantID = l.tenantID, matching the pattern in
loop_run.go's emitRun wrapper.
2026-03-24 21:14:48 +07:00
Duc Nguyenandviettranx af3fe2fe08 fix: add panic recovery to tool, cron, and summarization goroutines (#420)
* fix: add panic recovery to tool, cron, and summarization goroutines

A panicking tool, cron job, or auto-summarization goroutine crashes
the entire server process — disconnecting all clients and aborting
all running agents. Add defer/recover at three levels:

- safeExecute() in tools/registry.go: catches panics from any tool's
  Execute method and returns an error result to the LLM
- Agent loop goroutine (loop.go): defense-in-depth for non-tool code
  in the parallel tool execution goroutine
- Cron execution goroutine (service_execution.go): prevents a single
  panicking cron job from taking down the server
- Summarization goroutine (loop_history.go): prevents background
  auto-summarization panics from crashing the process

All recovery points log the panic value and stack trace at ERROR level
for debugging. Tool execution panics return actionable error messages
to the LLM so the agent can recover gracefully.

* refactor: extract panic recovery into safego.Recover helper

DRY the duplicated recover+stack+log boilerplate across 4 goroutine
sites into a shared safego.Recover(onPanic, attrs...) function.
Stack buffer bumped from 4096 to 8192 bytes.

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-24 21:09:16 +07:00
Duc Nguyenandviettranx ebc82d3265 fix: clear cron session in DB when not cached (cold-cache after restart) (#424)
* fix: clear cron session in DB when not cached (cold-cache after restart)

Fixes #365

PGSessionStore.Reset() only cleared the in-memory cache. After a server
restart the cache is empty, so Reset was a no-op — the next GetOrCreate
loaded the full accumulated history from DB, causing LLM tool loops from
contradictory context.

Add a DB fallback: when the session isn't in cache, issue a direct
UPDATE to clear messages and summary in PostgreSQL. This ensures cron
sessions always start clean regardless of server restarts.

The #294 fix (Reset+Save before each cron run) already had the right
intent but only worked within the same server lifetime.

* fix: move DB call outside mutex and log errors in Reset cold-cache path

ExecContext was running under s.mu.Lock(), blocking all session cache
operations during DB round-trip. Release the lock before the DB call.
Also log ExecContext errors instead of silently discarding them —
silent failure defeats the purpose of the cold-cache fix.

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-24 20:47:44 +07:00
viettranx 4276c043c6 fix(ui): scope API key create dialog to current tenant only
Owner sees only "System" + current tenant in dropdown (not all tenants).
Default selection is current tenant instead of system.
2026-03-24 20:45:54 +07:00
viettranx 7aa47d1baf refactor(security): remove cross-tenant bypass, enforce strict tenant isolation
- Add RoleOwner ("owner") to permissions hierarchy (above admin)
- Replace all IsCrossTenant/client.IsCrossTenant() permission guards with
  IsOwnerRole/client.IsOwner() role-based checks (20+ locations)
- Remove cross-tenant bypass from tenantClauseN SQL helper — always adds
  AND tenant_id = $N (fail-closed)
- Add explicit unscoped store methods for startup: ListAllProviders,
  ListAllInstances, ListAllEnabled, GetByIDUnscoped, GetTeamUnscoped
- Remove CrossTenant from auth layer: owner users get role "owner" with
  concrete tenant ID (MasterTenantID fallback), no bypass flag
- Remove crossTenant field from WS Client; connect response sends
  is_owner instead of cross_tenant
- Simplify event filter from 3-mode to 2-mode (tenant-scoped + owner sees system events)
- Fix channel instance loader to use inst.TenantID for agent lookup
- Fix agent embedding goroutine to capture tenant from caller context
- Fix provider cache invalidation to use event.TenantID
- Separate skills store: ListAllSystemSkills (system only) vs
  ListAllSkills (system + tenant-scoped), StoreMissingDeps restricted
  to system skills only
- Frontend: isCrossTenant → isOwner, add "owner" to UserRole type,
  RequireAdmin uses hasMinRole for owner > admin hierarchy
2026-03-24 20:40:59 +07:00
Minh NguyenandMinh Nguyen 7143dcf32c fix(ui): isolate contact search cache key (#434)
Co-authored-by: Minh Nguyen <gbut.277@gmail.com>
2026-03-24 16:05:03 +07:00
viettranx 651072a9ea feat(config): add system_configs DB table with per-tenant isolation and System Settings modal
- Add system_configs table (migration 029) with per-tenant key-value config
- Add SystemConfigStore interface with strict tenant isolation (no cross-tenant fallback)
- Add ApplySystemConfigs overlay: DB values → in-memory cfg at startup + after save
- Add System Settings modal (navbar gear icon) with 3 sections:
  - Embedding: verified 1536d models (OpenAI, OpenRouter, Mistral), custom verify endpoint
  - UX Behavior: tool_status, block_reply, intent_classify toggles
  - Pending Compaction: provider/model + threshold/keepRecent/maxTokens
- Fix block_reply label inversion: ON = delivers intermediate text (not suppresses)
- Fix embedding status endpoint to read from system_configs DB (not provider JSONB)
- Fix config page save to only sync current tenant (not all tenants)
- Fix bus event payload to use fresh context (prevent canceled request context)
- Remove config.json fallback for embedding provider/model resolution
- Add "More Config" link in modal footer to full config page
- Add i18n for system-settings namespace (en/vi/zh)
2026-03-24 16:01:02 +07:00
viettranx 0209f6f970 Revert "fix(http): propagate cross-tenant context in HTTP auth middleware (#427)"
This reverts commit 39b7c689f2.
2026-03-24 11:40:54 +07:00
Duc Nguyen 39b7c689f2 fix(http): propagate cross-tenant context in HTTP auth middleware (#427)
The requireAuth and requireAuthBearer middlewares correctly detect
cross-tenant admin status (gateway token + owner user ID) but never
call store.WithCrossTenant(ctx). This causes all HTTP tenant management
endpoints (GET/POST /v1/tenants, etc.) to return 403 "insufficient role"
because handlers check store.IsCrossTenant(ctx) which was always false.

The WebSocket path works correctly because the gateway Client object
tracks cross-tenant status independently via client.IsCrossTenant().

Fix: Add store.WithCrossTenant(ctx) in both auth functions when
auth.CrossTenant is true, matching the design of the WS path.

Affected endpoints:
- GET/POST /v1/tenants (list, create)
- GET/PATCH /v1/tenants/{id} (get, update)
- GET/POST/DELETE /v1/tenants/{id}/users (membership)
- Any future endpoint checking store.IsCrossTenant(ctx)
2026-03-24 11:28:53 +07:00
Duc Nguyenandviettranx eab3766c5f fix: check errors in cron store Scan and Unmarshal to prevent data corruption (#422)
* fix: check errors in cron store Scan and Unmarshal to prevent data corruption

Three unchecked errors in the cron store could silently corrupt data:

1. cron_update.go:44 — QueryRowContext().Scan() error ignored when
   fetching current schedule during partial updates. If DB fails,
   all schedule fields default to zero values, corrupting schedule_kind
   to empty string and bypassing all validation.

2. cron_update.go:167 — json.Unmarshal error ignored when parsing
   existing payload for merge. Corrupted JSON leaves payload zero-valued,
   wiping all existing fields (Message, Deliver, Channel, To) when
   only one field was being updated.

3. cron_scan.go:63 — json.Unmarshal error ignored when scanning cron
   job rows. Malformed payload JSON silently returns zero-valued payload,
   masking data corruption.

All three now return descriptive errors instead of proceeding with
corrupted data. NULL payload (valid case) is handled separately.

* fix: add NULL payload guard and check Marshal error in cron update

- Add len(payloadJSON) > 0 guard before Unmarshal in UpdateJob payload
  merge path, consistent with the same guard in scanCronRow. Without
  this, NULL payload (valid for new jobs) would return a false error
  instead of proceeding with the merge.

- Check json.Marshal error instead of discarding it, consistent with
  the error-checking theme of the parent commit.

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-24 10:43:08 +07:00
badgerbees 7fa2d201fb fix(agent): strip internal routing tags from outbound delivery (#416)
- Introduced StripMessageDirectives in the agent loop to remove internal
  [[name:value]] routing tags from user-facing content while preserving
  them in session store for model context
- Narrowed regex from wildcard `(?s)\[\[.*?\]\]` to structured
  `\[\[\w+(?::[^\]\n]+)?\]\]` to avoid false positives on legitimate content
- Preserved [[tts...]] tags for TTS AutoTagged pipeline compatibility
- Added 15 unit tests covering strip, preserve, and edge cases
- Fixed step numbering in agent loop (steps 5-9)
2026-03-24 10:24:10 +07:00
Luan VuandLuvu182 0881d7fc4e docs: use make up in Docker quick start, add optional service flags (#404)
- Update README Docker section to use `make up` instead of raw
  docker compose commands. `make up` handles network creation,
  version embedding from git tags, and database migrations.

- Add WITH_* flags to Makefile for optional compose services:
  WITH_BROWSER, WITH_OTEL, WITH_SANDBOX, WITH_TAILSCALE, WITH_REDIS.
  Uses WITH_ prefix to avoid conflicts with common env vars (e.g.
  BROWSER is set by many desktop environments).

- Document common make commands (up/down/logs/reset) and optional
  service flags with descriptions in a table format.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-24 10:00:13 +07:00
viettranx c43a8f125b fix(security): harden MCP bridge auth and add startup security warnings (#306)
MCP bridge now returns 403 when no gateway token is configured instead
of serving tools without authentication. Startup warnings added for
default Postgres password and open CORS configuration.
2026-03-24 09:59:07 +07:00
viettranx aadc4d6498 feat(teams): smart post-turn task decision, stale detection, and notification guardrails
- Add TaskActionFlags context-based tracking for member tool calls
  (Completed, Reviewed, Escalated, Progressed, Commented, Claimed)
- Replace aggressive auto-complete with flag-based switch/case:
  tool-completed → skip, reviewed → skip + renew lock,
  progress-only → renew lock (don't auto-complete),
  no flags → auto-complete (backward compat)
- Fix executeReject to use RejectTask (strict in_review guard)
  with auto re-dispatch that preserves dependent task blocking
- Add ticker Phase 4: mark in_review tasks stale after 4 hours
- Add ticker Phase 5: fix orphaned blocked tasks (all blockers terminal)
- Keep owner_agent_id during lock recovery for auto re-dispatch
- Add RunKind="notification" to team notify inbound messages
  with mutation blocking in TeamTasksTool (read-only enforced)
- Increase MessageBus buffer 500 → 1000
- Add ResetTaskStatus support for cancelled and in_review sources
2026-03-24 08:56:10 +07:00
viettranxandBadgerbees 6a2949de4a fix(telegram): clear draft after materializing DM stream
When using draft_transport in Telegram DMs, the preview draft sometimes
stays in the input area after the real message is delivered, showing a
brief duplicate reply.

Clear the stale draft by sending an empty sendMessageDraft as soon as
the final message is sent in Send(). The cleanup is asynchronous and
best-effort so it won't block message delivery.

Co-authored-by: Badgerbees <rapabelias@gmail.com>
2026-03-24 07:51:13 +07:00
viettranx 0bce64032a refactor(ui): move About button back to user menu dropdown 2026-03-24 00:09:42 +07:00
viettranx fe26f2d3e0 fix(ui): correct license to CC BY-NC 4.0 and move About to header
- Fix hardcoded MIT license → CC BY-NC 4.0 with link to CC website
- Move About button from user menu dropdown to topbar header
- Shorten menu label to "About" / "Giới thiệu" / "关于"
2026-03-24 00:06:54 +07:00
huyluseoandhuyluseo a550a13766 feat(ui): add About GoClaw dialog to user menu (#387)
* feat(ui): add About GoClaw dialog to user menu

- Add AboutDialog showing version, source code, license, docs, and bug report links
- Add "About GoClaw" item to UserMenu (with Info icon)
- Add i18n for en, vi, zh

* fix(docker): pass VERSION build arg to Dockerfile

Without this, Docker builds always produce `dev` as the version string.
Users can set GOCLAW_VERSION in .env or environment to embed the correct
version (e.g. from `git describe --tags --always`).

* feat(ui): show update status in About dialog

- Clean version display (strip git commit hash suffix)
- Show "Up to date" badge when on latest version
- Show update banner with link to release notes when new version available
- Fetch health data on dialog open to get latest update info
- Add i18n keys for update status (en, vi, zh)

* refactor(ui): clean version display across all UI locations

Extract cleanVersion() to shared utility. Apply to:
- About dialog (already had it inline)
- Sidebar connection status footer
- Overview page header

"v2.5.1-3-g4fd653c1" now displays as "v2.5.1" everywhere.

* feat(ui): show version in System Health card instead of header

- Add version cell with Tag icon to SystemHealthCard grid
- Green check when up to date, red when update available
- Revert cleanVersion from overview header (keep upstream behavior)
- Add i18n for systemHealth.version (en, vi, zh)

* fix(ui): move version to System Health card header to preserve grid layout

Version now shows in the card header row (right side) instead of as a
grid cell, keeping the original 6-cell (2x3) layout intact.

---------

Co-authored-by: huyluseo <thanhhuylu@example.com>
2026-03-23 23:53:06 +07:00
viettranx a7c34a6087 fix: address 4 bugs from issue #294 — context propagation, cron reliability, crash safety
1. Stale user_context_files after agent file update (#294.1)
   - Added PropagateContextFile() to AgentStore — single CTE query copies
     agent-level file to all existing user instances
   - Added `propagate` param to agents.files.set WS handler
   - Response includes `propagated` count

2. Cron session reuse — tool errors block future runs (#294.2)
   - Reset + Save cron session before each run
   - Each run starts clean, persisted to DB to survive restarts

3. Write-behind cache risk — crash loses messages (#294.3)
   - Added periodic checkpoint flush every 5 tool iterations
   - Messages persisted to session incrementally during long runs
   - Token calibration tracks checkpointed messages correctly

4. Silent cron delivery failures (#294.4)
   - Added slog.Warn when deliver=true but channel/chatID missing

Closes #294
2026-03-23 23:41:27 +07:00
viettranx e2dbd308f5 fix(ui): sync topbar nav item font sizes to text-sm
All topbar items (Documents, Language, Timezone, User menu) now use
text-sm consistently. Previously, non-select items used text-xs (12px)
while Select triggers inherited md:text-sm (14px) from the base
SelectTrigger component, causing a visible mismatch.
2026-03-23 23:41:10 +07:00
Nguyễn Hoàng Thức 0755df4998 fix(test): align TestResolveMediaPath with effectiveRestrict hardening (#381)
Two issues fixed:

1. effectiveRestrict() now always returns true (multi-tenant security),
   so the "unrestricted" test case expected wrong results — paths outside
   workspace are now blocked regardless of the restrict parameter.

2. Tests used bare Unix paths (/etc/passwd) which are not absolute on
   Windows (no drive letter), causing them to resolve inside the workspace
   and pass incorrectly. Added outsidePath() helper that prepends the
   volume name for cross-platform absolute paths.
2026-03-23 23:26:01 +07:00
7db55c67f6 fix(vision): fix Lark inline image parsing and MCP bridge read_image (#396)
* fix(vision): fix Lark inline image parsing and MCP bridge read_image

Two fixes for image reading through Lark channel with claude-cli provider:

1. Lark post messages use flat format {"content":[[...]]} but resolvePostElements
   only handled language-wrapped format {"zh_cn":{"content":[...]}}. Now checks
   flat format first, and skips non-map values in fallback iteration.

2. read_image tool failed via MCP bridge because bridge creates fresh HTTP context
   losing WithMediaImages. Fix: enrich <media:image> tags with path attribute
   (like documents), pass workspace through X-Workspace bridge header with HMAC,
   so read_image can load images from disk via path parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(security): add HMAC backward compat and guard workspace injection

- VerifyBridgeContext falls back to old signature format (without workspace)
  for sessions with pre-existing MCP configs, preventing 403 on server restart
- Workspace context injection now requires agent context (agentID/userID)
  to prevent unauthenticated path injection bypassing HMAC

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-23 23:11:33 +07:00
viettranx 462727ec8e fix(ui): reduce combobox focus ring to match Input component
ring-[3px] was clipping against dialog overflow boundaries.
Use ring-1 to match the standard Input component styling.
2026-03-23 22:57:49 +07:00
viettranx 83c79d1b3c feat(mcp): allow admin to set user credentials on behalf of any user
Add ?user_id query param to MCP credential endpoints so system admins
and tenant admin/owner can manage credentials for other users.

Backend:
- Add resolveTargetUserID() with permission checks (system admin,
  tenant admin/owner) and cross-tenant defense-in-depth
- Add TenantStore dependency to MCPUserCredentialsHandler

Frontend:
- Add searchable Combobox user selector for admin users in the
  credentials dialog (visible only when canManageUsers)
- Update credential API calls to pass optional userId param
- Add i18n keys for admin mode (en/vi/zh)
2026-03-23 22:57:39 +07:00
viettranx e1cb5c411b refactor(http): move gateway token to package-level auth state
Replace per-handler `token string` field with package-level
`pkgGatewayToken` via `InitGatewayToken()`, matching the existing
pattern for `pkgAPIKeyCache`, `pkgOwnerIDs`, etc.

- Remove `token` from ~30 HTTP handler structs and constructors
- Simplify `requireAuth(minRole, next)` signature (was 3 params)
- Simplify `resolveAuth(r)` signature (was 2 params)
- Rename `resolveAuthBearer` → `resolveAuthWithBearer` (clearer)
- Add `store.WithRole()`/`RoleFromContext()` to propagate caller
  role through context
- Inject role into context in `requireAuth` and `requireAuthBearer`
2026-03-23 22:57:26 +07:00
viettranx 5b749faa35 fix(teams): prevent leader self-assignment causing dual-session loop
Block team lead from being assigned as task owner across all dispatch
paths. When a leader assigns a task to itself, it gets dispatched to a
team session while still running on the main session — causing race
conditions, duplicate work, and potential infinite announce loops.

Guards added:
- executeCreate: reject assigneeID == team.LeadAgentID
- dispatchTaskToAgent: safety net auto-fail (retry/ticker edge cases)
- DispatchUnblockedTasks: auto-fail leader-owned pending tasks
- ProcessPendingTasks: skip before AssignTask
- executeRetry: explicit error before dispatch
- TeamsMethods.dispatchTaskToAgent: guard for WS/dashboard path

Also removed redundant GetTeamForAgent DB call in dispatchTaskToAgent
by passing *store.TeamData directly from callers.
2026-03-23 22:57:06 +07:00
viettranx 6e80a1af61 fix(skills): unblock agent read_file on skill paths and fix web UI file browser
- Add builtinSkillsDir + tenant-scoped dirs to read_file AllowPaths
- Use DB file_path in HTTP skill file handlers instead of hardcoded baseDir
- Add bundled dir fallback (system skills only) when managed copy is missing
- Tenant-scope publish_skill tool destination directory
- Extract walkSkillFiles/readSkillFile/skillSlugDir helpers (DRY)
- Return is_system from GetSkillFilePath for security-guarded fallback
2026-03-23 22:06:39 +07:00
viettranx b9d875472f refactor(ui): replace API Docs sidebar item with Documents header link and remove notification bell
- Remove "API Docs" from sidebar menu
- Add external "Documents" link (docs.goclaw.sh) to topbar header
- Remove unused notification bell icon from topbar
2026-03-23 18:05:27 +07:00
viettranx dc7f91b9f6 refactor(ui): simplify setup-skip storage logic
Remove side-effect migration code, unnecessary encodeURIComponent,
and implicit localStorage fallbacks from setup-skip utilities.
2026-03-23 17:50:01 +07:00
Minh NguyenandMinh Nguyen eaa0aecccf fix(ui): scope setup skip state by tenant (#372)
Co-authored-by: Minh Nguyen <gbut.277@gmail.com>
2026-03-23 17:49:17 +07:00
4868f6c4d8 feat(gateway): add version update checker (#374)
* feat(gateway): add version update checker

Check GitHub releases periodically (1h) and surface update availability
in the health endpoint + dashboard header. Version is auto-detected from
git tags via Makefile, falling back to VERSION file or build arg.

Backend: new UpdateChecker goroutine, health response includes
latestVersion/updateAvailable/updateUrl fields.

Frontend: version badge in overview header with clickable "available"
link when a newer release exists.

* fix(gateway): fix update checker compile errors and harden GitHub API call

- Export UpdateChecker/NewUpdateChecker to match server.go references
- Move StartUpdateChecker(ctx) after ctx declaration in gateway startup
- Add User-Agent header for GitHub API compliance
- Limit response body to 1MB via io.LimitReader
- Use strconv.Atoi instead of manual int parsing in parseSemver

---------

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-23 17:37:19 +07:00
viettranx 6b29ea056c fix(channels): propagate tenant_id to pending history for compaction/sweep
Channel factories create PendingHistory before InstanceLoader calls
SetTenantID on the channel, so tenantID was always uuid.Nil causing
"tenant_id required" errors on compaction sweep and auto-compact.

Fix: add SetTenantID to PendingHistory and propagate it from
InstanceLoader after channel creation. Also fix HTTP compact endpoint
using context.Background() instead of preserving request tenant context.
2026-03-23 17:28:49 +07:00
viettranx 59a52f1e2f fix(ui): replace bare crypto.randomUUID with uniqueId fallback and show WS disconnect error
- use-chat-sessions, skill-upload-dialog: replace crypto.randomUUID()
  with uniqueId() which has a UUID v4 fallback for non-secure contexts
  (HTTP without localhost) where crypto.randomUUID is unavailable
- use-chat-send: show localized error message when WebSocket is not
  connected instead of silently dropping the send
- Add error.notConnected i18n key to en/vi/zh chat locale files

Closes #352
2026-03-23 16:12:07 +07:00
viettranx 072d98a856 fix(channels): extract and download images from Lark post messages
Post messages with embedded img tags were only producing [image]
placeholder text — the actual image_key was discarded and never
downloaded. Now extractPostImageKeys() collects image keys from
post JSON, and resolvePostImages() downloads them via the existing
Lark resource API. Also handles reply-to-post with images.

Refactored: shared resolvePostElements() helper for language variant
lookup, mediaMaxBytes() helper, and dedup via seen map.
2026-03-23 15:56:11 +07:00
viettranx 9aaec59527 fix(channels): add periodic sweep for pending message compaction
Auto-compaction stopped working after server restarts because provider
resolution failure was silent and there was no retry mechanism. Add a
10-minute periodic sweep in the flusher loop that checks DB for groups
exceeding the compaction threshold, and warn when compaction provider
cannot be resolved at startup.
2026-03-23 15:45:35 +07:00
viettranx ed840dd6d7 fix(ui): use UTC timestamps and timezone-aware message display
Normalize message timestamps to UTC in backend, extract shared
messageToTimestamp helper (DRY), and render chat times using the
user's selected timezone from the topbar setting.
2026-03-23 15:21:32 +07:00
haidang1810 19d7d86030 fix(ui): use server timestamps for chat messages (#332) (#367) 2026-03-23 15:17:31 +07:00
viettranx f9b8ae1b76 fix(ui): fix cron page crash + redesign detail with markdown
- Add key props to cron routes (matching Agents/Sessions pattern)
- Fix React Rules of Hooks violation in CronPage (hooks after early return)
- Redesign overview tab: markdown preview/edit toggle for message,
  info grid with icons, agent select dropdown instead of raw UUID input
- Redesign run history: expandable entries with markdown for summary/error
- Show agent display name instead of UUID in list row, header, and overview
- Add i18n keys (edit, preview, noMessage) for en/vi/zh
2026-03-23 15:07:11 +07:00
viettranx e156e47343 fix(skills): add missing tenant_id to UpsertSystemSkill INSERT
After migration 000027 dropped the tenant_id default on skills table,
new system skill inserts failed with NOT NULL constraint violation.
2026-03-23 15:03:27 +07:00
viettranx 461397d710 docs: add multi-tenant architecture image to README 2026-03-23 14:37:42 +07:00
viettranx 4a03670bc8 fix: address 7 bugs from codebase review
1. providers/anthropic_stream.go — check scanner.Err() after SSE loop;
   truncated responses returned silently on network errors

2. config/config_load.go — trim whitespace in GOCLAW_OWNER_IDS;
   trailing spaces caused owner exact-match failures

3. cron/service.go — handle saveUnsafe() errors at 5 call sites;
   disk I/O failures lost cron state silently

4. tasks/task_ticker.go — separate timeouts for followups (15s) and
   recovery (30s); shared context risked followups starving recovery

5. hooks/use-ws-event.ts — useRef pattern for handler to prevent
   subscribe/unsubscribe churn with inline functions

6. markdown-renderer.tsx — AbortController for file fetch with abort
   on new fetch and on unmount

7. mcp-form-dialog.tsx — auto-split Command field when user pastes
   full command; fix splitShellTokens to preserve commas inside quotes
2026-03-23 14:16:40 +07:00
viettranx cb37383ccd feat(ui): show server version in sidebar connection status
Replace hardcoded "0.2.0" in WS connect response with the actual
build-injected version. Wire server version through WS client → auth
store → sidebar footer so it displays next to connection status.
2026-03-23 14:00:00 +07:00
viettranx 2d9d8adb0c docs: translate model steering system doc to English 2026-03-23 14:00:00 +07:00
viettranx ae6615d962 feat(teams): blocker escalation, audit events, review workflow + docs
- Add comment_type column (note/blocker) with migration 000028
- Blocker comments auto-fail task, cancel member session via
  EventTeamTaskFailed broadcast, and escalate to leader via
  InboundMessage (system:escalation bypasses debounce)
- Enrich audit events: fix tenant scope bug (context.Background →
  WithTenantID), add 4 missing types (commented/progress/updated/stale),
  populate data field (reason, comment_text, progress_percent)
- Add blocker escalation config toggle in team settings UI
- Add audit logs modal with paginated team events (Clock icon)
- Update features modal: all features now available (no coming soon)
- Update code comments: reviewer role not yet active, all flows via leader
- Update docs: remove v1/v2 versioning, add blocker escalation and
  review workflow sections, document enriched audit events
2026-03-23 13:51:28 +07:00
viettranx 8d3eb5bb6f feat(teams): task hints for weak models + force v2
- Add lead hint in search/list results with member+model info and task
  creation guidelines (description quality, complexity, model matching)
- Add member instructions in dispatch message (progress, comment, complete)
- Include member comments and attachments in announce content to leader
- Cap announce content at 50k runes to prevent context blowup
- Remove duplicate comment block in announce path
- Force all teams to v2: remove IsTeamV2 gate, v2Actions map,
  isConsumerTeamV2, and v1 TEAM.md branch
- Replace version comparison modal with features showcase modal
- Remove version selector from team settings UI
2026-03-23 12:14:29 +07:00
viettranx 57f249a89d fix(gateway): prevent panic on send to closed WebSocket client channel
Agent run goroutines can outlive the WebSocket client (e.g. after
reconnect). When the goroutine finishes and calls SendResponse/SendEvent,
the closed send channel causes a panic that crashes the server.

Add defer/recover to both methods to gracefully drop messages for
disconnected clients instead of panicking.
2026-03-23 12:11:47 +07:00
viettranx 2739984aed fix(telegram): resolve getUpdates long-poll timeout race (#361)
HTTP client timeout (30s) equaled getUpdates API timeout (30s), causing
context deadline exceeded errors (~210/hr) when Telegram held the
long-poll for the full duration. Increase HTTP client to 60s, reduce
getUpdates to 25s for 35s headroom.

Closes #361
2026-03-23 12:04:49 +07:00
viettranx 94f6bf2445 fix(security): close shell deny gaps for halt, su, doas, pkexec, runuser
Add missing deny patterns discovered during security audit:
- destructive_ops: halt, init/telinit 0|6, systemctl suspend/hibernate
- privilege_escalation: broaden su to block all forms (bare su, su root),
  add doas, pkexec (CVE-2021-4034), runuser
2026-03-23 10:59:26 +07:00
viettranx 0973b842a2 fix(ui): resolve nested button error and stale agentId in chat
- Provider list: change outer <button> to <div role="button"> to fix
  invalid nested <button> HTML (delete button inside row button)
- Chat page: derive agentId via useMemo from URL session key instead of
  useState initializer that only runs once (stale on session switch)
- Agent change no longer creates ghost sessions — navigates to /chat
  and lets handleSend create session on first message
- Clear messages when navigating away from session (empty key only);
  session-to-session switch still replaces atomically via loadHistory
- Split ROUTES.CHAT into CHAT (nav) + CHAT_PATTERN (route definition)

Closes #349
2026-03-23 10:44:03 +07:00