agents-function-tools 0.2.0__tar.gz → 0.4.0__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.
Files changed (31) hide show
  1. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/PKG-INFO +75 -19
  2. agents_function_tools-0.4.0/README.md +130 -0
  3. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/pyproject.toml +1 -1
  4. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/agents_function_tools.egg-info/PKG-INFO +75 -19
  5. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/agents_function_tools.egg-info/SOURCES.txt +4 -0
  6. agents_function_tools-0.4.0/src/function_tools/__init__.py +52 -0
  7. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/http.py +9 -28
  8. agents_function_tools-0.4.0/src/function_tools/network.py +209 -0
  9. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/openai_tools.py +236 -29
  10. agents_function_tools-0.4.0/src/function_tools/process.py +250 -0
  11. agents_function_tools-0.4.0/tests/test_network_and_process.py +134 -0
  12. agents_function_tools-0.4.0/tests/test_openai_tools.py +156 -0
  13. agents_function_tools-0.4.0/tests/test_public_api.py +14 -0
  14. agents_function_tools-0.2.0/README.md +0 -74
  15. agents_function_tools-0.2.0/src/function_tools/__init__.py +0 -21
  16. agents_function_tools-0.2.0/tests/test_openai_tools.py +0 -60
  17. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/LICENSE +0 -0
  18. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/setup.cfg +0 -0
  19. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/agents_function_tools.egg-info/dependency_links.txt +0 -0
  20. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/agents_function_tools.egg-info/requires.txt +0 -0
  21. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/agents_function_tools.egg-info/top_level.txt +0 -0
  22. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/archive.py +0 -0
  23. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/command.py +0 -0
  24. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/errors.py +0 -0
  25. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/host.py +0 -0
  26. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/responses.py +0 -0
  27. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/src/function_tools/workspace.py +0 -0
  28. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/tests/test_archive.py +0 -0
  29. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/tests/test_command.py +0 -0
  30. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/tests/test_host_and_http.py +0 -0
  31. {agents_function_tools-0.2.0 → agents_function_tools-0.4.0}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agents-function-tools
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Portable, policy-friendly system function tools for AI applications.
5
5
  License-Expression: Apache-2.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -18,16 +18,22 @@ Dynamic: license-file
18
18
 
19
19
  `agents-function-tools` is a portable Python library of policy-friendly system function tools. It contains no business model, Agent routing, domain workflow, database adapter, or code-review logic. Its Python import name is `function_tools`.
20
20
 
21
+ ## Install
22
+
23
+ ```bash
24
+ python -m pip install agents-function-tools
25
+ ```
26
+
21
27
  ## Included tools
22
28
 
23
- | Category | Tools | Effect class |
29
+ | Category | Tools | Recommended selector |
24
30
  |---|---|---|
25
- | Workspace read | List, read UTF-8, inspect metadata, glob-style find, hash files, disk usage | `safe_read` |
26
- | Workspace write | Write text, create directories, copy a regular file, move a path, delete a path | `workspace_write` |
27
- | ZIP | List archive entries; create and extract bounded ZIP archives | `safe_read` / `workspace_write` |
28
- | Network | Fetch bounded HTTPS text from configured hosts | `safe_read` |
29
- | Host | Non-sensitive system info, UTC time, explicitly allowlisted environment variables | `safe_read` |
30
- | Commands | Describe configured aliases; run one allowlisted executable with `shell=False` | `safe_read` / `workspace_execution` |
31
+ | Workspace read | List, read UTF-8, inspect metadata, glob-style find, hash files, disk usage | `files.read` |
32
+ | Workspace write | Write text, create directories, copy a regular file, move a path, delete a path | `files.write` |
33
+ | ZIP | List archive entries; create and extract bounded ZIP archives | `files.read` / `files.write` |
34
+ | Network | Fetch bounded HTTPS text; optionally build search URLs, resolve configured hosts, and probe configured TCP ports | `network.read` |
35
+ | Host | Non-sensitive system info, UTC time, explicitly allowlisted environment variables | `host.inspect` |
36
+ | Processes and commands | Describe configured aliases; optionally inspect allowlisted process names; run one allowlisted executable with `shell=False` | `process.inspect` / `process.execute` |
31
37
 
