AbstractAgent 0.3.17__tar.gz → 0.3.18__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.
- {abstractagent-0.3.17/src/abstractagent.egg-info → abstractagent-0.3.18}/PKG-INFO +4 -4
- {abstractagent-0.3.17 → abstractagent-0.3.18}/pyproject.toml +4 -4
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/sandbox/local.py +16 -2
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/tools/code_execution.py +42 -5
- {abstractagent-0.3.17 → abstractagent-0.3.18/src/abstractagent.egg-info}/PKG-INFO +4 -4
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent.egg-info/SOURCES.txt +1 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent.egg-info/requires.txt +3 -3
- abstractagent-0.3.18/tests/test_execute_python_sandbox.py +121 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_packaging_profiles.py +3 -3
- {abstractagent-0.3.17 → abstractagent-0.3.18}/LICENSE +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/README.md +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/setup.cfg +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/codeact_runtime.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/emit_inventory.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/generation_params.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/loop_hooks.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/media.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/memact_runtime.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/native_loop_registry.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/progress.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/react_runtime.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/read_orchestration.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/tool_allowlist.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/tool_failure_hints.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/transcripts.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/base.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/codeact.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/defaults.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/memact.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/react.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/task_reset.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/agents/unattended.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/logic/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/logic/builtins.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/logic/codeact.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/logic/memact.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/logic/react.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/logic/types.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/metrics/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/metrics/prefix_reuse.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/repl.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/sandbox/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/sandbox/interface.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/scripts/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/scripts/lmstudio_tool_eval.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/tools/__init__.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/tools/self_improve.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent.egg-info/dependency_links.txt +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent.egg-info/entry_points.txt +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent.egg-info/top_level.txt +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_active_context_policy_adoption.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_adversary_hardening_2026_07_13.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_agent_plan_review_modes.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_agent_speculation_inheritance.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_agent_stream_inheritance.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_agents_integration_llm.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_base_agent_trace_accessors.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_batching_prompt_rule.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_budget_triage_tail.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_builtin_tool_specs_optional_defaults.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_c2447_visit_lane_honesty.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_claim_0029_residue_2026_07_14.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_codeact_logic.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_codeact_runtime_verifier_policy.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_delegate_session_route_composition.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_emit_inventory_drift.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_gated_remainder_wave_2026_07_14.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_generation_params_media_policies.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_history_monster_clamp.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_improvement_wave_2026_07_13.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_llm_call_payload_requires_prompt_or_messages.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_lmstudio_tool_eval_metrics.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_loop_hooks.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_loop_hooks_0026_follow_ups.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_memact_codeact_guidance_durable_transcript.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_memact_session_memory.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_memact_workflow.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_memory_recall_tool.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_native_loop_registry.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_open_attachment_tool_available.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_operator_conclude.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_optimal_wave_batch2_2026_07_13.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_parse_prompt_cache_capture.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_prefix_reuse_metric.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_progress_detection.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_act_only_observation.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_active_attachments_disable_tools_first_iteration.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_adapter_forwards_context_attachments_media.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_delegate_agent_tool.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_election_fence_conformance.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_entity_dress_conformance.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_guidance_durable_transcript.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_history_window.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_logic.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_aborted_generation_retry.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_context_transcript_levels.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_followthrough_retry.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_side_effect_dedupe_comms.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_truncated_output_retry.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_max_iterations_concludes.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_phase_grant_boundary.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_planish_heuristic.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_prompt_prefix_stability.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_sight_lane_media_fold.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_turn_byte_stability.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_verifier_and_plan.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_visit_composition.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_visit_merge.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_workflow_system_prompt_and_tools_override.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_workflow_toolset_refresh.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_read_orchestration_guidance.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_reasoning_first_citizen_agent.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_review_failure_containment.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_run_start_seeds_runtime_provider_model.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_session_memory.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_sibling_loop_defects_batch.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_sibling_strict_transcripts_0011.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_stuck_streak_termination.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_tool_failure_hints.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_tool_loop_prefix_extension_a3.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_tool_observe_emits_result_for_tui.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_tools_editing.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_unseeded_render_limits.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_verifier_execution_preference.py +0 -0
- {abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_verifier_schema_strict.py +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractagent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
4
4
|
Summary: Agent implementations using AbstractRuntime and AbstractCore
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
|
-
Requires-Dist: abstractcore[tools]>=2.
|
|
8
|
+
Requires-Dist: abstractcore[tools]>=2.25.0
|
|
9
9
|
Requires-Dist: abstractruntime>=0.7.0
|
|
10
10
|
Provides-Extra: apple
|
|
11
|
-
Requires-Dist: abstractcore[all-apple]>=2.
|
|
11
|
+
Requires-Dist: abstractcore[all-apple]>=2.25.0; extra == "apple"
|
|
12
12
|
Requires-Dist: abstractruntime[apple]>=0.7.0; extra == "apple"
|
|
13
13
|
Provides-Extra: gpu
|
|
14
|
-
Requires-Dist: abstractcore[all-gpu]>=2.
|
|
14
|
+
Requires-Dist: abstractcore[all-gpu]>=2.25.0; extra == "gpu"
|
|
15
15
|
Requires-Dist: abstractruntime[gpu]>=0.7.0; extra == "gpu"
|
|
16
16
|
Provides-Extra: dev
|
|
17
17
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
@@ -4,22 +4,22 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "abstractagent"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.18"
|
|
8
8
|
description = "Agent implementations using AbstractRuntime and AbstractCore"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"abstractcore[tools]>=2.
|
|
12
|
+
"abstractcore[tools]>=2.25.0",
|
|
13
13
|
"abstractruntime>=0.7.0",
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
[project.optional-dependencies]
|
|
17
17
|
apple = [
|
|
18
|
-
"abstractcore[all-apple]>=2.
|
|
18
|
+
"abstractcore[all-apple]>=2.25.0",
|
|
19
19
|
"abstractruntime[apple]>=0.7.0",
|
|
20
20
|
]
|
|
21
21
|
gpu = [
|
|
22
|
-
"abstractcore[all-gpu]>=2.
|
|
22
|
+
"abstractcore[all-gpu]>=2.25.0",
|
|
23
23
|
"abstractruntime[gpu]>=0.7.0",
|
|
24
24
|
]
|
|
25
25
|
dev = [
|
|
@@ -11,7 +11,7 @@ import os
|
|
|
11
11
|
import subprocess
|
|
12
12
|
import sys
|
|
13
13
|
import time
|
|
14
|
-
from typing import Optional
|
|
14
|
+
from typing import Any, Optional
|
|
15
15
|
|
|
16
16
|
from .interface import ExecutionResult
|
|
17
17
|
|
|
@@ -22,9 +22,14 @@ class LocalSandbox:
|
|
|
22
22
|
*,
|
|
23
23
|
cwd: Optional[str] = None,
|
|
24
24
|
python_executable: Optional[str] = None,
|
|
25
|
+
command_sandbox: Any = None,
|
|
25
26
|
):
|
|
27
|
+
"""`command_sandbox`: an AbstractCore `abstractcore.tools.sandbox.Sandbox` (round 12). When
|
|
28
|
+
given, the interpreter runs inside it (OS sandbox bound to the run's workspaces, the host's
|
|
29
|
+
scrubbed environment, a private TMPDIR); the caller refuses before when it `refuses`."""
|
|
26
30
|
self._cwd = cwd or os.getcwd()
|
|
27
31
|
self._python = python_executable or sys.executable
|
|
32
|
+
self._command_sandbox = command_sandbox
|
|
28
33
|
|
|
29
34
|
def reset(self) -> None:
|
|
30
35
|
# Stateless sandbox (new subprocess per call).
|
|
@@ -47,10 +52,19 @@ class LocalSandbox:
|
|
|
47
52
|
except (TypeError, ValueError):
|
|
48
53
|
t = 0.0
|
|
49
54
|
eff_timeout = t if t > 0 else None
|
|
55
|
+
argv = [self._python, "-c", code]
|
|
56
|
+
env = None
|
|
57
|
+
box = self._command_sandbox
|
|
58
|
+
if box is not None:
|
|
59
|
+
if box.kind == "unsandboxed":
|
|
60
|
+
env = box.env_for()
|
|
61
|
+
else:
|
|
62
|
+
argv, env = box.wrap(argv, self._cwd)
|
|
50
63
|
try:
|
|
51
64
|
completed = subprocess.run(
|
|
52
|
-
|
|
65
|
+
argv,
|
|
53
66
|
cwd=self._cwd,
|
|
67
|
+
env=env,
|
|
54
68
|
capture_output=True,
|
|
55
69
|
text=True,
|
|
56
70
|
timeout=eff_timeout,
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
5
7
|
from abstractcore.tools import tool
|
|
6
8
|
|
|
7
9
|
|
|
@@ -25,16 +27,30 @@ def _bound_output(text: str, *, max_chars: int) -> str:
|
|
|
25
27
|
)
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
# Round 12: the one sentence when the installed AbstractCore has no command sandbox.
|
|
31
|
+
NO_CORE_SANDBOX = (
|
|
32
|
+
"Commands are refused: the installed AbstractCore has no command sandbox "
|
|
33
|
+
"(abstractcore.tools.sandbox, AbstractCore 2.25.0 or newer)."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
28
37
|
@tool(
|
|
29
38
|
name="execute_python",
|
|
30
|
-
description="Execute a Python snippet in a subprocess
|
|
39
|
+
description="Execute a Python snippet in a subprocess, sandboxed to this run's workspaces. Returns stdout/stderr/exit_code in full.",
|
|
31
40
|
when_to_use="When you need to compute, inspect files, or transform data using Python code",
|
|
41
|
+
# Stamped by the host (AbstractRuntime) with the run's workspace set; never model-facing.
|
|
42
|
+
hide_args=["_sandbox"],
|
|
32
43
|
)
|
|
33
|
-
def execute_python(code: str, timeout_s: float = 0.0, max_output_chars: int = 0) -> dict:
|
|
44
|
+
def execute_python(code: str, timeout_s: float = 0.0, max_output_chars: int = 0, _sandbox: Optional[dict] = None) -> dict:
|
|
34
45
|
"""Execute Python code in a local subprocess.
|
|
35
46
|
|
|
36
47
|
Notes:
|
|
37
|
-
-
|
|
48
|
+
- Round 12: when the host stamps `_sandbox` (the run's effective workspace set), the
|
|
49
|
+
interpreter runs inside AbstractCore's OS sandbox (macOS sandbox-exec, Linux bwrap/Landlock),
|
|
50
|
+
starting in the run's private workspace with the host's scrubbed environment. No sandbox
|
|
51
|
+
on the host (or an AbstractCore without one) = refused (`error` carries the sentence,
|
|
52
|
+
nothing runs). A host that configured AbstractCore's command policy refuses an unstamped
|
|
53
|
+
call. Library use without a host policy runs as before.
|
|
38
54
|
- `timeout_s <= 0` (the default) means NO timeout (ADR-0027: no low default
|
|
39
55
|
timeouts on correctness-critical paths — a 10s default silently killed
|
|
40
56
|
legitimate long computations and reported them as errors). Pass a positive
|
|
@@ -48,7 +64,24 @@ def execute_python(code: str, timeout_s: float = 0.0, max_output_chars: int = 0)
|
|
|
48
64
|
|
|
49
65
|
from ..sandbox.local import LocalSandbox
|
|
50
66
|
|
|
51
|
-
|
|
67
|
+
try:
|
|
68
|
+
from abstractcore.tools import sandbox as core_sandbox
|
|
69
|
+
except ImportError:
|
|
70
|
+
return {"stdout": "", "stderr": "", "exit_code": 126, "duration_ms": 0.0, "error": NO_CORE_SANDBOX, "success": False}
|
|
71
|
+
box = core_sandbox.sandbox_for_tool_call(_sandbox)
|
|
72
|
+
if box.refuses:
|
|
73
|
+
return {
|
|
74
|
+
"stdout": "",
|
|
75
|
+
"stderr": "",
|
|
76
|
+
"exit_code": 126,
|
|
77
|
+
"duration_ms": 0.0,
|
|
78
|
+
"error": box.refusal(),
|
|
79
|
+
"success": False,
|
|
80
|
+
"sandbox": box.describe(),
|
|
81
|
+
"sandbox_line": box.rendered_line(),
|
|
82
|
+
}
|
|
83
|
+
bound = box if (box.spec is not None or box.kind != core_sandbox.KIND_UNSANDBOXED) else None
|
|
84
|
+
sandbox = LocalSandbox(cwd=box.spec.private_workspace if box.spec is not None else None, command_sandbox=bound)
|
|
52
85
|
#[WARNING:TIMEOUT] caller-supplied only; <= 0 = unlimited (ADR-0027)
|
|
53
86
|
timeout = float(timeout_s)
|
|
54
87
|
result = sandbox.execute(code, timeout_s=timeout if timeout > 0 else None)
|
|
@@ -56,11 +89,15 @@ def execute_python(code: str, timeout_s: float = 0.0, max_output_chars: int = 0)
|
|
|
56
89
|
cap = int(max_output_chars)
|
|
57
90
|
except (TypeError, ValueError):
|
|
58
91
|
cap = 0
|
|
59
|
-
|
|
92
|
+
out = {
|
|
60
93
|
"stdout": _bound_output(result.stdout, max_chars=cap),
|
|
61
94
|
"stderr": _bound_output(result.stderr, max_chars=cap),
|
|
62
95
|
"exit_code": int(result.exit_code),
|
|
63
96
|
"duration_ms": float(result.duration_ms),
|
|
64
97
|
"error": result.error,
|
|
65
98
|
}
|
|
99
|
+
if bound is not None:
|
|
100
|
+
out["sandbox"] = bound.describe()
|
|
101
|
+
out["sandbox_line"] = bound.rendered_line()
|
|
102
|
+
return out
|
|
66
103
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractagent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
4
4
|
Summary: Agent implementations using AbstractRuntime and AbstractCore
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
|
-
Requires-Dist: abstractcore[tools]>=2.
|
|
8
|
+
Requires-Dist: abstractcore[tools]>=2.25.0
|
|
9
9
|
Requires-Dist: abstractruntime>=0.7.0
|
|
10
10
|
Provides-Extra: apple
|
|
11
|
-
Requires-Dist: abstractcore[all-apple]>=2.
|
|
11
|
+
Requires-Dist: abstractcore[all-apple]>=2.25.0; extra == "apple"
|
|
12
12
|
Requires-Dist: abstractruntime[apple]>=0.7.0; extra == "apple"
|
|
13
13
|
Provides-Extra: gpu
|
|
14
|
-
Requires-Dist: abstractcore[all-gpu]>=2.
|
|
14
|
+
Requires-Dist: abstractcore[all-gpu]>=2.25.0; extra == "gpu"
|
|
15
15
|
Requires-Dist: abstractruntime[gpu]>=0.7.0; extra == "gpu"
|
|
16
16
|
Provides-Extra: dev
|
|
17
17
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
@@ -63,6 +63,7 @@ tests/test_codeact_logic.py
|
|
|
63
63
|
tests/test_codeact_runtime_verifier_policy.py
|
|
64
64
|
tests/test_delegate_session_route_composition.py
|
|
65
65
|
tests/test_emit_inventory_drift.py
|
|
66
|
+
tests/test_execute_python_sandbox.py
|
|
66
67
|
tests/test_gated_remainder_wave_2026_07_14.py
|
|
67
68
|
tests/test_generation_params_media_policies.py
|
|
68
69
|
tests/test_history_monster_clamp.py
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
abstractcore[tools]>=2.
|
|
1
|
+
abstractcore[tools]>=2.25.0
|
|
2
2
|
abstractruntime>=0.7.0
|
|
3
3
|
|
|
4
4
|
[apple]
|
|
5
|
-
abstractcore[all-apple]>=2.
|
|
5
|
+
abstractcore[all-apple]>=2.25.0
|
|
6
6
|
abstractruntime[apple]>=0.7.0
|
|
7
7
|
|
|
8
8
|
[dev]
|
|
9
9
|
pytest>=7.0
|
|
10
10
|
|
|
11
11
|
[gpu]
|
|
12
|
-
abstractcore[all-gpu]>=2.
|
|
12
|
+
abstractcore[all-gpu]>=2.25.0
|
|
13
13
|
abstractruntime[gpu]>=0.7.0
|
|
14
14
|
|
|
15
15
|
[test]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Round 12: execute_python runs inside AbstractCore's OS command sandbox.
|
|
2
|
+
|
|
3
|
+
Real macOS tests (sandbox-exec) with scratch folders and markers; the runtime path
|
|
4
|
+
(AbstractRuntime stamps `_sandbox` on the CodeAct tool call) is exercised end to end."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from abstractcore.tools import sandbox as core_sandbox
|
|
15
|
+
from abstractcore.tools.sandbox import SandboxRow, SandboxSpec
|
|
16
|
+
|
|
17
|
+
from abstractagent.tools.code_execution import NO_CORE_SANDBOX, execute_python
|
|
18
|
+
|
|
19
|
+
MARK = "R12-AGENT-REFUSED-MARKER"
|
|
20
|
+
|
|
21
|
+
macos_only = pytest.mark.skipif(
|
|
22
|
+
sys.platform != "darwin" or not os.access(core_sandbox.SANDBOX_EXEC, os.X_OK),
|
|
23
|
+
reason="real sandbox test: this host is not macOS with /usr/bin/sandbox-exec (Linux backends are tested in abstractcore)",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture(autouse=True)
|
|
28
|
+
def _fresh_host():
|
|
29
|
+
core_sandbox._reset_host_for_tests()
|
|
30
|
+
yield
|
|
31
|
+
core_sandbox._reset_host_for_tests()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def t(tmp_path, monkeypatch):
|
|
36
|
+
root = Path(os.path.realpath(tmp_path))
|
|
37
|
+
for d in ("home", "priv", "refused", "rw"):
|
|
38
|
+
(root / d).mkdir()
|
|
39
|
+
(root / "refused/secret.txt").write_text(MARK)
|
|
40
|
+
monkeypatch.setenv("HOME", str(root / "home"))
|
|
41
|
+
return root
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _stamp(t: Path, refused=True):
|
|
45
|
+
return SandboxSpec(
|
|
46
|
+
private_workspace=str(t / "priv"),
|
|
47
|
+
posture="any_except_denied",
|
|
48
|
+
allowed=(SandboxRow(str(t / "rw"), "rw"),),
|
|
49
|
+
refused=(str(t / "refused"),) if refused else (),
|
|
50
|
+
).to_stamp()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_sandbox_arg_is_hidden_from_the_model():
|
|
54
|
+
assert "_sandbox" not in execute_python._tool_definition.parameters
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@macos_only
|
|
58
|
+
def test_refused_path_is_unreadable_from_python(t):
|
|
59
|
+
code = f"print(open({str(t / 'refused/secret.txt')!r}).read())"
|
|
60
|
+
res = execute_python(code, _sandbox=_stamp(t))
|
|
61
|
+
assert MARK not in res["stdout"] and res["exit_code"] != 0
|
|
62
|
+
assert "Operation not permitted" in res["stderr"]
|
|
63
|
+
assert res["sandbox"]["kind"] == "macos-sandbox-exec" and res["sandbox_line"] == "Sandbox: macOS sandbox-exec"
|
|
64
|
+
ok = execute_python(code, _sandbox=_stamp(t, refused=False))
|
|
65
|
+
assert MARK in ok["stdout"] # positive control
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@macos_only
|
|
69
|
+
def test_starts_in_the_private_workspace_with_a_private_tmpdir(t):
|
|
70
|
+
res = execute_python("import os,tempfile;open('w.txt','w').write('x');print(os.getcwd());print(tempfile.gettempdir())", _sandbox=_stamp(t))
|
|
71
|
+
assert res["stdout"].split() == [str(t / "priv"), str(t / "priv/.tmp")]
|
|
72
|
+
assert (t / "priv/w.txt").exists()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_fail_closed_without_a_sandbox(t, monkeypatch):
|
|
76
|
+
monkeypatch.setattr(core_sandbox, "host_sandbox_kind", lambda posture="allowed_only": core_sandbox.KIND_NONE)
|
|
77
|
+
res = execute_python(f"open({str(t / 'priv/ran')!r},'w')", _sandbox=_stamp(t))
|
|
78
|
+
assert res["success"] is False and res["error"].startswith("Commands are not sandboxed on this gateway host")
|
|
79
|
+
assert not (t / "priv/ran").exists()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_fail_closed_without_core_sandbox_module(t, monkeypatch):
|
|
83
|
+
import abstractcore.tools as core_tools
|
|
84
|
+
|
|
85
|
+
monkeypatch.setitem(sys.modules, "abstractcore.tools.sandbox", None)
|
|
86
|
+
monkeypatch.delattr(core_tools, "sandbox", raising=False)
|
|
87
|
+
res = execute_python(f"open({str(t / 'priv/ran')!r},'w')", _sandbox=_stamp(t))
|
|
88
|
+
assert res["success"] is False and res["error"] == NO_CORE_SANDBOX
|
|
89
|
+
assert not (t / "priv/ran").exists()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_configured_host_refuses_unstamped_call(t):
|
|
93
|
+
core_sandbox.configure_host(env={"PATH": "/usr/bin:/bin"})
|
|
94
|
+
res = execute_python(f"open({str(t / 'priv/ran')!r},'w')")
|
|
95
|
+
assert res["success"] is False and res["error"] == core_sandbox.REFUSED_NO_SPEC
|
|
96
|
+
assert not (t / "priv/ran").exists()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@macos_only
|
|
100
|
+
def test_codeact_tool_call_through_the_runtime_is_sandboxed(t):
|
|
101
|
+
"""A CodeAct-style TOOL_CALLS effect: the runtime stamps the run's workspaces on
|
|
102
|
+
execute_python; reading a refused workspace fails inside the sandbox."""
|
|
103
|
+
from abstractruntime.core.models import Effect, EffectType, RunState
|
|
104
|
+
from abstractruntime.integrations.abstractcore.effect_handlers import make_tool_calls_handler
|
|
105
|
+
from abstractruntime.integrations.abstractcore.tool_executor import MappingToolExecutor
|
|
106
|
+
|
|
107
|
+
handler = make_tool_calls_handler(tools=MappingToolExecutor.from_tools([execute_python]))
|
|
108
|
+
run_vars = {
|
|
109
|
+
"workspace_root": str(t / "priv"),
|
|
110
|
+
"workspace_access_mode": "all_except_ignored",
|
|
111
|
+
"workspace_allowed_paths": [str(t / "rw")],
|
|
112
|
+
"workspace_ignored_paths": str(t / "refused"),
|
|
113
|
+
}
|
|
114
|
+
run = RunState.new(workflow_id="wf", entry_node="n", session_id="s", vars=run_vars)
|
|
115
|
+
code = f"print(open({str(t / 'refused/secret.txt')!r}).read())"
|
|
116
|
+
effect = Effect(type=EffectType.TOOL_CALLS, payload={"tool_calls": [{"call_id": "c1", "name": "execute_python", "arguments": {"code": code, "_sandbox": {"private_workspace": "/", "posture": "any_except_denied"}}}]})
|
|
117
|
+
outcome = handler(run, effect, None)
|
|
118
|
+
res = outcome.result["results"][0]
|
|
119
|
+
out = res["output"]
|
|
120
|
+
assert MARK not in str(out)
|
|
121
|
+
assert out["sandbox"]["kind"] == "macos-sandbox-exec" and out["sandbox"]["refused"] == [str(t / "refused")]
|
|
@@ -18,7 +18,7 @@ def _project() -> dict:
|
|
|
18
18
|
def test_base_dependency_floors_match_gateway_alignment() -> None:
|
|
19
19
|
deps = set(_project()["dependencies"])
|
|
20
20
|
|
|
21
|
-
assert "abstractcore[tools]>=2.
|
|
21
|
+
assert "abstractcore[tools]>=2.25.0" in deps
|
|
22
22
|
assert "abstractruntime>=0.7.0" in deps
|
|
23
23
|
|
|
24
24
|
|
|
@@ -26,11 +26,11 @@ def test_hardware_profile_extras_are_core_runtime_cascades() -> None:
|
|
|
26
26
|
extras = _project()["optional-dependencies"]
|
|
27
27
|
|
|
28
28
|
assert set(extras["apple"]) == {
|
|
29
|
-
"abstractcore[all-apple]>=2.
|
|
29
|
+
"abstractcore[all-apple]>=2.25.0",
|
|
30
30
|
"abstractruntime[apple]>=0.7.0",
|
|
31
31
|
}
|
|
32
32
|
assert set(extras["gpu"]) == {
|
|
33
|
-
"abstractcore[all-gpu]>=2.
|
|
33
|
+
"abstractcore[all-gpu]>=2.25.0",
|
|
34
34
|
"abstractruntime[gpu]>=0.7.0",
|
|
35
35
|
}
|
|
36
36
|
assert "all-apple" not in extras
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/generation_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/native_loop_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/read_orchestration.py
RENAMED
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/adapters/tool_failure_hints.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent/scripts/lmstudio_tool_eval.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/src/abstractagent.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_builtin_tool_specs_optional_defaults.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_delegate_session_route_composition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_generation_params_media_policies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_election_fence_conformance.py
RENAMED
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_guidance_durable_transcript.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_aborted_generation_retry.py
RENAMED
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_context_transcript_levels.py
RENAMED
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_side_effect_dedupe_comms.py
RENAMED
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_react_loop_truncated_output_retry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_run_start_seeds_runtime_provider_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{abstractagent-0.3.17 → abstractagent-0.3.18}/tests/test_tool_observe_emits_result_for_tui.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|