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/:

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.

The daemon is the only process that executes tools. The CLI, the MCP servers, and the IDE workbench request execution over the Unix-socket JSON-RPC — they never run a tool in-process. This keeps DB access, approval gating, and streaming progress in one place. See architecture.
approval gate

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/:

CategoryWhat it doesExample tools
fileRead, write, edit, move, copy, stat files on disk.file_read · file_write · file_edit
gitLocal git — status/log/diff read, plus staged commits, branches, push/pull, rebase.git_status · git_diff · git_commit
shellRun commands: one-shot, detached (streamed), or a scripted pipeline; set cwd.shell_exec · shell_exec-detached
httpGeneric HTTP client — requests, file up/download, websocket.http_request · http_download · http_websocket
webWeb search and page fetch for research flows.web_search · web_fetch
ghGitHub via the gh CLI + REST/GraphQL — issues, PRs, runs, releases, repos, projects.gh_pr_create · gh_issue_view · gh_run_list
k8sWraps kubectl — get/describe/logs plus gated apply/delete/exec.k8s_get · k8s_logs · k8s_apply
pkgPackage managers — install/add/remove, audit, outdated (npm/pip/cargo/go auto-detect).pkg_install · pkg_audit · pkg_outdated
sshRun commands on remote hosts and copy files, using the system ssh client.ssh_exec · scp:upload · scp:download
testRun the detected test framework and parse failures; coverage and watch modes.test_run · test_coverage · test_watch
notifyOutbound messaging — always gated with the rendered message + destination.notify_slack · notify_email · notify_teams
searchLocal filesystem search — glob, content grep, directory listing, recently-modified.search_grep · search_glob · search_recent
graphQuery the indexed code-knowledge graph — semantic ANN, entity lookup, structural query.graph_search · graph_entity · graph_query
dbData-driver introspection over SQL engines, on-disk files (DuckDB), and the KV store.db_sql_sample · db_file_describe · db_kv_scan
dataHigher-level data analysis — lineage tracing and schema-drift detection.data_lineage · data_schema-drift
codeStructural code intel — locate a class, find its references/fields, walk migrations, scan ORMs.code_class_locate · code_class_references · code_orm_scan
cloudAWS / 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.

Categories are gated on capability: cloud, k8s and similar CLI wrappers register only when their binary is detected on PATH, so the LLM's tool list stays uncluttered on machines that don't have them.
beyond the wrappers — docs from the graph

Alongside the ~110 low-level wrappers, the daemon exposes a higher-level generation capability: insrc_docgen renders self-contained HTML documents (with embedded Mermaid diagrams) straight from the indexed code graph, every claim grounded in the entities + relations it came from. It runs with full parity across three surfaces — the insrc_docgen MCP tool, the docgen.generate / docgen.list daemon IPC, and a built-in tool — over one implementation, with an oversized-document subprocess fallback and a boot-time asset validator. See architecture → docs from the graph.

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:

Two implementations back it:

CliProvider — how cloud calls happen
# 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
Cloud LLM access happens only through the locally-installed 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.

Never 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