32
38
  Every tool returns the same JSON envelope with `ok`, `tool`, `effect`, `data`, and `error` fields. Paths are always relative to a configured workspace root.
33
39
 
@@ -40,22 +46,46 @@ Every tool returns the same JSON envelope with `ok`, `tool`, `effect`, `data`, a
40
46
  - Every workspace mutation and local command requires the SDK approval gate in addition to the orchestration approval policy. File copy accepts regular, non-symlink source files only.
41
47
  - ZIP creation rejects symlinks; ZIP extraction rejects path traversal and symlink entries before writing files.
42
48
  - HTTPS fetching requires an exact host allowlist, rejects redirects, URL credentials, and non-default ports, accepts only text-like content types, and blocks resolved private or loopback addresses. No host is enabled by default. Deployment still needs an egress proxy or firewall: application-layer DNS checks do not replace network isolation.
43
- - Host diagnostics intentionally exclude user identities, process lists, network configuration, installed software, and environment variables except for names explicitly configured by the host.
49
+ - Host diagnostics intentionally exclude user identities, network configuration, installed software, and environment variables except for names explicitly configured by the host. Process diagnostics are separate, disabled by default, and return only configured process names, PIDs, states, and start times.
44
50
  - Command execution accepts an argument array, never a shell string. Programs must be mapped by the host application, execution has a timeout, and output is truncated.
45
51
  - The local command runner is not an OS security sandbox. Production deployment must run the service or runner inside the company-approved container/sandbox with no production secrets and restricted network access.
46
- - Approval remains the orchestration layer's responsibility. Only expose `workspace_write` or `workspace_execution` tools to an Agent after the matching approval has been validated.
52
+ - Approval remains the orchestration layer's responsibility. Only attach `tools.write` or `tools.execute` after the matching approval policy has been validated.
47
53
 
48
54
  This is a controlled operating-system capability adapter, not a general shell, process-management, credential, service-control, or unrestricted-network interface. Give each business Agent only the smallest subset of these tools it needs.
49
55
 
50
56
  ## Example
51
57
 
58
+ ```python
59
+ from pathlib import Path
60
+
61
+ from agents import Agent
62
+
63
+ from function_tools import create_tools
64
+
65
+ tools = create_tools(Path("./workspace"))
66
+
67
+ agent = Agent(
68
+ name="Workspace assistant",
69
+ instructions="Use workspace tools when needed.",
70
+ tools=list(tools.files.read),
71
+ )
72
+ ```
73
+
74
+ Use the narrowest domain selector: `tools.files`, `tools.network`, `tools.host`, or `tools.process`. The v0.3-style `tools.read`, `tools.write`, and `tools.execute` selectors remain available for compatibility, but combine more capabilities. Every `*.write` and `*.execute` tool requires approval on every call. The SDK derives each FunctionTool's input schema from the Python signature and docstring.
75
+
76
+ ## Design specs
77
+
78
+ - [v0.4 tool expansion spec](docs/v0.4-tool-expansion-spec.md)
79
+
80
+ For configured commands, HTTPS hosts, or readable environment variables, use `ToolConfig`:
81
+
52
82
  ```python
53
83
  import sys
54
84
  from pathlib import Path
55
85
 
56
- from function_tools.openai_tools import ToolConfig, create_function_tools
86
+ from function_tools import ToolConfig, create_tools
57
87
 
58
- bundle = create_function_tools(
88
+ tools = create_tools(
59
89
  ToolConfig(
60
90
  workspace_root=Path("./workspace"),
61
91
  command_programs={"python": sys.executable},
@@ -63,16 +93,26 @@ bundle = create_function_tools(
63
93
  environment_variables=frozenset({"APP_ENV"}),
64
94
  )
65
95
  )
96
+ ```
66
97
 
67
- # Safe tools can be attached to an Agent immediately.
68
- safe_tools = list(bundle.safe_read)
98
+ Network diagnostics and process inspection are disabled until explicitly configured:
69
99
 
70
- # Select side-effect tools only after the policy and approval checks pass.
71
- write_tools = list(bundle.workspace_write)
72
- execution_tools = list(bundle.workspace_execution)
73
- ```
100
+ ```python
101
+ from function_tools import ToolConfig, create_tools
74
102
 
