Commit Graph
725 Commits
Author SHA1 Message Date
Kai (Tam Nhu) Tran 6959ea7479 feat(agent): exclude MCP bridge tools from read-only streak detector (#400)
MCP tools (mcp_*) are user-defined external integrations where
read-heavy workflows are expected and legitimate. The read-only
streak detector was designed for filesystem tool loops but caught
MCP tools in the default fallback, triggering false positives on
workflows like "query inbox + read 10 emails + summarize."

Treat mcp_* tools as neutral (same as exec/bash) since GoClaw
cannot determine whether an MCP tool is read or write.

Closes #399
2026-03-26 12:39:50 +07:00
Duc Nguyen 709d86c701 feat(ui): add stop button for running traces (#481)
* feat(ui): add stop button for running traces on traces page

Allows admins to abort running agent runs directly from the traces page,
useful for stopping channel-originated runs (Telegram, Discord, etc.)
without needing access to the chat page.

* style(ui): match stop button style to chat destructive button

* fix(ui): fix abort response handling and variable declaration order

- Check aborted field from chat.abort response for accurate feedback
- Move useTraces() before handleAbortRun to fix block-scoped variable error
- Add abortNotFound i18n key for when run already finished

* fix(ui): remove invalid pending status check, add cursor-pointer
2026-03-26 12:39:29 +07:00
viettranx 4d31fe0dda fix(workspace): fix file move bugs, migrate DnD to @dnd-kit
- Fix cross-scope move in team workspace "All" view (prevented invalid
  nested paths by detecting mismatched chat_id scopes)
- Add error toast on move failure instead of silent catch
- Auto-create destination subdirectories on move (os.MkdirAll)
- Add admin bypass for team workspace HTTP auth (matching RPC pattern)
- Clear stale activePath after move in storage page
- Migrate file tree DnD from native HTML5 to @dnd-kit with:
  - PointerSensor (distance=8 prevents accidental drags)
  - DragOverlay via portal (fixes offset in Radix Dialog)
  - Folder auto-expand on 800ms hover during drag
- Silent refresh after move to prevent tree flash/scroll loss
2026-03-25 22:17:33 +07:00
viettranx 31c41dac54 fix(skills): per-tenant skill toggle for non-master tenants
Skills page toggle was calling global /toggle endpoint (master-tenant-only),
returning 403 when owner switched to another tenant. Now uses per-tenant
override endpoints (PUT/DELETE /v1/skills/{id}/tenant-config) when scoped
to non-master tenant, matching builtin-tools page pattern.

- Add tenant_enabled merging to WS skills.list handler
- Add setTenantConfig/deleteTenantConfig to use-skills hook
- Add SkillTenantOverride component with badge + switch + reset
- Fix wrong MasterTenantID constant in both skills and builtin-tools pages
- Add i18n keys for en/vi/zh
2026-03-25 22:15:01 +07:00
viettranx f94fc1528e feat(ui): conversation-wide image gallery, layout fix, and session state restore
- Fix chat layout overflow when TaskPanel sidebar is open by adding min-w-0
  to flex containers in AppLayout and ChatPage
- Add conversation-wide image gallery with prev/next navigation (ArrowKeys)
  and image counter — collects images from both mediaItems and markdown
- Rename task panel header from "Tasks" to "Running Tasks"
- Add chat.session.status RPC to restore isRunning and activity state on
  session switch, preventing lost running indicators
- Add cancelled flag to session restore effect to prevent race conditions
  on rapid session switching
2026-03-25 19:41:53 +07:00
viettranx f793bba0e0 feat(teams): restore active tasks on session switch via teams.tasks.active-by-session RPC 2026-03-25 18:26:31 +07:00
viettranx 1355e4e0cb feat(ui): blob-cached media URLs and proper download links
Add useMediaUrl hook with in-memory blob cache to prevent image
flickering on session switch. Use toDownloadUrl for all download links.
2026-03-25 18:26:14 +07:00
viettranx bb74120e36 feat(files): add download=true query param for Content-Disposition attachment header 2026-03-25 18:26:04 +07:00
viettranx 258e378593 feat(memory): add chunk overlap support and clean up per-agent config
- Add ChunkOverlap to MemoryConfig (default 200) for context continuity
  at chunk boundaries during semantic search
- Implement overlap logic in ChunkText() with safety clamp at maxChunkLen/2
- Wire per-agent chunk_len/chunk_overlap overrides at IndexDocument time
  via RunContext, falling back to global PGMemoryConfig defaults
- Add sync.RWMutex to PGMemoryStore for thread-safe config updates
- Persist chunk settings via system_configs (embedding.max_chunk_len,
  embedding.chunk_overlap) with runtime refresh on config change
- Remove dead per-agent embedding_provider/embedding_model fields from
  agent Memory UI (system uses single global provider)
- Show Memory section always-visible in agent config (no toggle needed)
- Add chunk_overlap fields to System Settings modal and Config AI Defaults
- Update i18n (en/vi/zh) for all new and removed keys
2026-03-25 18:24:36 +07:00
viettranx da98a54393 fix(media): save browser screenshots and TTS audio to workspace instead of /tmp
Screenshots saved to {workspace}/screenshots/, TTS audio to {workspace}/tts/.
Agents can now access these files via read_file. Falls back to os.TempDir()
when workspace is unavailable.
2026-03-25 16:03:40 +07:00
viettranx 4b780bbffa refactor(workspace): extract layered resolver pipeline for workspace path computation
Replace inline workspace path computation in loop_context.go with composable
WorkspaceLayer pipeline (tenant → team → project → user/chat). Each layer is
a pure function that appends a path segment or is a no-op.

- New workspace_resolver.go: ResolveWorkspace, TenantLayer, TeamLayer,
  ProjectLayer (future), UserChatLayer, SanitizePathSegment
- 16 unit tests covering all layer combinations
- Migrate loop_context.go, loop_history.go, team_tasks_mutations.go
- Move sanitizePathSegment from agent to tools package (exported)
- Zero behavior change — identical paths for all scenarios
2026-03-25 16:03:31 +07:00
viettranx 28a17c4d6b feat(embedding): data-driven dimension config with multi-provider support
- Add `dimensions` field to EmbeddingSettings for per-provider truncation
- Default to 1536 dims to match pgvector schema, preventing INSERT failures
- Replace hardcoded Gemini-only dimension truncation with data-driven config
- Fix verify endpoint to apply dimensions and return mismatch warning
- Expand curated embedding models: Gemini (gemini-embedding-001), Cohere
  (embed-v4), DashScope (text-embedding-v3), OpenAI (text-embedding-3-large)
- Add dimensions input to provider detail UI with i18n (en/vi/zh)
- Add anthropic_native to NoEmbeddingTypes (incompatible auth format)
- System settings verify always requests 1536d to match production behavior
2026-03-25 14:50:43 +07:00
viettranx fabec18606 fix(docs): correct license badge and text to match CC BY-NC 4.0 2026-03-25 13:57:41 +07:00
viettranx ee4d34ae28 feat(browser): add timeout, idle auto-close, and max-pages safety mechanisms
Prevent resource leaks and hanging actions in the browser tool:
- Per-action context timeout (default 30s, configurable via timeoutMs param or config)
- Idle page reaper goroutine closes pages unused for 10min (configurable)
- Max pages per tenant (default 5) with LRU eviction
- RefStore cleanup on page close/evict/reap to prevent memory leaks
2026-03-25 13:55:33 +07:00
viettranx afbecd3e4c fix(ui): strip query params from file block name for display and extension detection 2026-03-25 13:49:27 +07:00
viettranx f0b5b61c8e fix(chat): isolate WS chat events by channel and session key
Add sessionKey to AgentEvent and filter in chat UI so events from
Telegram, cron, and other channels don't leak into WS chat page.
2026-03-25 13:40:54 +07:00
viettranx 5b614da90e feat(tools): semantic media filenames with LLM-provided hints
Replace opaque goclaw_gen_{nano} filenames with semantic naming.
Tools now accept filename_hint param; fallback to {type}_{agentKey}_{ts}.
2026-03-25 13:40:44 +07:00
viettranx cfb587e171 fix(message): strip embedded MEDIA: paths and add cross-tenant channel validation
Message tool sent raw MEDIA: paths to channels when LLMs embedded them
in multi-line text instead of using standalone prefix. Also added tenant
validation to prevent agents from sending to channels belonging to other
tenants.

- Extract embedded MEDIA: tokens from message text, resolve as media
  attachments, strip from visible content (keeps surrounding text)
- Add ChannelTenantChecker callback to validate channel belongs to
  calling agent's tenant before any send path
- Add ChannelTenantID() to channel Manager for tenant lookup
- Fix sendMedia() missing ContentType on MediaAttachment
- Fix .docx/.xlsx MIME types to proper OpenXML values
2026-03-25 13:23:46 +07:00
viettranx 4c987aff69 fix(files): heal bare workspace paths and deduplicate media gallery vs markdown
SignFileURLs converts bare /app/workspace/ paths in markdown links to
/v1/files/ URLs so they can be served. File handler uses workspace root
for ft= auth fallback search (no tenant context with signed tokens).

Strip standalone media links from markdown when MediaGallery already
renders the same file (prevents image/file appearing twice). Backend
dedup uses URL-context basename matching to avoid false positives.
2026-03-25 12:51:57 +07:00
viettranx 20869275f2 fix(ui): use deduped content in RichContent fast path
Fast path rendered original content prop instead of deduped content,
bypassing media link dedup for pure-markdown messages.
2026-03-25 12:39:51 +07:00
viettranx 4a692d51d8 fix(files): heal legacy media URLs and deduplicate file references
SignFileURLs now heals legacy session data at delivery time:
- Dedup double /v1/files/v1/files/ prefix from old mutation bug
- Strip stale ?ft= from markdown link display text
- Re-sign all URLs with fresh tokens (never skip)

File serving: use workspace root for ft= auth fallback search
(no tenant context available with signed tokens).

Deduplicate ContentSuffix media by basename to prevent duplicate
file links when agent text already references the same file.
Frontend dedup as defense-in-depth for legacy data.
2026-03-25 12:37:49 +07:00
viettranx 101c4c45f5 style(chat): improve task card layout — full text, progress bar above step message 2026-03-25 12:13:06 +07:00
viettranx 02f024ba06 fix(files): strip stale ft= tokens from legacy session data before re-signing
SignFileURLs previously skipped URLs containing ft=, leaving expired
tokens from legacy data that persisted signed URLs to the database.
Now strips any existing ft= parameter and always re-signs with a
fresh token at delivery time.
2026-03-25 12:09:01 +07:00
viettranx 7ad15a375a feat(chat): add realtime comment/attachment counters with toggleable task panel
Subscribe to team.task.commented and new team.task.attachment_added
events in chat view. Show inline notifications and increment counters
on active task cards. Add toggleable sidebar panel (auto-opens on
first task dispatch, auto-closes when all tasks complete). Mobile
renders as slide-over with backdrop overlay.
2026-03-25 12:06:58 +07:00
viettranx 5f1658cfad fix(gateway): prevent double /v1/files/ prefix in media URLs
OnEvent handler mutated MediaResult.Path in-place on the shared
RunResult.Media slice, corrupting paths for downstream consumers
(announce queue, outbound channels). Clone the media slice before
signing URLs. Add defense-in-depth guard in mediaToMarkdown to
skip prefix when path already starts with v1/files/ or v1/media/.
2026-03-25 12:06:41 +07:00
viettranx 0609b0cba7 fix(gateway): add nil guard in handler registration loop
Defensive check prevents panic if a nil handler is appended to the
handlers slice. Restores the nil safety that existed with per-handler
field checks in the pre-refactoring code.
2026-03-25 11:06:50 +07:00
viettranx b4a0997564 refactor(gateway): consolidate 27 handler fields into routeRegistrar slice
Replace 27 individual handler fields with single handlers []routeRegistrar
slice. BuildMux() registers all handlers via one loop instead of 27
nil-check blocks. Setter methods now append to slice. Reduces server.go
from 735 to 612 LOC (-123). No caller changes, no behavior change.
2026-03-25 10:26:11 +07:00
viettranx bc958f6620 fix(agents): let owners inherit tenant_id from auth context on create
Previously owners were required to explicitly provide tenant_id when
creating agents. Now it falls back to the auth context tenant_id,
matching the behavior of non-owner callers.
2026-03-25 10:21:54 +07:00
viettranx a9aedc843e fix(security): add tenant membership check to tenant-config endpoints
Prevent cross-tenant config manipulation by verifying caller has
owner/admin role within the target tenant before allowing set/delete
operations. Also adds MaxBytesReader, audit trail, and fixes middleware
consistency between skills and builtin_tools endpoints.
2026-03-25 10:21:39 +07:00
viettranx c5164255ef feat(skills): add per-tenant skill config HTTP endpoints (#428)
Add PUT/DELETE /v1/skills/{id}/tenant-config routes for managing
per-tenant skill visibility overrides. Enrich GET /v1/skills list
with tenant_enabled field when tenant-scoped. Add ListAll method
to SkillTenantConfigStore interface + PG implementation.
2026-03-25 09:58:56 +07:00
viettranx db0f62215a fix(store): use (v, ok) pattern for bool context accessors with RunContext fallback
Fix latent bug where explicit false on individual key would fall through
to RunContext. SelfEvolveFromContext, IsSharedMemory, IsSharedKG now
use (v, ok) type assertion to respect explicit false values.
2026-03-25 09:57:19 +07:00
viettranx 3bb524cb39 refactor(store): introduce typed RunContext for consolidated context injection
Add store.RunContext struct that consolidates 27 loop-injected context
values into a single typed struct. RunContext is built once in
loop_context.go and injected via store.WithRunContext(ctx, rc).

24 accessor functions (10 in store/context.go, 14 in tools/context_keys.go)
updated with RunContext fallback — check individual key first, then
RunContext. Consumers (170+ files) require zero changes.

This enables adding new scope fields (e.g. ProjectID) by adding a single
struct field instead of a new context key + With/From function pair.
2026-03-25 09:49:55 +07:00
viettranx d3a4398e78 fix(gateway): sign file URLs in run.completed events for real-time display
OnEvent callback only handled map[string]string payloads, but
run.completed uses map[string]any (contains usage map + media slice).
File URLs in content were never signed, and media paths were sent as
raw local paths without ?ft= hash tokens.

Switch on payload type to handle both shapes: sign content URLs and
convert media local paths to /v1/files/basename?ft=hash.
2026-03-25 09:48:39 +07:00
viettranx 9a24995e56 docs(store): restore inline doc comments on interface methods
Restore 3 godoc comments that were accidentally dropped during
AgentStore/SessionStore interface split: GetDefault hint, CanAccess
return values, and Get vs GetOrCreate distinction.
2026-03-25 09:19:36 +07:00
viettranx 382ec9aa51 refactor(gateway): split teams_tasks method file into queries and mutations
Move task create, assign, delete, bulk delete, and dispatch methods from
teams_tasks.go (858 LOC) to teams_tasks_mutations.go (415 LOC).
Original file reduced to 460 LOC (get, review, comments). No logic change.
2026-03-25 08:40:44 +07:00
viettranx f011b8cd15 refactor(store): split knowledge_graph.go into entity and relation files
Move relation CRUD, batch ingest, prune, stats, and scan helpers from
knowledge_graph.go (731 LOC) to knowledge_graph_relations.go (378 LOC).
Original file reduced to 365 LOC (entity ops + search). No logic change.
2026-03-25 08:36:06 +07:00
viettranx 066ba4dca6 chore: add pkg-helper binary to .gitignore 2026-03-25 08:35:28 +07:00
viettranx 25a123fef9 refactor(store): split AgentStore and SessionStore into focused sub-interfaces
AgentStore (27 methods) → 4 sub-interfaces:
- AgentCRUDStore (10): core CRUD operations
- AgentAccessStore (5): sharing and access control
- AgentContextStore (8): agent-level and per-user context files
- AgentProfileStore (4): user-agent profiles and instances

SessionStore (33 methods) → 3 sub-interfaces:
- SessionCoreStore (16): lifecycle, messages, history
- SessionMetadataStore (13): tokens, timing, calibration
- SessionListingStore (4): search, list, discovery

Both use composed interfaces for backward compatibility (same
pattern as TeamStore split). No implementation changes.
2026-03-25 08:32:24 +07:00
viettranx 7382aceb7e refactor(pkg-helper): extract hardcoded GID to constant and log chown errors
Replace magic number 1000 with goclawGID constant across all chown
calls. Add slog.Warn on chown failures instead of silently ignoring
them, improving debuggability for permission issues in production.
2026-03-25 08:32:00 +07:00
Thieu Nguyen 18824f841d fix(pkg-helper): restore group ownership on apk-packages persist file (#456)
* fix(pkg-helper): restore group ownership on apk-packages persist file

persistRemove() uses write-to-temp-then-rename which creates the new
file as root:root. The goclaw process (uid 1000, gid 1000) can't read
it, causing ListInstalledPackages to return nil for system packages.
After any package uninstall, the UI shows an empty package list.

Fix: chown listFile to root:goclaw (0:1000) after rename in
persistRemove and after initial creation in persistAdd.

* fix(ui): reset uninstall success icon to trash after timeout

After uninstalling a package, the success checkmark icon stayed
permanently instead of reverting to the delete (trash) icon.

Add setTimeout to reset actionStatus from "success" to "idle" after 2s,
matching the existing pattern used for error states.
2026-03-25 08:30:49 +07:00
Thieu Nguyen 424f3a975c refactor(docker): rename network "shared" to "goclaw-net" (#405)
The generic name "shared" conflicts with other projects on the same
host. Use a project-scoped name to avoid collisions.
2026-03-25 08:27:39 +07:00
Luan VuandLuvu182 95ce4200dd fix(ui): session detail missing tool calls, memory dialog toast errors (#458)
1. Session detail: isDisplayable() filtered out assistant messages with
   only tool_calls or thinking (no text content). Live chat renders these
   via MergedToolGroup but session history silently dropped them.

2. Memory document dialog: load error showed "Failed to create document"
   (wrong i18n key toast.failedCreate). Save error used the same wrong key.
   Fixed to use toast.docLoadFailed and toast.docUpdateFailed respectively.
   Added missing docLoadFailed key to en/vi/zh locale files.

3. Memory dialogs: all 5 dialogs missing aria-describedby={undefined} on
   DialogContent, causing Radix UI console warnings about missing
   Description for accessibility.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-25 08:26:35 +07:00
viettranx 1f72f0d179 fix(docker): harden credential sync and move CLI mount to overlay
- Replace chmod 644 temp file with install(1) -m 600 for atomic copy
  with correct ownership — eliminates world-readable window in /tmp
- Move ${HOME}/.claude volume mount from base docker-compose.yml to
  optional docker-compose.claude-cli.yml overlay — avoids breaking
  deployments where $HOME is unset (CI/CD, systemd, Swarm)
2026-03-25 08:21:11 +07:00
viettranx 2ce975af73 fix(store): restore alias validation guard in WhereClauseAlias
Re-add character-by-character alias validation that was present in the
old tenantClauseNAlias but missing from the new WhereClauseAlias.
Defense-in-depth against SQL injection via table alias parameter.
2026-03-25 08:20:10 +07:00
viettranx a080446db3 refactor(store): remove deprecated tenantClauseN/tenantClauseNAlias
All callers migrated to scopeClause/scopeClauseAlias in previous commit.
Remove the old functions. tenantIDForInsert and requireTenantID retained
(still have callers).
2026-03-25 08:20:10 +07:00
viettranx e81318a663 refactor(store): migrate all tenantClauseN callers to scopeClause
Migrate 72 calls across 16 files from tenantClauseN/tenantClauseNAlias
to scopeClause/scopeClauseAlias. Old functions retained in helpers.go
as definitions only (no callers). All store queries now use the
scope-based helpers, ready for project-level scoping when needed.
2026-03-25 08:20:10 +07:00
viettranx deb821063a refactor(store): add QueryScope abstraction for composable multi-level isolation
Introduce store.QueryScope struct that generates WHERE clauses for
tenant + optional project scoping. Add scopeClause/scopeClauseAlias
wrappers in pg/helpers.go as drop-in replacements for tenantClauseN.

Old tenant-only helpers remain for existing callers (76 occurrences).
Migrate providers.go (4 calls) as proof-of-concept. Future callers
should use scopeClause for project-scope readiness.
2026-03-25 08:20:10 +07:00
viettranx 379da91a68 refactor(agent): extract post-loop finalization to loop_finalize.go
Move sanitization, media dedup, session flush, bootstrap cleanup, and
RunResult construction from runLoop() into dedicated finalizeRun() method.
Reduces loop.go from 1123 to 956 LOC (-167). No behavior change.
2026-03-25 08:20:10 +07:00
viettranx ebef1951bf fix(agent): restore iteration key in slog, event payloads, and LLM prompts
Fix mechanical find-replace that leaked rs.iteration into string literals,
slog keys, and event payload map keys. Restores "iteration" as the
user-facing/frontend-facing key name.
2026-03-25 08:20:10 +07:00
viettranx ae18f24cd9 feat(ui): drag-and-drop file/folder move in Storage and Workspace
Backend:
- PUT /v1/storage/move?from=&to= — admin-only, atomic os.Rename
  with protected dir, path traversal, symlink escape, and overwrite
  prevention checks.
- PUT /v1/teams/{teamId}/workspace/move — same security with team
  membership check and EventWorkspaceFileChanged broadcast.

Frontend:
- Files and folders are draggable in the file tree (HTML5 drag API).
- Folders are drop targets with visual ring highlight on dragover.
- Root-level drop zone for moving items to root directory.
- Guards: can't drop into self, can't drop folder into descendant,
  can't drop into protected dirs (server-side enforced).
- Wired in both Storage page and Team Workspace dialog.
2026-03-25 08:20:10 +07:00