* fix(telegram): propagate local_key for forum topic routing in team notifications
Team task status messages (dispatched, completed, progress) were always
delivered to the General topic in Telegram forum groups because the
notification pipeline had no access to the originating topic's local_key.
Root cause: wireTeamProgressNotifySubscriber in gateway_events.go published
OutboundMessage with no Metadata, so the Telegram adapter had no
message_thread_id to route to the correct forum topic.
Fix has two parts:
1. Team notify path (root cause):
- Add LocalKey field to TeamTaskEventPayload (protocol)
- Extract local_key from tool context in WithContextInfo()
- Add LocalKey to NotifyRoutingMeta
- Pass LocalKey through to OutboundMessage Metadata in both
leader mode (InboundMessage) and direct mode (OutboundMessage)
2. MCP bridge context (supporting):
- Propagate local_key and session_key through bridge HTTP headers
- Add X-Local-Key and X-Session-Key to BridgeContext
- Extract and inject into tool context in gateway middleware
- Include in HMAC signature for integrity
Closes#798
* fix(telegram): pass LocalKey from task metadata in all dispatch/fail broadcast sites
The initial fix added LocalKey to the event payload and WithContextInfo(),
but 4 broadcast call sites use individual With* options instead of
WithContextInfo — so LocalKey was never populated for:
- fallback_dispatch (team_tasks_create.go)
- dispatch_unblocked (team_tool_dispatch.go)
- post_turn dispatch (team_tool_validation.go)
- blocker/fail (team_tasks_blocker.go)
Add WithLocalKey() option function and extract TaskMetaLocalKey from task
metadata at each site, matching the existing TaskMetaPeerKind pattern.
* test(mcp): add HMAC verification tests for extra params (localKey, sessionKey)
- Add tests for SignBridgeContext/VerifyBridgeContext with extra params
- Test backward compat fallback for pre-localKey sessions
- Test that param order matters in signature
- Add clarifying comment for routing context injection security model
---------
Co-authored-by: Jens Henke <jens@henke.dk>
Co-authored-by: viettranx <viettranx@gmail.com>
- Guard uuid.Nil before converting to string in loop.go to avoid
sending zero UUID as X-Tenant-ID header when no tenant is set
- Return (ok, tenantVerified) from VerifyBridgeContext so callers
only inject tenant_id when HMAC actually covers it (level 1)
- Add unit tests for SignBridgeContext and VerifyBridgeContext
including security test for tenant injection via HMAC fallback
Problem:
Memory documents saved via write_file → MemoryInterceptor are stored
in memory_documents (PutDocument succeeds via fail-open tenant fallback),
but IndexDocument fails with "tenant_id required" because the MCP bridge
chain never propagates tenant_id. This results in 0 memory_chunks,
making memory_search return empty for all agents using claude_cli provider.
Root cause:
- BridgeContext struct lacks TenantID field
- writeMCPConfigInternal never sets X-Tenant-ID header
- bridgeContextMiddleware never extracts/injects tenant_id into context
- PutDocument uses tenantIDForInsert() (fail-open, defaults to master)
- IndexDocument uses scopeClause() → requireTenantID() (fail-closed)
Fix:
- Add OptTenantID constant and BridgeContext.TenantID field
- Pass tenant_id from agent loop context → provider opts → MCP headers
- Add X-Tenant-ID to bridge request headers (HMAC-signed)
- Extract X-Tenant-ID in bridgeContextMiddleware → store.WithTenantID()
- Backward-compatible HMAC verification with 3 fallback levels
(with tenant, without tenant, without workspace+tenant)
Affected: all agents using claude_cli provider type.
Also fixes: "KG embeddings backfill failed: tenant_id required"
Co-authored-by: admin <admin@admins-MacBook-Pro.local>
* fix(vision): fix Lark inline image parsing and MCP bridge read_image
Two fixes for image reading through Lark channel with claude-cli provider:
1. Lark post messages use flat format {"content":[[...]]} but resolvePostElements
only handled language-wrapped format {"zh_cn":{"content":[...]}}. Now checks
flat format first, and skips non-map values in fallback iteration.
2. read_image tool failed via MCP bridge because bridge creates fresh HTTP context
losing WithMediaImages. Fix: enrich <media:image> tags with path attribute
(like documents), pass workspace through X-Workspace bridge header with HMAC,
so read_image can load images from disk via path parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(security): add HMAC backward compat and guard workspace injection
- VerifyBridgeContext falls back to old signature format (without workspace)
for sessions with pre-existing MCP configs, preventing 403 on server restart
- Workspace context injection now requires agent context (agentID/userID)
to prevent unauthenticated path injection bypassing HMAC
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: viettranx <viettranx@gmail.com>
Replace all hardcoded ~/.goclaw path constructions with configurable
sources (cfg.ResolvedDataDir() for service dirs, cfg.Agents.Defaults.Workspace
for agent workspaces). This fixes data persistence issues in Docker
deployments where paths differ from local dev.
- Add DataDir field to Config with ResolvedDataDir() resolver
- Add ResolvedDataDirFromEnv() package-level helper for packages without Config
- Populate StoreConfig.SkillsStorageDir (was never set, caused hardcoded fallback)
- Agent workspaces now use subdirectory format (workspace/{key}) for volume compatibility
- Remove dead GOCLAW_SESSIONS_STORAGE env/config (sessions moved to PostgreSQL)
- Fix deploy-stg.sh trailing space after backslash + remove deprecated GOCLAW_MODE
- Add GOCLAW_SKILLS_DIR override in docker-compose for volume persistence
- 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
* 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 Claude CLI as an LLM provider (subscription-based, no API key needed).
The CLI manages session history, tool execution, and context while GoClaw
forwards messages and streams responses.
Key features:
- Claude CLI provider with session persistence (--resume)
- MCP bridge server exposing GoClaw tools to CLI via streamable-http
- Security hooks (shell deny patterns, workspace path restrictions)
- Per-session mutex preventing concurrent CLI calls
- Onboard wizard for Claude CLI setup and auth verification
- Web UI for adding/managing Claude CLI provider with auth status
- Provider registry Close() for proper shutdown cleanup
Security:
- CLI path validation (only "claude" or absolute paths from DB)
- Token auth middleware for MCP bridge endpoint
- Shell injection prevention in hook scripts (single-quoted paths)
- Relative path resolution before workspace boundary checks
- Resource leak prevention on provider replace/unregister
Co-authored-by: nhokboo <nhokboo@users.noreply.github.com>