75
- The SDK derives each FunctionTool's input schema from the Python signature and docstring. For production, keep the groups separate when attaching them to an Agent; do not use `bundle.all` by default.
103
+ tools = create_tools(
104
+ ToolConfig(
105
+ workspace_root="./workspace",
106
+ http_allowed_hosts=frozenset({"api.example.internal"}),
107
+ search_engines={"bing": "https://www.bing.com/search"},
108
+ network_allowed_ports=frozenset({443}),
109
+ process_name_allowlist=frozenset({"python.exe"}),
110
+ )
111
+ )
112
+
113
+ network_tools = tools.network.read
114
+ process_tools = tools.process.inspect
115
+ ```
76
116
 
77
117
  ## License
78
118
 
@@ -88,3 +128,19 @@ uv run --python 3.10 pytest
88
128
  ```
89
129
 
90
130
  Tests do not call the OpenAI API and do not require `OPENAI_API_KEY`.
131
+
132
+ ## Release acceptance
133
+
134
+ Before every PyPI release, run:
135
+
136
+ ```powershell
137
+ uv run python scripts/release_check.py
138
+ ```
139
+
140
+ The gate checks the lockfile, formatting, linting, tests, wheel and source-distribution contents, package metadata, a clean `python -m pip install --no-deps <wheel>`, and a separate clean runtime installation with dependencies.
141
+
142
+ Use the following command to run that gate and publish only when it passes:
143
+
144
+ ```powershell
145
+ uv run python scripts/publish.py
146
+ ```
@@ -0,0 +1,130 @@
1
+ # Agents Function Tools
2
+
3
+ `agents-function-tools` is a portable Python library of policy-friendly system function tools. It contains no business model, Agent routing, domain workflow, database adapter, or code-review logic. Its Python import name is `function_tools`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ python -m pip install agents-function-tools
9
+ ```
10
+
11
+ ## Included tools
12
+
13
+ | Category | Tools | Recommended selector |
14
+ |---|---|---|
15
+ | Workspace read | List, read UTF-8, inspect metadata, glob-style find, hash files, disk usage | `files.read` |
16
+ | Workspace write | Write text, create directories, copy a regular file, move a path, delete a path | `files.write` |
17
+ | ZIP | List archive entries; create and extract bounded ZIP archives | `files.read` / `files.write` |
18
+ | Network | Fetch bounded HTTPS text; optionally build search URLs, resolve configured hosts, and probe configured TCP ports | `network.read` |
19
+ | Host | Non-sensitive system info, UTC time, explicitly allowlisted environment variables | `host.inspect` |
20
+ | Processes and commands | Describe configured aliases; optionally inspect allowlisted process names; run one allowlisted executable with `shell=False` | `process.inspect` / `process.execute` |
21
+
22
+ Every tool returns the same JSON envelope with `ok`, `tool`, `effect`, `data`, and `error` fields. Paths are always relative to a configured workspace root.
23
+
24
+ ## Safety boundary
25
+
26
+ - Path traversal and access outside the workspace root are rejected.
27
+ - The workspace root cannot be deleted.
28
+ - Recursive deletion must be explicit.
29
+ - File reads, writes, hashes, and archive expansion have byte limits.
30
+ - Every workspace mutation and local command requires the SDK approval gate in addition to the orchestration approval policy. File copy accepts regular, non-symlink source files only.
31
+ - ZIP creation rejects symlinks; ZIP extraction rejects path traversal and symlink entries before writing files.
32
+ - HTTPS fetching requires an exact host allowlist, rejects redirects, URL credentials, and non-default ports, accepts only text-like content types, and blocks resolved private or loopback addresses. No host is enabled by default. Deployment still needs an egress proxy or firewall: application-layer DNS checks do not replace network isolation.
33
+ - Host diagnostics intentionally exclude user identities, network configuration, installed software, and environment variables except for names explicitly configured by the host. Process diagnostics are separate, disabled by default, and return only configured process names, PIDs, states, and start times.
34
+ - Command execution accepts an argument array, never a shell string. Programs must be mapped by the host application, execution has a timeout, and output is truncated.
35
+ - The local command runner is not an OS security sandbox. Production deployment must run the service or runner inside the company-approved container/sandbox with no production secrets and restricted network access.
36
+ - Approval remains the orchestration layer's responsibility. Only attach `tools.write` or `tools.execute` after the matching approval policy has been validated.
37
+
38
+ This is a controlled operating-system capability adapter, not a general shell, process-management, credential, service-control, or unrestricted-network interface. Give each business Agent only the smallest subset of these tools it needs.
39
+
40
+ ## Example
41
+
42
+ ```python
43
+ from pathlib import Path
44
+
45
+ from agents import Agent
46
+
47
+ from function_tools import create_tools
48
+
49
+ tools = create_tools(Path("./workspace"))
50
+
51
+ agent = Agent(
52
+ name="Workspace assistant",
53
+ instructions="Use workspace tools when needed.",
54
+ tools=list(tools.files.read),
55
+ )
56
+ ```
57
+
58
+ Use the narrowest domain selector: `tools.files`, `tools.network`, `tools.host`, or `tools.process`. The v0.3-style `tools.read`, `tools.write`, and `tools.execute` selectors remain available for compatibility, but combine more capabilities. Every `*.write` and `*.execute` tool requires approval on every call. The SDK derives each FunctionTool's input schema from the Python signature and docstring.
59
+
60
+ ## Design specs
61
+
62
+ - [v0.4 tool expansion spec](docs/v0.4-tool-expansion-spec.md)
63
+
64
+ For configured commands, HTTPS hosts, or readable environment variables, use `ToolConfig`:
65
+
66
+ ```python
67
+ import sys
68
+ from pathlib import Path
69
+
70
+ from function_tools import ToolConfig, create_tools
71
+
72
+ tools = create_tools(
73
+ ToolConfig(
74
+ workspace_root=Path("./workspace"),
75
+ command_programs={"python": sys.executable},
76
+ http_allowed_hosts=frozenset({"api.example.internal"}),
77
+ environment_variables=frozenset({"APP_ENV"}),
78
+ )
79
+ )
80
+ ```
81
+
82
+ Network diagnostics and process inspection are disabled until explicitly configured:
83
+
84
+ ```python
85
+ from function_tools import ToolConfig, create_tools
86
+
87
+ tools = create_tools(
88
+ ToolConfig(
89
+ workspace_root="./workspace",
90
+ http_allowed_hosts=frozenset({"api.example.internal"}),
91
+ search_engines={"bing": "https://www.bing.com/search"},
92
+ network_allowed_ports=frozenset({443}),
93
+ process_name_allowlist=frozenset({"python.exe"}),
94
+ )
95
+ )
96
+
97
+ network_tools = tools.network.read
98
+ process_tools = tools.process.inspect
99
+ ```
100
+
101
+ ## License
102
+
103
+ Apache-2.0. See [LICENSE](LICENSE).
104
+
105
+ ## Development
106
+
107
+ Use Python 3.10 or newer:
108
+
109
+ ```powershell
110
+ uv sync --python 3.10
111
+ uv run --python 3.10 pytest
112
+ ```
113
+
114
+ Tests do not call the OpenAI API and do not require `OPENAI_API_KEY`.
115
+
116
+ ## Release acceptance
117
+
118
+ Before every PyPI release, run:
119
+
120
+ ```powershell
121
+ uv run python scripts/release_check.py
122
+ ```
123
+
124
+ The gate checks the lockfile, formatting, linting, tests, wheel and source-distribution contents, package metadata, a clean `python -m pip install --no-deps <wheel>`, and a separate clean runtime installation with dependencies.
125
+
126
+ Use the following command to run that gate and publish only when it passes:
127
+
128
+ ```powershell
129
+ uv run python scripts/publish.py
130
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agents-function-tools"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "Portable, policy-friendly system function tools for AI applications."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agents-function-tools
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Portable, policy-friendly system function tools for AI applications.
5
5
  License-Expression: Apache-2.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -18,16 +18,22 @@ Dynamic: license-file
