agent-framework-tools 1.0.0a260424__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-framework-tools
3
+ Version: 1.0.0a260424
4
+ Summary: Built-in tools for the Microsoft Agent Framework (local shell, and more).
5
+ Author-email: Microsoft <af-support@microsoft.com>
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ License-File: LICENSE
18
+ Requires-Dist: agent-framework-core>=1.2.2,<2
19
+ Requires-Dist: psutil>=5.9
20
+ Project-URL: homepage, https://aka.ms/agent-framework
21
+ Project-URL: issues, https://github.com/microsoft/agent-framework/issues
22
+ Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
23
+ Project-URL: source, https://github.com/microsoft/agent-framework/tree/main/python
24
+
25
+ # agent-framework-tools
26
+
27
+ Alpha built-in tools for the Microsoft Agent Framework. A home for first-party
28
+ Python tools that plug into any chat client's shell / function surface. The
29
+ first tool is `LocalShellTool`.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install agent-framework-tools --pre
35
+ ```
36
+
37
+ ## `LocalShellTool` quick start
38
+
39
+ ```python
40
+ import asyncio
41
+ from agent_framework import Agent
42
+ from agent_framework.openai import OpenAIChatClient
43
+ from agent_framework_tools.shell import LocalShellTool
44
+
45
+
46
+ async def main() -> None:
47
+ client = OpenAIChatClient(model="gpt-5.4-nano")
48
+ async with LocalShellTool() as shell:
49
+ agent = Agent(
50
+ client=client,
51
+ instructions="You are a helpful assistant that can run shell commands.",
52
+ tools=[client.get_shell_tool(func=shell.as_function())],
53
+ )
54
+ result = await agent.run("Print the current working directory.")
55
+ print(result.text)
56
+
57
+
58
+ asyncio.run(main())
59
+ ```
60
+
61
+ ### Modes
62
+
63
+ - **Persistent** (default): a single long-lived shell session. `cd`, `export`,
64
+ and shell functions persist across tool invocations.
65
+ - **Stateless** (`mode="stateless"`): each command runs in a fresh subprocess.
66
+
67
+ ### Safety
68
+
69
+ > **`LocalShellTool` is not a sandbox.** It runs commands directly on the
70
+ > host with the agent process's privileges. The actual security boundary
71
+ > is **approval-in-the-loop**. For untrusted input use a sandboxed
72
+ > executor — see [`agent-framework-hyperlight`](#relationship-to-agent-framework-hyperlight).
73
+
74
+ Defenses (in priority order):
75
+
76
+ - **Approval-in-the-loop** — every command surfaces as a
77
+ `user_input_request`; nothing runs without consent. Disabling this
78
+ requires `acknowledge_unsafe=True`.
79
+ - **Process-tree termination on timeout** via `psutil`, so child
80
+ processes (`make`, watchers, network tools) cannot survive the timeout.
81
+ - **Output truncation** to 64 KiB (head + tail with marker).
82
+ - **Audit hook** (`on_command=…`) for SIEM / append-only logs.
83
+ - **Optional command-pattern filter** via `ShellPolicy(denylist=[...],
84
+ allowlist=[...])`. **Empty by default.** This is a UX pre-filter, not a
85
+ security boundary — operators are expected to supply patterns that
86
+ match their workload (and they can be defeated by trivial obfuscation
87
+ such as `\rm -rf /`, `${RM:=rm} -rf /`, `python -c "…"`, encoded
88
+ payloads, or PowerShell-native equivalents). Real isolation comes from
89
+ approval gating and the sandbox tier (`DockerShellTool`). See
90
+ `tests/test_security.py` for the documented residual risk surface.
91
+
92
+ Override with `ShellPolicy`:
93
+
94
+ ```python
95
+ from agent_framework_tools.shell import LocalShellTool, ShellPolicy
96
+
97
+ shell = LocalShellTool(
98
+ policy=ShellPolicy(allowlist=[r"^ls\b", r"^cat\b", r"^git status$"]),
99
+ approval_mode="never_require",
100
+ acknowledge_unsafe=True, # required to bypass approval
101
+ )
102
+ ```
103
+
104
+ ### Cross-OS
105
+
106
+ - **Windows**: `pwsh -NoProfile -Command -` (falls back to `powershell.exe`).
107
+ - **Linux / macOS**: `/bin/bash --noprofile --norc` (falls back to `/bin/sh`).
108
+ - Override via the `shell=` constructor argument or the
109
+ `AGENT_FRAMEWORK_SHELL` environment variable.
110
+
111
+ ## `ShellEnvironmentProvider` — context provider
112
+
113
+ A model talking to a PowerShell session will sometimes default to bash
114
+ syntax (`export FOO=bar`, `ls -la`, `> /dev/null`) and vice versa.
115
+ `ShellEnvironmentProvider` is an `AIContextProvider` that probes the live
116
+ shell once per session — family, version, OS, working directory, and a
117
+ configurable list of CLI tools (`git`, `node`, `python`, `docker` by
118
+ default) — and injects a system-prompt block describing the shell idiom
119
+ to use and the available CLIs.
120
+
121
+ ```python
122
+ from agent_framework_tools.shell import (
123
+ LocalShellTool,
124
+ ShellEnvironmentProvider,
125
+ ShellEnvironmentProviderOptions,
126
+ )
127
+
128
+ shell = LocalShellTool()
129
+ provider = ShellEnvironmentProvider(
130
+ shell,
131
+ ShellEnvironmentProviderOptions(probe_tools=("git", "uv", "node")),
132
+ )
133
+ agent = Agent(
134
+ client=client,
135
+ tools=[client.get_shell_tool(func=shell.as_function())],
136
+ context_providers=[provider],
137
+ )
138
+ ```
139
+
140
+ Probe failures from expected error types (timeouts, policy rejections,
141
+ spawn failures) are recorded as `None` fields in the snapshot rather
142
+ than raised; a missing CLI never fails the agent. A failed first probe
143
+ does not poison the cache — the next call retries.
144
+
145
+ ## `DockerShellTool` — sandboxed tier
146
+
147
+ When commands originate from untrusted input (e.g. the model is acting on
148
+ prompt-injected document content), prefer `DockerShellTool`. With the
149
+ default isolation flags and a trusted container runtime, the container
150
+ is the intended security boundary and approval gating becomes optional.
151
+
152
+ ```python
153
+ import asyncio
154
+ from agent_framework_tools.shell import DockerShellTool
155
+
156
+
157
+ async def main() -> None:
158
+ async with DockerShellTool(
159
+ image="mcr.microsoft.com/azurelinux/base/core:3.0",
160
+ approval_mode="never_require", # container is the boundary
161
+ ) as shell:
162
+ result = await shell.run("uname -a && id")
163
+ print(result.stdout)
164
+
165
+
166
+ asyncio.run(main())
167
+ ```
168
+
169
+ Defaults applied to every container:
170
+
171
+ - `--network none` — no host or external network.
172
+ - `--user 65534:65534` — runs as `nobody:nogroup`.
173
+ - `--read-only` root filesystem; only mounted host paths are writable.
174
+ - `--cap-drop ALL` and `--security-opt no-new-privileges`.
175
+ - `--memory 512m`, `--pids-limit 256`, ephemeral `tmpfs /tmp`.
176
+
177
+ To expose a host directory, pass `host_workdir="/path"` (mounted
178
+ read-only by default; `mount_readonly=False` to allow writes). Swap the
179
+ container runtime with `docker_binary="podman"`.
180
+
181
+ ## Sandbox tiers at a glance
182
+
183
+ | Use case | Tool | Sandbox |
184
+ |---|---|---|
185
+ | Run *code* (untrusted) | `HyperlightCodeActProvider.execute_code` (`agent-framework-hyperlight`) | Hyperlight WASM microVM |
186
+ | Run *shell* (untrusted) | `DockerShellTool` | OCI container (network-off, non-root, capabilities dropped) |
187
+ | Run *shell* (trusted dev) | `LocalShellTool` | Approval-in-the-loop |
188
+
189
+ ## Relationship to `agent-framework-hyperlight`
190
+
191
+ `agent-framework-hyperlight` is a **code** sandbox (a single WASM guest
192
+ loaded into a microVM, called via a hostcall ABI — there is no kernel,
193
+ userland, or shell binary inside). It is the right tier for executing
194
+ generated *code*. For sandboxing *shell* commands, the realistic tier is
195
+ OCI, which `DockerShellTool` provides.
196
+
@@ -0,0 +1,171 @@
1
+ # agent-framework-tools
2
+
3
+ Alpha built-in tools for the Microsoft Agent Framework. A home for first-party
4
+ Python tools that plug into any chat client's shell / function surface. The
5
+ first tool is `LocalShellTool`.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install agent-framework-tools --pre
11
+ ```
12
+
13
+ ## `LocalShellTool` quick start
14
+
15
+ ```python
16
+ import asyncio
17
+ from agent_framework import Agent
18
+ from agent_framework.openai import OpenAIChatClient
19
+ from agent_framework_tools.shell import LocalShellTool
20
+
21
+
22
+ async def main() -> None:
23
+ client = OpenAIChatClient(model="gpt-5.4-nano")
24
+ async with LocalShellTool() as shell:
25
+ agent = Agent(
26
+ client=client,
27
+ instructions="You are a helpful assistant that can run shell commands.",
28
+ tools=[client.get_shell_tool(func=shell.as_function())],
29
+ )
30
+ result = await agent.run("Print the current working directory.")
31
+ print(result.text)
32
+
33
+
34
+ asyncio.run(main())
35
+ ```
36
+
37
+ ### Modes
38
+
39
+ - **Persistent** (default): a single long-lived shell session. `cd`, `export`,
40
+ and shell functions persist across tool invocations.
41
+ - **Stateless** (`mode="stateless"`): each command runs in a fresh subprocess.
42
+
43
+ ### Safety
44
+
45
+ > **`LocalShellTool` is not a sandbox.** It runs commands directly on the
46
+ > host with the agent process's privileges. The actual security boundary
47
+ > is **approval-in-the-loop**. For untrusted input use a sandboxed
48
+ > executor — see [`agent-framework-hyperlight`](#relationship-to-agent-framework-hyperlight).
49
+
50
+ Defenses (in priority order):
51
+
52
+ - **Approval-in-the-loop** — every command surfaces as a
53
+ `user_input_request`; nothing runs without consent. Disabling this
54
+ requires `acknowledge_unsafe=True`.
55
+ - **Process-tree termination on timeout** via `psutil`, so child
56
+ processes (`make`, watchers, network tools) cannot survive the timeout.
57
+ - **Output truncation** to 64 KiB (head + tail with marker).
58
+ - **Audit hook** (`on_command=…`) for SIEM / append-only logs.
59
+ - **Optional command-pattern filter** via `ShellPolicy(denylist=[...],
60
+ allowlist=[...])`. **Empty by default.** This is a UX pre-filter, not a
61
+ security boundary — operators are expected to supply patterns that
62
+ match their workload (and they can be defeated by trivial obfuscation
63
+ such as `\rm -rf /`, `${RM:=rm} -rf /`, `python -c "…"`, encoded
64
+ payloads, or PowerShell-native equivalents). Real isolation comes from
65
+ approval gating and the sandbox tier (`DockerShellTool`). See
66
+ `tests/test_security.py` for the documented residual risk surface.
67
+
68
+ Override with `ShellPolicy`:
69
+
70
+ ```python
71
+ from agent_framework_tools.shell import LocalShellTool, ShellPolicy
72
+
73
+ shell = LocalShellTool(
74
+ policy=ShellPolicy(allowlist=[r"^ls\b", r"^cat\b", r"^git status$"]),
75
+ approval_mode="never_require",
76
+ acknowledge_unsafe=True, # required to bypass approval
77
+ )
78
+ ```
79
+
80
+ ### Cross-OS
81
+
82
+ - **Windows**: `pwsh -NoProfile -Command -` (falls back to `powershell.exe`).
83
+ - **Linux / macOS**: `/bin/bash --noprofile --norc` (falls back to `/bin/sh`).
84
+ - Override via the `shell=` constructor argument or the
85
+ `AGENT_FRAMEWORK_SHELL` environment variable.
86
+
87
+ ## `ShellEnvironmentProvider` — context provider
88
+
89
+ A model talking to a PowerShell session will sometimes default to bash
90
+ syntax (`export FOO=bar`, `ls -la`, `> /dev/null`) and vice versa.
91
+ `ShellEnvironmentProvider` is an `AIContextProvider` that probes the live
92
+ shell once per session — family, version, OS, working directory, and a
93
+ configurable list of CLI tools (`git`, `node`, `python`, `docker` by
94
+ default) — and injects a system-prompt block describing the shell idiom
95
+ to use and the available CLIs.
96
+
97
+ ```python
98
+ from agent_framework_tools.shell import (
99
+ LocalShellTool,
100
+ ShellEnvironmentProvider,
101
+ ShellEnvironmentProviderOptions,
102
+ )
103
+
104
+ shell = LocalShellTool()
105
+ provider = ShellEnvironmentProvider(
106
+ shell,
107
+ ShellEnvironmentProviderOptions(probe_tools=("git", "uv", "node")),
108
+ )
109
+ agent = Agent(
110
+ client=client,
111
+ tools=[client.get_shell_tool(func=shell.as_function())],
112
+ context_providers=[provider],
113
+ )
114
+ ```
115
+
116
+ Probe failures from expected error types (timeouts, policy rejections,
117
+ spawn failures) are recorded as `None` fields in the snapshot rather
118
+ than raised; a missing CLI never fails the agent. A failed first probe
119
+ does not poison the cache — the next call retries.
120
+
121
+ ## `DockerShellTool` — sandboxed tier
122
+
123
+ When commands originate from untrusted input (e.g. the model is acting on
124
+ prompt-injected document content), prefer `DockerShellTool`. With the
125
+ default isolation flags and a trusted container runtime, the container
126
+ is the intended security boundary and approval gating becomes optional.
127
+
128
+ ```python
129
+ import asyncio
130
+ from agent_framework_tools.shell import DockerShellTool
131
+
132
+
133
+ async def main() -> None:
134
+ async with DockerShellTool(
135
+ image="mcr.microsoft.com/azurelinux/base/core:3.0",
136
+ approval_mode="never_require", # container is the boundary
137
+ ) as shell:
138
+ result = await shell.run("uname -a && id")
139
+ print(result.stdout)
140
+
141
+
142
+ asyncio.run(main())
143
+ ```
144
+
145
+ Defaults applied to every container:
146
+
147
+ - `--network none` — no host or external network.
148
+ - `--user 65534:65534` — runs as `nobody:nogroup`.
149
+ - `--read-only` root filesystem; only mounted host paths are writable.
150
+ - `--cap-drop ALL` and `--security-opt no-new-privileges`.
151
+ - `--memory 512m`, `--pids-limit 256`, ephemeral `tmpfs /tmp`.
152
+
153
+ To expose a host directory, pass `host_workdir="/path"` (mounted
154
+ read-only by default; `mount_readonly=False` to allow writes). Swap the
155
+ container runtime with `docker_binary="podman"`.
156
+
157
+ ## Sandbox tiers at a glance
158
+
159
+ | Use case | Tool | Sandbox |
160
+ |---|---|---|
161
+ | Run *code* (untrusted) | `HyperlightCodeActProvider.execute_code` (`agent-framework-hyperlight`) | Hyperlight WASM microVM |
162
+ | Run *shell* (untrusted) | `DockerShellTool` | OCI container (network-off, non-root, capabilities dropped) |
163
+ | Run *shell* (trusted dev) | `LocalShellTool` | Approval-in-the-loop |
164
+
165
+ ## Relationship to `agent-framework-hyperlight`
166
+
167
+ `agent-framework-hyperlight` is a **code** sandbox (a single WASM guest
168
+ loaded into a microVM, called via a hostcall ABI — there is no kernel,
169
+ userland, or shell binary inside). It is the right tier for executing
170
+ generated *code*. For sandboxing *shell* commands, the realistic tier is
171
+ OCI, which `DockerShellTool` provides.
@@ -0,0 +1,14 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """Built-in tools for the Microsoft Agent Framework."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import importlib.metadata
8
+
9
+ try:
10
+ __version__ = importlib.metadata.version(__name__)
11
+ except importlib.metadata.PackageNotFoundError:
12
+ __version__ = "0.0.0"
13
+
14
+ __all__ = ["__version__"]
@@ -0,0 +1,53 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """Cross-platform local shell tool for the Microsoft Agent Framework."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from ._docker import (
8
+ DEFAULT_IMAGE as DOCKER_DEFAULT_IMAGE,
9
+ )
10
+ from ._docker import (
11
+ DockerNotAvailableError,
12
+ DockerShellTool,
13
+ is_docker_available,
14
+ )
15
+ from ._environment import (
16
+ ShellEnvironmentProvider,
17
+ ShellEnvironmentProviderOptions,
18
+ ShellEnvironmentSnapshot,
19
+ ShellFamily,
20
+ default_instructions_formatter,
21
+ )
22
+ from ._executor_base import ShellExecutor
23
+ from ._policy import ShellDecision, ShellPolicy, ShellRequest
24
+ from ._tool import LocalShellTool
25
+ from ._types import (
26
+ ShellCommandError,
27
+ ShellExecutionError,
28
+ ShellMode,
29
+ ShellResult,
30
+ ShellTimeoutError,
31
+ )
32
+
33
+ __all__ = [
34
+ "DOCKER_DEFAULT_IMAGE",
35
+ "DockerNotAvailableError",
36
+ "DockerShellTool",
37
+ "LocalShellTool",
38
+ "ShellCommandError",
39
+ "ShellDecision",
40
+ "ShellEnvironmentProvider",
41
+ "ShellEnvironmentProviderOptions",
42
+ "ShellEnvironmentSnapshot",
43
+ "ShellExecutionError",
44
+ "ShellExecutor",
45
+ "ShellFamily",
46
+ "ShellMode",
47
+ "ShellPolicy",
48
+ "ShellRequest",
49
+ "ShellResult",
50
+ "ShellTimeoutError",
51
+ "default_instructions_formatter",
52
+ "is_docker_available",
53
+ ]