Skip to content

Python: feat(python): Add local-codeact package with AST validation#6091

Draft
eavanvalkenburg wants to merge 1 commit into
microsoft:mainfrom
eavanvalkenburg:feature-local-codeact-python
Draft

Python: feat(python): Add local-codeact package with AST validation#6091
eavanvalkenburg wants to merge 1 commit into
microsoft:mainfrom
eavanvalkenburg:feature-local-codeact-python

Conversation

@eavanvalkenburg
Copy link
Copy Markdown
Member

Add agent-framework-local-codeact alpha package with AST validation for Foundry hosted agents

Add agent-framework-local-codeact alpha package for running LLM-generated
Python code in Foundry hosted agents and other sandboxed environments.

Key features:
- Subprocess execution by default (isolated process)
- Optional unsafe in-process mode for debugging
- AST-based allow-list code validation
- Customizable allowed/blocked imports and builtins
- Host tool bridge with framed JSON-lines IPC
- File mount system with capture and limits
- .NET portability features (python_executable, runner_script)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 26, 2026 16:09
@moonbox3 moonbox3 added documentation Improvements or additions to documentation python labels May 26, 2026
@github-actions github-actions Bot changed the title feat(python): Add local-codeact package with AST validation Python: feat(python): Add local-codeact package with AST validation May 26, 2026
@moonbox3
Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/local_codeact/agent_framework_local_codeact
   _bridge.py1955372%26, 30, 37–42, 54, 63, 73–75, 82, 133, 146–147, 150–152, 154, 163, 168–170, 174, 179, 182, 189, 199–200, 210–213, 217, 221–223, 242–244, 252–253, 260–261, 275–278, 280–281, 292
   _execute_code_tool.py2213882%55, 57, 83, 85, 88, 100, 108, 113, 115, 117, 125–126, 128, 144, 147, 150–153, 155, 160–161, 165, 228, 245–247, 284, 297–300, 304, 309, 314, 390–391, 433
   _files.py1203471%23, 27, 29, 37, 45–49, 70, 87–88, 94, 97–98, 106, 110–115, 131, 138–139, 143, 146–147, 149–150, 152–153, 156–157
   _instructions.py381560%16, 35, 41–45, 47–50, 55, 110–111, 113
   _provider.py34876%81, 85, 89, 93, 97, 101, 105, 109
   _runner.py1239621%21–24, 27, 30–37, 40, 44, 48, 52–60, 64–70, 72, 74–75, 89–91, 95–96, 100–113, 117–118, 120–121, 131–133, 142–143, 147–151, 154, 156–157, 159, 164–166, 168–171, 173, 184–197, 206, 210
   _types.py220100% 
   _validator.py741086%264–265, 279–280, 296–297, 301, 303, 327, 417
TOTAL37203456887% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7301 34 💤 0 ❌ 0 🔥 1m 57s ⏱️

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new alpha Python workspace package, agent-framework-local-codeact, intended to enable CodeAct-style execution of model-generated Python in externally sandboxed environments (e.g., Foundry hosted agents), with subprocess execution, file-mount capture, and AST-based validation.

Changes:

  • Registers agent-framework-local-codeact in the Python workspace (uv/pyproject) and marks it as alpha in PACKAGE_STATUS.md.
  • Introduces LocalExecuteCodeTool / LocalCodeActProvider with subprocess runner + IPC bridge, file-mount capture helpers, and dynamic instructions.
  • Adds unit tests plus usage samples and package documentation.

Reviewed changes

