|
|
|
|
"description": "The canonical tool-identity envelope, attached to a tool-call event `_meta`\nas one nested object under [`TOOL_META_KEY`].\n\n```json\n\"x.ai/tool\": {\n \"version\": 1,\n \"name\": \"read_file\",\n \"kind\": \"read\",\n \"namespace\": \"grok_build\",\n \"label\": \"Read\",\n \"read_only\": true,\n \"input\": { \"path\": \"...\" }\n}\n```\n\nConsumer contract:\n- **`label`** is the cross-harness grouping/display key: equivalent tools\n share it (grok `read_file` → `\"Read\"`).\n- **`kind`** is a finer discriminator (`metadata.kind()`), *not* guaranteed\n equal for equivalent ops across harnesses (listing is `list` in one\n toolset, `list_dir` in another); prefer `label` to join, tolerate unknowns.\n- **`name`** is the harness-specific model-facing name; for diagnostics.\n For harness-initiated events (e.g. the `bash_mode` marker), `raw_input`\n is not guaranteed to match `name`'s schema.\n- **`input`** is a canonical *projection*, not a mirror: cross-harness keys\n only, so some raw fields are intentionally dropped (e.g. grep flags,\n `replace_all`), and bulky payload\n fields (edit `old_string`/`new_string`, full write contents) are never\n projected — read them from `raw_input`. It is omitted entirely\n when no stable shape exists (MCP / dynamic / out-of-scope). When a field or\n the whole dict is absent, fall back to `raw_input` on this or an earlier\n update for the same `tool_call_id` (some updates, e.g. a parse failure,\n carry neither and rely on the merge below).\n- **Lifecycle:** updates for one call share a `tool_call_id` — merge across\n them (last write wins); `input` may arrive on a later update.\n- **Versioning:** additive changes (new object fields, new `kind` / `label`\n values) don't bump `version`. Unknown `kind` degrades to `\"other\"`;\n `namespace` is a closed enum (no `other` sink), so a new toolset fails\n strict typed deserialization of the whole envelope — intentional, to force\n typed consumers with exhaustive matches to update. Out-of-tree consumers\n should read `namespace` loosely (as a string) and, on any `x.ai/tool`\n parse failure, treat it as absent and fall back to `raw_input` + the ACP\n `kind`. `version` bumps only on removal or meaning change.",
|