18
18
 
19
19
  `agents-function-tools` is a portable Python library of policy-friendly system function tools. It contains no business model, Agent routing, domain workflow, database adapter, or code-review logic. Its Python import name is `function_tools`.
20
20
 
21
+ ## Install
22
+
23
+ ```bash
24
+ python -m pip install agents-function-tools
25
+ ```
26
+
21
27
  ## Included tools
22
28
 
23
- | Category | Tools | Effect class |
29
+ | Category | Tools | Recommended selector |
24
30
  |---|---|---|
25
- | Workspace read | List, read UTF-8, inspect metadata, glob-style find, hash files, disk usage | `safe_read` |
26
- | Workspace write | Write text, create directories, copy a regular file, move a path, delete a path | `workspace_write` |
27
- | ZIP | List archive entries; create and extract bounded ZIP archives | `safe_read` / `workspace_write` |
28
- | Network | Fetch bounded HTTPS text from configured hosts | `safe_read` |
29
- | Host | Non-sensitive system info, UTC time, explicitly allowlisted environment variables | `safe_read` |
30
- | Commands | Describe configured aliases; run one allowlisted executable with `shell=False` | `safe_read` / `workspace_execution` |
31
+ | Workspace read | List, read UTF-8, inspect metadata, glob-style find, hash files, disk usage | `files.read` |
32
+ | Workspace write | Write text, create directories, copy a regular file, move a path, delete a path | `files.write` |
33
+ | ZIP | List archive entries; create and extract bounded ZIP archives | `files.read` / `files.write` |
34
+ | Network | Fetch bounded HTTPS text; optionally build search URLs, resolve configured hosts, and probe configured TCP ports | `network.read` |
35
+ | Host | Non-sensitive system info, UTC time, explicitly allowlisted environment variables | `host.inspect` |
36
+ | Processes and commands | Describe configured aliases; optionally inspect allowlisted process names; run one allowlisted executable with `shell=False` | `process.inspect` / `process.execute` |
31
37
 