Copilot reviewed 20 out of 22 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
python/uv.lock Adds the new editable workspace member and lock metadata.
python/pyproject.toml Registers the new workspace package.
python/PACKAGE_STATUS.md Marks agent-framework-local-codeact as alpha.
python/packages/local_codeact/pyproject.toml New package definition, tooling config, and test tasks.
python/packages/local_codeact/README.md Package docs, security posture, and configuration surface.
python/packages/local_codeact/AGENTS.md Package architecture and contributor notes.
python/packages/local_codeact/LICENSE MIT license for the new package.
python/packages/local_codeact/agent_framework_local_codeact/init.py Public API exports for the package.
python/packages/local_codeact/agent_framework_local_codeact/_types.py Public types for execution mode, mounts, and limits.
python/packages/local_codeact/agent_framework_local_codeact/_validator.py AST-based code validation layer.
python/packages/local_codeact/agent_framework_local_codeact/_bridge.py Parent-side subprocess bridge + tool dispatch.
python/packages/local_codeact/agent_framework_local_codeact/_runner.py Child-process runner implementing the JSON-lines protocol.
python/packages/local_codeact/agent_framework_local_codeact/_files.py Mount normalization + symlink-safe file capture.
python/packages/local_codeact/agent_framework_local_codeact/_instructions.py Dynamic CodeAct instructions and tool descriptions.
python/packages/local_codeact/agent_framework_local_codeact/_execute_code_tool.py Main execute_code tool orchestration and output shaping.
python/packages/local_codeact/agent_framework_local_codeact/_provider.py Context provider that injects the run-scoped tool + instructions.
python/packages/local_codeact/tests/local_codeact/test_validator.py Validator allow/block behavior tests.
python/packages/local_codeact/tests/local_codeact/test_local_codeact.py Tool/provider behavior, subprocess execution, mounts, and limits tests.
python/packages/local_codeact/samples/README.md Sample index and run instructions.
python/packages/local_codeact/samples/local_execute_code.py Local usage sample for direct tool invocation.
python/packages/local_codeact/samples/foundry_hosted_agent.py Foundry hosted-agent wiring sample.
python/packages/local_codeact/agent_framework_local_codeact/py.typed Marks the package as typed.

env=self._env,
execution_mode=self._execution_mode,
python_executable=self._python_executable,
runner_script=self._runner_script,
Comment on lines +94 to +102
"isinstance",
"issubclass",
"hasattr",
"getattr",
"setattr",
"callable",
"type",
"id",
"hash",
Comment on lines +306 to +316
def visit_Call(self, node: ast.Call) -> None:
"""Validate function calls."""
# Check for blocked builtins
if isinstance(node.func, ast.Name):
func_name = node.func.id
if func_name in self._blocked_builtins:
self._errors.append(f"Call to builtin '{func_name}' is not allowed")
elif func_name not in self._allowed_builtins and func_name != "call_tool":
# Allow user-defined functions and registered tools (validated at runtime)
# We only block known dangerous builtins here
pass
await process.wait()
return dict(result_dict)
if message_type == "error":
details = str(message.get("traceback") or message.get("message") or "Unknown execution error.")
Comment on lines +12 to +21
from agent_framework_local_codeact import FileMount, LocalExecuteCodeTool, ProcessExecutionLimits

"""This sample demonstrates configuring and invoking Local CodeAct without Foundry hosting.

Local CodeAct executes LLM-generated Python in the local agent environment. This
sample is meant for a disposable sandbox, container, or VM. It shows the
configuration surface directly on `LocalExecuteCodeTool`: host tools, explicit
environment variables, workspace/file mounts, execution limits, and subprocess
execution mode.
"""
Comment on lines +109 to +115
"""
Sample output:
Text: Local CodeAct sample report

Text: {"usd": 20.0, "eur": 18.4}
Data: /output/report.txt
"""
Comment on lines +43 to +46
"""
Sample output:
Configure and return your model client here.
"""
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 88%

✓ Correctness

After thorough examination of the local-codeact package implementation, I found no correctness bugs. The code demonstrates excellent engineering practices: proper error handling with early validation, safe resource cleanup using try-finally blocks and context managers, correct subprocess management with timeout handling, secure AST validation with comprehensive allow/block lists, and proper IPC serialization with JSON-safe conversions. All test assertions correctly match the implementation behavior. The Windows environment variable handling (SYSTEMROOT, COMSPEC, PATHEXT) is intentional and necessary for subprocess creation. The validator's permissive approach to user-defined functions is documented and tested. Edge cases like subprocess death, tool call failures, timeout during execution, and symlink handling are all properly managed.

✓ Security Reliability

