* feat(providers): add ACP provider for orchestrating external coding agents (#189)
Implement native Go ACP (Agent Client Protocol) client as a new Provider.
Enables GoClaw to orchestrate any ACP-compatible agent (Claude Code, Codex
CLI, Gemini CLI) as a subprocess via JSON-RPC 2.0 over stdio.
- Add bidirectional JSON-RPC 2.0 transport over stdio pipes
- Add subprocess process pool with idle TTL reaping and crash recovery
- Add ACP session lifecycle (initialize, session/new, session/prompt)
- Add tool bridge for agent-initiated fs/terminal/permission requests
- Add workspace sandboxing, shell deny patterns, and env var filtering
- Wire config-based and DB-based provider registration paths
- Export DefaultDenyPatterns from tools package for reuse
* feat(providers): add changelog entry for ACP provider integration
* fix(tools): prevent workspace traversal bypass via /tmp/ fallback in resolveMediaPath
Reject paths containing ".." in the isInTempDir fallback to prevent
workspace escape where traversal path still resolves inside /tmp/.
* fix(tools): block workspace-sibling paths in resolveMediaPath /tmp/ fallback
When workspace is inside /tmp/, traversal paths like workspace/../X
resolve to /tmp/ siblings that pass isInTempDir. Reject paths inside
the workspace parent directory to prevent this escape.
* feat(providers): add ACP provider web UI and live reload via pubsub
Web UI for creating/editing ACP providers with dedicated form fields
(binary, args, idle TTL, permission mode, work directory). ACP providers
now update immediately without gateway restart via cache invalidation
pubsub pattern.
Frontend:
- New ACPSection form component with i18n (en/vi/zh)
- Provider form dialog integration with ACP state management
- ACP type badge on providers list page
- Settings field added to provider TypeScript types
Backend:
- ACP models handler (claude/codex/gemini) without API key requirement
- Binary path validation + LookPath verification in verify handler
- Provider CRUD emits cache.invalidate events via msgBus
- Subscriber in gateway_managed.go re-registers ACP providers from DB
- ACP core improvements from code review (helpers, jsonrpc, process,
terminal, tool_bridge)
---------
Co-authored-by: viettranx <viettranx@gmail.com>
StorageHandler was hardcoded to browse ~/.goclaw/, which is empty in
Docker where volumes mount to separate paths (GOCLAW_WORKSPACE).
Use the already-resolved workspace variable so the Storage page
correctly shows workspace files regardless of deployment layout.
* feat(workspace): add team shared workspace for file collaboration
- Add workspace_write and workspace_read tools for agents to share files across team members
- Create team_workspaces DB table with migration 000017 (file metadata, pinning, tags)
- Implement PostgreSQL store layer for workspace CRUD operations
- Add RPC handlers for workspace list/read/delete from web UI
- Build React workspace tab with file listing, content preview, and delete
- Propagate workspace channel/chatID scope through delegation chain
- Auto-allow workspace tools in agent tool policy when agent belongs to a team
- Inject team workspace guidance into system prompt for team agents
- Add /reset command handler for clearing session history
- Harden MCP bridge context middleware to reject headers when no gateway token
- Add i18n strings for workspace UI in en/vi/zh locales
* feat(teams): add comprehensive task management with followup reminders and recovery
- Add task followup/reminder system with auto-set on lead agent reply and auto-clear when user responds on channel
- Add task recovery ticker to re-dispatch stale/pending tasks periodically
- Add task scopes, filtering by status/channel/chatID, and task events
- Add WS RPC handlers for task CRUD, assignments, comments, events, and bulk operations (teams_tasks.go)
- Add task detail dialog, settings UI for followup config, and scope filtering in web dashboard
- Add migrations 000018 (team_tasks_v2) and 000019 (task_followup)
- Extend team_tasks_tool with await_reply, clear_followup actions
- Auto-complete/fail team tasks when delegate agent finishes
- Add workspace file listing and team tool manager enhancements
* docs(teams): add team system architecture and playbook ideas documentation
- Add TEAM_SYSTEM.md with full architecture design covering task management, shared workspace, and delegation engine subsystems
- Add TEAM_PLAYBOOK_IDEAS.md outlining future team coordination layers (playbook, member capabilities, auto-learned patterns)
- Document data models, status flows, tool actions, followup reminder system, task ticker, execution locking, and workspace scope model
* fix(teams): resolve 6 critical bugs in team task system
- Fix unblock SQL: check array_length after array_remove (not before)
- Enforce single-team leadership in team creation
- Add requireLead() for approve/reject tool actions
- Validate cross-team dependency references in blocked_by
- Add team_id to handoff route for multi-team isolation
- Set blocked_by DEFAULT '{}' to prevent NULL array issues
* refactor(workspace): use stable userID as scope key instead of connection UUID
Workspace scope changed from (team_id, channel, chat_id) to (team_id, userID).
Fixes workspace fragmentation across WS tab refreshes and reconnections.
* feat(teams): add V1/V2 versioning with feature gating and optimized prompts
- IsTeamV2() helper gates advanced features (locking, followup, review, audit)
- V2 tool actions rejected for V1 teams with clear error message
- Ticker, gateway consumer, delegation hooks respect version flag
- TEAM.md renders v1/v2 sections conditionally
- Tool descriptions and params optimized (~38% token reduction)
- UI: version toggle in settings, V2 Beta badge, conditional rendering
- i18n: version modal keys for en/vi/zh
* fix(migration): use VARCHAR(255) for user ID columns and add metadata JSONB
- assignee_user_id, user_id, actor_id: TEXT → VARCHAR(255)
- Add metadata JSONB to team_task_comments and team_task_attachments
---------
Co-authored-by: Nam Nguyen Ngoc <namnn.0911@gmail.com>
Message tool prompting now explicitly tells the LLM not to use it for
replying to the user — prevents false activations on phrases like
"gởi lại cho tôi" (send it back to me).
Handoff tool disabled by default since it's rarely used and causes
confusion with spawn. Admins can re-enable via DB/UI if needed.
- Add hasKG flag to MemorySearchTool, inject hint in results when KG is enabled
- Wire SetHasKG(true) in gateway when KG store is available
- Improve knowledge_graph_search tool description with concrete use cases
- Update system prompt KG guidance to be more actionable
Inject user follow-up messages into the running agent loop at turn
boundaries instead of queueing them for a new run. This preserves
context so the LLM sees both tool results and user follow-ups together.
- Add InjectedMessage type and drainInjectChannel helper
- Add InjectCh to ActiveRun with buffered channel (cap=5)
- Drain injection channel at two points in agent loop (after tool
results and before no-tool-calls exit)
- Route steer/new_task intents to InjectMessage with scheduler fallback
- WebSocket: inject into running loop when session is busy
- Remove IntentClassify config toggle (always on)
- Web UI: show send + stop buttons side by side during agent run
- i18n: add injection acknowledgment messages (en/vi/zh)
MessageTool.parseMediaPath() was hardcoded to only allow files in /tmp/,
while all other filesystem tools (read_file, write_file, edit, exec) use
workspace-aware resolvePath() with restrict_to_workspace enforcement.
This meant agents could create files in their workspace via write_file
but couldn't send them as attachments — only /tmp/ files from
create_image/create_audio worked.
Replace parseMediaPath() with resolveMediaPath() that:
- Reuses resolvePath() for consistent security (symlink, hardlink, traversal)
- Honors per-agent workspace + restrict_to_workspace from context
- Still allows /tmp/ as fallback (for create_image, create_audio, etc.)
- Supports relative paths resolved against workspace
- Updates tool description so LLM knows about MEDIA: prefix
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Replace direct ActivityStore injection with event-driven audit system.
Handlers emit audit events via msgBus.Broadcast(), a single subscriber
with buffered channel persists to activity_logs table.
Coverage expanded from 3 agent CRUD actions to ~65 audit points across
all HTTP handlers and WebSocket RPC methods including agents, providers,
skills, MCP servers, cron, sessions, teams, pairing, and more.
* docs: add brainstorm report for discord guild-user memory
* docs: update brainstorm report with corrected root cause analysis
* feat(discord): per-user memory scope in guild channels
Fixes shared USER.md between guild members by scoping userID to
"guild:{guildID}:user:{senderID}" for Discord group messages.
Updates all group-context prefix checks (write permissions, writer
cache, cron peer kind, history filter) to include the new guild: prefix.
Closes#165
* feat(infra): add runtime package support for skills
Install nodejs, npm, pandoc, github-cli + pre-install Python packages
(openpyxl, pandas, python-pptx, markitdown) and Node packages
(docx, pptxgenjs). Configure runtime dirs for agent pip/npm installs
with PIP_TARGET, NPM_CONFIG_PREFIX, NODE_PATH to enable dynamic
package installation in read-only container environment.
* feat(infra): add bundled skills with runtime package support
- Add 5 bundled skills: docx, pdf, pptx, xlsx, skill-creator from container skills-store
- Wire GOCLAW_BUILTIN_SKILLS_DIR env var in gateway and CLI
- Support optional runtime packages alongside dynamic skill loading
- Update Dockerfile to COPY bundled-skills at /app/bundled-skills/
- Add PIP_CACHE_DIR in docker-entrypoint.sh for clean pip installs
- Document bundled skills in 14-skills-runtime.md section 6
* feat(infra): remove ai-multimodal skill directory from bundled skills
Remove the ai-multimodal skill package as part of consolidating runtime
package support for bundled skills. This directory is no longer needed
in the bundled skills structure.
* feat(ci): add semantic release and Docker Hub publishing
Add go-semantic-release workflow to auto-create semver tags on merge to
main. Extend docker-publish to push all variants to both GHCR and
Docker Hub (digitop/goclaw).
* feat(skills): add system skills infrastructure with is_system column, dep scanning, and seeder
- Migration 000017: add is_system boolean column with partial index
- Store layer: UpsertSystemSkill, delete protection, IsSystemSkill
- ListAccessible auto-includes system skills (no grants needed)
- ListWithGrantStatus returns is_system field
- Dependency scanner: auto-detect deps from scripts/ or skill-manifest.json
- Dependency checker: verify system binaries, Python/Node packages
- Seeder: seed bundled skills into DB on startup (idempotent via hash)
- Gateway wiring: GOCLAW_BUNDLED_SKILLS_DIR env for bundled skills
- HTTP: delete guard (403), slug conflict check (409), rescan-deps endpoint
- UI: System badge, hide delete for system skills, rescan deps button
- Agent skills tab: "Always available" for system skills
- i18n: en/vi/zh keys for system skills, deps scanning
* feat(skills): conditional system prompt, skill manifests, and Zip Slip fix
- System prompt: only show package list when python3/node are available
- Add skill-manifest.json for pdf, docx, xlsx, pptx bundled skills
- Fix Zip Slip vulnerability in office/unpack.py (all 3 copies)
* refactor(skills): extract shared office code to _shared/ and deduplicate
Move office scripts (pack, unpack, validate, schemas, validators) from
duplicated copies in docx/xlsx/pptx to skills/_shared/office/ with
symlinks. Remove soffice.py (non-functional in containers) and update
SKILL.md references to use soffice binary directly. Update seeder
copyDir to follow symlinks.
Removes ~45K lines of duplicate code across 3 skills.
* fix(skills): address code review findings for system skills integration
- H1: Remove dead symlink branch in copyDir (filepath.Walk follows symlinks)
- H3: Fix rescan-deps to query ALL skills (including archived) and re-activate
when deps become available; add ListAllSkills() + Status field to SkillInfo
- H4: Add Status field to SkillCreateParams, stop overloading Visibility
- M1: Batch Python/Node dep checks into single subprocess per runtime
- M4: Add rows.Err() check in ListSkills to prevent caching partial results
* feat(skills): async dep checking with realtime WS events
Split Seed() into sync DB upsert + async CheckDepsAsync() goroutine.
Gateway startup no longer blocks on Python/Node subprocess dep checks.
- Seed() returns seeded skills list, all initially status="active"
- CheckDepsAsync() runs in background, emits skill.deps.checked per-skill
- skill.deps.complete event emitted when all checks finish
- Each failed dep check: archives skill + BumpVersion() for immediate
cache invalidation so next agent turn picks up the change
- UI: use-query-invalidation listens to skill.deps.* events → auto-refresh
skills list in realtime
* feat(skills): system skills integration with toggle, dep checking, and per-item install
- Add is_system, deps, enabled columns to skills table (migration 017)
- Seed bundled core skills (pdf, docx, pptx, xlsx, skill-creator) on startup
- PYTHONPATH-based dep detection — eliminates false positives from local modules
- Per-item dep install UI with individual status (installing/success/error)
- Enable/disable toggle for core and custom skills (independent of dep status)
- Re-run dep check when skill is toggled back on
- Inline skill thresholds: 40 skills / 5000 tokens before switching to search mode
- Fix UpsertSystemSkill: backfill null file_hash without bumping DB version
- Remove redundant skill-manifest.json files (replaced by deps JSONB column)
- Show author from frontmatter in custom skills tab
- Runtime checker for python3/pip3/node/npm availability
- WS events for dep checking/installing progress
- docs: add 15-core-skills-system.md, 16-skill-publishing.md
---------
Co-authored-by: Goon <duy@wearetopgroup.com>
- Set providerType on Suno case in registerProvidersFromDB (was missing,
causing misrouting for custom-named Suno providers)
- Fix dbTypeToMediaType: replace dead "openai" key with "openai_compat"
to match actual store constant, add "bailian" → "dashscope" mapping
- Clone entry.Params before injecting _provider_type to avoid mutating
the original chain config
Media tools (create_image, create_video, create_audio, read_audio,
read_video, read_document) routed API calls based on provider name
pattern matching (e.g. strings.HasPrefix(name, "gemini")). This breaks
when users give custom names to DB providers — a Gemini provider named
"chatgpt-sap-het" would be misrouted to the OpenAI-compat endpoint,
causing 404 errors.
Fix: carry the DB provider_type through OpenAIProvider, resolve it via
typedProvider interface in ExecuteWithChain, and inject as _provider_type
param for callProvider routing. Name-based heuristic kept as fallback
for config-file providers that don't have a DB type.
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Add alias support to the tool Registry so Claude Code skills can reference
Anthropic tool names (Read, Write, Edit, Bash, etc.) and have them resolve
to GoClaw canonical tools (read_file, write_file, edit, exec, etc.).
- Registry: aliases map, RegisterAlias(), resolve(), Aliases()
- Policy engine: auto-includes alias defs when canonical tool passes filter
- System prompt: alias entries in coreToolSummaries + missing use_skill
- Legacy toolAliases migrated to Registry.RegisterAlias() at startup
The embedding provider resolution only matched 3 hardcoded names
(openai, openrouter, gemini), silently failing for DB-stored providers
like "openai-embedding". This caused memory chunks to be stored
without vectors even when a valid embedding provider was configured.
Changes:
- resolveEmbeddingProvider: fallback to provider registry for DB-stored
provider names when hardcoded match fails
- gateway startup: read per-agent memory config from DB (priority over
config file defaults) for embedding provider resolution
- memory IndexDocument: log embedding errors instead of swallowing them
- memory admin ListChunks: return full chunk text instead of truncating
to 200 chars, avoiding confusing partial content in the UI
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
- A1+C2: Include token usage in run.completed event payload for WS clients
- A2: Cost tracking with model pricing config, cost calculation, and cost summary API
- A3: Budget enforcement per agent with monthly budget limits (migration 000015)
- C1: External wake/trigger API (POST /v1/agents/{id}/wake) for orchestrators
- C3: Activity audit trail with structured logging and queryable API
- UI: Activity page, cost stat card on overview, budget section in agent detail
- i18n: Complete en/vi/zh translations for all new features
Delegate session keys (delegate:{uuid8}:{agentKey}:{delegationId}) were
not parsed by makeSchedulerRunFunc, causing fallback to default agent ID
which doesn't exist in managed-mode DBs. Add switch/case to handle both
agent: and delegate: prefixes.
* refactor: remove managed/standalone mode distinction from codebase
Standalone mode is deprecated; managed mode is now the only mode.
Remove redundant "managed mode" qualifiers from comments, docs,
and error messages. Error strings now reference "database stores"
instead of "managed mode" for clarity.
* improve(onboard): streamline onboard process and env setup
Simplify onboard wizard, extract helpers to dedicated file,
update env example and entrypoint for default managed mode,
clean up prepare-env script, update i18n catalogs.
Cron jobs ran in isolated sessions with no context about who requested
them or where to deliver responses. The agent would misroute responses
(e.g., using team_message instead of replying to the original chat).
- Inject ExtraSystemPrompt with job name, requester ID, and delivery
target so the agent knows to produce content directly
- Pass client.UserID() in Web UI cron.create instead of empty string
- Update seed defaults to use chain format {"providers":[...]}
- Add startup auto-migration for existing legacy flat settings in DB
- Remove legacy flat format parsing from parseChainSettings()
- Add ~/.goclaw/cli-workspaces/ to read_file allowed paths
- Enables agents to read working files from CLI workspace sessions
Co-authored-by: Nam Nguyen Ngoc <namnn.0911@gmail.com>
Pass contactCollector through channel manager to all channel handlers
(Telegram, Discord, Feishu, Slack, Zalo) so contacts are automatically
collected when users interact with the agent.
* feat(cron): configurable default timezone for cron expressions
Cron expressions (e.g. "0 8 * * *") are evaluated relative to a timezone.
Without an explicit per-job timezone, they default to the server's system
timezone, which may not match the user's local time — especially in Docker
containers (default UTC) or multi-region deployments.
This adds a `default_timezone` setting to `CronConfig` (IANA format, e.g.
"Asia/Ho_Chi_Minh") that is applied as fallback when a cron job has no
explicit `schedule.tz`. The setting is configurable via the UI config page
(Integrations → Cron Scheduler) and hot-reloads on config changes.
Backend:
- Add `DefaultTimezone` field to `CronConfig`
- Add `SetDefaultTimezone()` to `CronStore` interface + PG implementation
- Apply default TZ in `AddJob()` when `schedule.TZ` is empty
- Wire at startup + subscribe to config change events for hot reload
- Update cron tool description so LLM knows about gateway default
Frontend:
- Add timezone dropdown (20 common IANA timezones) to Cron config section
- Add i18n keys for en, vi, zh
* fix(cron): apply default timezone to existing jobs via computeNextRun
Pass defaultTZ as fallback to computeNextRun so existing cron jobs
(with timezone = NULL in DB) also use the gateway's configured default
timezone when computing next_run_at. This ensures old jobs benefit
from the timezone setting without needing a DB migration or backfill.
---------
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
* feat(providers): add Ollama local and Ollama Cloud provider support
Adds two new provider variants:
ollama — local/self-hosted Ollama instance
- Gated on providers.ollama.host in config (or GOCLAW_OLLAMA_HOST env)
- No API key required; Ollama's OpenAI-compat endpoint accepts any Bearer value
- Defaults to http://localhost:11434/v1 (configurable for LAN/remote hosts)
- Default model: llama3.3
ollama-cloud — Ollama Cloud (managed remote inference)
- Gated on providers.ollama_cloud.api_key (or GOCLAW_OLLAMA_CLOUD_API_KEY env)
- Bearer token from ollama.com/settings/keys
- Default base URL: https://ollama.com/v1 (overridable via api_base)
- Default model: llama3.3
Both variants use the existing NewOpenAIProvider (OpenAI-compat) — no new
provider struct needed. Both are registered from config file and DB (via
llm_providers table with ProviderOllama / ProviderOllamaCloud types).
OllamaCloud.APIKey follows all existing secret handling patterns:
MaskedCopy, StripSecrets, StripMaskedSecrets.
* feat(providers): wire Ollama into web UI and fix DB registration
- Add ollama + ollama_cloud to PROVIDER_TYPES constants (dropdowns)
- Fix setup wizard: skip API key requirement for Ollama local (isOllama)
- Fix bootstrap status: recognize Ollama local as no-API-key provider
- Add ollama_cloud to config-page KNOWN_PROVIDERS list
- Fix gateway_providers.go: move ProviderOllama before APIKey=='' guard
so DB-registered local Ollama providers actually register at startup
(same pattern as ClaudeCLI, which also needs no API key)
Add Provider, Model, MaxTokens to PendingCompactionConfig so users can
override the LLM used for pending message summarization via the config
UI. Falls back to agent's provider/model when not set. Increase default
max_tokens from 512 to 4096. Add allowEmpty prop to ProviderModelSelect
to prevent auto-selecting first provider when empty means "use default".
- Fix compact endpoint using random provider instead of agent's configured provider+model
- Wire auto-compaction for all 5 channel types (telegram, discord, slack, feishu, zalo_personal)
via PendingCompactable interface and InstanceLoader
- Add global PendingCompactionConfig (threshold, keep_recent) to ChannelsConfig
- Wire global config through InstanceLoader and PendingMessagesHandler
- Increase compaction timeout from 45s to 180s for slow providers
- Add pending compaction config card to Behavior tab in config page
- Add HowItWorksCard (expanded by default) and toast notifications to pending messages page
- Add i18n support for all new strings (en/vi/zh)
- Add reviewer role to team system (backend + UI) for evaluate_loop workflows
- Fix handoff/evaluate_loop/delegate_search system prompt entries (were showing as custom tool)
- Filter inactive agents from delegation queries (DelegateTargets, SearchDelegateTargets)
- Fix agent link direction display (flip outbound→inbound when viewed from target side)
- Improve builtin tool seed descriptions with detailed action-oriented text
- Add i18n support for builtin tool descriptions (en/vi/zh frontend locale files)
- Notify LLM when KG extraction triggered on memory write
* fix(gateway): extract media forwarding helper and fix group writer cron/media gaps
- Extract appendMediaToOutbound() helper to deduplicate media attachment conversion across all outbound paths
- Add media forwarding to cron job handler, handoff announce, and teammate message flows
- Fix silent reply suppression to allow media-only responses (no text content)
- Skip group writer refusal prompt for system-initiated runs (cron, delegate, subagent) with empty senderID
* fix(agent): protect identity files in system-initiated group writer runs
- Move writer list lookup before senderID check so cron/delegate/subagent
runs still load group writer config
- Inject file protection prompt for system-initiated runs instead of
skipping entirely, preventing modification of SOUL.md, IDENTITY.md,
AGENTS.md, and USER.md
- Retain fail-open behavior when writer list is unavailable
---------
Co-authored-by: Nam Nguyen Ngoc <namnn.0911@gmail.com>
Extract CompactGroup() from runCompaction() for shared use by both
auto-compact and HTTP compact endpoint. Wire provider registry into
PendingMessagesHandler so /v1/pending-messages/compact performs actual
LLM summarization (keep 15 recent + summary) instead of hard delete.
Add rows-affected guard in Compact() to prevent duplicate summaries
when concurrent compactions race on the same key.
- Update go.mod and Dockerfile to Go 1.26
- Apply `go fix ./...` stdlib modernizations across 170+ files
- Add `go fix` to post-implementation checklist in CLAUDE.md
- Fix go fix misapplied rewrite in loop_history.go
- Add tool status display on channels during tool execution (streaming preview + reactions)
- Emit agent.activity events at phase transitions (thinking, tool_exec, compacting)
- Enrich delegation progress with per-member activity and tool info
- Add LLM-based intent classifier for DM status queries when agent is busy
- Keyword fast-path for cancel/status patterns (no LLM cost)
- Falls back to LLM classification with 5s timeout
- Supports status_query (immediate reply) and cancel (abort run) intents
- Register/unregister runs in makeSchedulerRunFunc for channel inbound tracking
- Add sessionRuns secondary index in Router for O(1) IsSessionBusy lookups
- Add intent_classify config toggle (global default + per-agent override)
- Add tool_status config toggle for channel tool status display
- Add i18n keys and translations (en/vi/zh) for status messages
- Add web UI config toggles for intent_classify and tool_status
* feat: add Z.ai provider support (general API + coding plan)
Add Z.ai (GLM) as a new LLM provider with two variants:
- `zai`: general API (api.z.ai/api/paas/v4)
- `zai_coding`: coding plan (api.z.ai/api/coding/paas/v4)
Reuses OpenAIProvider — Z.ai API is OpenAI-compatible with Bearer
token auth, SSE streaming, and reasoning_content support.
Includes: store constants, config struct fields, env var loading
(GOCLAW_ZAI_API_KEY, GOCLAW_ZAI_CODING_API_KEY), secret masking,
config + DB registration, onboard wizard, and UI provider types.
Default model: glm-5
Closes#100
* docs: add Z.ai provider entries to providers documentation
Remove the 700+ line interactive wizard (provider, model, channels, TTS,
features) since the web UI setup wizard already handles all of this.
The onboard command now only: prompts for Postgres DSN, tests connection,
generates gateway token + encryption key, runs migrations, seeds placeholder
providers, and saves .env.local with the 3 required secrets.
Delete 8 helper files: onboard_auto, onboard_verify, onboard_models,
onboard_resolve, onboard_claude_cli, onboard_tts, onboard_feishu, onboard_zalo.
* 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>
- 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
* 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.
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
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.
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.