Commit Graph
715 Commits
Author SHA1 Message Date
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
viettranx c1377de943 refactor(agent): extract tool result processing to loop_tools.go
DRY: deduplicate tool result processing between single-tool and parallel-tool
paths. New processToolResult() and checkReadOnlyStreak() methods on *Loop
handle loop detection, event emission, media collection, deliverables, and
spawn tracking. Reduces loop.go from 1324 to 1123 LOC (-201). No behavior change.
2026-03-25 08:20:10 +07:00
viettranx cd7426454e refactor(agent): introduce runState struct to encapsulate loop mutable state
Consolidate ~20 local variables from runLoop() into a single runState
struct. This enables future extraction of loop sub-operations (tool
execution, media processing, streaming) as methods on *runState without
passing individual variables. No behavior change.

Fields moved: loopDetector, totalUsage, iteration, totalToolCalls,
finalContent, finalThinking, asyncToolCalls, bootstrapWriteDetected,
mediaResults, deliverables, pendingMsgs, blockReplies, lastBlockReply,
checkpointFlushedMsgs, midLoopCompacted, teamTaskCreates,
teamTaskSpawns, skillNudge70Sent, skillNudge90Sent, skillPostscriptSent
2026-03-25 08:20:10 +07:00
viettranx 96fbc79cd2 refactor(store): split TeamStore into focused sub-interfaces
Split monolithic TeamStore (61 methods) into 6 focused interfaces:
- TeamCRUDStore: core team + member operations (13 methods)
- TaskStore: task CRUD + lifecycle transitions (22 methods)
- TaskCommentStore: comments, events, attachments (10 methods)
- TaskRecoveryStore: stale detection + recovery (6 methods)
- TaskFollowupStore: follow-up scheduling (7 methods)
- TeamAccessStore: user access grants (5 methods)

TeamStore is now a composed interface embedding all sub-interfaces,
maintaining full backward compatibility. No implementation changes.
2026-03-25 08:20:10 +07:00
viettranx 96bf0ddb68 refactor(agent): extract context injection to loop_context.go
Move agent/tenant/user/workspace context enrichment, input guard, and
message truncation from runLoop() into dedicated injectContext() method.
Reduces loop.go from 1553 to 1346 LOC. No behavior change.
2026-03-25 08:20:10 +07:00
viettranx 81c8414423 refactor(store): fix goroutine safety issues across cron, sessions, skills, and gateway
- Replace 16x bare db.Query/db.Exec with context-aware variants to prevent
  orphan DB operations when parent context is cancelled
- Thread baseCtx through PGCronStore lifecycle (Start/Stop) for all internal
  scheduler, executor, and job cache operations
- Add ctx parameter to StoreMissingDeps and ListSystemSkillDirs, update all callers
- Add sync.WaitGroup tracking to fire-and-forget goroutines in gateway consumer
  handlers (subagent announce, teammate message) with safego.Recover panic protection
- Add safego.Recover to 3 Slack channel goroutines to prevent WaitGroup hang on panic
2026-03-25 08:20:10 +07:00
Luan VuandLuvu182 d63a7d4ced fix(docker): auto-sync host Claude CLI credentials and show Docker-aware login instructions (#398)
When running in Docker, the Claude CLI provider setup showed `claude auth login`
which doesn't work inside a container. This change:

- Mounts host ~/.claude as read-only into the container
- Entrypoint syncs credentials to a writable volume (respects cap_drop: ALL)
- Backend detects Docker via /.dockerenv and returns `in_docker` in auth-status API
- UI shows `docker compose exec goclaw claude auth login` for Docker deployments

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-25 08:19:52 +07:00
viettranx 19eef35653 feat(storage): bump upload limit to 50MB and expand protected dirs
- Increase maxFileSizeBytes from 10MB to 50MB across backend and frontend
- Add media/ and tenants/ to protectedDirs — prevent uploads into
  system-managed directories (only teams/ and custom dirs are writable)
- Update i18n strings (en/vi/zh) to reflect 50MB limit
2026-03-24 23:17:51 +07:00
viettranx 0a57678430 fix(ui): capture active folder for storage upload target
Upload was always going to root because activeFolder derived from
activePath which only tracks file selections (folders expand instead
of selecting). Now capture the folder at Upload button click time
and show the target path in the dialog description.
2026-03-24 22:50:59 +07:00
viettranx 9ee82be219 feat(ui): add file upload to Team Workspace and Storage pages
Allow admins/operators to upload files via the web UI so agents can
read them with read_file during collaboration.

Backend:
- POST /v1/teams/{teamId}/workspace/upload — multipart upload with
  team membership check, tenant isolation, blocked extensions, 10MB
  limit, 100-file quota, symlink escape prevention, and real-time
  EventWorkspaceFileChanged broadcast.
- POST /v1/storage/files — admin-only storage upload with protected
  dir rejection and size cache invalidation.
- Export MaxFileSizeBytes, MaxFilesPerScope, IsBlockedExtension from
  tools package for reuse in HTTP handlers.

Frontend:
- Reusable FileUploadDialog component (drop zone, multi-file,
  per-file status, client-side validation).
- Upload button added to Team Workspace dialog (scope-aware) and
  Storage page (folder-aware).
- i18n strings for en/vi/zh.
2026-03-24 22:48:11 +07:00
viettranx b221ed2fb6 fix(store): add missing WithAgentKey context function
Commit a8815a94 introduced a call to store.WithAgentKey() in
agent/loop.go but never added the function to store/context.go,
breaking the build on main.
2026-03-24 22:47:57 +07:00