The local CodeAct package provides defense-in-depth controls for executing LLM-generated Python code, with AST validation, subprocess isolation, and explicit environment control. The implementation is generally sound for its stated purpose (use in external sandboxes like Foundry). However, there are three reliability concerns: (1) the AST validator allows 'open' in ALLOWED_BUILTINS while blocking it in BLOCKED_BUILTINS, creating conflicting policy; (2) subprocess environment building on Windows includes parent environment keys that could leak sensitive data; (3) the validator allows delattr/setattr which could modify object internals unsafely. The package correctly disclaims being a security sandbox and documents required external isolation.

✓ Test Coverage

The test suite provides solid coverage of core functionality (subprocess execution, tool calling, validation, file capture, environment isolation). However, several edge cases and error paths lack coverage: (1) invalid input validation for constructors (empty/invalid paths, negative limits), (2) error handling for subprocess failures (invalid Python executable, runner script errors, malformed bridge responses), (3) boundary conditions for limits (exact limit sizes, total capture limits), (4) file mount edge cases (duplicate mounts, overlapping paths, permission errors), (5) race conditions in async tool calls, and (6) error recovery paths in the bridge protocol. The existing tests are well-structured and verify the happy paths thoroughly.

✓ Design Approach

The design approach is sound for the stated goal of adding AST-validated local code execution for Foundry hosted agents. The validation correctly runs before all execution paths, the subprocess bridge properly serializes concurrent tool calls via async locks, symlink handling prevents directory traversal, and the custom allow/block list semantics are clearly documented. All test cases in the diff are consistent with the implementation.


Automated review by eavanvalkenburg's agents

Comment on lines +53 to +104

# Allowed builtin function names that generated code may call.
ALLOWED_BUILTINS: set[str] = {
"print",
"len",
"str",
"int",
"float",
"bool",
"list",
"dict",
"tuple",
"set",
"frozenset",
"range",
"enumerate",
"zip",
"map",
"filter",
"sorted",
"reversed",
"sum",
"min",
"max",
"abs",
"round",
"pow",
"divmod",
"all",
"any",
"chr",
"ord",
"hex",
"oct",
"bin",
"format",
"repr",
"ascii",
"bytes",
"bytearray",
"memoryview",
"isinstance",
"issubclass",
"hasattr",
"getattr",
"setattr",
"callable",
"type",
"id",
"hash",
"next",
"iter",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Policy conflict: 'open' is included in ALLOWED_BUILTINS (line 96) but also in BLOCKED_BUILTINS (line 117). This creates ambiguous validation behavior. The test suite expects 'open' to be blocked (no test validates opening files), and the security posture requires blocking it. Remove 'open' from ALLOWED_BUILTINS.

Evidence from diff line 96 in ALLOWED_BUILTINS: "open",
Evidence from diff line 117 in BLOCKED_BUILTINS: "open",

Suggested change
# Allowed builtin function names that generated code may call.
ALLOWED_BUILTINS: set[str] = {
"print",
"len",
"str",
"int",
"float",
"bool",
"list",
"dict",
"tuple",
"set",
"frozenset",
"range",
"enumerate",
"zip",
"map",
"filter",
"sorted",
"reversed",
"sum",
"min",
"max",
"abs",
"round",
"pow",
"divmod",
"all",
"any",
"chr",
"ord",
"hex",
"oct",
"bin",
"format",
"repr",
"ascii",
"bytes",
"bytearray",
"memoryview",
"isinstance",
"issubclass",
"hasattr",
"getattr",
"setattr",
"callable",
"type",
"id",
"hash",
"next",
"iter",
ALLOWED_BUILTINS: set[str] = {
"print",
"len",
"str",
"int",
"float",
"bool",
"list",
"dict",
"tuple",
"set",
"frozenset",
"range",
"enumerate",
"zip",
"map",
"filter",
"sorted",
"reversed",
"sum",
"min",
"max",
"abs",
"round",
"pow",
"divmod",
"all",
"any",
"chr",
"ord",
"hex",
"oct",
"bin",
"format",
"repr",
"ascii",
"bytes",
"bytearray",
"memoryview",
"isinstance",
"issubclass",
"hasattr",
"getattr",
"setattr",
"callable",
"type",
"id",
"hash",
"next",
"iter",
"slice",
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants