返回 DeepSeek-Reasonix
TOOL_CONTRACT.md
根目录 / docs / TOOL_CONTRACT.md
1 # Tool Contract
2
3 Structured file mutations require a live host observation of the target's
4 current version. Any successful text window establishes it; reading coverage
5 and whole-file completion are not host gates. See [File observation
6 lifecycle](READ_EVIDENCE_LIFECYCLE.md).
7
8 <a href="./TOOL_CONTRACT.zh-CN.md">简体中文</a>
9
10 This document records the Reasonix compile-time built-in tool contract. The
11 provider surface is selected once when the session boots: POSIX hosts expose
12 `bash`; Windows hosts expose `pwsh`. Compatibility aliases remain executable
13 for old session replay but are omitted from new provider schemas.
14
15 | Tool | Read-only | Description |
16 | --- | --- | --- |
17 | `bash` | false | Execute a command in the shell and return combined stdout/stderr. Use for builds, tests, git, package managers, etc. To search/read/list/edit/move files, prefer the dedicated tools (grep, read_file, ls, glob, edit_file, move_file) over shell grep/cat/ls/find/sed/mv/Move-Item - they behave identically on every OS. For symbol search or architecture questions, prefer LSP/read tools and targeted grep before shell commands. |
18 | `pwsh` | false | Windows-only provider shell. Execute one PowerShell command in an isolated process. `description` is required for new calls; `timeout_ms` applies only to foreground work; `run_in_background=true` returns a `pwsh-*` job id. Use PowerShell 5.1-compatible `;` and `if ($?) {}` syntax. |
19 | `bash_output` | true | Hidden compatibility alias for old sessions. New calls use `job_output`. |
20 | `code_index` | true | Lightweight built-in code symbol index. Prefer lsp_* for language semantics and installed code graph MCP tools for call graph, impact, and architecture relationships; use this as the local fallback for file outlines and symbol definition candidates, then verify with read_file or grep. |
21 | `compress` | true | Compress a selected part of the current model-visible conversation without deleting visible history. Use only when the user explicitly asks for context compression. Choose `before` to summarize everything before the uniquely matched user turn while keeping that turn and later context, or `after` to summarize from that turn through the last completed turn while keeping the active turn. The anchor must be an exact, unique excerpt from a real user message; use a longer excerpt if the tool reports multiple matches. |
22 | `create_goal` | false | Create and activate one long-running goal from a directly authorized human turn. Omitting or setting max_goal_rounds to null means unlimited automatic rounds. It never overwrites an unfinished goal. |
23 | `delete_range` | false | Delete a contiguous text range from a file using exact start/end text anchors. Each anchor must match exactly one line. Returns unified diff on success. Use for large deletions - smaller changes should use edit_file. |
24 | `delete_symbol` | false | Delete a named symbol (function, method, type, interface, const, var) from a Go source file using AST parsing. For non-Go files, use delete_range with manual anchors. |
25 | `edit_file` | false | Replace an exact string in a file with another. old_string must occur exactly once; add surrounding context to disambiguate. Use for targeted edits instead of rewriting the whole file. |
26 | `glob` | true | Find files matching a glob pattern (e.g. "*.go", "internal/*/*.go", "**/*.test.ts"). Supports shell metacharacters * ? [] and the recursive ** pattern. Independent globs with no data dependency should be issued in the same round. |
27 | `get_goal` | true | Read the current goal together with its live activation and stop reason. Returns goal: null when the session has no current goal. |
28 | `grep` | true | Search for a regular expression in a file, or recursively under a directory (skips hidden files and files matched by .gitignore). Returns matching lines as path:line:text, capped at 200 matches. Independent searches with no data dependency should be issued in the same round. |
29 | `job_kill` | false | Request cancellation of a running background job by job id. Returns immediately; the process tree settles as killed once shutdown completes. |
30 | `job_output` | true | Read output from a background job. Reads are non-blocking unless wait=true; every response includes the current status. Do not busy-poll a running job. |
31 | `kill_shell` | false | Hidden compatibility alias for old sessions. New calls use `job_kill`. |
32 | `ls` | true | List the entries of a directory. Directories are shown with a trailing slash; files show their byte size. Set recursive=true to list all nested files depth-first (skips .git/node_modules). Independent directory reads with no data dependency should be issued in the same round. |
33 | `move_file` | false | Move or rename a file from source_path to destination_path. Creates the destination parent directory as needed. Use instead of shell mv, Move-Item, or ren for file moves so workspace confinement and file-edit permissions apply. |
34 | `multi_edit` | false | Apply a list of edits to a single file atomically: each edit runs against the result of the previous one, all in memory; the file is rewritten only if every edit succeeds. Cheaper and safer than chaining edit_file calls - a failure in step 3 leaves the file untouched instead of half-edited. |
35 | `notebook_edit` | false | Edit one cell of a Jupyter notebook (.ipynb). Target a cell by 0-based cell_number (or cell_id). edit_mode: "replace" (default) swaps the cell's source; "insert" adds a new cell after cell_number (use -1 to prepend at the top), taking cell_type and new_source; "delete" removes the cell. cell_type is "code" or "markdown" (required for insert). Editing a code cell clears its outputs. Prefer this over edit_file for notebooks - it keeps the JSON valid. |
36 | `present` | true | Declare 1 to 8 existing files as user-facing deliverables after writing them and before the final answer. The host validates every path atomically and records only file paths and optional descriptions; it does not copy, execute, upload, or expose file bytes to the model result. |
37 | `read_file` | true | Read one bounded text window with optional line offset/limit. Output prefixes each line with its 1-based number. Any successful window observes the current file version for later structured edits. Use the next-window hint to page only when more content is useful. Legacy intent and cursor fields are accepted as navigation hints and never create a whole-file completion requirement. |
38 | `todo_write` | true | Replace the current model-maintained task list. Todo states describe progress without serial execution or host signoff requirements. |
39 | `update_goal` | false | Apply an exact goal ID/revision lifecycle action: edit, pause, resume, complete, or blocked. Direct human turns may use every action; the exact automatic goal round may only complete or block its own goal. The retired continue protocol is rejected. |
40 | `view_image` | true | Read a local PNG, JPEG, GIF, or WebP image by path and return visual content through native vision or the configured image-understanding model. Use this for image paths instead of read_file. Maximum file size: 3 MiB; maximum dimensions: 40 million pixels. |
41 | `wait` | true | Hidden compatibility alias for old sessions. New calls use `job_output(wait=true)`. |
42 | `web_fetch` | true | Fetch a URL over HTTPS/HTTP and return its text content. HTML pages are reduced to readable text; JSON / plain text / markdown bodies come back verbatim. Use to read documentation pages, API responses, or source files hosted somewhere the local filesystem can't reach. |
43 | `write_file` | false | Create or replace a text file. A missing target is created without overwriting a concurrent creator. Replacing an existing target requires a current host observation from read_file or a prior successful structured mutation. |
44
45 `update_goal` uses `action` to select its mutation fields: only `edit` consumes
46 `objective` and `max_goal_rounds`, and only `blocked` consumes `blocked_reason`.
47 Omit unused fields; schema-valid echoed values and placeholders in unused fields
48 are ignored, including empty or null text fields. `pause`, `resume`, and `complete`
49 never edit the objective or round limit. For `edit`, omitting `max_goal_rounds`
50 preserves the limit while null removes it; omitting `objective` or passing null
51 preserves the objective, while a replacement must be non-empty. `blocked` still
52 requires a non-empty reason. Exact revision, host authority, and lifecycle checks
53 still apply.
54
55 Successful results identify ignored non-null text fields and explicitly supplied
56 unused round limits in their instruction. The returned goal is the effective
57 state; accepting a lifecycle action does not apply edits carried in other fields.
58
59 ## Schema Snapshot
60
61 The exact canonical schemas are intentionally tested in code rather than copied by hand here. Run:
62
63 ```bash
64 go test ./internal/tool -run TestBuiltinToolContractDocumentation
65 ```
66
67 The test checks that every registered built-in tool has a documented name, read-only flag, description row, and canonical schema generated by `tool.BuiltinContractEntries`.
68
69 ## Default Full Boot Surface
70
71 In a default full-token boot, Reasonix sends the built-in tools above plus the
72 session, memory, skill, subagent, LSP, install, and slash-command tools below:
73
74 Every session uses this exact executor tool surface plus one stable
75 proxy, `use_capability`, so optional MCP servers (including `auto_start=false`)
76 can be inspected and called without changing provider-visible schemas
77 mid-session. The model chooses verification, review, and completion from task context. The host enforces action permissions, preapproval Plan write restrictions, sandboxing, leases, and structured-file stale-version protection. An ordinary tool failure does not skip later independent calls in the same batch.
78
79 ## Unified Boot Surface
80
81 Every session uses the same provider-visible core tools and the same
82 `use_capability` proxy.
83
84 The two-model Planner and all task/fleet sub-agents also use `use_capability`
85 (and never direct `mcp__*` schemas). Planner and ordinary writer-capable
86 sub-agents may call installed or project-configured MCP without
87 `readOnlyHint`; Planner leaves `destructiveHint` tools for the Executor, while
88 ordinary sub-agents use the trusted MCP path (live authorization plus explicit
89 deny only). Writer/destructive calls are still serialized and recorded as
90 execution facts for workspace leases and UI display. Strict read-only sub-agents
91 share the same proxy schema and Host connections but still require
92 `readOnlyHint` and non-destructive at execution time. Dual-model
93 attaches independent proxy frontends to both Planner and Executor so a
94 capability discovered during planning remains directly callable after handoff;
95 their ledgers/audits are isolated while Host connections are shared. A
96 single-model session has no independent Planner.
97
98 `use_capability` is a fixed-schema proxy: prefer `search(query)` then `inspect`
99 one exact id then `call`. `action=list` is a compact diagnostic inventory.
100 Independent `list`/`search`/`inspect` calls are read-only and may be issued
101 together. Resolution is side-effect free: `action=list` returns compact,
102 sorted configured MCP server summaries without expanding every cached tool
103 description or starting any server. Use `action=inspect` on one enabled
104 `mcp-server:<name>` to read that server's live or cached tool directory without
105 starting it. `action=call` on a
106 not-yet-connected server resolves to a deferred target, Plan re-checks only an
107 explicit phase opt-out on the real target, and the server process starts only
108 after the permission gate and PreToolUse hooks approve the call. On-demand children
109 share the session lifetime (they outlive the starting call and exit with the
110 session); `action=inspect` lists live tools for connected servers and cached
111 schemas otherwise, never starting a process. First discovery of a server with
112 no schema cache goes through `action=call` on the `mcp-server:` id itself: it
113 resolves to a gated connect (permission name = the server's dedicated
114 `mcp_connect__<server>` identity, so an exact rule such as
115 `deny = ["mcp_connect__github"]` blocks process startup) that connects after
116 approval and returns the live tool directory. MCP tool rules remain exact;
117 `mcp__github__*` is not a tool-name glob. Installing an MCP authorizes the
118 Planner to use its non-destructive tools; third-party servers that omit
119 `destructiveHint` are treated as user-install trust. Before every connect or
120 `tools/call`, the frontend re-checks the current runtime enablement,
121 authorization, and exact Host connection identity; another project/tab's
122 same-name shared client is rejected without process, network, or tool dispatch.
123
124 The fixed proxy's provider-visible name, description, schema, and ordering do
125 not change when MCP inventory changes.
126
127 When the current frontend has a session reader, the same fixed proxy also lists
128 the read-only `session:tool_result` capability. It pages the complete local copy
129 of one tool result by UTF-8 byte offset without adding a top-level schema. Calls
130 require `tool_call_id`; new truncation markers also provide a stable
131 `result_ref`, which is required to disambiguate repeated call IDs. `offset`
132 defaults to 0, `limit` defaults to 16KiB and is capped at 24KiB. Each response
133 starts with `result_ref`, actual offset, `next_offset`, `total_bytes`, full
134 SHA-256, and `complete`, followed by the raw page. The reader is bound to the
135 current Agent session and is not inherited from a parent when a capability
136 frontend is cloned. A restricted child that already has `use_capability` may
137 read only its own results; an allowed-tools profile without the proxy is not
138 widened.
139
140 `ask`, `docs`, `explore`, `fleet`, `forget`, `history`, `install_skill`, `install_source`,
141 `list_sessions`, `lsp_definition`, `lsp_diagnostics`, `lsp_hover`,
142 `lsp_references`, `memory`, `parallel_tasks`, `read_only_skill`,
143 `read_only_task`, `read_session`, `read_skill`, `read_subagent_result`, `remember`, `research`,
144 `review`, `run_skill`, `security_review`, `slash_command`, `task`.
145
146 `parallel_tasks` and `fleet` keep their combined result below the single-tool
147 output limit by returning a fair preview and a stable `Subagent reference` for
148 every persisted child. `read_subagent_result` pages through one referenced
149 final answer by UTF-8 byte offset, so long parallel research remains lossless
150 without injecting every report into the parent context at once. References are
151 restricted to the current conversation lineage and workspace.
152
153 Persisted child results also carry an explicit `status` (`completed`, `partial`,
154 `failed`, or `cancelled`) and `retryable` flag. A partial or failed child may
155 include its last visible answer and reference; use `read_subagent_result` for
156 inspection and the original `task`/`run_skill` `continue_from` parameter for a
157 retryable continuation. `session:tool_result` is only for ordinary tool output.
158
159 `use_capability` (`action` = `list` | `inspect` | `call` | `decline`) is on the
160 provider-visible surface for every task. Host verification obligations come
161 from real tool actions, not from preclassifying the prompt. Optional tools stay registered for host dispatch but are not
162 expanded into the top-level provider schema; the model reaches them through
163 `use_capability` without cache-breaking schema churn.
164
165 `internal/boot.TestBootToolContractMatchesProviderVisibleSurface` verifies the
166 actual boot registry contract against the provider request, including read-only
167 flags and canonical schemas.
168
169 ## Unified Boot Surface (every task)
170
171 Every task starts with the same lean provider-visible core: direct
172 coding tools, background-shell lifecycle tools, and the stable capability proxy:
173
174 `bash` on POSIX or `pwsh` on Windows, `job_output`, `job_kill`, `edit_file`,
175 `read_file`, `view_image`, `write_file`, `compress` (when registered), and
176 `use_capability`.
177
178 Optional tools (`glob`, `grep`, `ls`, `web_fetch`, MCP, skills, subagents, docs,
179 session history, memory mutation, workflow, and so on) remain in the host
180 registry for dispatch. The model lists, inspects, calls, or declines them via
181 `use_capability` without changing the provider tool list. Task risk changes host
182 planning, verification, and review policy, not which tools appear on the
183 provider-visible surface. The retired `connect_tool_source` path is no longer registered.
184
185 ## Invalid arguments and recovery
186
187 The host validates concrete tool arguments before extension interception,
188 permission prompts, hooks, write leases, subagent execution, or tool dispatch.
189 An extension replacement is resolved and validated again. Invalid arguments are
190 an unexecuted tool error, not a permission refusal: correcting the input can
191 succeed on any subsequent call without an inspect action or a new user turn.
192 Normal permission and execution checks still apply to a corrected call.
193
194 Errors retain the target name, schema fingerprint, violation paths, and
195 `argument_validation:<tool>:<fingerprint>:<category>` diagnostic signature.
196 Feedback identifies whether parameters belong at the direct tool's input root
197 or inside a capability call's `arguments`. A conservative, value-free hint may
198 identify a single redundant `arguments` wrapper when the inner object satisfies
199 the concrete contract, including conditional validation. This is advice only:
200 the host never unwraps, coerces, fills, or executes the supplied parameters as
201 part of diagnosis. Legitimate `arguments` fields and nested skill contracts are
202 preserved. Empty/null validation compatibility remains unchanged.
203
204 Input errors returned before capability resolution also receive contract
205 feedback when the outer schema establishes the error. Successful resolution is
206 not subjected to a new envelope gate; unavailable targets and authorization
207 errors keep their own reasons. A malformed host schema is a configuration
208 problem, not something the model can fix by rewriting arguments. Existing
209 third-party MCP schema-compilation fallback remains unchanged.
210
211 `inspect` remains a contract discovery operation, not an unlock requirement.
212 There is no schema-specific error counter or third-failure tool lock. The shared
213 storm breaker gives a soft convergence hint after three consecutive equivalent
214 failed batches; multiple calls in one batch do not add multiple rounds, and a
215 successful result resets the existing failure streak. Parameter-only failures
216 receive correction advice rather than instructions about bypassing permissions.
217 If correction remains unsuccessful, the model may report tool argument
218 generation failure and unfinished work. This does not mark the work completed.
219 Real permission, Plan-mode, hook, and write-loop restrictions remain enforced.
220
221 Convergence is advisory. With `MaxSteps=0` and no explicit budget, there is no
222 fixed-round hard stop; configured step/spend limits and cancellation still work.
223 No extra repair-model request, provider-specific switch, or tool-schema change
224 is introduced. Feedback is bounded to 4 KiB and appended to the failed tool
225 result without rewriting prior messages or the stable provider prefix. Additional
226 feedback consumes context tokens; historical error messages are left intact.
227
228 Argument validation/failure/skip/remote-dispatch counters retain their meaning;
229 internal wrapper checks do not count as additional calls. The legacy
230 `capability_loop_guard.RepeatFailures` and `BlockedCalls` fields remain in metrics
231 for compatibility but are no longer incremented by new runs. They are not
232 repurposed as storm-intervention counters; existing `loop_guard` notices describe
233 those interventions. No session/config migration is required. Downgrading restores
234 the older error-recovery behavior without changing the stored conversation.
235
235 lines MARKDOWN