Files
Eduard van Valkenburg d08200d00e Python: Bump package versions for 1.12.0 release (#7238)
* Bump Python package versions for 1.12.0 release

Bump packages represented in the 1.12.0 changelog, promote Foundry Hosting, Azure Content Understanding, Gemini, Mistral, Monty, and Tools to beta, and apply the requested beta cohort date stamp. Root and core move to 1.12.0, released and RC packages use their selected increments, alpha packages including Hosting MCP use the 260721 stamp, and core floors are raised only for proven consumers.

Copilot-Session: 2dd9980a-b869-4c16-8642-75b7a6d6ebdf

* fix version in readme

* Add Responses conversation ID changes to release notes

Include the breaking Hosting Responses conversation ID helper changes from #7234 in the Python 1.12.0 changelog.

Copilot-Session: 2dd9980a-b869-4c16-8642-75b7a6d6ebdf
2026-07-21 15:45:00 +00:00

172 lines
6.1 KiB
Markdown

# agent-framework-tools
Beta built-in tools for the Microsoft Agent Framework. A home for first-party
Python tools that plug into any chat client's shell / function surface. The
first tool is `LocalShellTool`.
## Installation
```bash
pip install agent-framework-tools --pre
```
## `LocalShellTool` quick start
```python
import asyncio
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
from agent_framework.tools import LocalShellTool
async def main() -> None:
client = OpenAIChatClient(model="gpt-5.4-nano")
async with LocalShellTool() as shell:
agent = Agent(
client=client,
instructions="You are a helpful assistant that can run shell commands.",
tools=[client.get_shell_tool(func=shell.as_function())],
)
result = await agent.run("Print the current working directory.")
print(result.text)
asyncio.run(main())
```
### Modes
- **Persistent** (default): a single long-lived shell session. `cd`, `export`,
and shell functions persist across tool invocations.
- **Stateless** (`mode="stateless"`): each command runs in a fresh subprocess.
### Safety
> **`LocalShellTool` is not a sandbox.** It runs commands directly on the
> host with the agent process's privileges. The actual security boundary
> is **approval-in-the-loop**. For untrusted input use a sandboxed
> executor — see [`agent-framework-hyperlight`](#relationship-to-agent-framework-hyperlight).
Defenses (in priority order):
- **Approval-in-the-loop** — every command surfaces as a
`user_input_request`; nothing runs without consent. Disabling this
requires `acknowledge_unsafe=True`.
- **Process-tree termination on timeout** via `psutil`, so child
processes (`make`, watchers, network tools) cannot survive the timeout.
- **Output truncation** to 64 KiB (head + tail with marker).
- **Audit hook** (`on_command=…`) for SIEM / append-only logs.
- **Optional command-pattern filter** via `ShellPolicy(denylist=[...],
allowlist=[...])`. **Empty by default.** This is a UX pre-filter, not a
security boundary — operators are expected to supply patterns that
match their workload (and they can be defeated by trivial obfuscation
such as `\rm -rf /`, `${RM:=rm} -rf /`, `python -c "…"`, encoded
payloads, or PowerShell-native equivalents). Real isolation comes from
approval gating and the sandbox tier (`DockerShellTool`). See
`tests/test_security.py` for the documented residual risk surface.
Override with `ShellPolicy`:
```python
from agent_framework.tools import LocalShellTool, ShellPolicy
shell = LocalShellTool(
policy=ShellPolicy(allowlist=[r"^ls\b", r"^cat\b", r"^git status$"]),
approval_mode="never_require",
acknowledge_unsafe=True, # required to bypass approval
)
```
### Cross-OS
- **Windows**: `pwsh -NoProfile -Command -` (falls back to `powershell.exe`).
- **Linux / macOS**: `/bin/bash --noprofile --norc` (falls back to `/bin/sh`).
- Override via the `shell=` constructor argument or the
`AGENT_FRAMEWORK_SHELL` environment variable.
## `ShellEnvironmentProvider` — context provider
A model talking to a PowerShell session will sometimes default to bash
syntax (`export FOO=bar`, `ls -la`, `> /dev/null`) and vice versa.
`ShellEnvironmentProvider` is an `AIContextProvider` that probes the live
shell once per session — family, version, OS, working directory, and a
configurable list of CLI tools (`git`, `node`, `python`, `docker` by
default) — and injects a system-prompt block describing the shell idiom
to use and the available CLIs.
```python
from agent_framework.tools import (
LocalShellTool,
ShellEnvironmentProvider,
ShellEnvironmentProviderOptions,
)
shell = LocalShellTool()
provider = ShellEnvironmentProvider(
shell,
ShellEnvironmentProviderOptions(probe_tools=("git", "uv", "node")),
)
agent = Agent(
client=client,
tools=[client.get_shell_tool(func=shell.as_function())],
context_providers=[provider],
)
```
Probe failures from expected error types (timeouts, policy rejections,
spawn failures) are recorded as `None` fields in the snapshot rather
than raised; a missing CLI never fails the agent. A failed first probe
does not poison the cache — the next call retries.
## `DockerShellTool` — sandboxed tier
When commands originate from untrusted input (e.g. the model is acting on
prompt-injected document content), prefer `DockerShellTool`. With the
default isolation flags and a trusted container runtime, the container
is the intended security boundary and approval gating becomes optional.
```python
import asyncio
from agent_framework.tools import DockerShellTool
async def main() -> None:
async with DockerShellTool(
image="mcr.microsoft.com/azurelinux/base/core:3.0",
approval_mode="never_require", # container is the boundary
) as shell:
result = await shell.run("uname -a && id")
print(result.stdout)
asyncio.run(main())
```
Defaults applied to every container:
- `--network none` — no host or external network.
- `--user 65534:65534` — runs as `nobody:nogroup`.
- `--read-only` root filesystem; only mounted host paths are writable.
- `--cap-drop ALL` and `--security-opt no-new-privileges`.
- `--memory 512m`, `--pids-limit 256`, ephemeral `tmpfs /tmp`.
To expose a host directory, pass `host_workdir="/path"` (mounted
read-only by default; `mount_readonly=False` to allow writes). Swap the
container runtime with `docker_binary="podman"`.
## Sandbox tiers at a glance
| Use case | Tool | Sandbox |
|---|---|---|
| Run *code* (untrusted) | `HyperlightCodeActProvider.execute_code` (`agent-framework-hyperlight`) | Hyperlight WASM microVM |
| Run *shell* (untrusted) | `DockerShellTool` | OCI container (network-off, non-root, capabilities dropped) |
| Run *shell* (trusted dev) | `LocalShellTool` | Approval-in-the-loop |
## Relationship to `agent-framework-hyperlight`
`agent-framework-hyperlight` is a **code** sandbox (a single WASM guest
loaded into a microVM, called via a hostcall ABI — there is no kernel,
userland, or shell binary inside). It is the right tier for executing
generated *code*. For sandboxing *shell* commands, the realistic tier is
OCI, which `DockerShellTool` provides.