Files
ai-harness/docs/05-tools.md
T
Erik Simon 8b16348b4c M0: scaffold Cargo workspace, core types, event bus, permission engine, storage actor
Seven-crate workspace per docs/01-architecture.md. harness-core gets the
domain types (Session/Message/Part/ToolState), a broadcast EventBus, the
last-match-wins wildcard permission evaluator, and a SQLite storage actor
(dedicated thread + mpsc, JSON-blob rows) with roundtrip tests. All other
crates are compiling stubs. CI runs fmt/clippy -D warnings/test.
2026-07-08 16:28:42 +02:00

3.5 KiB

05 — Built-in Tools

harness-tools. All parameter structs derive schemars::JsonSchema (single source of truth for the schema sent to the model). Invalid-params errors are returned to the model as tool errors with the schema echoed (opencode's invalid pattern).

Shared: output truncation

truncate.rs: cap output at 30,000 chars; keep head + tail with ... [N chars truncated; full output: <data_dir>/tool-output/<call_id>.txt] ...; full output spilled to the session data dir.

Tool table

Tool Params Behavior Permission key / pattern
bash {command, timeout_ms?, cwd?, description} tokio::process::Command sh -c, own process group (setsid); kill group on cancel/timeout (default 2 min, max 10); merge stdout+stderr; truncate bash / command string; "always" pattern = <first word> * via shell-words
read {file_path, offset?, limit?} 2000-line default window, cat -n numbering, 2000-char line clamp, binary/image detection; reports context files to the JobBoard when in a child session read / worktree-relative path (bundled default: allow)
edit {file_path, old_string, new_string, replace_all?} replacer chain (below); CRLF/BOM preservation; per-file tokio::Mutex lock map; unified diff via similar in permission metadata + part metadata; LSP diagnostics appended edit / relative path; always-pattern *
write {file_path, content} mkdir -p; diff vs existing in metadata; LSP diagnostics edit (same key as opencode) / relative path
glob {pattern, path?} ignore::WalkBuilder (respects .gitignore) + globset; sort mtime desc; cap 100 none (read-only)
grep {pattern, path?, include?} grep-regex + grep-searcher, gitignore-aware; cap 100 matches, sorted by file mtime none
todowrite / todoread list of {content, status: pending|in_progress|completed, priority} stored on the session row (JSON); part metadata drives a TUI checklist none
webfetch {url, format: text|markdown|html, timeout?} reqwest GET (redirect cap 5), 5 MB cap, HTML→markdown via htmd, truncate webfetch / URL
task see 04-multiagent.md spawn/reuse subagent sessions task / agent name
skill {name} returns the SKILL.md body on demand skill / name (default allow)

MCP tools are registered alongside these, namespaced {server}_{tool} — see 09-integrations.md.

Edit tool: replacer chain

Port opencode's replacer chain verbatim from ~/repos/opencode/packages/opencode/src/tool/edit.ts, including its test table (edit.test.ts) — this is the highest-regression-risk code in the project:

  1. Simple (exact match)
  2. LineTrimmed
  3. BlockAnchor (first/last line anchors, ≥0.65 similarity on middle lines, Levenshtein)
  4. WhitespaceNormalized
  5. IndentationFlexible
  6. EscapeNormalized
  7. TrimmedBoundary
  8. ContextAware
  9. MultiOccurrence (with replace_all)

Plus the disproportionate-match guard (reject matches that differ too much in size from old_string).

Registry & agent filtering

ToolRegistry::for_agent(agent):

  1. Filter by the agent's tools wildcard map ({ "write": false, "mcp_*": false }).
  2. Hard-remove tools denied for the session depth (e.g. task, todowrite at depth > 0 by default).
  3. The task tool's description is generated dynamically, listing available subagents with their descriptions (so routing knowledge comes from agent frontmatter, not hardcoded text).