Tools & providers
Two capability surfaces sit under the daemon: a registry of ~110 built-in tool wrappers it executes on behalf of any client, and the LLMProvider abstraction that routes completions to a local model or to the claude / codex CLIs. Everything runs through the daemon — clients never open a socket to the cloud themselves.
The tool registry
Every executable capability — read a file, run a shell command, open a PR, tail pod logs, sample a Parquet file — is registered exactly once as a Tool and exposed through a single registry + executor. The pieces live under src/daemon/tools/:
registry.ts— the unified registry every tool registers into.executor.ts— validates input against the tool's JSON schema, fires the approval gate when required, then runs it.builtins/— the ~110 built-in wrappers, one domain per subdirectory.
A single registration serves two callers: the LLM (via the tool-call protocol) and daemon controllers (via kind: 'tool' tasks). Same schema, same execution, same approval flow.
Read-only tools (git_status, file_read, search_grep) run ungated. Mutating tools fire an Approve / Skip / Edit gate with a diff or command preview before execute(); destructive actions (force-push, k8s_delete, git_reset --hard) add an extra confirm. Some tools are risk-tiered — low-risk shell commands auto-run in auto-accept mode, medium and high always gate.
Categories
Tool IDs follow a domain_verb_noun convention (a handful of deeper namespaces use domain:sub:action). One row per capability domain, with real IDs drawn from src/daemon/tools/builtins/:
| Category | What it does | Example tools |
|---|---|---|
| file | Read, write, edit, move, copy, stat files on disk. | file_read · file_write · file_edit |
| git | Local git — status/log/diff read, plus staged commits, branches, push/pull, rebase. | git_status · git_diff · git_commit |
| shell | Run commands: one-shot, detached (streamed), or a scripted pipeline; set cwd. | shell_exec · shell_exec-detached |
| http | Generic HTTP client — requests, file up/download, websocket. | http_request · http_download · http_websocket |
| web | Web search and page fetch for research flows. | web_search · web_fetch |
| gh | GitHub via the gh CLI + REST/GraphQL — issues, PRs, runs, releases, repos, projects. | gh_pr_create · gh_issue_view · gh_run_list |
| k8s | Wraps kubectl — get/describe/logs plus gated apply/delete/exec. | k8s_get · k8s_logs · k8s_apply |
| pkg | Package managers — install/add/remove, audit, outdated (npm/pip/cargo/go auto-detect). | pkg_install · pkg_audit · pkg_outdated |
| ssh | Run commands on remote hosts and copy files, using the system ssh client. | ssh_exec · scp:upload · scp:download |
| test | Run the detected test framework and parse failures; coverage and watch modes. | test_run · test_coverage · test_watch |
| notify | Outbound messaging — always gated with the rendered message + destination. | notify_slack · notify_email · notify_teams |
| search | Local filesystem search — glob, content grep, directory listing, recently-modified. | search_grep · search_glob · search_recent |
| graph | Query the indexed code-knowledge graph — semantic ANN, entity lookup, structural query. | graph_search · graph_entity · graph_query |
| db | Data-driver introspection over SQL engines, on-disk files (DuckDB), and the KV store. | db_sql_sample · db_file_describe · db_kv_scan |
| data | Higher-level data analysis — lineage tracing and schema-drift detection. | data_lineage · data_schema-drift |
| code | Structural code intel — locate a class, find its references/fields, walk migrations, scan ORMs. | code_class_locate · code_class_references · code_orm_scan |
| cloud | AWS / GCP / Azure CLI wrappers — reads auto-run, mutations gate. Registers only when the CLI is on PATH. | cloud_aws_s3_ls · cloud_gcp_run_deploy · cloud_az_vm_list |
A related docs family (docs_retrieve, docs_project_context, docs_summary_get) surfaces indexed documentation to the same callers.
LLM providers
All model interaction goes through one interface, LLMProvider, defined in src/shared/types.ts. Application code never imports an SDK directly — it holds an LLMProvider and calls its methods:
complete(messages, opts)— one completion.stream(messages, opts)— token stream (async iterable).embed(text)— embedding vector (empty array if unsupported).completeStructured<T>(...)— schema-enforced JSON (see below).supportsTools— whether the provider can drive tool-calls.
Two implementations back it:
OllamaProvider local
src/agent/providers/ollama.ts. Local LLM (qwen3.6:35b-a3b analyzer/shaper core) and embeddings (qwen3-embedding:0.6b). Embeddings are always local, so Ollama is a hard prerequisite; the analyzer model is only needed when reasoning runs through Ollama rather than the claude / codex CLI.
CliProvider CLI
src/agent/providers/cli-provider.ts. A subprocess wrapper around the installed claude and codex binaries. Structured-output aware; cloud auth + quota are delegated to the CLIs' OAuth sessions.
# claude: structured output via native --json-schema
$ claude --print --output-format json --json-schema '<inline>'
# codex: structured output via --output-schema
$ codex exec --output-schema <tmpfile> --json
# auth + quota belong to the CLI's own OAuth session — no keys on our side
claude and codex CLI binaries, wrapped by CliProvider. There are no API keys stored on our side and no direct REST calls to Anthropic / OpenAI / Gemini / Mistral from the daemon process. Direct REST providers must never be reintroduced — auth and quota stay with the user's CLI OAuth sessions.Structured output
src/agent/providers/structured-output.ts gives providers a schema-validated JSON path. It compiles the caller's JSON Schema with ajv (draft 2020-12, singleton compiled-validator cache) and validates every response. On a validation mismatch the helper re-issues the completion with the errors appended to the conversation, retrying up to maxAttempts (default 3) so the model can self-correct.
The provider's wire layer enforces the schema natively where it can — Ollama via format, the CLIs via --json-schema / --output-schema — and ajv re-validates as a defensive backstop. A provider without a native structured-output API sets its capability flag off and throws a clear "not supported" error, so callers gate on the flag at config time.
Promise.all anything that reaches an LLM provider — cloud (CliProvider) or local Ollama embed / complete. Always drive it with a serial for…of loop and sequential awaits. Parallel fan-out to a provider blows quota, races the local model, and reorders streamed output.Where next
Analyze
Graph-grounded exploration recipes and the 7-layer context bundle — the read-only side of the daemon.
readWorkflow
define → design → plan → build, with tools driving the tracker and provider stamping the model.
readArchitecture
How the daemon owns tool execution, DB access and the IPC contract clients depend on.
read