Add `code_mode_native` tool metadata and `CodeMode(native_capabilities=...)` to keep tools out of `run_code`
- Closes #9243
Why
CodeMode used code_arg_name (a trace-rendering hint) as its fold policy, users could only opt a capability's tools out by knowing their internal names, and Shell's background lifecycle was split between a native start_command and sandboxed check_command/stop_command (pydantic/pydantic-ai-harness#592, #593). This gives each concern its own knob, in the harness only, with no core change.
New public surface
- Tool metadata key
code_mode_native:Truekeeps a tool native whateverCodeMode(tools=...)selects;Falselets the selector decide even when the tool carriescode_arg_name. It is separate from the existingcode_modeselection tag, soSetToolMetadata(code_mode=True)on every tool doesn't foldShell. CodeMode(native_capabilities=[FileSystem, ...])(keyword-only): keeps every tool whose contributing capability, or one it wraps (PrefixTools, a resolvedDynamicCapability), is an instance of a listed class. UsesToolDefinition.capability_id+RunContext.capabilities.
User-visible behavior
Agent(model, capabilities=[LocalWorkspace('.'), Shell(), CodeMode()])
# before: run_code, run_command, start_command (check/stop_command inside run_code)
# after: run_code, run_command, start_command, check_command, stop_command
Shell, the ACP terminal, CapabilityCreation, and DynamicWorkflow now declare code_mode_native=True; code_arg_name stays only as the trace hint. clai2's speculative mode sets code_mode_native=False on shell instead of deleting code_arg_name, so folded shell calls keep code rendering in traces. Override someone else's declaration with SetToolMetadata(tools=['shell'], code_mode_native=False).
Verification
tests/harness/code_mode/test_code_mode.py:test_tool_declares_whether_it_stays_native(declared / undeclaredcode_arg_name/ declared foldable, withtoolsnaming the tool),test_native_capabilities_keep_a_capabilitys_tools_native(direct,PrefixTools,DynamicCapability),test_native_capabilities_rejects_namestests/harness/shell/test_shell.py::TestCodeModeInterop(whole family native in both orders; metadata pinned per tool)- Existing ACP,
CapabilityCreation,DynamicWorkflow, and clai2 speculative-mode tests pass unchanged apart from the metadata assertion.
What changes for existing users
Shell + CodeMode users now see check_command/stop_command as regular tool calls rather than functions inside run_code (issue problem 3); nothing else changes.
Scope and what's left
This is a first PR. Kept deliberately: a tool with code_arg_name and no code_mode_native key still stays native, so third-party code tools keep today's behavior; dropping that fallback (the issue's end state) is a follow-up decision. Not covered: Logfire rendering of code_arg_language: 'shell' (issue problem 4). native_capabilities takes classes, so it can't be set from an agent spec (rejected with a UserError); CodeMode's spec schema generation already fails on main because of os_access.
Checklist
- No breaking changes in accordance with the version policy.
- Any permitted compatibility impact has a label, warning, migration, release note, and exact API-check waiver.
- PR title is fit for the release changelog.