32
38
  Every tool returns the same JSON envelope with `ok`, `tool`, `effect`, `data`, and `error` fields. Paths are always relative to a configured workspace root.
33
39
 
@@ -40,22 +46,46 @@ Every tool returns the same JSON envelope with `ok`, `tool`, `effect`, `data`, a
40
46
  - Every workspace mutation and local command requires the SDK approval gate in addition to the orchestration approval policy. File copy accepts regular, non-symlink source files only.
41
47
  - ZIP creation rejects symlinks; ZIP extraction rejects path traversal and symlink entries before writing files.
42
48
  - HTTPS fetching requires an exact host allowlist, rejects redirects, URL credentials, and non-default ports, accepts only text-like content types, and blocks resolved private or loopback addresses. No host is enabled by default. Deployment still needs an egress proxy or firewall: application-layer DNS checks do not replace network isolation.
43
- - Host diagnostics intentionally exclude user identities, process lists, network configuration, installed software, and environment variables except for names explicitly configured by the host.
49
+ - Host diagnostics intentionally exclude user identities, network configuration, installed software, and environment variables except for names explicitly configured by the host. Process diagnostics are separate, disabled by default, and return only configured process names, PIDs, states, and start times.
44
50
  - Command execution accepts an argument array, never a shell string. Programs must be mapped by the host application, execution has a timeout, and output is truncated.
45
51
  - The local command runner is not an OS security sandbox. Production deployment must run the service or runner inside the company-approved container/sandbox with no production secrets and restricted network access.
46
- - Approval remains the orchestration layer's responsibility. Only expose `workspace_write` or `workspace_execution` tools to an Agent after the matching approval has been validated.
52
+ - Approval remains the orchestration layer's responsibility. Only attach `tools.write` or `tools.execute` after the matching approval policy has been validated.
47
53
 
48
54
  This is a controlled operating-system capability adapter, not a general shell, process-management, credential, service-control, or unrestricted-network interface. Give each business Agent only the smallest subset of these tools it needs.
49
55
 
50
56
  ## Example
51
57
 
