Commit Graph
510 Commits
Author SHA1 Message Date
viettranx 63eff188ad feat(kg): add knowledge graph with LLM extraction, traversal, and graph visualization
- KnowledgeGraphStore interface + PostgreSQL implementation (recursive CTE traversal, 5s timeout)
- LLM entity extraction pipeline triggered on memory writes (background goroutine)
- knowledge_graph_search agent tool with search + traversal modes
- HTTP API: CRUD entities, traverse, extract, stats, graph endpoints
- Web UI: KG tab on memory page with table/graph toggle, entity detail, manual extraction
- Force-directed graph visualization using @xyflow/react + d3-force
- Builtin tool seed with configurable provider/model/confidence settings
2026-03-09 17:11:20 +07:00
9249d9e358 fix(claude-cli): check scanner.Err() and increase stream buffer to 10MB (#96)
Fixes #94. The stream-json scanner never checked scanner.Err() after the
scan loop, so if a line exceeded the 1MB buffer limit the scanner would
stop with bufio.ErrTooLong and the response would be silently truncated.

- Check scanner.Err() after the loop and return an explicit error
- Increase max buffer from 1MB to 10MB to handle large tool outputs

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-09 15:49:25 +07:00
11bed0cc01 fix(mcp-bridge): per-session security context + media forwarding (#91)
* fix(mcp-bridge): add per-session agent context and HMAC verification

- Add per-session MCP config with X-Agent-ID/X-User-ID headers instead
  of shared global config file
- Sign bridge context headers with HMAC-SHA256 to prevent forgery
- Add bridgeContextMiddleware to verify signatures on MCP bridge requests
- Store MCP configs in ~/.goclaw/mcp-configs/ outside agent workDir
- Use atomic writes (tmp + rename) for MCP config files
- Fix provider rename leaving ghost registry entries
- Remove provider_type from mutable fields on update
- Tighten temp dir permissions from 0755 to 0700

* feat(mcp-bridge): propagate channel routing context through MCP bridge

- Pass channel, chat_id, and peer_kind from agent loop to CLI provider options
- Inject X-Channel, X-Chat-ID, X-Peer-Kind headers in bridge context middleware
- Add BridgeContext struct to bundle per-call context for MCP config generation
- Include channel routing headers in per-session MCP config files
- Expose "message" tool via MCP bridge for cross-channel messaging
- Add extract helpers for new option keys in claude_cli_session.go

* feat(mcp-bridge): forward media attachments to outbound message bus

- Wire MessageBus into gateway server and MCP bridge handler
- Publish tool result media files to outbound bus for channel delivery
- Extract channel/chatID/peerKind from tool context for proper routing
- Add mimeFromExt helper for content-type detection on attachments

* feat(mcp-bridge): inject per-agent DB-backed MCP servers into Claude CLI config

- Add MCPServerLookup type to resolve agent-specific MCP servers from DB
- Wire MCPServerStore through provider registration and HTTP handler
- Extract mcpServerEntryToConfig helper to deduplicate transport config logic
- Add JSON-to-Go helpers (jsonToStringSlice, jsonToStringMap) for DB fields
- Merge per-agent MCP servers at config write time without overriding static entries

* fix(mcp-bridge): use Media struct fields and prefer explicit MimeType

- Map Media.Path to attachment URL instead of treating Media as string
- Use Media.MimeType when available, fall back to extension-based detection

* refactor(providers): deduplicate option extractors and extract bridge media forwarding

- Replace per-field extractors (extractSessionKey, extractAgentID, etc.) with generic extractStringOpt/extractBoolOpt
- Add bridgeContextFromOpts helper to build BridgeContext in one call
- Extract forwardMediaToOutbound from inline block in makeToolHandler
- Change NewBridgeServer msgBus param from variadic to explicit pointer

* fix(providers): validate provider_type on update instead of silently dropping it

- Add explicit validation against ValidProviderTypes with 400 response
- Remove silent delete(updates, "provider_type") that hid invalid values
- Caller now receives clear error when submitting unsupported provider_type

* fix(providers): add header injection validation to MCP bridge headers

- Extend CRLF/null-byte checks to agentID, channel, chatID, and peerKind
- Previously only userID had header injection prevention
- Prevents HTTP header injection via crafted values in MCP config

* fix(mcp-bridge): sign all context fields in HMAC and remove legacy code

- Sign all 5 bridge context fields (agentID|userID|channel|chatID|peerKind)
  in HMAC instead of only agentID|userID to prevent channel routing forgery
- Propagate context.Context into MCPServerLookup to respect request
  cancellation instead of using context.Background()
- Remove legacy BuildCLIMCPConfig, WithClaudeCLIMCPConfig, mcpConfigPath,
  and mcpCleanup (dead code since system is PG-only)
- Use mime.TypeByExtension before custom fallback in mimeFromExt
- Add debug log when media forwarding is skipped due to missing context
- Add thread-safety comment to SetMCPServerLookup

---------

Co-authored-by: Nam Nguyen Ngoc <namnn.0911@gmail.com>
Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-09 15:23:56 +07:00
viettranx b284f963f5 feat(memory): add memory management page with CRUD, search, and indexing
Add full-stack memory document management:
- Backend: extend MemoryStore with admin queries (ListAllDocumentsGlobal,
  GetDocumentDetail, ListChunks), HTTP handler with auth middleware
- Frontend: memory page with agent/scope filters, document table with
  pagination, view/edit dialog with content and chunks tabs, create dialog
  with scope selection, semantic search dialog
- UI fixes: reduce input/textarea focus ring width, prevent ring clipping
  in dialog scroll containers, widen memory dialogs on desktop
2026-03-09 14:54:41 +07:00
a321af8b04 feat(ui): add tool call details with arguments, results, and thinking display (#92)
- Enhance tool call cards with expandable arguments/result sections and inline summary
- Display thinking/reasoning blocks in chat messages (uses existing ThinkingBlock component)
- Reconstruct tool details from session history for persistent display
- Emit tool result content and truncated arguments in agent events
- Fix sanitize to always return cleaned content instead of empty string

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-09 12:57:55 +07:00
4721cf26bd fix: replace crypto.randomUUID() with fallback for older browsers (#93)
Fixes #87. crypto.randomUUID() is unavailable in browsers older than
Chrome 92 / Firefox 95 / Safari 15.4, causing an uncaught TypeError
that crashes the React tree and renders a blank page when opening
builtin-tool settings or MCP grant dialogs.

Add a uniqueId() helper that uses crypto.randomUUID() when available
and falls back to a Math.random-based UUID v4 generator otherwise.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
2026-03-09 12:56:35 +07:00
viettranx 5f7ca84876 feat(channels): persist pending messages to PostgreSQL with Web UI
- Add channel_pending_messages table with UUID v7 PK, sender_id tracking
- Implement PendingMessageStore interface with batched flush (3s/20 msgs)
- Add LLM-based auto-compaction when entries exceed threshold (50)
- Wire persistent history into all channel factories (Telegram, Discord, Slack, Feishu, Zalo)
- Extract channel type constants (TypeTelegram, TypeDiscord, etc.) to eliminate magic strings
- Add HTTP API endpoints for pending messages management (list, view, compact, clear)
- Add Pending Messages dashboard page with group titles resolved from session metadata
- Track sender_id across entire pipeline (migration → store → history → handlers)
2026-03-09 12:39:43 +07:00
viettranx 967f7ae46f refactor: split gateway, consumer, onboard, and agent loop into smaller files
Extract helper functions and move existing functions to dedicated files:
- cmd/gateway.go → gateway_channels_setup.go (channel registration, RPC wiring, event subscribers)
- cmd/gateway_consumer.go → gateway_consumer_helpers.go, gateway_consumer_process.go
- cmd/gateway_managed.go → gateway_http_handlers.go (wireHTTP)
- cmd/onboard.go → onboard_resolve.go (API key resolution helpers)
- internal/agent/loop.go → loop_run.go (Run entry point)

No logic changes — only code movement between files within the same package.
2026-03-09 10:49:58 +07:00
viettranx e85624ce96 refactor: split large Go files into smaller modules for maintainability
Split 11 Go files exceeding 500 lines into smaller, focused files:
- channels/manager.go → manager.go + dispatch.go + runs.go + events.go
- channels/slack/channel.go → channel.go + send.go + utils.go
- channels/slack/handlers.go → handlers.go + handlers_mention.go + handlers_files.go
- channels/telegram/handlers.go → handlers.go + handlers_utils.go
- channels/zalo/personal/channel.go → channel.go + send.go + listen.go + handlers.go
- gateway/methods/agents.go → agents.go + agents_create.go + agents_update.go + agents_delete.go
- http/summoner.go → summoner.go + summoner_regenerate.go + summoner_prompts.go + summoner_utils.go
- http/skills.go → skills.go + skills_upload.go + skills_versions.go + skills_grants.go
- http/mcp.go → mcp.go + mcp_tools.go + mcp_grants.go + mcp_requests.go
- store/pg/cron.go → cron.go + cron_crud.go + cron_update.go + cron_exec.go

No logic changes — pure file reorganization to keep files under 200 lines.
2026-03-09 10:41:54 +07:00
viettranx 31ba182b40 feat(zalo): strip markdown from outbound messages for clean plain text
Zalo API does not support any markup rendering. LLM output containing
markdown artifacts (**bold**, `code`, headers, etc.) was sent as-is,
making messages hard to read. Add StripMarkdown() to convert markdown
to clean plain text, and inject a system prompt hint instructing the
model to output plain text on Zalo channels.
2026-03-09 10:16:30 +07:00
viettranx 86ba785fe3 fix(security): enforce lead-only RBAC on team task create/cancel, broadcast, and auto-create
Members could create/cancel tasks, broadcast to team, and auto-create
tasks via spawn — all without role checks. System relied entirely on
LLM prompt behavior. Now enforced at system level via requireLead().
2026-03-09 10:00:41 +07:00
viettranx 3c03d1f482 feat(cron): add detail page, running status, realtime events, and pagination
- Add /cron/:id detail page with job info, payload, run history
- Make cron.run async: respond immediately, execute in background
- Set last_status="running" before execution, emit CronEvent via WS
- Add CronEvent (running/completed/error) broadcast to all WS clients
- Add server-side pagination to GetRunLog (offset + total count)
- Show loading spinner on Run button when job is running (list + detail)
- Enrich CronRunLogEntry with duration, input/output tokens
- Make job names clickable in overview card → /cron/:id
- Fix refresh button animation using isFetching instead of isPending
2026-03-09 09:44:57 +07:00
viettranx 08ca343972 fix(cron): deliver agent response instead of raw payload message 2026-03-09 09:44:45 +07:00
viettranx 1f8bcbb559 fix(pairing): pass senderID to approve callback for group pairings 2026-03-09 09:44:28 +07:00
viettranx f62f60b094 fix(zalo): handle polymorphic content.data field in file upload events
The Zalo personal listener's control event handler assumed content.data
was always an object with a URL field. In practice it can also be a
plain string, causing JSON unmarshal errors. Use json.RawMessage and
inspect the first byte to handle both forms.
2026-03-09 09:44:17 +07:00
viettranx ba479c3153 refactor: align context key assignments for improved readability 2026-03-09 08:34:54 +07:00
viettranx 4b2d49cda9 feat: Clear Zalo personal channel group history after sending messages to the agent. 2026-03-09 08:33:37 +07:00
Duc NguyenandGitHub e05a4018c9 fix: use platform type instead of instance name in system prompt + Zalo group routing (#90)
* fix(agent): use ChannelType in system prompt for proper channel context

The system prompt was using the channel instance name (e.g. "zep-lao") instead
of the platform type (e.g. "zalo_personal"), causing the LLM to not understand
which messaging platform it's running on. This led to context confusion where
the bot would ask users which channel to send to instead of using the current one.

Changes:
- Add ChannelType field to RunRequest and SystemPromptConfig
- Thread channel type from consumer/cron → agent loop → system prompt
- Add WithToolChannelType/ToolChannelTypeFromCtx for tool context
- Register channel types for both config-based and DB-loaded instances
- Fix Zalo group thread type detection with approvedGroups cache
- Update cron handler to resolve channel type for cron-triggered runs

* refactor(channels): add Type() to Channel interface, remove channelTypes map

Move channel type from a separate map in Manager to the Channel interface
itself. BaseChannel.Type() falls back to Name() for config-based channels
where name == type. Extracts resolveChannelType helper to DRY up 6
repeated resolution blocks across consumer and cron handlers.

* feat(zalo): add pending group history for conversation context

Zalo personal groups now record non-@mentioned messages in a ring buffer
(default 50, configurable via history_limit). When the bot IS mentioned,
pending history is flushed as context — matching Telegram/Discord/Feishu.

Separated mention gating from policy gating in checkGroupPolicy for
cleaner control flow.
2026-03-09 08:30:45 +07:00
SpencerSwaggerandGitHub 3f850bc713 fix(docker): remove gateway config check, unnecessary anymore (#88) 2026-03-09 07:09:15 +07:00
1df0e518b6 fix(docker): update Makefile and compose for managed mode defaults (#86)
- Remove obsolete docker-compose.managed.yml reference from COMPOSE
- Add docker-compose.postgres.yml to default COMPOSE (required for managed mode)
- Add shared external network for cross-stack service discovery
- Add make targets: net, dev, migrate
- Fix UI healthcheck to use 127.0.0.1 instead of localhost

Co-authored-by: Viet Tran <viettranx@gmail.com>
2026-03-09 07:06:01 +07:00
Duc NguyenandGitHub 137a986d4f feat(channels): add Slack channel (#83)
* feat(channels): add Slack channel via Socket Mode (#37)

Implement Slack integration using Socket Mode (xapp-/xoxb- tokens):
- Event-driven messaging via app_mention + message events
- Policy checks: open, pairing, allowlist, disabled (DM + group)
- Thread participation with configurable TTL
- Markdown-to-mrkdwn formatting pipeline
- Streaming support (edit-in-place + native ChatStreamer)
- SSRF-protected file downloads
- Debounce, dedup, reactions, group history context
- 170 unit tests (format, helpers, stream, SSRF)

Fix BaseChannel.HandleMessage allowlist to also check chatID,
enabling group allowlist with channel IDs across all channels.

Closes #37

* feat(slack): add file/media support and edit-to-mention handling

- Wire inbound file download into handleMessage (images, audio, documents)
- Add media.go with resolveMedia, classifyMime, buildMediaTags
- Extract shared ExtractDocumentContent to channels/media_utils.go (DRY with Telegram)
- Support file_share and message_changed subtypes
- Handle edit-to-mention: respond when user edits old message to add @bot
- Add MediaMaxBytes config field (default 20MB)
- Fix debounce media accumulation (was silently dropping files)
- Add 60s HTTP client timeout on file downloads
- Refactor downloadFile signature for slack.File compatibility
2026-03-09 07:02:37 +07:00
viettranx d820ac02c2 docs: update README project status — move tested features to production, replace custom tools with hooks 2026-03-08 23:19:33 +07:00
viettranx 2a61562503 feat(ui): editable session title, detail page spacing, and session list enhancements 2026-03-08 23:14:35 +07:00
viettranx faf66cf453 fix(tools): remove unsupported duration_seconds param from MiniMax music API
MiniMax music generation API silently ignores duration_seconds — audio
length is determined by lyrics length (vocal) or model default (~3-4 min
for instrumental). Updated tool description to clarify this limitation.
2026-03-08 23:05:53 +07:00
viettranx d839e034af feat(tools): add exec path exemptions and tool arguments in events
- Add AllowPathExemptions to ExecTool for fine-grained deny bypass (skills-store)
- Include tool call arguments in tool.result event payloads
2026-03-08 22:40:09 +07:00
viettranx 9d0af657e5 fix(tools): correct media provider params and UI fixes
- MiniMax audio: fix invalid params (sample_rate/bitrate as int, auto
  instrumental when no lyrics, pass duration_seconds)
- MiniMax/DashScope image: map aspect_ratio to provider size format
- Gemini video: read person_generation from chain params instead of hardcode
- GetParamInt: add string-to-int coercion for UI select values
- UI: fix combobox portal selection bug, dropdown overflow clipping,
  provider chain form spacing, skill dialog min-height
- Update bitrate options to numeric bps values in params schema
2026-03-08 22:32:08 +07:00
viettranx 157d09c021 feat(ui): sortable provider chain with drag-and-drop and portal combobox
Replace simple JSON settings modal with sortable provider chain cards for
media tools. Each card supports provider/model selection, timeout, retries,
and typed provider-specific params via schema.

- Add @dnd-kit/core + @dnd-kit/sortable for drag-and-drop reordering
- New media-provider-chain-form.tsx: sortable card list with DnD
- New media-provider-params-schema.ts: typed params per tool x provider_type
- Combobox: add portalContainer prop to escape dialog overflow clipping
- Title Case formatting for dialog titles
2026-03-08 20:10:20 +07:00
viettranx 01d75ac7fe refactor(tools): migrate read_* tools to provider chain and add media models
Refactor read_image, read_document, read_video, read_audio to use
ResolveMediaProviderChain + ExecuteWithChain for consistent fallback behavior.
Add hardcoded model lists for MiniMax, DashScope, and Suno providers.
2026-03-08 20:10:10 +07:00
viettranx 6345df3136 feat(tools): add create_audio tool for music and sound effects
New tool supporting MiniMax music generation, ElevenLabs sound effects,
and Suno music (stub). Registers Suno as a valid provider type.

- create_audio.go: tool definition with provider chain for music, direct ElevenLabs for SFX
- create_audio_minimax.go: MiniMax music API (/music_generation)
- create_audio_elevenlabs.go: ElevenLabs sound effects API (/v1/sound-generation)
- create_audio_suno.go: stub for future Suno integration
- Add ProviderSuno type to store and gateway registration
2026-03-08 20:10:00 +07:00
viettranx 5815437f78 feat(tools): add media provider chain with ordered fallback and retry
Refactor create_image and create_video to use a shared provider chain system.
Each tool now supports an ordered list of providers with per-entry timeout,
max retries, and provider-specific params. Includes MiniMax and DashScope
image/video generation implementations.

- New media_provider_chain.go: shared chain resolution, retry execution, limitedReadAll
- create_image: refactored to ExecuteWithChain, added MiniMax + DashScope providers
- create_video: refactored to ExecuteWithChain, added MiniMax async video generation
- Backward compatible with legacy {provider, model} settings format
2026-03-08 20:09:43 +07:00
viettranx d70e58ae41 feat: add conditional Python 3 and pip installation via ENABLE_PYTHON build argument. 2026-03-08 16:58:43 +07:00
viettranx 454fed11fb feat(ui): render use_skill events distinctly in realtime events panel
Show skill name instead of raw "use_skill" tool name, amber-colored
text, "activated" badge, and skip displaying raw arguments for skill
activation events.
2026-03-08 16:52:18 +07:00
viettranx 5536313335 feat(tools): add use_skill marker tool for skill activation observability
Add a no-op use_skill tool that generates tool.call/tool.result events
in tracing spans and realtime, making skill activations visible in
observability. The actual skill loading still happens via read_file.

Web UI renders use_skill events with a distinct Zap icon and skill name
instead of the generic wrench icon.
2026-03-08 16:45:26 +07:00
viettranx 62c28e1b3e feat(channels): unified media pipeline for Discord, Feishu, and WebSocket
Extract shared media utilities (MediaInfo, BuildMediaTags, TranscribeAudio,
DetectMIMEType) into internal/channels/media/ and refactor Telegram to use
them. Add full inbound/outbound media support to Discord and Feishu channels
(STT transcription, document extraction, media tags, voice agent routing).
Add WebSocket media upload/serve endpoints and MIME-aware media tags in
chat.send. Split large channel files for maintainability.
2026-03-08 16:39:46 +07:00
viettranx 47cc11bfc0 feat(metadata): add JSONB metadata to sessions, profiles, and pairing
Persist friendly names (display_name, username, chat_title) from channel
handlers into sessions, user profiles, and pairing records. Web UI renders
metadata with graceful fallback to raw IDs.

- Add migration 000011: metadata JSONB columns on sessions,
  user_agent_profiles, pairing_requests, paired_devices
- Extend SessionStore/AgentStore/PairingStore interfaces with metadata ops
- Extract and persist channel metadata in gateway consumer
- Extend sessions.patch and add PATCH instances metadata HTTP endpoint
- Update frontend sessions page, detail page, and instances tab
- Delete legacy file-based internal/pairing/service.go
- Update docs references to reflect DB-backed pairing
2026-03-08 15:42:44 +07:00
viettranx e1a6801a7a fix(tools): correct Veo API, media ref ordering, video tag, and model verify
- Fix create_video: use predictLongRunning API instead of generateContent
  (async polling flow: POST → poll every 10s → download video from URI)
- Fix durationSeconds as int (not string) per actual Gemini API requirement
- Fix MediaRef collection order: historical first, current last, so
  refs[len-1] always returns the most recent file (fixes read_audio
  picking up old file instead of current voice message)
- Remove misleading "video not yet supported" text from Telegram handler
  that prevented LLM from calling read_video tool
- Add isNonChatModel() to skip chat-based verify for generation models
  (veo-*, dall-e-*, imagen-*, gemini-*-image)
2026-03-08 15:21:08 +07:00
viettranx 691ddce8fb feat(tools): add read_audio, read_video, create_video tools and fix system prompt tool filtering
- Add read_audio tool with Gemini File API, OpenAI input_audio, and fallback support
- Add read_video tool with Gemini File API and base64 fallback for video analysis
- Add create_video tool with Gemini Veo and OpenRouter chat completions support
- Add shared gemini_file_api.go for upload → poll → generateContent pipeline
- Add shared openai_compat_call.go for custom JSON chat completions
- Fix system prompt showing denied tools: use filteredToolNames() instead of tools.List()
- Wire audio/video MediaRef context propagation in agent loop
- Register new tools in seed data, policy groups, and web UI settings
- Enforce duration (max 30s) and aspect_ratio limits on create_video
2026-03-08 14:43:18 +07:00
viettranx ea185b3f6c feat(agents): add self-evolution config and instances management for predefined agents
Self-Evolution: predefined agents can now optionally evolve their SOUL.md
(communication style/tone only) when self_evolve is enabled in other_config.
Identity, name, and operating instructions remain locked. Context propagation
flows through LoopConfig → Loop → context.WithValue → interceptor carve-out.
System prompt guides the agent on what it can/cannot evolve.

Instances Tab: new HTTP endpoints and UI tab for viewing/editing per-user
USER.md files on predefined agents. Includes owner-only access checks,
fileName validation (USER.md only), and cache invalidation.

UI: self-evolve toggle in General tab, create dialog, and setup wizard.
Agent type and evolve/static badges with tooltip explanations on cards
and detail header. TooltipProvider added to agents list and detail pages.
2026-03-08 14:27:40 +07:00
viettranx 0f2737ce53 feat(media): persistent media storage, read_document tool, and pipeline refactor
- Add persistent media storage (internal/media/) replacing temp file deletion
- Add MediaRef type for lightweight media references in session messages
- Refactor media pipeline to use bus.MediaFile{Path, MimeType} across all channels
- Add read_document builtin tool for PDF/DOCX/XLSX analysis via Gemini native API
- Move image sanitization from Telegram to shared agent/media layer
- Add media reload for multi-turn conversations (images from last 5 messages)
- Add reply-to-message media resolution for Telegram (re-download on reply)
- Add media inventory to compaction summary to preserve awareness after truncation
- Fix coreToolSummaries for read_image, read_document, create_image tools
- Add real-time trace update events via WebSocket broadcast
- Improve trace detail UI with media refs and tool result display
2026-03-08 14:00:34 +07:00
viettranx d9cdbb8e6e feat(storage): add workspace file browser with delete and size display
Add Storage page under Monitoring menu to browse, view, and manage
files inside ~/.goclaw/. Skills directories are visible but
deletion-protected. Extract shared file browser components from
skills page for reuse.
2026-03-08 13:18:18 +07:00
viettranx 85e7198f6b feat(skills): add syntax highlighting, CSV tables, responsive layout
- Add highlight.js with tree-shaken language imports for code viewer
- Add CSV file rendering as styled table with sticky headers
- Add resizable file tree panel with drag handle
- Add mobile stacked layout with back navigation
- Strip frontmatter from .md files in file viewer
- Add responsive dialog breakpoints (sm/md/lg/xl/2xl)
- Split skill-detail-dialog into 4 focused modules (<200 lines each)
- Add highlight.js CSS with dark mode overrides
2026-03-08 10:47:42 +07:00
viettranx 2a4db0da94 feat(skills): add file browser, version selector, and security hardening
- Add skill file browser with tree panel and content viewer (syntax highlight per file type)
- Add version selector to browse all skill versions on disk
- Add 3 new API endpoints: GET /v1/skills/{id}/versions, files, files/{path}
- Make skill name clickable to open detail dialog (remove redundant View button)
- Filter system artifacts (__MACOSX, .DS_Store, Thumbs.db) from ZIP extraction and file listing
- Add symlink protection in extraction, file listing, and file reading
- Strengthen path traversal prevention with prefix validation
- Strip frontmatter from .md files in file viewer
- Fix double scrollbar in file content panel
2026-03-08 10:19:09 +07:00
viettranx 7652fe214d fix(ui): strip outer pre from ReactMarkdown to fix code block styling
ReactMarkdown wraps fenced code blocks in <pre><code>, but CodeBlock
renders its own <pre>. The outer <pre> inherited prose dark background,
causing nested styling conflicts. Override pre component to render
fragment, use not-prose on CodeBlock to fully escape prose styling.
2026-03-08 07:55:35 +07:00
viettranx 73e061ee6d feat(ui): add image lightbox and auto-scroll on send
- Replace target=_blank image links with in-page lightbox modal
  (click image to view full-size, click backdrop or Escape to close)
- Force scroll-to-bottom when user sends a message, regardless of
  current scroll position
2026-03-08 00:41:49 +07:00
viettranx a53f3e092f fix(media): use absolute paths and relative URLs for WS media delivery
- FilesHandler now serves files by absolute path (auth-token protected)
  instead of requiring a workspace root, supporting multiple agent workspaces
- mediaToMarkdown generates relative URLs (/v1/files/...) instead of
  absolute http://host:port URLs so images work from any client origin
- Deduplicate media collection in agent loop: prefer result.Media over
  MEDIA: prefix parsing to prevent duplicate images
2026-03-08 00:41:38 +07:00
viettranx c1962e4659 feat(skills): add edit modal, drag-and-drop upload, fix frontmatter stripping
- Add skill edit dialog (name, description, visibility, tags)
- Add drag-and-drop support to upload modal with visual feedback
- Fix frontmatter not stripped in skill detail view (CRLF normalization)
- Include visibility, tags, version in skill list/detail API responses
- Change default skill upload visibility from private to internal
- Add visibility column and version display to skills table
2026-03-08 00:16:33 +07:00
viettranx 1bc5393f00 fix(ui): fix code block text color in light mode for markdown renderer
Override Tailwind Typography prose defaults that set dark pre background
with hardcoded light text. Use theme-aware CSS variables (--muted, --foreground)
so code blocks are readable in both light and dark modes.
2026-03-08 00:15:20 +07:00
viettranx 96845d1e44 fix(media): set result.Media on create_image and add MEDIA: fallback in subagent exec
Root cause: create_image tool only set ForLLM:"MEDIA:path" but never
populated result.Media. The main agent loop parses MEDIA: prefix via
parseMediaResult(), but the subagent exec loop only checked result.Media
— so media paths were silently lost for all subagent/spawn workflows.

This caused the entire downstream pipeline (task.Media → AnnounceQueue →
PublishInbound → ContentSuffix → session) to receive empty media, making
images invisible in WS chat despite Telegram working fine.

Fixes:
- create_image.go: set result.Media = []string{imagePath}
- subagent_exec.go: add MEDIA: prefix fallback parsing as safety net
2026-03-08 00:06:13 +07:00
viettranx e06b44179c fix(ui): sync toolStreamRef on tool.result to prevent stale overwrite
tool.result updated React state via functional update but didn't sync
toolStreamRef.current. When the next tool.call arrived and set state
to toolStreamRef.current (direct value), it overwrote completed
statuses with stale "calling" phase — causing tool cards to stay
stuck at "Running..." forever.
2026-03-07 23:48:41 +07:00
viettranx 56dd8b5885 fix(ws-media): plumb subagent media paths through announce to inbound message
Root cause: SubagentTask had no Media field. Tool results with media
(e.g. image generation) captured result.Media but it was discarded.
AnnounceQueueItem also had no Media field, so PublishInbound for
subagent announces always had empty Media — ContentSuffix never triggered.

Changes:
- Add Media []string to SubagentTask (subagent.go)
- Capture result.Media from tool executions in subagent_exec.go
- Add Media []string to AnnounceQueueItem (announce_queue.go)
- Pass task.Media through direct publish and batched announce drain
- Collect batch media in gateway.go announce queue drain callback
2026-03-07 23:41:14 +07:00