Squash merge PR #111 after resolving docs/changelog conflicts. Local checks covered tools/config and both Go builds; PR CI run 26704622503 passed release-versioning, go, and web.
27 KiB
03 - Tools System
The tools system bridges the agent loop and the external environment. When the LLM emits a tool call, the registry handles rate limiting, credential scrubbing, policy enforcement, and virtual filesystem routing before returning results for the next LLM iteration.
1. Overview
Tools are the agent's hands. The registry owns all tool instances and mediates every invocation — injecting request context, enforcing rate limits, running deny-pattern checks, and scrubbing credentials from output. Agents never call tools directly; everything routes through Registry.ExecuteWithContext.
Key concerns the tool system manages:
- Discovery — which tools exist and which a given agent may use (policy engine)
- Invocation — per-call context injection, rate limiting, execution, result scrubbing
- Isolation — workspace path containment, path traversal prevention, Docker sandbox routing
- Customization — per-tenant config overlay, custom shell-based tools at runtime
2. Registry & Lifecycle
sequenceDiagram
participant AL as Agent Loop
participant R as Registry
participant RL as Rate Limiter
participant T as Tool
participant SC as Scrubber
AL->>R: ExecuteWithContext(name, args, channel, chatID, ...)
R->>R: Inject per-call context values
R->>RL: Allow(sessionKey)?
alt Rate limited
RL-->>R: Error: rate limit exceeded
else Allowed
RL-->>R: OK
R->>T: Execute(ctx, args)
T-->>R: Result
R->>SC: ScrubCredentials(result.ForLLM)
R->>SC: ScrubCredentials(result.ForUser)
SC-->>R: Cleaned result
end
R-->>AL: Result
Per-call context values injected before execution: channel identity, chat ID, peer kind, sandbox key. Tool instances are shared safely across goroutines because mutable state lives in context, not structs.
Custom tools (shell-based, stored in DB) use a two-phase registry:
- Global tools (no agent scope) are loaded at startup into a shared registry.
- Per-agent tools are merged on first access; the registry is cloned, never mutated globally.
- Cache invalidation via pub/sub causes a reload + router invalidation on next agent turn.
3. Tool Capabilities
Each tool carries structured metadata describing side-effect class, group membership, and workspace requirements.
| Capability | Meaning | Examples |
|---|---|---|
read-only |
No side effects; safe to retry | read_file, web_search, memory_search |
mutating |
Modifies state or external systems | write_file, exec, cron, team_tasks |
async |
Returns immediately; result delivered later | spawn |
mcp-bridged |
Proxied to an external MCP server | MCP tools registered dynamically |
Capabilities are inferred from tool name when no explicit metadata is registered. The policy engine can use capability class to gate entire sets (e.g. restrict an agent to read-only tools).
The agent loop also uses this metadata for parallel tool-call scheduling. Only registered read-only tools are eligible for bounded parallel raw I/O. Mutating, async, MCP-bridged, exec/bash, wait, and unknown tools stay sequential by default. PreToolUse hooks run before any parallel I/O so hooks can block or rewrite arguments consistently.
4. Built-in Tool Inventory
Filesystem (group:fs)
| Tool | Description |
|---|---|
read_file |
Read file contents with optional line range |
write_file |
Write or create a file |
edit |
Apply targeted edits to a file (old/new string replace) |
list_files |
List directory contents |
Runtime (group:runtime)
| Tool | Description |
|---|---|
exec |
Execute a shell command; supports credentialed CLI mode for secure credential injection |
Credentialed CLI mode — when the invoked binary is registered in secure_cli_binaries, the exec tool injects encrypted env vars directly into the child process (no shell involved) and verifies the agent has an explicit grant. Shell-wrapper unwrapping (up to depth 3) prevents bypass via sh -c. Fail-closed on DB error.
Web (group:web)
| Tool | Description |
|---|---|
web_search |
Search the web (Exa, Tavily, Brave, DuckDuckGo provider chain) |
web_fetch |
Fetch and parse a URL (HTML → Markdown); domain allow/block policy |
Memory (group:memory)
| Tool | Description |
|---|---|
memory_search |
Search memory documents (BM25 + vector hybrid) — returns L1 abstracts |
memory_get |
Retrieve a specific memory document by ID |
memory_expand |
Load full episodic memory content (L2 deep retrieval) |
Memory layers: L1 (memory_search) returns ranked abstracts; L2 (memory_expand) loads the full summary for a given episodic ID.
Sessions (group:sessions)
| Tool | Description |
|---|---|
sessions_list |
List active sessions |
sessions_history |
View session message history |
sessions_send |
Send a message to a session |
session_status |
Get current session status |
spawn |
Spawn a subagent or delegate to another session |
Knowledge & Vault (group:knowledge / group:goclaw)
| Tool | Description |
|---|---|
vault_search |
Unified hybrid search across vault docs, memory, and knowledge graph |
vault_read |
Read full content of a vault document by doc_id |
knowledge_graph_search |
Search knowledge graph entities and relationships |
skill_search |
Search available skills (BM25) |
Automation (group:automation)
| Tool | Description |
|---|---|
cron |
Manage scheduled tasks (create, list, delete) |
datetime |
Get current date/time with timezone support |
heartbeat |
Configure agent periodic proactive check-ins |
Messaging (group:messaging)
| Tool | Description |
|---|---|
message |
Send a message to a channel |
send_file |
Send an existing workspace file as a chat attachment (with optional caption); marks DeliveredMedia to prevent duplicate delivery |
create_forum_topic |
Create a Telegram forum topic |
list_group_members |
List members in a group chat (Feishu/Lark) |
Delegation
| Tool | Description |
|---|---|
delegate |
Inter-agent task delegation via agent_links (async/sync modes with timeout) |
Teams (group:team)
| Tool | Description |
|---|---|
team_tasks |
Task board: create, list, get, claim, complete, cancel, assign, review, approve, reject, comment, progress, attach, ask_user, search, update |
Media Generation
| Tool | Description |
|---|---|
create_image |
Generate images from text (OpenAI, Gemini, MiniMax, DashScope, BytePlus) |
create_audio |
Generate audio/music/sound effects (MiniMax, ElevenLabs) |
create_video |
Generate video from text/image (MiniMax, Gemini, BytePlus) |
tts |
Text-to-speech synthesis (OpenAI, ElevenLabs, Edge, MiniMax) |
Media Reading
| Tool | Description |
|---|---|
read_image |
Analyze/describe images using vision AI (Gemini, Anthropic, OpenRouter, DashScope) |
read_audio |
Transcribe audio to text using Gemini File API, native OpenAI audio input, or OpenAI-compatible transcription models; unsupported provider/model routes fail closed instead of sending audio as image data |
read_document |
Extract and analyze documents (PDF, DOCX, images). Supports local-first extraction via pdftotext/pandoc before falling back to cloud vision (opt-in via config) |
read_video |
Analyze/transcribe video content |
Skills & Content
| Tool | Description |
|---|---|
use_skill |
Activate a skill (marker tool for observability) |
publish_skill |
Register a skill directory in the database |
skill_manage |
Manage skill lifecycle (admin operations) |
5. Tool Contracts (JSON Schemas)
User-facing parameter schemas for the most commonly configured tools.
web_search tenant config shape
{
"provider_order": ["brave", "exa"],
"brave": { "enabled": true, "max_results": 5 },
"exa": { "enabled": false },
"duckduckgo": { "enabled": false }
}
provider_order: provider preference list; unknown names silently ignored.- Per-provider:
enabled(bool) +max_results(int). DuckDuckGoenabled: falseis ignored — it is always the final fallback. - API keys go in
config_secrets, never in settings JSON.
web_fetch tenant config shape
{
"policy": "allow_all",
"allowed_domains": ["github.com", "*.example.com"],
"blocked_domains": ["malicious.com"]
}
tts tenant config shape
{
"primary": "elevenlabs",
"default_voice_id": "pMsXgVXv3BLzUgSXRplE",
"default_model": "eleven_flash_v2_5"
}
primary: provider (elevenlabs,openai,edge,minimax).- Per-agent overrides:
agent.other_config.tts_voice_id,agent.other_config.tts_model_id.
document_parser config shape
{
"local_first": false,
"max_pages": 200,
"timeout_sec": 30,
"min_text_len": 16
}
Controls local-first document text extraction in the read_document tool.
local_first: Enable local extraction viapdftotext(PDF) andpandoc(DOCX) before cloud vision fallback (defaultfalse— opt-in). Requires binaries on PATH; present infullDocker variant or builds withENABLE_FULL_SKILLS=true.max_pages: Page limit for PDF extraction (default 200). Passed topdftotext -l.timeout_sec: Per-extraction timeout in seconds (default 30). Process group killed on timeout.min_text_len: Minimum characters (after trim) to consider extraction successful; shorter output triggers cloud fallback (default 16).
Note: Config values are captured at tool construction (startup) and not picked up by hot-reload. Binary availability is re-checked per call, so runtime binary installations are detected without restart. Any extraction miss (disabled, unsupported mime, missing binary, timeout, empty output) transparently falls back to the cloud vision chain with no caller-visible difference.
Custom tool definition
{
"name": "dns_lookup",
"description": "Look up DNS records for a domain",
"parameters": {
"type": "object",
"properties": {
"domain": { "type": "string", "description": "Domain name" },
"record_type": { "type": "string", "enum": ["A", "AAAA", "MX", "CNAME", "TXT"] }
},
"required": ["domain"]
},
"command": "dig +short {{.record_type}} {{.domain}}",
"timeout_seconds": 10,
"enabled": true
}
Credentialed binary config (secure_cli_binaries table)
{
"binary_name": "gh",
"encrypted_env": {"GH_TOKEN": "ghp_..."},
"deny_args": ["auth\\s+", "ssh-key"],
"timeout_seconds": 30,
"tips": "GitHub CLI. Available: gh api, gh repo, gh issue, etc."
}
Available presets: gh, gcloud, aws, kubectl, terraform.
Credentialed CLI keyword allowlist
config.tools.commandKeywordAllowlist lets operators allow specific product or security vocabulary inside selected credentialed CLI content arguments without disabling deny_args.
Example:
{
"tools": {
"commandKeywordAllowlist": [
{
"id": "github-content",
"command": "gh",
"subcommands": ["issue create", "issue edit", "pr create", "pr comment"],
"args": ["--body", "--title"],
"argPositions": [],
"keywords": ["secret", "secrets", "token", "credential"],
"reason": "Allow security vocabulary in GitHub issue and PR prose"
}
]
}
}
The rule above allows gh issue create --body "secret rotation notes" but still blocks command paths like gh secret set TOKEN. argPositions are 0-based after the matched subcommand. The scanner evaluates command arguments only; it does not read the contents of files passed through arguments such as --body-file.
6. Interception Layer
Virtual Filesystem Routing
Filesystem calls are intercepted before hitting host disk. Two interceptors route specific paths to the database.
flowchart TD
CALL["read_file / write_file"] --> INT1{"ContextFile<br/>Interceptor?"}
INT1 -->|Handled| DB1[("DB: agent_context_files<br/>/ user_context_files")]
INT1 -->|Not handled| INT2{"Memory<br/>Interceptor?"}
INT2 -->|Handled| DB2[("DB: memory_documents")]
INT2 -->|Not handled| SBX{"Sandbox enabled?"}
SBX -->|Yes| DOCKER["Docker container"]
SBX -->|No| HOST["Host filesystem (workspace-scoped)"]
ContextFileInterceptor — routes 6 known bootstrap files (SOUL.md, IDENTITY.md, AGENTS.md, TOOLS.md, USER.md, BOOTSTRAP.md) to the DB instead of disk. Routing depends on agent type:
- Open agents: all files are per-user; agent-level file is the fallback template.
- Predefined agents: only
USER.mdis per-user; all others come from agent-level store.
MemoryInterceptor — routes MEMORY.md, memory.md, and memory/* paths to memory_documents. Writing a .md file automatically triggers chunking + embedding.
Path Security
resolvePath() joins relative paths with the workspace root, applies filepath.Clean(), and verifies the result starts with the workspace prefix — preventing path traversal attacks.
Workspace Context
Filesystem and shell tools read their workspace from context (injected by the agent loop per user and agent). This enables per-user workspace isolation without tool code changes.
Policy Engine
The policy engine determines which tools reach the LLM through a layered allow pipeline:
flowchart TD
ALL["All registered tools"] --> S1
S1["1. Global Profile (full/minimal/coding/messaging)"] --> S2
S2["2. Provider Profile Override"] --> S3
S3["3. Global Allow List (intersection)"] --> S4
S4["4. Provider Allow Override"] --> S5
S5["5. Agent Allow"] --> S6
S6["6. Agent + Provider Allow"] --> S7
S7["7. Group Allow"]
S7 --> DENY["Apply Deny Lists (global → agent)"]
DENY --> ALSO["Apply AlsoAllow (additive union)"]
ALSO --> SUB{"Subagent?"}
SUB -->|Yes| SUBDENY["Apply subagent deny list<br/>+ leaf deny at max depth"]
SUB -->|No| FINAL["Final tool list → LLM"]
SUBDENY --> FINAL
Profiles:
| Profile | Tool Set |
|---|---|
full |
All registered tools |
coding |
group:fs, group:runtime, group:sessions, group:memory, group:web, read_image, create_image, skill_search |
messaging |
group:messaging, group:web, sessions read, read_image, skill_search |
minimal |
session_status only |
Tool Groups (reference group:<name> in allow/deny lists):
| Group | Members |
|---|---|
fs |
read_file, write_file, list_files, edit, send_file |
runtime |
exec |
web |
web_search, web_fetch |
memory |
memory_search, memory_get |
sessions |
sessions_list, sessions_history, sessions_send, spawn, session_status |
automation |
cron |
messaging |
message, create_forum_topic, list_group_members |
team |
team_tasks |
goclaw |
All native built-in tools (composite) |
MCP groups (mcp, mcp:{serverName}) are registered dynamically at connection time.
Per-request allow list — channels can inject a final intersection step via message metadata (e.g. Telegram forum topics restrict tools per topic).
7. Custom Tools
Custom tools are shell-based tools defined at runtime via the HTTP API — no recompile or restart needed. Stored in the custom_tools table.
Authoring fields:
| Field | Required | Purpose |
|---|---|---|
name |
yes | Tool identifier (lowercase, underscores) |
description |
yes | Shown to the LLM as tool description |
parameters |
yes | JSON Schema object defining tool params |
command |
yes | Shell command with {{.param}} Go template placeholders |
timeout_seconds |
no | Execution timeout (default 60s) |
env |
no | Encrypted environment variables injected at runtime |
enabled |
no | Toggle without deleting (default true) |
Credentialed CLI env entries support two API/UI kinds:
sensitive(default): encrypted at rest, masked in normal API responses, replace-only in UI, and flattened only at credential injection time.value: encrypted at rest but visible to authorized admins in API/UI for non-secret settings such as public URLs, domains, limits, regions, and feature flags.
Legacy env JSON like {"TOKEN":"..."} is still accepted and treated as sensitive.
Execution: Template placeholders are rendered with shell-escaped argument values, then run via sh -c. The same deny-pattern check as the exec tool applies — no reverse shells, no curl | sh, etc.
Scope:
agent_id = NULL→ global; available to all agents.agent_id = <uuid>→ available only to that agent.
8. Credential Scrubbing
Tool output is automatically scrubbed before being returned to the LLM or the user. The scrubber intercepts both the ForLLM and ForUser result fields.
Static patterns cover well-known credential shapes including provider API keys, cloud access keys, generic key-value assignments, connection string URIs, and long hex strings. All matches are replaced with [REDACTED].
Dynamic scrubbing — values can be registered at runtime (e.g. server IPs, deployment-specific tokens). Thread-safe; checked alongside static patterns on every tool result.
The exact patterns are intentionally not published here (defense-in-depth). The scrubber is always enabled in the registry by default.
8a. Credential adapter framework
For tool binaries that need per-user typed credentials (PAT, SSH key,
kubeconfig, .pgpass, etc.), the CredentialAdapter interface in
internal/tools/credential_adapter.go transforms a stored credential into
the argv/env/ephemeral-file shape the binary expects.
Name() string— the value stored insecure_cli_binaries.adapter_name.ShouldInject(argv []string) bool— gate that skips local-only subcommands (e.g.git statusdoes not trigger injection).Prepare(...) (*Injection, error)— returns the four-fieldInjection{ArgvPrefix, Env, Cleanup, ScrubValues}consumed bycredentialed_exec.go.
Adapters are registered in their own init() via RegisterAdapter. Lookup
falls back to the passthrough no-op adapter on unknown/empty names, so
unrelated presets (gh, aws, gcloud, kubectl, terraform, gws)
keep their legacy env-injection path bit-for-bit.
Per-injection audit: every adapter run emits one
slog.Warn("security.system_env_injection", …) line with the field schema
documented in 09-security.md § 14.
To author a new adapter (kubectl, docker, npm, aws, …), follow the worked
mappings + interface-validation gate in
credential-adapter-playbook.md. User-facing
config for the shipped git adapter is documented in
git-credential-adapter.md.
9. Per-Tenant Config (4-Tier Overlay)
Tools that support tenant configuration resolve a merged settings map at execution time. Priority order (most specific wins):
Tool Execute(ctx, params)
↓
Merged settings map:
1. Per-agent override (agents.builtin_tool_settings)
2. Tenant override (builtin_tool_tenant_configs.settings)
3. Global default (builtin_tools.settings)
4. Hardcoded fallback (tool internal defaults)
Merge is per-tool-name: tenant entry for web_search wins wholesale over global default — no deep field merge. Tools that do not read the settings map are unaffected.
exec reads settings.timeout_seconds for host command execution. The REST API validates exec settings as a JSON object with optional integer timeout_seconds in the 1..3600 range. Missing or invalid runtime values fall back to 60 seconds, while values above the maximum are clamped to 3600 seconds for defense in depth. Docker sandbox tool calls still use sandbox_config.timeout_sec; this setting only controls the host exec built-in.
Secret vs non-secret split:
- Non-secret config (provider priorities, limits, domain policies) →
builtin_tool_tenant_configs.settings - Secrets (API keys, tokens) →
config_secretstable (AES-256-GCM encrypted, tenant-scoped)
Never put credentials in the settings JSON blob — backend does not validate the split.
Cache invalidation: settings changes propagate via pub/sub (tenant-scoped). Next agent turn re-resolves automatically.
Tenant admin workflow: Settings → Builtin Tools → gear icon → typed form when available, otherwise JSON editor → Save. Changes take effect on the next agent turn. "Reset to default" reverts to platform defaults.
Master-scope guard: Writes to global builtin_tools table require master tenant scope. Tenant admins use the /tenant-config endpoint. Same guard applies on the WS config methods.
Current adopters: exec, web_search, web_fetch, tts, create_image, read_image, create_audio, read_audio, knowledge_graph_search.
Shell Deny-Groups (Runtime Config)
Global shell deny-groups are controlled via config.tools.shellDenyGroups (map[string]bool). Operators can toggle deny-group classes (e.g. package_install, env_dump) at runtime from the /config Web UI without restarting the gateway.
Merge semantics:
- Global config serves as base (
config.tools.shellDenyGroups) - Per-agent overrides in
agents.other_config.shell_deny_groups(if set) - Per-key: agent value takes precedence over global value
- Multi-tenant invariant: each tenant's config is isolated
Live reload: Changes to config.tools.shellDenyGroups and config.tools.commandKeywordAllowlist propagate via bus.TopicConfigChanged pub/sub. Next agent turn automatically applies new toggles.
Deny-group classes (from internal/tools/shell_deny_groups.go — all denied by default):
| Class | Blocks |
|---|---|
destructive_ops |
rm -rf, dd, mkfs, shutdown, fork bombs |
data_exfiltration |
curl/wget piped to shell, curl POST, DNS tools, /dev/tcp |
reverse_shell |
nc, bash -i, sh -i, reverse-shell payloads |
code_injection |
eval/exec on untrusted input, dynamic code loaders |
privilege_escalation |
sudo, su, setuid abuse |
dangerous_paths |
writes to /etc, /root, system dirs |
env_injection |
export of sensitive env, LD_PRELOAD tricks |
container_escape |
mount, nsenter, capability changes |
crypto_mining |
xmrig and other miners |
filter_bypass |
encoding/quoting tricks to evade pattern matching |
network_recon |
nmap, masscan and similar scanners |
package_install |
apt, yum, brew, pip, npm install (separately routes to approval) |
persistence |
cron edits, systemd unit writes, rc.local |
process_control |
kill -9 of arbitrary PIDs, killall |
env_dump |
env, printenv (full-environment dumps) |
10. MCP Integration
GoClaw integrates with Model Context Protocol (MCP) servers. The MCP Manager connects to external tool servers and registers their tools in the tool registry with a configurable prefix (e.g. mcp_servername_toolname).
Transports:
| Transport | Description |
|---|---|
stdio |
Launch subprocess; communicate via stdin/stdout |
sse |
Connect to SSE endpoint via URL |
streamable-http |
Connect to HTTP streaming endpoint |
Reliability: Health checks every 30 seconds. Reconnection via exponential backoff (2s initial, 60s max, 10 attempts).
Access control:
flowchart TD
REQ["LoadForAgent(agentID, userID)"] --> QUERY["Join mcp_servers + agent_grants + user_grants"]
QUERY --> SERVERS["Accessible servers list"]
SERVERS --> CONNECT["Connect (stdio/sse/http)"]
CONNECT --> DISCOVER["ListTools() from server"]
DISCOVER --> FILTER["filterTools()\n1. Remove deny-listed tools\n2. Intersect with allow list (if set)\n3. Deny takes priority"]
FILTER --> REGISTER["Register in tool registry"]
Grant types:
| Grant | Table | Scope |
|---|---|---|
| Agent grant | mcp_agent_grants |
Per server + agent; tool_allow, tool_deny JSONB arrays |
| User grant | mcp_user_grants |
Per server + user; tool_allow, tool_deny JSONB arrays |
Access request workflow: Users request server access → admins approve/reject → on approval a grant is created transactionally.
11. Team Tools
Teams add a shared coordination layer on top of delegation: a task board and peer communication.
Architecture
When a user messages the team lead:
- Lead sees
TEAM.mdin its system prompt (teammate list + roles). - Lead posts tasks to the shared board.
- Teammates claim tasks and work in parallel (each has their own session).
- Lead synthesizes results and replies to the user.
Only the lead receives TEAM.md — teammates discover context through tools, saving tokens on idle agents.
Task Board (team_tasks)
Actions: create, list, get, claim (race-safe row lock), complete, cancel, assign, review, approve, reject, comment (note/blocker types), progress, attach, ask_user, search, update.
Completing a task auto-unblocks dependent tasks listed in blocked_by.
Message Routing
Teammate results route through the message bus with a "teammate:" prefix, surfacing to the lead and ultimately the user.
12. Media Tools
Media tools follow a provider-chain pattern: multiple backends are tried in priority order; the first successful response wins.
Generation chain (configurable via per-tenant config):
- Images: OpenAI DALL-E → Gemini → MiniMax → DashScope → BytePlus
- Audio/music: MiniMax → ElevenLabs
- Video: MiniMax → Gemini → BytePlus
- TTS: ElevenLabs → OpenAI → Edge → MiniMax
Reading chain:
- Images: Gemini → Anthropic → OpenRouter → DashScope
- Audio/documents/video: Resolve service or Gemini File API
Media outputs are written to the agent workspace and referenced by path or URL in the result. File naming is deterministic (content-hashed) to enable deduplication.
13. Subagent System
Subagents are child agent instances spawned to handle parallel or complex tasks.
stateDiagram-v2
[*] --> Spawning: spawn(task, label)
Spawning --> Running: Limits pass
Spawning --> Rejected: Limit exceeded
Running --> Completed: Task finished
Running --> Failed: LLM error
Running --> Cancelled: cancel / steer / parent abort
Completed --> Archived: After 60 min
Limits:
| Constraint | Default |
|---|---|
| Max concurrent | 8 (across all parents) |
| Max spawn depth | 1 |
| Max children per agent | 5 |
| Archive after | 60 min |
| Max iterations | 20 per subagent |
Actions: spawn (async, returns immediately), run (sync, blocks until done), list, cancel (by ID / "all" / "last"), steer (cancel + respawn with new message).
Subagents share the same SecureCLIStore as their parent — the credentialed binary gate cannot be bypassed by delegating exec to a child.
14. File Reference
| Module | Path | Purpose |
|---|---|---|
| Registry & policy | internal/tools/ |
Tool registry, policy engine, rate limiter, scrubber, capability metadata |
| MCP bridge | internal/mcp/ |
MCP server connections, tool bridge, access grants |
| Custom tools | internal/tools/ (dynamic_loader.go, dynamic_tool.go) |
Runtime shell-based custom tool loading and execution |
| Team tools | internal/tools/ (team_tasks_tool.go, team_tool_*.go) |
Task board backend, team tool dispatch and cache |
Use grep or your editor's symbol search for specific files.