58
+ ```python
59
+ from pathlib import Path
60
+
61
+ from agents import Agent
62
+
63
+ from function_tools import create_tools
64
+
65
+ tools = create_tools(Path("./workspace"))
66
+
67
+ agent = Agent(
68
+ name="Workspace assistant",
69
+ instructions="Use workspace tools when needed.",
70
+ tools=list(tools.files.read),
71
+ )
72
+ ```
73
+
74
+ Use the narrowest domain selector: `tools.files`, `tools.network`, `tools.host`, or `tools.process`. The v0.3-style `tools.read`, `tools.write`, and `tools.execute` selectors remain available for compatibility, but combine more capabilities. Every `*.write` and `*.execute` tool requires approval on every call. The SDK derives each FunctionTool's input schema from the Python signature and docstring.
75
+
76
+ ## Design specs
77
+
78
+ - [v0.4 tool expansion spec](docs/v0.4-tool-expansion-spec.md)
79
+
80
+ For configured commands, HTTPS hosts, or readable environment variables, use `ToolConfig`:
81
+
52
82
  ```python
53
83
  import sys
54
84
  from pathlib import Path
55
85
 
56
- from function_tools.openai_tools import ToolConfig, create_function_tools
86
+ from function_tools import ToolConfig, create_tools
57
87
 
58
- bundle = create_function_tools(
88
+ tools = create_tools(
59
89
  ToolConfig(
60
90
  workspace_root=Path("./workspace"),
61
91
  command_programs={"python": sys.executable},
@@ -63,16 +93,26 @@ bundle = create_function_tools(
63
93
  environment_variables=frozenset({"APP_ENV"}),
64
94
  )
65
95
  )
96
+ ```
66
97
 
67
- # Safe tools can be attached to an Agent immediately.
68
- safe_tools = list(bundle.safe_read)
98
+ Network diagnostics and process inspection are disabled until explicitly configured:
69
99
 
70
- # Select side-effect tools only after the policy and approval checks pass.
71
- write_tools = list(bundle.workspace_write)
72
- execution_tools = list(bundle.workspace_execution)
73
- ```
100
+ ```python
101
+ from function_tools import ToolConfig, create_tools
74
102
 
75
- The SDK derives each FunctionTool's input schema from the Python signature and docstring. For production, keep the groups separate when attaching them to an Agent; do not use `bundle.all` by default.
103
+ tools = create_tools(
104
+ ToolConfig(
105
+ workspace_root="./workspace",
106
+ http_allowed_hosts=frozenset({"api.example.internal"}),
107
+ search_engines={"bing": "https://www.bing.com/search"},
108
+ network_allowed_ports=frozenset({443}),
109
+ process_name_allowlist=frozenset({"python.exe"}),
110
+ )
111
+ )
112
+
113
+ network_tools = tools.network.read
114
+ process_tools = tools.process.inspect
115
+ ```
76
116
 
77
117
  ## License
78
118
 
@@ -88,3 +128,19 @@ uv run --python 3.10 pytest
88
128
  ```
89
129
 
90
130
  Tests do not call the OpenAI API and do not require `OPENAI_API_KEY`.
131
+
132
+ ## Release acceptance
133
+
134
+ Before every PyPI release, run:
135
+
136
+ ```powershell
137
+ uv run python scripts/release_check.py
138
+ ```
139
+
140
+ The gate checks the lockfile, formatting, linting, tests, wheel and source-distribution contents, package metadata, a clean `python -m pip install --no-deps <wheel>`, and a separate clean runtime installation with dependencies.
141
+
142
+ Use the following command to run that gate and publish only when it passes:
143
+
144
+ ```powershell
145
+ uv run python scripts/publish.py
146
+ ```
@@ -12,11 +12,15 @@ src/function_tools/command.py
12
12
  src/function_tools/errors.py
13
13
  src/function_tools/host.py
14
14
  src/function_tools/http.py
15
+ src/function_tools/network.py
15
16
  src/function_tools/openai_tools.py
17
+ src/function_tools/process.py
16
18
  src/function_tools/responses.py
17
19
  src/function_tools/workspace.py
18
20
  tests/test_archive.py
19
21
  tests/test_command.py
20
22
  tests/test_host_and_http.py
23
+ tests/test_network_and_process.py
21
24
  tests/test_openai_tools.py
25
+ tests/test_public_api.py
22
26
  tests/test_workspace.py
@@ -0,0 +1,52 @@
1
+ """Portable, policy-friendly system function tools for AI applications."""
2
+
3
+ from .archive import ZipArchive
4
+ from .command import CommandPolicy, LocalCommandRunner
5
+ from .errors import FoundationToolError
6
+ from .host import HostInspector
7
+ from .http import HttpPolicy, HttpTextClient
8
+ from .network import NetworkInspector, NetworkPolicy
9
+ from .openai_tools import (
10
+ FilesystemToolGroup,
11
+ FunctionToolBundle,
12
+ HostToolGroup,
13
+ NetworkToolGroup,
14
+ ProcessToolGroup,
15
+ ToolConfig,
16
+ create_filesystem_tools,
17
+ create_function_tools,
18
+ create_host_tools,
19
+ create_network_tools,
20
+ create_process_tools,
21
+ create_tools,
22
+ )
23
+ from .process import ProcessInspector
24
+ from .workspace import Workspace
25
+
26
+ __version__ = "0.4.0"
27
+
28
+ __all__ = [
29
+ "CommandPolicy",
30
+ "FoundationToolError",
31
+ "FilesystemToolGroup",
32
+ "FunctionToolBundle",
33
+ "HostInspector",
34
+ "HostToolGroup",
35
+ "HttpPolicy",
36
+ "HttpTextClient",
37
+ "LocalCommandRunner",
38
+ "NetworkInspector",
39
+ "NetworkPolicy",
40
+ "NetworkToolGroup",
41
+ "ProcessInspector",
42
+ "ProcessToolGroup",
43
+ "ToolConfig",
44
+ "Workspace",
45
+ "ZipArchive",
46
+ "create_filesystem_tools",
47
+ "create_function_tools",
48
+ "create_host_tools",
49
+ "create_network_tools",
50
+ "create_process_tools",
51
+ "create_tools",
52
+ ]
@@ -1,7 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import ipaddress
4
- import socket
5
3
  import ssl
6
4
  from dataclasses import dataclass
7
5
  from typing import Any
@@ -10,6 +8,7 @@ from urllib.parse import urlparse
10
8
  from urllib.request import HTTPRedirectHandler, HTTPSHandler, Request, build_opener
11
9
 
12
10
  from .errors import FoundationToolError
11
+ from .network import NetworkPolicy
13
12
 
14
13
 
15
14
  @dataclass(frozen=True)
@@ -36,6 +35,11 @@ class HttpTextClient:
36
35
 
37
36
  def __init__(self, policy: HttpPolicy) -> None:
38
37
  self.policy = policy
38
+ self._network_policy = NetworkPolicy(
39
+ allowed_hosts=policy.allowed_hosts,
40
+ timeout_seconds=policy.timeout_seconds,
41
+ allow_private_addresses=policy.allow_private_addresses,
42
+ )
39
43
 
40
44
  def fetch(self, url: str) -> dict[str, Any]:
41
45
  parsed = self._validate_url(url)
@@ -43,7 +47,7 @@ class HttpTextClient:
43
47
  request = Request(
44
48
  url,
45
49
  headers={
46
- "User-Agent": "company-agent-service/0.1",
50
+ "User-Agent": "agents-function-tools",
47
51
  "Accept": "text/plain,text/html,application/json",
48
52
  },
49
53
  )
@@ -106,31 +110,8 @@ class HttpTextClient:
106
110
  raise FoundationToolError(
107
111
  "INVALID_URL", "Only HTTPS default-port URLs without user credentials are allowed."
108
112
  )
109
- if host not in self.policy.allowed_hosts:
110
- allowed = ", ".join(sorted(self.policy.allowed_hosts)) or "none"
111
- raise FoundationToolError(
112
- "HOST_NOT_ALLOWED", f"Host is not allowed. Allowed hosts: {allowed}."
113
- )
113
+ self._network_policy.validate_host(host)
114
114
  return parsed
115
115
 
116
116
  def _validate_addresses(self, hostname: str) -> None:
117
- try:
118
- addresses = {
119
- item[4][0] for item in socket.getaddrinfo(hostname, None, type=socket.SOCK_STREAM)
120
- }
121
- except socket.gaierror as error:
122
- raise FoundationToolError(
123
- "DNS_LOOKUP_FAILED", "Allowed host could not be resolved.", retryable=True
124
- ) from error
125
- if not addresses:
126
- raise FoundationToolError(
127
- "DNS_LOOKUP_FAILED", "Allowed host has no resolved addresses."
128
- )
129
- if self.policy.allow_private_addresses:
130
- return
131
- for address in addresses:
132
- ip = ipaddress.ip_address(address)
133
- if not ip.is_global:
134
- raise FoundationToolError(
135
- "PRIVATE_ADDRESS_BLOCKED", "Resolved address is not public."
136
- )
117
+ self._network_policy.resolve_public_addresses(hostname)