diff --git a/.agents/README.md b/.agents/README.md index 199a2255..564df9b5 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -2,6 +2,13 @@ ## Current public pair — 2026-10-25 +[Exhaustive Sandbox help follow-up](notes/proposed/process/2026-10-25-sandbox-help-namespace.md) +records four remaining class-help namespace errors in public a5 and the bounded +a6 candidate. Its [audit](evidence/verify/2026-10-25-sandbox-help-namespace.md) +distinguishes ordinary help from intentional migration/third-party examples; +the same candidate corrects current bilingual API/lifecycle examples and removes +unimplemented design menus. Candidate publication is not yet established. + [Subsequent public-a4 example acceptance](evidence/verify/2026-10-25-public-a4-examples.md) records 14 actual cloud examples, 29 independent typed-not-found checks and the separate local runner/timeout controls. It preserves earlier browser failures and diff --git a/.agents/evidence/verify/2026-10-25-current-design-examples.json b/.agents/evidence/verify/2026-10-25-current-design-examples.json new file mode 100644 index 00000000..bddc10ac --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-current-design-examples.json @@ -0,0 +1,254 @@ +{ + "scope": "two entire unchanged current-page executable blocks; EN/ZH identical; actual public a5 packages, canonical ABX, new isolated batch HOME, outside checkout, no source injection/model call", + "results": [ + { + "block": "imperative", + "source_sha256": "173cc6ddad063cd124a21ae4a880cfd15332901c0e7d033f0a7a1a5118bde918", + "english_chinese_identical": true, + "public_packages": { + "client": "0.4.0a5", + "server": "0.4.0a5" + }, + "exit": 0, + "seconds": 2.876, + "expected_stdout": "Hello, Agent Sandbox!", + "created": 1, + "typed_not_found_before_observer_cleanup": true, + "observer_recovery_used": false, + "result": "PASS" + }, + { + "block": "errors", + "source_sha256": "8b634238e05cee863332480a7070bb9d36f6e90e8519a713513a666cddb6e0ac", + "english_chinese_identical": true, + "public_packages": { + "client": "0.4.0a5", + "server": "0.4.0a5" + }, + "exit": 0, + "seconds": 7.298, + "expected_stdout": "E3001", + "created": 1, + "typed_not_found_before_observer_cleanup": true, + "observer_recovery_used": false, + "result": "PASS" + } + ], + "created": 2, + "independent_absent": 2, + "result": "PASS", + "current_executable_block_occurrences": 6, + "exact_block_hash_to_documents": { + "173cc6ddad063cd124a21ae4a880cfd15332901c0e7d033f0a7a1a5118bde918": [ + "docs/en/design/sdk-api-design.md", + "docs/zh/design/sdk-api-design.md", + "docs/en/design/sandbox-types.md", + "docs/zh/design/sandbox-types.md" + ], + "8b634238e05cee863332480a7070bb9d36f6e90e8519a713513a666cddb6e0ac": [ + "docs/en/design/sdk-api-design.md", + "docs/zh/design/sdk-api-design.md" + ] + }, + "independent_second_readback": { + "unique_owned": 2, + "independent_typed_not_found": 2, + "recovery_used": false, + "result": "PASS" + }, + "document_import_validation": { + "public_client": "0.4.0a5", + "public_server": "0.4.0a5", + "documents_scanned": 206, + "owned_import_occurrences": 155, + "unique_actual_imports": 34, + "successful": 33, + "exclusions": [ + "docs/en/archive/agents-api-review.md", + "docs/en/archive/cli-followups.md", + "docs/en/archive/fine-grained-capabilities-review.md", + "docs/en/archive/opensandbox-e2e-adversarial.md", + "docs/en/archive/provider-opensandbox-research.md", + "docs/en/archive/providers-and-credentials-review.md", + "docs/en/archive/server-sdk-v2/01-principles.md", + "docs/en/archive/server-sdk-v2/02-operation-model.md", + "docs/en/archive/server-sdk-v2/03-policy-and-permissions.md", + "docs/en/archive/server-sdk-v2/04-builtin-modules.md", + "docs/en/archive/server-sdk-v2/05-agents-api.md", + "docs/en/archive/server-sdk-v2/06-lazy-install.md", + "docs/en/archive/server-sdk-v2/07-client-mirror.md", + "docs/en/archive/server-sdk-v2/08-template-building-ux.md", + "docs/en/archive/server-sdk-v2/09-ai-codegen-usability.md", + "docs/en/archive/server-sdk-v2/README.md", + "docs/en/design/agents-api.md", + "docs/en/design/provider-engine.md", + "docs/zh/archive/agents-api-review.md", + "docs/zh/archive/cli-followups.md", + "docs/zh/archive/fine-grained-capabilities-review.md", + "docs/zh/archive/opensandbox-e2e-adversarial.md", + "docs/zh/archive/provider-opensandbox-research.md", + "docs/zh/archive/providers-and-credentials-review.md", + "docs/zh/archive/server-sdk-v2/01-principles.md", + "docs/zh/archive/server-sdk-v2/02-operation-model.md", + "docs/zh/archive/server-sdk-v2/03-policy-and-permissions.md", + "docs/zh/archive/server-sdk-v2/04-builtin-modules.md", + "docs/zh/archive/server-sdk-v2/05-agents-api.md", + "docs/zh/archive/server-sdk-v2/06-lazy-install.md", + "docs/zh/archive/server-sdk-v2/07-client-mirror.md", + "docs/zh/archive/server-sdk-v2/08-template-building-ux.md", + "docs/zh/archive/server-sdk-v2/09-ai-codegen-usability.md", + "docs/zh/archive/server-sdk-v2/README.md", + "docs/zh/design/agents-api.md", + "docs/zh/design/provider-engine.md" + ], + "developer_dependency_exclusions": [ + { + "statement": "from agent_sandbox.providers.testing import ProviderContractTests", + "result": "EXCLUDED", + "reason": "explicit developer test helper requires pytest" + } + ], + "results": [ + { + "statement": "from agent_sandbox import AsyncSandbox", + "result": "PASS" + }, + { + "statement": "from agent_sandbox import ProviderSandbox", + "result": "PASS" + }, + { + "statement": "from agent_sandbox import ProviderSandbox as Sandbox", + "result": "PASS" + }, + { + "statement": "from agent_sandbox import Sandbox", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.agent.mcp_http import create_mcp_app", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.api import ServerClient, ServerCapabilities", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.api import launch_template", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.api.session_manager import SessionManager", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.code_interpreter import Sandbox", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.declarative import sandbox", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models import FileSizeLimitError", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.errors import CommandTimeoutError", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.errors import EndpointNotAvailableError, FilePolicyDeniedError, PayloadTooLargeError, RateLimitedError, ServerAPIError", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.errors import SandboxError", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.errors import SandboxError, AuthenticationError, InvalidAPIKeyError, TokenExpiredError, InvalidCredentialsError, SandboxCreationError, TemplateNotFoundError, QuotaExceededError, RegionUnavailableError, TemplateParseError, ExecutionError, CommandTimeoutError, ProcessError, CodeExecutionError, CapabilityNotSupportedError, FileOperationError, FileSizeLimitError, FileNotFoundError_, PermissionDeniedError, NetworkError, ConnectionError_, GitHubRateLimitError, SessionError, SessionNotFoundError, SessionAlreadyExistsError, ProviderError, ProviderNotFoundError, ProviderConfigError, ProviderConflictError, ProviderFeatureNotSupportedError, ProviderResourceNotFoundError, ProviderInstallError, ProviderIntegrityError, ProviderTrustError, ProviderVersionError, ProviderManifestError, ProviderProtocolError, ProviderProcessError, ProviderTimeoutError, ProviderMismatchError", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.errors import SandboxError, InvalidAPIKeyError, TemplateNotFoundError, SandboxNotFoundError, CapabilityNotSupportedError, FileNotFoundError_, ConnectionError_", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.sandbox import SandboxStatus", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.models.session import SessionInfo", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.providers import Provider, register_provider", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.providers import commands", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.providers.testing import ProviderContractTests", + "result": "EXCLUDED", + "reason": "explicit developer test helper requires pytest" + }, + { + "statement": "from agent_sandbox.server import CapabilityGroup, ServerPolicy, default_table, start", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import CapabilityGroup, ServerResponse, default_table", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import CapabilityGroup, command, default_table, start", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import CapabilityGroup, default_table", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import SandboxServer", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import SandboxServer, CommandRegistry", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import SandboxServer, ServerPolicy", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import SandboxServer, ServerPolicy, default_table", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import command, start", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server import start", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.server.types import SSEResponse, ServerResponse", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.session.local import LocalSessionStore", + "result": "PASS" + }, + { + "statement": "from agent_sandbox.transport.config import explain_config", + "result": "PASS" + } + ], + "network_business_calls": 0, + "result": "PASS", + "limitations": "Only owned SDK import statements are executed, not arbitrary whole historical/proposal/optional-dependency examples. Partial snippets use individually parsed import lines; source audit is separate." + } +} diff --git a/.agents/evidence/verify/2026-10-25-public-a5-sandbox-help-negative.json b/.agents/evidence/verify/2026-10-25-public-a5-sandbox-help-negative.json new file mode 100644 index 00000000..05530911 --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-public-a5-sandbox-help-negative.json @@ -0,0 +1,40 @@ +{ + "package": "0.4.0a5", + "rows": [ + { + "class": "agent_sandbox._standard.sandbox_sync.main.Sandbox", + "displayed_import": "from e2b import Sandbox", + "actual_old_import_fails": true, + "correct_import": "from agent_sandbox import Sandbox", + "correct_object_identity": true, + "external_comparison_label": false + }, + { + "class": "agent_sandbox._standard.sandbox_async.main.AsyncSandbox", + "displayed_import": "from e2b import AsyncSandbox", + "actual_old_import_fails": true, + "correct_import": "from agent_sandbox import AsyncSandbox", + "correct_object_identity": true, + "external_comparison_label": false + }, + { + "class": "agent_sandbox.code_interpreter.code_interpreter_sync.Sandbox", + "displayed_import": "from e2b_code_interpreter import Sandbox", + "actual_old_import_fails": true, + "correct_import": "from agent_sandbox.code_interpreter import Sandbox", + "correct_object_identity": true, + "external_comparison_label": false + }, + { + "class": "agent_sandbox.code_interpreter.code_interpreter_async.AsyncSandbox", + "displayed_import": "from e2b_code_interpreter import AsyncSandbox", + "actual_old_import_fails": true, + "correct_import": "from agent_sandbox.code_interpreter import AsyncSandbox", + "correct_object_identity": true, + "external_comparison_label": false + } + ], + "e2b_installed": false, + "e2b_code_interpreter_installed": false, + "scope": "actual public class help imports; no cloud calls" +} diff --git a/.agents/evidence/verify/2026-10-25-public-a5-scoped-delivery.json b/.agents/evidence/verify/2026-10-25-public-a5-scoped-delivery.json new file mode 100644 index 00000000..f0fdc536 --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-public-a5-scoped-delivery.json @@ -0,0 +1,236 @@ +{ + "publication": { + "version": "0.4.0a5", + "run_id": 40222172993, + "run_attempt": 1, + "publisher": "SUCCESS", + "source_commit": "c7b4a55ff930753eb35866b161184f946b04d46c", + "artifact_id": 12610066901, + "files": [ + { + "package": "agent-sandbox-client", + "filename": "agent_sandbox_client-0.4.0a5-py3-none-any.whl", + "sha256": "5792e54bf5bd93f9ed04aa810979414276039810bea03f284f621f6fb86bb970", + "candidate_metadata_download_equal": true, + "metadata_http": 200, + "download_http": 200 + }, + { + "package": "agent-sandbox-client", + "filename": "agent_sandbox_client-0.4.0a5.tar.gz", + "sha256": "7df303c2f6abfc786b7d2b5b33bf4f4ce1663150615ca2ac979737a2fcfb6e5b", + "candidate_metadata_download_equal": true, + "metadata_http": 200, + "download_http": 200 + }, + { + "package": "agent-sandbox-server", + "filename": "agent_sandbox_server-0.4.0a5-py3-none-any.whl", + "sha256": "9ec4fcf26abb4741c969eca825f290f0efc0ac687429568b9518530b0f53c6be", + "candidate_metadata_download_equal": true, + "metadata_http": 200, + "download_http": 200 + }, + { + "package": "agent-sandbox-server", + "filename": "agent_sandbox_server-0.4.0a5.tar.gz", + "sha256": "552c005fe11643f99bcf364683bfdb28d76e57bf13117a1e82277cf1b3cbed6e", + "candidate_metadata_download_equal": true, + "metadata_http": 200, + "download_http": 200 + } + ], + "result": "PASS" + }, + "installation": { + "packages": { + "agent-sandbox-client": "0.4.0a5", + "agent-sandbox-server": "0.4.0a5" + }, + "python": "3.13.13", + "standard_sync_async_provider_server_imports": true, + "installed_package_location": "site-packages", + "reviewed_template_source_bytes_match": true, + "old_provider_kwargs_bind_rejections": 24, + "source_injection": false, + "install_extra": "none", + "result": "PASS" + }, + "scoped_help": { + "packages": { + "agent-sandbox-client": "0.4.0a5", + "agent-sandbox-server": "0.4.0a5" + }, + "e2b_installed": false, + "template_help": [ + { + "entry": "Template.build", + "imports_resolve": true + }, + { + "entry": "Template.build_in_background", + "imports_resolve": true + }, + { + "entry": "Template.get_build_status", + "imports_resolve": true + }, + { + "entry": "Template.exists", + "imports_resolve": true + }, + { + "entry": "Template.alias_exists", + "imports_resolve": true + }, + { + "entry": "Template.assign_tags", + "imports_resolve": true + }, + { + "entry": "Template.remove_tags", + "imports_resolve": true + }, + { + "entry": "Template.get_tags", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.build", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.build_in_background", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.get_build_status", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.exists", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.alias_exists", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.assign_tags", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.remove_tags", + "imports_resolve": true + }, + { + "entry": "AsyncTemplate.get_tags", + "imports_resolve": true + }, + { + "entry": "TemplateBuilder.set_start_cmd", + "imports_resolve": true + }, + { + "entry": "TemplateBuilder.set_ready_cmd", + "imports_resolve": true + }, + { + "entry": "default_build_logger", + "imports_resolve": true + }, + { + "entry": "wait_for_port", + "imports_resolve": true + }, + { + "entry": "wait_for_url", + "imports_resolve": true + }, + { + "entry": "wait_for_process", + "imports_resolve": true + }, + { + "entry": "wait_for_file", + "imports_resolve": true + }, + { + "entry": "wait_for_timeout", + "imports_resolve": true + } + ], + "provider_help": [ + { + "method": "create", + "documented_names_valid": true + }, + { + "method": "create_sync", + "documented_names_valid": true + }, + { + "method": "connect", + "documented_names_valid": true + }, + { + "method": "connect_sync", + "documented_names_valid": true + }, + { + "method": "list", + "documented_names_valid": true + }, + { + "method": "list_sync", + "documented_names_valid": true + }, + { + "method": "kill_by_id", + "documented_names_valid": true + }, + { + "method": "kill_by_id_sync", + "documented_names_valid": true + }, + { + "method": "get_info_by_id", + "documented_names_valid": true + }, + { + "method": "get_info_by_id_sync", + "documented_names_valid": true + }, + { + "method": "set_timeout_by_id", + "documented_names_valid": true + }, + { + "method": "set_timeout_by_id_sync", + "documented_names_valid": true + } + ], + "network_operations": 0, + "source_injection": false, + "script_sha256": "7d8e83d923e5c4f627e0fe66a272e4e8dcf8dfcf81fdf9c4dda22634d5e40216", + "result": "PASS" + }, + "http": { + "packages": { + "agent-sandbox-client": "0.4.0a5", + "agent-sandbox-server": "0.4.0a5" + }, + "source_install": false, + "scope": "official public non-all PyPI a5 install; real local HTTP, no cloud/model call", + "health": true, + "shell_exit": 0, + "shell_stdout": "4\n", + "file_roundtrip": true, + "owned_process_exit": -15, + "owned_pid_independently_absent": true, + "script_sha256": "ebedcbbaa6e75e58f351b4a0c3c49316893d1064ffb86394f26386ecf808963d", + "result": "PASS", + "candidate_harness_reused": true + }, + "known_limit": "24 Template help + 12 Provider help passed; four Sandbox class help errors confirmed later, corrected by a6 candidate" +} diff --git a/.agents/evidence/verify/2026-10-25-sandbox-help-candidate-http.json b/.agents/evidence/verify/2026-10-25-sandbox-help-candidate-http.json new file mode 100644 index 00000000..3873369d --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-sandbox-help-candidate-http.json @@ -0,0 +1,16 @@ +{ + "packages": { + "agent-sandbox-client": "0.4.0a6", + "agent-sandbox-server": "0.4.0a6" + }, + "source_install": false, + "scope": "candidate wheels, real local HTTP; no cloud or model call", + "health": true, + "shell_exit": 0, + "shell_stdout": "4\n", + "file_roundtrip": true, + "owned_process_exit": -15, + "owned_pid_independently_absent": true, + "script_sha256": "0e8d281c713235803c76f4f8edcbff2263db7b9088356a793458d293bb2175a8", + "result": "PASS" +} diff --git a/.agents/evidence/verify/2026-10-25-sandbox-help-namespace.json b/.agents/evidence/verify/2026-10-25-sandbox-help-namespace.json new file mode 100644 index 00000000..4e5d5262 --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-sandbox-help-namespace.json @@ -0,0 +1,112 @@ +{ + "base_commit": "c7b4a55ff930753eb35866b161184f946b04d46c", + "candidate_version": "0.4.0a6", + "python_files_audited": 390, + "docstrings_audited": 2358, + "help_upstream_import_residue_before": 4, + "help_upstream_import_residue_after": [], + "upstream_runtime_imports": [], + "changed_source": [ + { + "file": "client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_async.py", + "executable_ast_unchanged": true, + "candidate_sha256": "a41afd248db766f262c77c7b255f861ca034026d20b729e89842c7d73149350e" + }, + { + "file": "client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_sync.py", + "executable_ast_unchanged": true, + "candidate_sha256": "54ecf57b1d8ffa03c75ea01a786834995f57016b1b32e7c87a6504c573dca1c6" + }, + { + "file": "client-sdk/src/agent_sandbox/_standard/sandbox_async/main.py", + "executable_ast_unchanged": true, + "candidate_sha256": "b92268eb1377a5f91578e3bed2f7b5a5a5be7098116948336a575d667d15ce52" + }, + { + "file": "client-sdk/src/agent_sandbox/_standard/sandbox_sync/main.py", + "executable_ast_unchanged": true, + "candidate_sha256": "bf11221b71387d408f93575e3dfb94e2579d1a19d0bab163fc00c5884ce1b377" + } + ], + "notices_preserved": [ + "client-sdk/NOTICE", + "client-sdk/src/agent_sandbox/_standard/LICENSE", + "client-sdk/src/agent_sandbox/_standard/SOURCES.json", + "client-sdk/src/agent_sandbox/code_interpreter/LICENSE" + ], + "maintained_markdown_files_audited": 174, + "upstream_markdown_import_contexts": [ + { + "file": "docs/en/guide/command-map.md", + "line": 14, + "classification": "explicit third-party product comparison", + "action": "preserve" + }, + { + "file": "docs/en/guide/migrate-from-alicloud-express.md", + "line": 18, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/en/guide/migrate-from-e2b.md", + "line": 12, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/en/guide/migrate-from-e2b.md", + "line": 13, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/en/guide/migrate-from-e2b.md", + "line": 14, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/zh/guide/command-map.md", + "line": 14, + "classification": "explicit third-party product comparison", + "action": "preserve" + }, + { + "file": "docs/zh/guide/migrate-from-alicloud-express.md", + "line": 17, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/zh/guide/migrate-from-e2b.md", + "line": 12, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/zh/guide/migrate-from-e2b.md", + "line": 13, + "classification": "explicit previous-import migration table", + "action": "preserve" + }, + { + "file": "docs/zh/guide/migrate-from-e2b.md", + "line": 14, + "classification": "explicit previous-import migration table", + "action": "preserve" + } + ], + "skills_audited": [ + "agent-sandbox-provider/SKILL.md", + "agent-sandbox-sdk/SKILL.md" + ], + "regression_before": { + "failed": 5, + "passed": 24 + }, + "regression_after": { + "focused_passed": 54 + }, + "scope": "source candidate only; not public a6" +} diff --git a/.agents/evidence/verify/2026-10-25-sandbox-help-namespace.md b/.agents/evidence/verify/2026-10-25-sandbox-help-namespace.md new file mode 100644 index 00000000..5013fc1e --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-sandbox-help-namespace.md @@ -0,0 +1,123 @@ +# Verification: exhaustive Sandbox help namespace follow-up + +- Date: 2026-10-25 +- Result: PARTIAL — source candidate only; local server failures are retained, and exact-head CI, final review, merge and publication remain pending. +- Issue: https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/130 +- Base: immutable public a5 source `c7b4a55ff930753eb35866b161184f946b04d46c`. +- Environment: Linux, Python 3.11 development; independent public-a5 negative reproduction used official non-all packages on Python 3.13.13. + +## Actual defect and bounded scope + +The [public-a5 observation](2026-10-25-public-a5-sandbox-help-negative.json) records +four class-help imports actually failing with ModuleNotFoundError. The correct +existing root and code_interpreter exports resolve to the exact inspected classes. +These are ordinary first-party Examples; no migration/comparison label excuses +the imports. Standard API names and upstream attribution are preserved. + +The candidate changes four docstrings and three paired version/pin declarations +to a6. All 390 shipped Python files were inspected, covering 2358 module/class/ +function docstrings: four import residues before and zero afterward, no upstream +runtime imports. Removing docstrings leaves identical execution AST in all four +changed modules. NOTICE, both MIT licenses and original SOURCES remain identical. +The [complete audit](2026-10-25-sandbox-help-namespace.json) includes actual file +hashes and all retained Markdown contexts. + +All 174 maintained Markdown files, including both Skills, were checked. The ten +remaining upstream-import statements are explicitly previous-import columns in +EN/ZH migration tables (eight) or labeled third-party product comparison rows +(two). They remain correct and unchanged. The older engineering note's quoted +bug description is evidence, not a live tutorial. Historical merged receipts, +design/decision history and upstream provenance are not mechanically rewritten. + +## Commands and results + +```bash +python -m pytest client-sdk/tests/test_api/test_template_help.py client-sdk/tests/test_api/test_standard_contract.py client-sdk/tests/test_api/test_provider_docstrings.py -q --no-cov +make lint format-check typecheck +make build dist-check +bash scripts/check-repo-health.sh +make hygiene +``` + +Before correction: four new class-import failures plus exhaustive source audit +failure, with all previous 24 Template cases passing. After correction: 54 focused +PASS, including all 28 real help imports, exhaustive source audit, existing +standard contract checks and 12 Provider docstrings. No cloud build is invoked +by these tests. Both-package ruff/format/mypy passed. Full package outcomes and paired builds +are recorded below, including the actual local server failures. + +Full client: 2318 PASS, 26 skipped (127.41s). Both wheel/sdist pairs built; twine, +both py.typed markers and fresh combined installation passed. Installed candidate +wheels passed all 28 real help imports, all 390 source files / 2358 docstrings with +zero upstream import residue, 12 Provider docstrings and pip check. Real local +HTTP health, shell/file roundtrip and independent owned-process absence passed: +[candidate HTTP](2026-10-25-sandbox-help-candidate-http.json). The exhaustive unit +guard asserts both source roots exist and each scan is nonempty, without a fixed +file-count assumption. Health: 106 PASS, one existing runbook WARN, zero FAIL; +hygiene passed. + +The local server full suite returned nonzero: 878 PASS / two FAIL (314.43s). +`test_upgrade_without_a_key_is_a_400_not_a_hang` returned 501 rather than 400; +`test_transfer_encoding_rejected_in_policy_mode` returned 502 rather than 400. +These same low-level assertions also failed in earlier a4/a5 local runs. They +remain failures, with no test or gate weakened; the required exact-head remote +matrix must independently pass. No unverified root cause is asserted. + +## Preserved publication and real-component scopes + +Public a5 publisher run [40222172993](https://github.com/Alibaba-Agent-Sandbox/python-sdk/actions/runs/40222172993) +succeeded on attempt1. Original artifact12610066901 contained four files matching +official metadata and actual download bytes. Fresh official non-all a5 installed +matching server5; pip check, standard/Provider/server imports, 24 Template help +imports, 12 Provider docstrings, reviewed source bytes and real local HTTP/files/ +owned-process absence passed. This established that exact scope, not all public +help: the later four-class negative observation is preserved separately above. +[Preserved scoped a5 delivery](2026-10-25-public-a5-scoped-delivery.json) contains +its actual four hashes, package installation, help subset and HTTP receipt. + +The [public-a4 examples](2026-10-25-public-a4-examples.md) remain 14 actual cloud +examples with 29 typed-not-found checks and the separate 10-check local runner. +They are not a5/a6 cloud evidence. Original browser failure remains unchanged. + +## Cleanup and outstanding gates + +Help regressions create no cloud resources. Candidate installed-wheel HTTP and +owned-process checks passed as recorded above. Required current-head CI, +independent review, normal merge, immutable a6 tag and official publisher/readback +remain pending. Maintained installation guidance still selects public a4 until +a6 publication is verified; no unpublished package recommendation is introduced. + +## Current bilingual API/lifecycle documents + +A later independent public-a5 audit found a nonexistent TimeoutError import and +an unimplemented Config policy. The current sdk-api-design page also mixed +ProviderSandbox with interpreter operations, retired Provider keywords and spec4 +capability claims. sandbox-types presented future APIs and unsupported general +pricing/startup/retention promises. Correct both EN/ZH current pages in this +candidate; do not add these speculative APIs or rewrite historical evidence. + +The four pages now contain six executable Python blocks corresponding to two +byte-identical programs. Both were extracted unchanged and run using official +non-all public a5, canonical ABX configuration, one new isolated batch HOME +outside checkout and no source injection or model call: + +- Command/file/context cleanup: PASS, 2.876s, expected greeting and file readback. +- base + commands.run("sleep 100", timeout=5): actual typed CommandTimeoutError + E3001 with finally deletion and transport closure: PASS, 7.298s. +- Both resources were already absent when observed; no recovery deletion was + counted passing. A second independent process returned typed SandboxNotFoundError + for both exact owned identifiers (2/2). + +[Execution/import receipt](2026-10-25-current-design-examples.json) records hashes, +all six block locations, outcomes and cleanup. A further scan enumerated 206 +Markdown/RST files, explicitly excluding archives and labeled historical/ +unimplemented design pages. It found 155 current owned-SDK import occurrences / +34 unique statements: 33 executed successfully in public a5; the developer +ProviderContractTests import requires pytest and is explicitly excluded. Partial +snippets use individually parsed import-line fallback. This does not claim all +optional-dependency examples or proposals ran. Migration/third-party tables stay +unchanged. The original independent negative findings are preserved. + +Earlier head01a1 CI is history only. The new docs delta needs fresh exact-head +checks, required CI and independent review before merge. Executable-AST equality +remains limited to the four Python modules; Markdown behavior is verified here. diff --git a/.agents/notes/proposed/process/2026-10-25-sandbox-help-namespace.md b/.agents/notes/proposed/process/2026-10-25-sandbox-help-namespace.md new file mode 100644 index 00000000..426045e3 --- /dev/null +++ b/.agents/notes/proposed/process/2026-10-25-sandbox-help-namespace.md @@ -0,0 +1,68 @@ +# Decision: finish owned namespaces in public Sandbox help + +Status: proposed + +## Problem + +Public a5 fixed 24 Template help imports but four ordinary Sandbox class examples +still import unavailable e2b/e2b_code_interpreter packages. The interpreter classes +have a distinct public entry and must not be replaced by root Sandbox classes. + +Current bilingual API/lifecycle pages also mix unsupported Provider interpreter +calls, retired configuration names and an unimplemented global cleanup policy. +Their examples must describe actual APIs rather than a speculative type menu. + +## Decision + +Correct only the four class-help imports and prepare minimum lockstep a6. +Keep immutable a5 and all its scoped results. Correct the two bilingual current +API/lifecycle pages and delete unimplemented persistent/hibernated/global-cleanup +examples and unsupported pricing/startup claims. Do not implement those proposals. +Keep interpreter/application prerequisites separate. Audit every shipped Python source +docstring and every maintained Markdown/Skill import context, preserving explicit +migration inputs, third-party comparisons, valid standard parameters and notices. +Retain public-a4 installation recommendations until a6 is actually public, then +update maintained instructions once. No compatibility alias or new dependency. + +## API Design + +Root standard classes import from agent_sandbox; interpreter classes import from +agent_sandbox.code_interpreter. Callable behavior, signatures and exports are +unchanged; only help and three paired version/dependency declarations change. + +## Alternatives considered + +Adding upstream packages would conceal incorrect first-party help. Broadly +renaming E2B references would damage attribution and legitimate contracts. +Changing immutable a5 or recommending unpublished a6 is not acceptable. + +## Dependencies + +- https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/130 +- https://github.com/Alibaba-Agent-Sandbox/python-sdk/pull/129 +- https://github.com/Alibaba-Agent-Sandbox/python-sdk/releases/tag/v0.4.0a5 + +## Test Strategy + +Execute four class-help imports and assert exact identity in the correct namespace. +Retain 24 Template imports and 12 Provider help checks. Audit all 390 source files, +2358 docstrings and 174 maintained Markdown files including both Skills; prove +four source residues become zero without upstream runtime imports or executable +AST changes excluding docs. Run owner checks and required exact-head CI. After +normal merge/tag/publication compare four public artifact hashes, fresh-install +non-all a6 and verify all 28 help imports, Provider help, real HTTP/files and +independent owned-process absence. + +Run all six Python block occurrences in the four current design documents as +two byte-identical unique programs using official public packages. Require actual +typed E3001 and independent owned-resource absence. Execute current documented +owned-SDK imports with explicit history/proposal/development exclusions. + +## Acceptance criteria + +All shipped first-party help imports resolve without upstream SDK dependencies. +Legitimate migration/comparison examples and notices are preserved. Normal CI, +independent review and merge precede the existing paired publisher and official +readback. No new runtime feature, API, alias, dependency or release framework. + +[Verification](../../../evidence/verify/2026-10-25-sandbox-help-namespace.md). diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a4476c..1fe3e0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Fixed +- Correct current bilingual API/lifecycle examples to use actual base commands/files, canonical Provider parameters and typed timeout cleanup; remove unimplemented global cleanup and speculative persistence/billing guarantees. / 修正当前双语 API/生命周期示例,使用真实 base 命令/文件、规范 Provider 参数与类型化超时清理;删除未实现全局策略及推测的持久化/计费承诺。 + +- Correct four remaining public Sandbox class-help imports, preserving the distinct standard and interpreter entries. Audit all shipped Python docstrings and maintained Markdown/Skills; retain explicit third-party and migration examples. Prepare lockstep client/server 0.4.0a6 without runtime/API changes. / 修正四处剩余沙箱类帮助导入,区分标准入口与解释器入口;全量审查源码文档及维护中的 Markdown/技能,保留明确的第三方与迁移示例;准备同版本双包 0.4.0a6,运行行为与 API 不变。 + +### Fixed + - Correct 24 maintained Template help imports to use existing public `agent_sandbox` exports, without changing standard API parameters, runtime behavior or upstream attribution. Prepare lockstep client/server 0.4.0a5; preserve subsequent public-a4 cloud/local example acceptance separately. / 修正 24 处模板帮助示例导入,使用现有 `agent_sandbox` 公开名称,不改标准参数、运行行为或上游归属;准备同版本双包 0.4.0a5,另存公开 a4 的后续云端和本地示例证据。 ### Fixed diff --git a/client-sdk/pyproject.toml b/client-sdk/pyproject.toml index 906a1a50..935fdcbf 100644 --- a/client-sdk/pyproject.toml +++ b/client-sdk/pyproject.toml @@ -44,7 +44,7 @@ dependencies = [ "tomli>=2.0; python_version < '3.11'", # The client uses the matching local server runtime for its server/declarative helpers. # Keep the required distribution version equal to ``_version.py``. - "agent-sandbox-server==0.4.0a5", + "agent-sandbox-server==0.4.0a6", ] [project.optional-dependencies] diff --git a/client-sdk/src/agent_sandbox/_standard/sandbox_async/main.py b/client-sdk/src/agent_sandbox/_standard/sandbox_async/main.py index 904fd157..cab82b97 100644 --- a/client-sdk/src/agent_sandbox/_standard/sandbox_async/main.py +++ b/client-sdk/src/agent_sandbox/_standard/sandbox_async/main.py @@ -60,7 +60,7 @@ class AsyncSandbox(SandboxApi): Example: ```python - from e2b import AsyncSandbox + from agent_sandbox import AsyncSandbox sandbox = await AsyncSandbox.create() ``` diff --git a/client-sdk/src/agent_sandbox/_standard/sandbox_sync/main.py b/client-sdk/src/agent_sandbox/_standard/sandbox_sync/main.py index 17c1b99a..a36df85b 100644 --- a/client-sdk/src/agent_sandbox/_standard/sandbox_sync/main.py +++ b/client-sdk/src/agent_sandbox/_standard/sandbox_sync/main.py @@ -58,7 +58,7 @@ class Sandbox(SandboxApi): Example: ```python - from e2b import Sandbox + from agent_sandbox import Sandbox sandbox = Sandbox.create() ``` diff --git a/client-sdk/src/agent_sandbox/_version.py b/client-sdk/src/agent_sandbox/_version.py index b147458b..48b9a7eb 100644 --- a/client-sdk/src/agent_sandbox/_version.py +++ b/client-sdk/src/agent_sandbox/_version.py @@ -1 +1 @@ -__version__ = "0.4.0a5" +__version__ = "0.4.0a6" diff --git a/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_async.py b/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_async.py index 5a846e0c..790d6957 100644 --- a/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_async.py +++ b/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_async.py @@ -52,7 +52,7 @@ class AsyncSandbox(BaseAsyncSandbox): Example: ```python - from e2b_code_interpreter import AsyncSandbox + from agent_sandbox.code_interpreter import AsyncSandbox sandbox = await AsyncSandbox.create() ``` """ diff --git a/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_sync.py b/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_sync.py index 6e43f90d..33d131b9 100644 --- a/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_sync.py +++ b/client-sdk/src/agent_sandbox/code_interpreter/code_interpreter_sync.py @@ -48,7 +48,7 @@ class Sandbox(BaseSandbox): Example: ```python - from e2b_code_interpreter import Sandbox + from agent_sandbox.code_interpreter import Sandbox sandbox = Sandbox.create() ``` diff --git a/client-sdk/tests/test_api/test_template_help.py b/client-sdk/tests/test_api/test_template_help.py index 9d6547d8..3b4388a5 100644 --- a/client-sdk/tests/test_api/test_template_help.py +++ b/client-sdk/tests/test_api/test_template_help.py @@ -2,10 +2,13 @@ import ast import inspect +import re +from pathlib import Path import pytest import agent_sandbox +import agent_sandbox.code_interpreter as code_interpreter TEMPLATE_METHODS = ( "build", @@ -52,3 +55,48 @@ def test_template_help_imports_use_public_exports(entry: object) -> None: assert namespace[imported.asname or imported.name] is getattr( agent_sandbox, imported.name ) + + +@pytest.mark.parametrize( + ("entry", "module"), + [ + (agent_sandbox.Sandbox, "agent_sandbox"), + (agent_sandbox.AsyncSandbox, "agent_sandbox"), + (code_interpreter.Sandbox, "agent_sandbox.code_interpreter"), + (code_interpreter.AsyncSandbox, "agent_sandbox.code_interpreter"), + ], +) +def test_sandbox_class_help_imports_resolve_to_the_correct_entry( + entry: object, module: str +) -> None: + statements = [ + line.strip() + for line in (inspect.getdoc(entry) or "").splitlines() + if line.strip().startswith("from ") + ] + assert statements + for statement in statements: + node = ast.parse(statement).body[0] + assert isinstance(node, ast.ImportFrom) + assert node.module == module + namespace: dict[str, object] = {} + exec(statement, namespace) + assert namespace[node.names[0].name] is entry + + +def test_all_shipped_python_help_uses_the_owned_namespace() -> None: + root = Path(__file__).resolve().parents[3] + upstream_import = re.compile(r"\b(?:from|import)\s+e2b(?:_code_interpreter)?(?=[.\s]|$)") + for package in ("client-sdk", "server-sdk"): + source_root = root / package / "src" + assert source_root.is_dir() + sources = list(source_root.rglob("*.py")) + assert sources + for source in sources: + tree = ast.parse(source.read_text()) + for node in ast.walk(tree): + if isinstance( + node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef) + ): + doc = ast.get_docstring(node) or "" + assert not upstream_import.search(doc), source.relative_to(root) diff --git a/docs/en/DESIGN.md b/docs/en/DESIGN.md index bd894771..3f5452ac 100644 --- a/docs/en/DESIGN.md +++ b/docs/en/DESIGN.md @@ -77,7 +77,7 @@ Each topic below has its own dedicated design document with complete specificati |-------|----------|---------| | **System Architecture** | [architecture.md](design/architecture.md) | Standard client source port, native provider extension layers, and their separate protocol paths | | **SDK API Design** | [sdk-api-design.md](design/sdk-api-design.md) | Sandbox class, file operations, code execution, commands, network, session management | -| **Sandbox Type System** | [sandbox-types.md](design/sandbox-types.md) | Ephemeral sandboxes (implemented), persistent sandboxes (future), sandbox lifecycle | +| **Sandbox Lifecycle and Ownership** | [sandbox-types.md](design/sandbox-types.md) | Owned cleanup, borrowed connections and provider-dependent operations | | **Providers and Credentials** | [providers-and-credentials.md](design/providers-and-credentials.md) | One environment variable per configuration key, precedence, provider plugins (`abx provider ...`), install/trust model, EPP/1 protocol, E8xxx errors; review log in [providers-and-credentials-review.md](archive/providers-and-credentials-review.md) | ### Templates diff --git a/docs/en/README.md b/docs/en/README.md index 093a5732..e30813a1 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -91,7 +91,7 @@ Start here only when you are changing the product. Day-to-day use is in the guid |---|----------|-------------| | 1 | [Architecture Design](design/architecture.md) | Layered architecture overview | | 2 | [SDK API Design](design/sdk-api-design.md) | SDK public API specification | -| 3 | [Sandbox Types](design/sandbox-types.md) | Sandbox type taxonomy | +| 3 | [Sandbox Lifecycle and Ownership](design/sandbox-types.md) | Owned cleanup, borrowed connections and provider-dependent operations | | 4 | [MCP Server](design/mcp-server.md) | MCP tool server design (Python server, HTTP app, FC artifact) | | 5 | [Template System](design/template-system.md) | Template resolution, versioning and registry | | 6 | [Server API](design/server-api.md) | In-sandbox HTTP server endpoints | diff --git a/docs/en/design/sandbox-types.md b/docs/en/design/sandbox-types.md index e3f6822a..71b83a50 100644 --- a/docs/en/design/sandbox-types.md +++ b/docs/en/design/sandbox-types.md @@ -1,321 +1,65 @@ -# Sandbox Type System Design +# Sandbox Lifecycle and Ownership -> Agent Sandbox provides three sandbox modes, covering all scenarios from one-off scripts to long-running services. Currently, **only the Ephemeral sandbox is fully supported**; Persistent and Hibernated sandboxes are future plans. +This page describes current ProviderSandbox lifecycle behavior. It does not define +additional persistent/hibernated SDK types or a global cleanup service. Standard +`Sandbox` / `AsyncSandbox` retain their separate +[standard contract](../guide/migrate-from-e2b.md). ---- +## Create an owned sandbox -## 1. Three Sandbox Modes - -### Ephemeral Sandbox - -**Positioning**: Disposable, one-time execution environments — the most commonly used mode. **Currently the only fully supported sandbox type.** - -```python -# Default is ephemeral sandbox -sb = await Sandbox.create(template="code-interpreter") -result = await sb.run_code("print('hello')") -await sb.kill() # All data is lost after destruction - -# Context Manager for automatic destruction -async with await Sandbox.create(template="code-interpreter") as sb: - result = await sb.run_code("print('hello')") - # Destroyed on exit -``` - -### Persistent Sandbox — 🔮 Future Plan - -> **⚠️ Requires underlying capability support; currently a future plan.** Persistent sandboxes depend on the underlying platform's persistent storage capabilities and will be implemented once Alibaba Cloud FC supports them. - -**Positioning**: Long-running development/service environments with state preserved across sessions. - -```python -# Future API — pending underlying support -from agent_sandbox import ProviderSandbox as Sandbox - -sb = await Sandbox.create( - template="python-base", - persistent=True, - name="my-dev-env", -) - -# Install dependencies (state will be preserved) -await sb.commands.run("pip install flask sqlalchemy") - -# Reconnect later -sb = await Sandbox.connect("my-dev-env") -result = await sb.commands.run("pip list") # flask, sqlalchemy still present -``` - -### Hibernated Sandbox — 🔮 Future Plan - -> **⚠️ Requires underlying capability support; currently a future plan.** Hibernated sandboxes depend on the underlying platform's Snapshot / CRIU capabilities and will be implemented once support is available. - -**Positioning**: State is frozen and suspended; wakes up restored to the frozen moment, saving billing costs. - -```python -# Future API — pending underlying support -from agent_sandbox import ProviderSandbox as Sandbox - -sb = await Sandbox.create( - template="python-data-science", - hibernate_after=300, - on_exit="hibernate", -) - -# Use the sandbox... -await sb.run_code("import pandas as pd; df = pd.read_csv('data.csv')") - -await sb.hibernate() # Stop billing -sb = await Sandbox.connect("sb-xxx") -await sb.wake_up() # Restore to state at hibernation -``` - ---- - -## 2. Comparison Matrix - -| Feature | Ephemeral | Persistent 🔮 | Hibernated 🔮 | -|---------|-----------|---------------|---------------| -| **Implementation Status** | **Currently Available** | Future Plan | Future Plan | -| **Lifecycle** | Destroyed when task ends | Runs continuously until manually destroyed | Can be woken at any time after freezing | -| **State Persistence** | None | Full persistence | Snapshot-based freezing | -| **Filesystem** | tmpfs (RAM disk) | Persistent storage | Snapshot on freeze | -| **Processes** | Stop when task completes | Run in background continuously | Freeze/restore | -| **Network** | Temporary port mapping | Fixed domain/ports | Restored on wake | -| **Startup Time** | Cold start ~2s | Already running ~0s | Wake ~3-5s | -| **Billing** | Pay per usage duration | Continuous billing | Low/free during hibernation | -| **Max Duration** | Default 5 minutes | Unlimited | Hibernation can last 30 days | -| **Typical Scenarios** | Code execution, data analysis | Dev environments, web services | Intermittently used projects | -| **Auto Cleanup** | Auto-destroyed on timeout | Manual management | Auto-cleaned after retention period | - ---- - -## 3. Lifecycle State Machine - -```mermaid -stateDiagram-v2 - [*] --> Creating : create() - Creating --> Running : Ready - Running --> Dead : kill() - Running --> Paused : pause() Requires allowlist - Paused --> Running : resume() -``` - -> **Note**: The `pause()` / `resume()` state transitions require allowlist permissions and are currently restricted features. Snapshot-related state transitions are future plans and are not reflected in the current state machine. - -State transition rules: - -| Transition | Condition | Status | -|-----------|-----------|--------| -| Creating → Running | Normal startup | Currently supported | -| Creating → Dead | Creation failed | Currently supported | -| Running → Dead | `kill()` or timeout | Currently supported | -| Running → Paused | `pause()`, requires allowlist | Restricted feature | -| Paused → Running | `resume()` | Restricted feature | -| Paused → Dead | `kill()` | Restricted feature | - ---- - -## 4. API Examples by Mode - -### Ephemeral Sandbox — Typical Workflow - -```python -from agent_sandbox import ProviderSandbox as Sandbox - -# Scenario: AI Agent executes one-off code -async def execute_code(code: str) -> str: - async with await Sandbox.create(template="code-interpreter") as sb: - result = await sb.run_code(code) - return result.text - -# Scenario: Batch data processing -async def process_batch(items: list[str]) -> list[str]: - results = [] - async with await Sandbox.create(template="python-data-science") as sb: - for item in items: - result = await sb.run_code(f"process('{item}')") - results.append(result.text) - return results -``` - -### Persistent Sandbox — Development Environment (🔮 Future Plan) - -> **The following APIs will be implemented once underlying capabilities are available.** +Creating `base` provides the platform's command/file environment. It does not +start a template application or imply interpreter, PTY, storage persistence or +other conditional provider operations. This complete example owns its sandbox: ```python -from agent_sandbox import ProviderSandbox as Sandbox - -# Create or connect to a persistent development environment -try: - sb = await Sandbox.connect("my-dev-env") -except SandboxNotFoundError: - sb = await Sandbox.create( - template="full-stack", - persistent=True, - name="my-dev-env", - cpu=4, - memory=8192, - env={"NODE_ENV": "development"}, - ) - -# Start a development server (background) -process = await sb.commands.start("npm run dev") - -# Get access URL -url = await sb.network.get_url(3000) -print(f"Dev server: {url}") +import asyncio -# File watching -async for event in sb.files.watch("/app/src"): - print(f"File changed: {event.path} ({event.type})") -``` - -### Hibernated Sandbox — Intermittent Projects (🔮 Future Plan) - -> **The following APIs will be implemented once underlying capabilities are available.** - -```python -from agent_sandbox import ProviderSandbox as Sandbox +from agent_sandbox import ProviderSandbox -# Create a sandbox that supports hibernation -sb = await Sandbox.create( - template="python-data-science", - name="ml-project", - hibernate_after=600, # Auto-hibernate after 10 minutes of inactivity - on_exit="hibernate", -) -# Train a model (time-consuming operation) -await sb.run_code(""" -import joblib -from sklearn.ensemble import RandomForestClassifier -model = RandomForestClassifier(n_estimators=1000) -model.fit(X_train, y_train) -joblib.dump(model, '/app/model.pkl') -""") +async def main(): + async with await ProviderSandbox.create(template="base", timeout=60) as sandbox: + result = await sandbox.commands.run("printf 'Hello, Agent Sandbox!'") + assert result.exit_code == 0 + print(result.stdout) + path = "/home/user/data.csv" + await sandbox.files.write(path, "name,age\nAlice,30\nBob,25\n") + assert await sandbox.files.read(path) == "name,age\nAlice,30\nBob,25\n" -# Leave, sandbox auto-hibernates (billing stops) -# ... days later ... -# Wake up, continue working -sb = await Sandbox.connect("ml-project") -await sb.wake_up() - -# Model files and environment are still there -result = await sb.run_code(""" -import joblib -model = joblib.load('/app/model.pkl') -print(f"Model loaded, features: {model.n_features_in_}") -""") -``` - -### Snapshot — Environment Cloning (🔮 Future Plan) - -> **The following APIs will be implemented once underlying Snapshot capabilities are available.** - -```python -# Create a snapshot on a trained environment -snapshot_id = await sb.snapshot("trained-model-v1") - -# Create multiple new sandboxes from the snapshot (for parallel inference) -workers = [] -for i in range(5): - w = await Sandbox.create( - from_snapshot=snapshot_id, - name=f"inference-worker-{i}", - ) - workers.append(w) - -# Each worker has the complete model environment +asyncio.run(main()) ``` ---- - -## 5. Auto-Cleanup Policies - -### Ephemeral Sandbox +The context manager destroys this sandbox when the body finishes, including when +an exception is raised. See [getting started](../guide/getting-started.md) for +installation/account setup and [SDK usage](../guide/sdk-usage.md) for actual API +signatures. The documented timeout is a resource/command setting; it is not a +universal platform billing or data-retention guarantee. -| Trigger | Action | Default | -|---------|--------|---------| -| Reached `timeout` | Force destroy | 300s | -| Context Manager exit | Graceful destroy | — | -| Client disconnected | Wait → destroy | Wait 30s | -| All processes exited | Destroy | — | +## Attach to an existing sandbox -### Persistent Sandbox (🔮 Future Plan) +`await ProviderSandbox.connect(sandbox_id)` attaches a client. `close()` releases +its local connection; `kill()` deletes the remote sandbox. Delete only resources +whose lifecycle you own. For a borrowed instance, close the client when finished +and leave deletion to its owner. There is no automatic account-wide orphan cleanup +implied by losing a client connection or by the final process exiting. -| Trigger | Action | Default | -|---------|--------|---------| -| Manual `kill()` | Destroy | — | -| CLI `abx kill` | Destroy | — | -| Account overdue | Freeze → destroy after 7 days | — | +## Template applications and interpreter execution -### Global Cleanup Policy +For a template application, use the unified CLI launch flow and its readiness +checks before calling the resolved application service with its token. See +[template launch](../guide/template-workflows.md). A familiar platform template +name alone does not perform service startup. -```python -from agent_sandbox import Config - -# Global auto-cleanup configuration -Config.set( - auto_cleanup=True, - ephemeral_timeout=300, # Max ephemeral sandbox lifetime - hibernate_idle=600, # Idle wait time before hibernation - hibernate_retention=2592000, # Hibernation retention period (30 days) - orphan_cleanup=True, # Clean up orphaned sandboxes - orphan_grace_period=3600, # Orphan sandbox grace period (1 hour) -) -``` - ---- - -## 6. Billing Model - -### Billing Dimensions - -``` -Total Cost = Compute Cost + Storage Cost + Network Cost - -Compute Cost = CPU unit price × CPU cores × runtime duration - + Memory unit price × memory size × runtime duration - + GPU unit price × GPU count × runtime duration - -Storage Cost = Persistent storage unit price × storage size × retention duration (🔮 Future) - -Network Cost = Public outbound traffic × traffic unit price -``` - -### Billing Characteristics by Mode - -| Mode | Compute Cost | Storage Cost | Notes | -|------|-------------|--------------|-------| -| Ephemeral | Runtime duration × resources | None | Most economical, pay-as-you-go | -| Persistent | Continuous billing | Continuous billing | Suitable for long-term development (🔮 Future Plan) | -| Hibernated | Billed only during runtime | Snapshot storage fee | Normal rate during runtime, storage fee only during hibernation (🔮 Future Plan) | - -### Cost Estimation Examples - -``` -Ephemeral sandbox (1C/2G, running 5 minutes): - CPU: ¥0.00015/core/sec × 1 × 300 = ¥0.045 - Memory: ¥0.00003/GB/sec × 2 × 300 = ¥0.018 - Total: ¥0.063 - -Persistent sandbox (2C/4G, running 24 hours) 🔮: - CPU: ¥0.00015 × 2 × 86400 = ¥25.92 - Memory: ¥0.00003 × 4 × 86400 = ¥10.37 - Storage: ¥0.0003/GB/hour × 10 × 24 = ¥0.072 - Total: ¥36.36 - -Hibernated sandbox (2C/4G, running 2 hours + hibernated 22 hours) 🔮: - Running: (¥0.00015 × 2 + ¥0.00003 × 4) × 7200 = ¥3.02 - Hibernated: ¥0.0003 × 10 × 22 = ¥0.066 - Total: ¥3.09 (91% savings compared to persistent mode) -``` +Structured interpreter calls use the standard +`agent_sandbox.code_interpreter` entry and its documented template prerequisite; +Express ProviderSandbox does not implement interpreter operations. -### Cost Optimization Tips +## Conditional platform features -1. **One-off tasks**: Use ephemeral sandboxes with a reasonable timeout -2. **Intermittent development**: Use hibernated sandboxes with `hibernate_after=300` (🔮 Future Plan) -3. **Batch processing**: Use SandboxPool with shared warm sandboxes -4. **Long-running services**: Use persistent sandboxes + auto-scaling (🔮 Future Plan) +Pause/resume, snapshots and persistent storage depend on the specific provider, +platform and account. Consult its current documentation and exact operation +contract. The SDK does not promise universal persistent/hibernated modes, free +storage, fixed startup latency or retention periods. There is no public +`Config.set(...)` global cleanup policy. No future mode flags or speculative +lifecycle APIs are part of this page. diff --git a/docs/en/design/sdk-api-design.md b/docs/en/design/sdk-api-design.md index ca6d46ce..40ace513 100644 --- a/docs/en/design/sdk-api-design.md +++ b/docs/en/design/sdk-api-design.md @@ -1,206 +1,140 @@ -> The standard public API is now synchronous Sandbox / asynchronous AsyncSandbox. The provider/server extension design below uses ProviderSandbox, including its native `_sync` wrappers; it does not define the standard client API. Standard clients do not switch protocol when `ABX_PROVIDER` changes. See [migration](../guide/migrate-from-e2b.md). - # SDK API Design -> The `agent-sandbox-client` package is the **runtime** client: create a sandbox, use it, kill it. It offers two ways to write that code (imperative and declarative). Building and deploying templates belongs to the `abx` CLI, and running an agent belongs to the Agents API. - ---- - -## Table of Contents - -1. [Design rules](#design-rules) -2. [Paradigm 1: Imperative](#paradigm-1-imperative) -3. [Paradigm 2: Declarative decorator](#paradigm-2-declarative-decorator) -4. [Three things called "command"](#three-things-called-command) -5. [Running an agent](#running-an-agent) -6. [Configuration](#configuration) -7. [Sandbox core API](#sandbox-core-api) -8. [Errors](#errors) -9. [Not in the SDK](#not-in-the-sdk) - ---- +This page describes the current asynchronous `ProviderSandbox` provider/server +extensions. Root `Sandbox` / `AsyncSandbox` and `agent_sandbox.code_interpreter` +are separate standard contracts; see [E2B migration](../guide/migrate-from-e2b.md). +It is a current overview, not a proposal for additional APIs. ## Design rules -- **Runtime only.** The SDK has no image builder, no Dockerfile generation and no registry push. `abx template build|deploy` owns that pipeline, so there is one build path to document and test. -- **One way to call a named command.** A named command is a function registered in the sandbox's SandboxServer. The client calls it with `sandbox.run("name", **kwargs)`, which is `POST /commands/name`. `template.yaml` has no `custom_commands` key. -- **One way to run an agent.** The Agents API (`/v1/agents/**`) on the SandboxServer. The SDK has no `sb.agent.*` helpers. -- **Typed, explicit surface.** No `__getattr__` dispatch; every method has a signature that mypy checks (`py.typed`). -- **Async first, sync twin.** Every coroutine has a `_sync` twin that manages an event loop for you. -- **Explicit standard and extension contracts.** This page describes the native ProviderSandbox protocol, configuration and error model. `agent_sandbox` exports standard `Sandbox` / `AsyncSandbox`; those clients and `code_interpreter` follow the pinned Alibaba-supported E2B contract. See [Migrate from E2B](../guide/migrate-from-e2b.md). - ---- +- ProviderSandbox creates and operates sandboxes. Source image preparation belongs + to the CLI: normally `abx template launch `, with build/deploy as + advanced operations. Standard Template builders retain their separate contract. +- Shell commands, interpreter execution and template application commands use + distinct APIs and prerequisites. Creating `base` does not start an application. +- Provider declarations and the actual account determine supported operations. + A generic method's presence does not certify every provider's implementation. +- Public signatures, typed errors and explicit lifecycle ownership define the API. ## Paradigm 1: Imperative -```python -from agent_sandbox import ProviderSandbox as Sandbox - -sb = await Sandbox.create(template="code-interpreter") - -result = await sb.run_code("print('Hello, AliCloud!')") -print(result.text) - -result = await sb.commands.run("ls -la /app") -print(result.stdout) - -await sb.files.write("/app/data.csv", "name,age\nAlice,30\nBob,25") -content = await sb.files.read("/app/data.csv") -files = await sb.files.list("/app") - -await sb.kill() -``` - -Synchronous mode: +This complete program runs a command and transfers file content on Express `base`. +Use your account's documented canonical ABX environment configuration. The context +manager destroys this owned sandbox even when the body raises an exception. ```python -sb = Sandbox.create_sync(template="code-interpreter") -result = sb.run_code_sync("print(1+1)") -sb.kill_sync() -``` +import asyncio -Context manager (the sandbox is killed on exit): +from agent_sandbox import ProviderSandbox -```python -async with await Sandbox.create(template="code-interpreter") as sb: - result = await sb.run_code("import sys; print(sys.version)") -``` -Streaming output: +async def main(): + async with await ProviderSandbox.create(template="base", timeout=60) as sandbox: + result = await sandbox.commands.run("printf 'Hello, Agent Sandbox!'") + assert result.exit_code == 0 + print(result.stdout) + path = "/home/user/data.csv" + await sandbox.files.write(path, "name,age\nAlice,30\nBob,25\n") + assert await sandbox.files.read(path) == "name,age\nAlice,30\nBob,25\n" -```python -async for chunk in sb.commands.stream("pip install pandas && python train.py"): - if chunk.type == "stdout": - print(chunk.data, end="") - elif chunk.type == "stderr": - print(f"[ERR] {chunk.data}", end="") - elif chunk.type == "exit": - print(f"\nExit code: {chunk.exit_code}") + +asyncio.run(main()) ``` ---- +For structured interpreter execution, use the standard +`agent_sandbox.code_interpreter` entries with their documented +`code-interpreter-v1` prerequisite. Express ProviderSandbox does not implement +interpreter operations. See [SDK usage](../guide/sdk-usage.md) for lifecycle and +synchronous wrappers; child client APIs keep their documented signatures. ## Paradigm 2: Declarative decorator -Inspired by Modal. The decorator runs a local function in a remote sandbox. See [Declarative usage](../guide/declarative-usage.md) for every parameter. - -```python -from agent_sandbox.declarative import sandbox - -@sandbox(template="code-interpreter-v1", packages=["pandas"]) -def analyze(data): - import pandas as pd - return pd.DataFrame(data).describe().to_dict() - -# create sandbox -> upload function -> run -> return result -> kill sandbox -result = analyze({"col1": [1, 2, 3], "col2": [4, 5, 6]}) -``` - -- `keep_alive=True` keeps the sandbox after the call; `sandbox_id="..."` reuses an existing one. -- `async def` functions are supported, so several calls can run under `asyncio.gather`. -- `@sandbox.register` (inside a sandbox) registers a named command on the SandboxServer; see the next section. - ---- +The existing `@sandbox` decorator executes a function in a remotely prepared +runtime and owns its cleanup unless the caller explicitly retains or reuses a +sandbox. Packages, interpreter and serializer prerequisites are described in +[declarative usage](../guide/declarative-usage.md). Registration on SandboxServer +uses the existing `@sandbox.register` or server command registration; this does +not create a new platform capability. ## Three things called "command" -| What | Call | Wire | -|------|------|------| -| Raw shell command | `sandbox.commands.run("ls -la")` | envd process API | -| Named command | `sandbox.run("name", x=1)` | `POST /commands/name` on the SandboxServer | -| Catalogue of named commands | `sandbox.list_commands()` | `GET /commands` on the SandboxServer | - -`sandbox.run()` never runs a shell string. A name that is not registered raises `CommandNotFoundError`; any other server error raises `ServerAPIError`. Providers without an HTTP data plane (`epp://`) raise `ProviderFeatureNotSupportedError` (E8004). - -```python -sb = await Sandbox.create(template="python-hello") - -for c in await sb.list_commands(): # list[ServerCommand] - print(c.name, c.description, c.args) - -value = await sb.run("hello", name="Alice") -``` - -Named commands are registered in the template's `commands.py` (or with `@sandbox.register`). See [Authoring templates](../guide/authoring-templates.md) and [Template server API](../guide/template-server-api.md). +| Operation | Entry | Prerequisite | +| --- | --- | --- | +| Shell process | `sandbox.commands.run(...)` | Provider process support | +| Registered application command | Application client's `commands.run(...)` | Running SandboxServer service and its application token | +| Registered command discovery | Application client's `commands.list()` | The same running service | -**Capabilities.** `template.yaml` declares `capabilities` (`shell`, `files`, `code`, `terminal`, `ports`) for discovery and documentation. Only `code` is a runtime gate in the client: `run_code()` raises `CapabilityNotSupportedError` (E3004) when `code` is missing. `sandbox.capabilities` returns the effective set. +First launch the entire template with `abx template launch `. Connect +using the returned sandbox information and `application_client(..., token=...)` +so provider gateway headers and application authentication are preserved. See +[template launch](../guide/template-workflows.md) and +[template server API](../guide/template-server-api.md). A plain platform create +with a familiar template name does not perform this startup sequence. ---- +Specification 5 declares services in `provides` plus `start_cmd` / `ready_cmd`. +It does not use the retired template `capabilities` list as a runtime guarantee. ## Running an agent -There is one entry: the Agents API, an HTTP session API on the in-sandbox server (`/v1/agents/**`) that drives Codex and Qwen Code. Install the agent CLI in the template image and call the API. See [Agents API](../guide/agents-api.md) for the guide and the complete case. - ---- +The in-sandbox Agents API exposes `/v1/agents/**` for configured agent adapters. +Install and configure the intended agent CLI in its template, then use the +[Agents API guide](../guide/agents-api.md). A local protocol fixture is distinct +from a real configured model invocation. ## Configuration -Resolution order, highest first: +Use `ABX_SANDBOX_API_KEY`, `ABX_SANDBOX_BASE_URL` and `ABX_SANDBOX_DOMAIN` for project +platform configuration. ProviderSandbox per-call overrides use +`sandbox_api_key`, `sandbox_base_url` and `sandbox_domain`. Standard clients retain +their separate valid `api_key` contract. See [configuration](../reference/configuration.md) +for precedence and provider-specific fields; do not infer generic platform support +from an optional field name. -``` -Code parameters > Environment variables > .env file > ~/.abx/config.toml > Defaults -``` - -```bash -export ABX_SANDBOX_API_KEY=your-api-key # primary credential -export ALICLOUD_ACCESS_KEY_ID=your-ak # optional AK/SK pair -export ALICLOUD_ACCESS_KEY_SECRET=your-sk -``` - -Per-call overrides are keyword arguments of `Sandbox.create()` (`api_key`, `api_url`, `domain`, `access_key_id`, `access_key_secret`). The complete key list is in [Configuration](../reference/configuration.md) and [Environment variables](../guide/environment-variables.md). +## Sandbox core API ---- +The [API reference](../reference/api-reference.md) and +[SDK usage guide](../guide/sdk-usage.md) own complete signatures. Native lifecycle, +command/file clients, network resolution and application clients are distinct +from standard interpreter APIs. PTY and other conditional operations require the +owning provider and actual platform support. `close()` releases a connection; +`kill()` deletes a resource and should only be called for a sandbox you own. -## Sandbox core API +## Errors -The full signatures are in the [API reference](../reference/api-reference.md). The shape: +Provider errors derive from `SandboxError` and carry `code`, `message`, `suggestion` +and `docs_url`; standard clients retain their separate exception contract. This +complete program catches a real process deadline as `CommandTimeoutError` (E3001). +Cleanup runs in `finally`, and transport closure runs even if deletion raises. -| Group | Members | -|-------|---------| -| Lifecycle | `create`, `connect`, `list`, `kill`, `kill_by_id`, `set_timeout`, `is_running`, `pause`, `resume`, `refresh_info` | -| Properties | `id`, `provider`, `status`, `url`, `info`, `capabilities` | -| Code | `run_code` (requires the `code` capability) | -| Commands | `commands.run` / `stream` / `start` / `list` / `kill` (raw shell and processes), `run(name, **kwargs)`, `list_commands()` | -| Files | `files.read` / `read_bytes` / `write` / `list` / `remove` / `exists` / `make_dir` / `move` / `upload` / `download` / `watch` | -| Network | `network.get_host`, `network.get_url`, `network.get_access_headers`, `network.resolve` | -| Terminal | `get_terminal` (PTY over WebSocket) | -| In-sandbox server | `server`, `server_at(port)`, `start_server(...)` return a typed `ServerClient` | +```python +import asyncio -Results are Pydantic models: `CodeResult`, `ProcessResult`, `ProcessChunk`, `FileInfo`, `SandboxInfo`. A sandbox's `metadata` is on `sandbox.info`. +from agent_sandbox import ProviderSandbox +from agent_sandbox.models.errors import CommandTimeoutError ---- -## Errors +async def main(): + sandbox = await ProviderSandbox.create(template="base", timeout=60) + try: + await sandbox.commands.run("sleep 100", timeout=5) + except CommandTimeoutError as error: + print(error.code) # E3001 + finally: + try: + await sandbox.kill() + finally: + await sandbox.close() -Every exception derives from `SandboxError` and carries `code`, `message`, `suggestion` and `docs_url`. -```python -from agent_sandbox import ProviderSandbox as Sandbox, SandboxError -from agent_sandbox.models.errors import TemplateNotFoundError, TimeoutError - -try: - sb = await Sandbox.create(template="code-interpreter") - result = await sb.run_code("import time; time.sleep(100)", timeout=5) -except TemplateNotFoundError as e: - print(f"[{e.code}] {e.message}") -except TimeoutError as e: - print(f"[{e.code}] {e.message}") -except SandboxError as e: - print(f"[{e.code}] {e.message}") - print(f"Fix suggestion: {e.suggestion}") +asyncio.run(main()) ``` -The full code list is in [Error codes](../reference/error-codes.md). - ---- +See [error codes](../reference/error-codes.md). Do not replace the typed command +timeout with a nonexistent generic SDK `TimeoutError` import. ## Not in the SDK -| Not provided | Use instead | -|--------------|-------------| -| Template build, push, deploy (`Image`, `DockerBuilder`, `Sandbox.deploy`) | `abx template build` and `abx template deploy` | -| Natural-language sandbox creation, `Sandbox.plan()` | `abx create --prompt "DESCRIPTION"` and `abx template scaffold "DESCRIPTION"` in the CLI | -| `sb.agent.*` helpers, `Agent(...)` | The [Agents API](../guide/agents-api.md) | -| `Sandbox.custom()`, `run_command()`, `custom_commands` in `template.yaml` | `sandbox.run(name, **kwargs)` with a server-registered command | -| `SandboxPool` | Not planned; create sandboxes per task | -| Per-sandbox VPC, OSS mount, custom domain (`agent_sandbox.extensions`) | The platform has no per-sandbox API for them: VPC is template `vpcConfig`, volumes are control-plane resources, custom domains are account-level | +Python ProviderSandbox does not build/push source images or create a sandbox from +a natural-language prompt; use the CLI's current launch/scaffold/create guides. +There is no global `Config.set(...)` auto-cleanup policy or compatibility alias. +Use explicit owned lifecycle cleanup and the provider's actual timeout support. diff --git a/docs/en/guide/getting-started.md b/docs/en/guide/getting-started.md index 8f9751cc..3ac47aff 100644 --- a/docs/en/guide/getting-started.md +++ b/docs/en/guide/getting-started.md @@ -97,6 +97,10 @@ Template builder helpers are public imports from this installed package: No separate `e2b` installation is needed for these names. Standard template APIs retain their documented platform/account prerequisites and public parameters. +Standard sandbox classes use `from agent_sandbox import Sandbox, AsyncSandbox`. +Interpreter classes use `from agent_sandbox.code_interpreter import Sandbox, AsyncSandbox`; +these are distinct entries. Their imports require no separate upstream SDK. + The [2026-10-25 public-a4 example record](../../../.agents/evidence/verify/2026-10-25-public-a4-examples.md) separates 14 real cloud examples from local protocol fixtures, timeout controls and independent cleanup checks. A passing example is not whole-catalog coverage. diff --git a/docs/zh/DESIGN.md b/docs/zh/DESIGN.md index e132d9f2..03568739 100644 --- a/docs/zh/DESIGN.md +++ b/docs/zh/DESIGN.md @@ -77,7 +77,7 @@ graph TB |------|------|------| | **系统架构** | [architecture.md](design/architecture.md) | 标准客户端源码移植、原生 Provider 扩展分层及各自协议路径 | | **SDK API 设计** | [sdk-api-design.md](design/sdk-api-design.md) | Sandbox 类、文件操作、代码执行、命令、网络、会话管理 | -| **沙箱类型体系** | [sandbox-types.md](design/sandbox-types.md) | 临时沙箱(已实现)、持久沙箱(远期)、沙箱生命周期 | +| **沙箱生命周期与资源归属** | [sandbox-types.md](design/sandbox-types.md) | 自有资源清理、借用连接与 Provider 条件能力 | | **提供方与凭证** | [providers-and-credentials.md](design/providers-and-credentials.md) | 每个配置键一个环境变量、优先级、提供方插件(`abx provider ...`)、安装与信任模型、EPP/1 协议、E8xxx 错误;评审记录见 [providers-and-credentials-review.md](archive/providers-and-credentials-review.md) | ### 模板 diff --git a/docs/zh/README.md b/docs/zh/README.md index 5ee61921..b4e7565f 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -91,7 +91,7 @@ Agent Sandbox 是可切换提供方的沙箱 SDK。默认提供方是阿里云 |---|------|------| | 1 | [系统架构设计](design/architecture.md) | 六层分层架构总览、各层职责、目录结构 | | 2 | [SDK API 设计](design/sdk-api-design.md) | 两种使用范式、命令与错误模型、SDK 边界 | -| 3 | [沙箱类型体系](design/sandbox-types.md) | 临时/持久/休眠三种模式、生命周期状态机、计费模型 | +| 3 | [沙箱生命周期与资源归属](design/sandbox-types.md) | 自有资源清理、借用连接与 Provider 条件能力 | | 4 | [MCP Server](design/mcp-server.md) | 7 个 P0 工具定义、会话绑定、STDIO 与 Streamable HTTP 传输、IDE 配置 | | 5 | [模板体系](design/template-system.md) | 官方模板、自定义模板、sandbox.yaml 规范、版本管理、模板市场 | | 6 | [Server API](design/server-api.md) | 容器内 HTTP Server 端点定义 | diff --git a/docs/zh/design/sandbox-types.md b/docs/zh/design/sandbox-types.md index d1027bd5..056efaea 100644 --- a/docs/zh/design/sandbox-types.md +++ b/docs/zh/design/sandbox-types.md @@ -1,321 +1,52 @@ -# 沙箱类型体系设计 +# 沙箱生命周期与资源归属 -> Agent Sandbox 提供三种沙箱模式,覆盖从一次性脚本到长期运行服务的全部场景。当前**仅临时沙箱(Ephemeral)完整支持**,持久沙箱和休眠沙箱为远期规划。 +本页说明当前 ProviderSandbox 生命周期,不定义额外的持久化/休眠 SDK 类型或全局清理服务。 +标准 `Sandbox` / `AsyncSandbox` 保留独立的[标准接口约定](../guide/migrate-from-e2b.md)。 ---- +## 创建自己负责的沙箱 -## 1. 三种沙箱模式 - -### 临时沙箱(Ephemeral) - -**定位**:用完即弃的一次性执行环境,最常用的模式。**当前唯一完整支持的沙箱类型。** - -```python -# 默认就是临时沙箱 -sb = await Sandbox.create(template="code-interpreter") -result = await sb.run_code("print('hello')") -await sb.kill() # 销毁后数据全部丢失 - -# Context Manager 自动销毁 -async with await Sandbox.create(template="code-interpreter") as sb: - result = await sb.run_code("print('hello')") - # 退出即销毁 -``` - -### 持久沙箱(Persistent)— 🔮 远期规划 - -> **⚠️ 需底层能力支持,当前为远期规划。** 持久沙箱依赖底层平台的持久化存储能力,需等待阿里云 FC 支持后实现。 - -**定位**:长期运行的开发/服务环境,状态跨会话保留。 - -```python -# Future API — 待底层支持 -from agent_sandbox import ProviderSandbox as Sandbox - -sb = await Sandbox.create( - template="python-base", - persistent=True, - name="my-dev-env", -) - -# 安装依赖(状态会保留) -await sb.commands.run("pip install flask sqlalchemy") - -# 稍后重新连接 -sb = await Sandbox.connect("my-dev-env") -result = await sb.commands.run("pip list") # flask, sqlalchemy 仍在 -``` - -### 休眠沙箱(Hibernated)— 🔮 远期规划 - -> **⚠️ 需底层能力支持,当前为远期规划。** 休眠沙箱依赖底层平台的 Snapshot / CRIU 等能力,需等待底层支持后实现。 - -**定位**:状态冻结后挂起,唤醒时恢复到冻结时刻,节省计费。 - -```python -# Future API — 待底层支持 -from agent_sandbox import ProviderSandbox as Sandbox - -sb = await Sandbox.create( - template="python-data-science", - hibernate_after=300, - on_exit="hibernate", -) - -# 使用沙箱... -await sb.run_code("import pandas as pd; df = pd.read_csv('data.csv')") - -await sb.hibernate() # 停止计费 -sb = await Sandbox.connect("sb-xxx") -await sb.wake_up() # 恢复到休眠时状态 -``` - ---- - -## 2. 对比矩阵 - -| 特性 | 临时(Ephemeral) | 持久(Persistent)🔮 | 休眠(Hibernated)🔮 | -|------|-------------------|--------------------|--------------------| -| **实现状态** | **当前可用** | 远期规划 | 远期规划 | -| **生命周期** | 任务结束即销毁 | 持续运行直到手动销毁 | 冻结后可随时唤醒 | -| **状态持久** | 无 | 全量持久化 | 快照式冻结 | -| **文件系统** | tmpfs(内存盘) | 持久化存储 | 冻结时快照 | -| **进程** | 任务完成即停止 | 后台持续运行 | 冻结/恢复 | -| **网络** | 临时端口映射 | 固定域名/端口 | 唤醒后恢复 | -| **启动时间** | 冷启动 ~2s | 已在运行 ~0s | 唤醒 ~3-5s | -| **计费** | 按使用时长 | 持续计费 | 休眠期间低费/免费 | -| **最大时长** | 默认 5 分钟 | 无限制 | 休眠可保持 30 天 | -| **典型场景** | 代码执行、数据分析 | 开发环境、Web 服务 | 间歇性使用的项目 | -| **自动清理** | 超时自动销毁 | 手动管理 | 超过保留期自动清理 | - ---- - -## 3. 生命周期状态机 - -```mermaid -stateDiagram-v2 - [*] --> Creating : create() - Creating --> Running : 就绪 - Running --> Dead : kill() - Running --> Paused : pause() 需白名单 - Paused --> Running : resume() -``` - -> **说明**:`pause()` / `resume()` 状态转换需白名单权限,当前为受限功能。Snapshot 相关状态转换为远期规划,不在当前状态机中体现。 - -状态转换规则: - -| 转换 | 条件 | 状态 | -|------|------|------| -| Creating → Running | 正常启动 | 当前支持 | -| Creating → Dead | 创建失败 | 当前支持 | -| Running → Dead | `kill()` 或超时 | 当前支持 | -| Running → Paused | `pause()`,需白名单 | 受限功能 | -| Paused → Running | `resume()` | 受限功能 | -| Paused → Dead | `kill()` | 受限功能 | - ---- - -## 4. 各模式 API 示例 - -### 临时沙箱 — 典型工作流 - -```python -from agent_sandbox import ProviderSandbox as Sandbox - -# 场景:AI Agent 执行一次性代码 -async def execute_code(code: str) -> str: - async with await Sandbox.create(template="code-interpreter") as sb: - result = await sb.run_code(code) - return result.text - -# 场景:批量数据处理 -async def process_batch(items: list[str]) -> list[str]: - results = [] - async with await Sandbox.create(template="python-data-science") as sb: - for item in items: - result = await sb.run_code(f"process('{item}')") - results.append(result.text) - return results -``` - -### 持久沙箱 — 开发环境(🔮 远期规划) - -> **以下 API 待底层能力支持后实现。** +创建 `base` 获得平台命令/文件环境,不会启动模板应用,也不自动代表解释器、PTY、 +持久化存储等条件能力。以下完整示例负责自己创建的沙箱: ```python -from agent_sandbox import ProviderSandbox as Sandbox - -# 创建或连接持久开发环境 -try: - sb = await Sandbox.connect("my-dev-env") -except SandboxNotFoundError: - sb = await Sandbox.create( - template="full-stack", - persistent=True, - name="my-dev-env", - cpu=4, - memory=8192, - env={"NODE_ENV": "development"}, - ) - -# 启动开发服务器(后台运行) -process = await sb.commands.start("npm run dev") - -# 获取访问地址 -url = await sb.network.get_url(3000) -print(f"开发服务器: {url}") +import asyncio -# 文件监听 -async for event in sb.files.watch("/app/src"): - print(f"文件变更: {event.path} ({event.type})") -``` - -### 休眠沙箱 — 间歇性项目(🔮 远期规划) - -> **以下 API 待底层能力支持后实现。** - -```python -from agent_sandbox import ProviderSandbox as Sandbox +from agent_sandbox import ProviderSandbox -# 创建支持休眠的沙箱 -sb = await Sandbox.create( - template="python-data-science", - name="ml-project", - hibernate_after=600, # 10 分钟无操作自动休眠 - on_exit="hibernate", -) -# 训练模型(耗时操作) -await sb.run_code(""" -import joblib -from sklearn.ensemble import RandomForestClassifier -model = RandomForestClassifier(n_estimators=1000) -model.fit(X_train, y_train) -joblib.dump(model, '/app/model.pkl') -""") +async def main(): + async with await ProviderSandbox.create(template="base", timeout=60) as sandbox: + result = await sandbox.commands.run("printf 'Hello, Agent Sandbox!'") + assert result.exit_code == 0 + print(result.stdout) + path = "/home/user/data.csv" + await sandbox.files.write(path, "name,age\nAlice,30\nBob,25\n") + assert await sandbox.files.read(path) == "name,age\nAlice,30\nBob,25\n" -# 离开,沙箱自动休眠(停止计费) -# ... 几天后 ... -# 唤醒,继续工作 -sb = await Sandbox.connect("ml-project") -await sb.wake_up() - -# 模型文件和环境都还在 -result = await sb.run_code(""" -import joblib -model = joblib.load('/app/model.pkl') -print(f"模型已加载,特征数: {model.n_features_in_}") -""") -``` - -### 快照 — 环境复制(🔮 远期规划) - -> **以下 API 待底层 Snapshot 能力支持后实现。** - -```python -# 在训练好的环境上创建快照 -snapshot_id = await sb.snapshot("trained-model-v1") - -# 从快照创建多个新沙箱(用于并行推理) -workers = [] -for i in range(5): - w = await Sandbox.create( - from_snapshot=snapshot_id, - name=f"inference-worker-{i}", - ) - workers.append(w) - -# 每个 worker 都有完整的模型环境 +asyncio.run(main()) ``` ---- - -## 5. 自动清理策略 - -### 临时沙箱 +上下文管理器在代码完成或抛出异常时销毁此沙箱。安装与账号配置见 +[开始使用](../guide/getting-started.md),实际签名见 [SDK 用法](../guide/sdk-usage.md)。 +超时是资源/命令配置,不是跨平台通用的计费或数据保留承诺。 -| 触发条件 | 动作 | 默认值 | -|----------|------|--------| -| 达到 `timeout` 时间 | 强制销毁 | 300s | -| Context Manager 退出 | 优雅销毁 | — | -| 客户端断开连接 | 等待 → 销毁 | 等待 30s | -| 进程全部退出 | 销毁 | — | +## 连接已有沙箱 -### 持久沙箱(🔮 远期规划) +`await ProviderSandbox.connect(sandbox_id)` 连接已有实例。`close()` 释放本地连接, +`kill()` 删除远程沙箱;只删除自己负责生命周期的资源。借用实例时完成后关闭客户端, +让资源所有者负责删除。客户端断开或最后一个进程退出,不意味着 SDK 自动清理全账号的孤儿资源。 -| 触发条件 | 动作 | 默认值 | -|----------|------|--------| -| 手动 `kill()` | 销毁 | — | -| CLI `abx kill` | 销毁 | — | -| 账户欠费 | 冻结 → 7 天后销毁 | — | +## 模板应用与解释器 -### 全局清理策略 +模板应用先走 CLI 统一启动流程和就绪检查,再使用令牌调用解析出的应用服务,见 +[模板启动](../guide/template-workflows.md)。仅提供一个熟悉的平台模板名,不会自动启动服务。 -```python -from agent_sandbox import Config - -# 全局配置自动清理 -Config.set( - auto_cleanup=True, - ephemeral_timeout=300, # 临时沙箱最大存活时间 - hibernate_idle=600, # 休眠前空闲等待时间 - hibernate_retention=2592000, # 休眠保留期(30天) - orphan_cleanup=True, # 清理无主沙箱 - orphan_grace_period=3600, # 无主沙箱宽限期(1小时) -) -``` - ---- - -## 6. 计费模型 - -### 计费维度 - -``` -总费用 = 计算费用 + 存储费用 + 网络费用 - -计算费用 = CPU 单价 × CPU 核数 × 运行时长 - + 内存单价 × 内存大小 × 运行时长 - + GPU 单价 × GPU 数量 × 运行时长 - -存储费用 = 持久存储单价 × 存储大小 × 保留时长(🔮远期) - -网络费用 = 公网出流量 × 流量单价 -``` - -### 各模式计费特点 - -| 模式 | 计算费用 | 存储费用 | 说明 | -|------|----------|----------|------| -| 临时 | 运行时长 × 资源 | 无 | 最经济,按需付费 | -| 持久 | 持续计费 | 持续计费 | 适合长期开发(🔮 远期规划) | -| 休眠 | 仅运行时计费 | 快照存储费 | 运行时正常费率,休眠期仅收存储费(🔮 远期规划) | - -### 费用估算示例 - -``` -临时沙箱(1C/2G,运行 5 分钟): - CPU: ¥0.00015/核/秒 × 1 × 300 = ¥0.045 - 内存: ¥0.00003/GB/秒 × 2 × 300 = ¥0.018 - 总计: ¥0.063 - -持久沙箱(2C/4G,运行 24 小时)🔮: - CPU: ¥0.00015 × 2 × 86400 = ¥25.92 - 内存: ¥0.00003 × 4 × 86400 = ¥10.37 - 存储: ¥0.0003/GB/小时 × 10 × 24 = ¥0.072 - 总计: ¥36.36 - -休眠沙箱(2C/4G,运行 2 小时 + 休眠 22 小时)🔮: - 运行: (¥0.00015 × 2 + ¥0.00003 × 4) × 7200 = ¥3.02 - 休眠: ¥0.0003 × 10 × 22 = ¥0.066 - 总计: ¥3.09(比持久模式节省 91%) -``` +结构化解释器使用标准 `agent_sandbox.code_interpreter` 入口及其文档规定的模板前提; +极速版 ProviderSandbox 不实现解释器操作。 -### 费用优化建议 +## 条件平台能力 -1. **一次性任务**:使用临时沙箱,设置合理 timeout -2. **间歇性开发**:使用休眠沙箱,设置 `hibernate_after=300`(🔮 远期规划) -3. **批量处理**:使用 SandboxPool 共享预热沙箱 -4. **长期服务**:使用持久沙箱 + 自动扩缩容(🔮 远期规划) +暂停/恢复、快照和持久化存储取决于具体 Provider、平台和账号,以当前平台文档及精确操作 +约定为准。SDK 不承诺跨平台通用的持久化/休眠模式、免费存储、固定启动时间或保留期, +也没有公开的 `Config.set(...)` 全局清理策略。本页不提供未来模式参数或推测的生命周期 API。 diff --git a/docs/zh/design/sdk-api-design.md b/docs/zh/design/sdk-api-design.md index 4514960a..34d1ab45 100644 --- a/docs/zh/design/sdk-api-design.md +++ b/docs/zh/design/sdk-api-design.md @@ -1,206 +1,122 @@ -> 标准公开 API 现在使用同步 Sandbox 与异步 AsyncSandbox。下文 Provider/Server 扩展及原生 `_sync` 包装器对应 ProviderSandbox,不定义标准客户端 API;`ABX_PROVIDER` 不切换标准客户端协议。见[迁移指南](../guide/migrate-from-e2b.md)。 - # SDK API 设计 -> `agent-sandbox-client` 包是**运行时**客户端:创建沙箱、使用沙箱、销毁沙箱。它提供两种写法(命令式和声明式)。模板的构建与部署归 `abx` CLI,运行 Agent 归 Agents API。 - ---- - -## 目录 - -1. [设计原则](#设计原则) -2. [范式一:命令式](#范式一命令式) -3. [范式二:声明式装饰器](#范式二声明式装饰器) -4. [三种叫「命令」的东西](#三种叫命令的东西) -5. [运行 Agent](#运行-agent) -6. [配置](#配置) -7. [Sandbox 核心 API](#sandbox-核心-api) -8. [错误](#错误) -9. [SDK 不提供的能力](#sdk-不提供的能力) - ---- +本页描述当前异步 `ProviderSandbox` 的 Provider/server 扩展。根入口 +`Sandbox` / `AsyncSandbox` 和 `agent_sandbox.code_interpreter` 是独立的标准接口约定, +见[从 E2B 迁移](../guide/migrate-from-e2b.md)。本页是当前能力概览,不是新增 API 提案。 ## 设计原则 -- **只做运行时。** SDK 没有镜像构建器、Dockerfile 生成和镜像仓库推送。这条流水线由 `abx template build|deploy` 负责,所以构建路径只有一条,只需要写一份文档、测一遍。 -- **命名命令只有一种调用方式。** 命名命令是注册在沙箱内 SandboxServer 上的函数。客户端用 `sandbox.run("name", **kwargs)` 调用,也就是 `POST /commands/name`。`template.yaml` 没有 `custom_commands` 键。 -- **运行 Agent 只有一个入口。** SandboxServer 上的 Agents API(`/v1/agents/**`)。SDK 没有 `sb.agent.*` 辅助方法。 -- **显式、带类型的接口。** 不用 `__getattr__` 分发;每个方法都有 mypy 检查的签名(`py.typed`)。 -- **异步优先,同步孪生。** 每个协程都有 `_sync` 版本,由它替你管理事件循环。 -- **明确标准与扩展契约。** 本文描述原生 ProviderSandbox 的协议、配置和错误模型。`agent_sandbox` 导出标准 `Sandbox`/`AsyncSandbox`;标准客户端及 `code_interpreter` 遵循固定的阿里云 E2B 契约。见[从 E2B 迁移](../guide/migrate-from-e2b.md)。 - ---- +- ProviderSandbox 创建、操作沙箱。源码镜像准备属于 CLI:通常使用 + `abx template launch `;build/deploy 是高级操作。标准 Template 构建器 + 保留其独立接口约定。 +- Shell 命令、解释器执行和模板应用命令有不同入口与前提。创建 `base` 不会启动应用。 +- Provider 声明和真实账号决定可用操作;有同名方法不代表所有平台都实现了它。 +- 公开签名、类型化错误和明确的资源归属约束接口。 ## 范式一:命令式 -```python -from agent_sandbox import ProviderSandbox as Sandbox - -sb = await Sandbox.create(template="code-interpreter") - -result = await sb.run_code("print('Hello, AliCloud!')") -print(result.text) - -result = await sb.commands.run("ls -la /app") -print(result.stdout) - -await sb.files.write("/app/data.csv", "name,age\nAlice,30\nBob,25") -content = await sb.files.read("/app/data.csv") -files = await sb.files.list("/app") - -await sb.kill() -``` - -同步模式: +以下完整程序在极速版 `base` 执行命令、读写文件。使用自己账号文档规定的 ABX +环境配置。上下文管理器在异常时也会销毁本程序创建的沙箱。 ```python -sb = Sandbox.create_sync(template="code-interpreter") -result = sb.run_code_sync("print(1+1)") -sb.kill_sync() -``` +import asyncio -上下文管理器(退出时销毁沙箱): +from agent_sandbox import ProviderSandbox -```python -async with await Sandbox.create(template="code-interpreter") as sb: - result = await sb.run_code("import sys; print(sys.version)") -``` -流式输出: +async def main(): + async with await ProviderSandbox.create(template="base", timeout=60) as sandbox: + result = await sandbox.commands.run("printf 'Hello, Agent Sandbox!'") + assert result.exit_code == 0 + print(result.stdout) + path = "/home/user/data.csv" + await sandbox.files.write(path, "name,age\nAlice,30\nBob,25\n") + assert await sandbox.files.read(path) == "name,age\nAlice,30\nBob,25\n" -```python -async for chunk in sb.commands.stream("pip install pandas && python train.py"): - if chunk.type == "stdout": - print(chunk.data, end="") - elif chunk.type == "stderr": - print(f"[ERR] {chunk.data}", end="") - elif chunk.type == "exit": - print(f"\nExit code: {chunk.exit_code}") + +asyncio.run(main()) ``` ---- +结构化解释器使用标准 `agent_sandbox.code_interpreter` 入口及其文档规定的 +`code-interpreter-v1` 前提;极速版 ProviderSandbox 不实现解释器操作。 +生命周期与同步封装见 [SDK 用法](../guide/sdk-usage.md),子客户端遵循各自公开签名。 ## 范式二:声明式装饰器 -灵感来自 Modal。装饰器把本地函数放到远程沙箱里运行。全部参数见[声明式用法](../guide/declarative-usage.md)。 - -```python -from agent_sandbox.declarative import sandbox - -@sandbox(template="code-interpreter-v1", packages=["pandas"]) -def analyze(data): - import pandas as pd - return pd.DataFrame(data).describe().to_dict() - -# 创建沙箱 -> 上传函数 -> 执行 -> 返回结果 -> 销毁沙箱 -result = analyze({"col1": [1, 2, 3], "col2": [4, 5, 6]}) -``` - -- `keep_alive=True` 在调用后保留沙箱;`sandbox_id="..."` 复用已有沙箱。 -- 支持 `async def` 函数,因此可以用 `asyncio.gather` 并发多次调用。 -- `@sandbox.register`(在沙箱内使用)把命名命令注册到 SandboxServer,见下一节。 - ---- +已有 `@sandbox` 在准备好的远程运行环境中执行函数,默认负责资源清理;明确保留或复用 +沙箱时遵循对应生命周期约定。包、解释器与序列化前提见[声明式用法](../guide/declarative-usage.md)。 +SandboxServer 可通过已有 `@sandbox.register` 或 server 命令注册机制注册函数,注册不会 +自动产生平台能力。 ## 三种叫「命令」的东西 -| 是什么 | 调用 | 线协议 | -|--------|------|--------| -| 裸 shell 命令 | `sandbox.commands.run("ls -la")` | envd 进程 API | -| 命名命令 | `sandbox.run("name", x=1)` | SandboxServer 的 `POST /commands/name` | -| 命名命令目录 | `sandbox.list_commands()` | SandboxServer 的 `GET /commands` | - -`sandbox.run()` 不会执行 shell 字符串。名称未注册时抛 `CommandNotFoundError`,其他 server 错误抛 `ServerAPIError`。没有 HTTP 数据面的 provider(`epp://`)抛 `ProviderFeatureNotSupportedError`(E8004)。 - -```python -sb = await Sandbox.create(template="python-hello") - -for c in await sb.list_commands(): # list[ServerCommand] - print(c.name, c.description, c.args) - -value = await sb.run("hello", name="Alice") -``` - -命名命令注册在模板的 `commands.py` 里(或用 `@sandbox.register`)。见[编写模板](../guide/authoring-templates.md)和[模板 server API](../guide/template-server-api.md)。 +| 操作 | 入口 | 前提 | +| --- | --- | --- | +| Shell 进程 | `sandbox.commands.run(...)` | Provider 进程能力 | +| 注册的应用命令 | 应用客户端 `commands.run(...)` | 运行中的 SandboxServer 服务及应用令牌 | +| 注册命令发现 | 应用客户端 `commands.list()` | 同一运行中的服务 | -**能力(capabilities)。** `template.yaml` 声明 `capabilities`(`shell`、`files`、`code`、`terminal`、`ports`),用于发现与文档。客户端只有 `code` 是运行时闸门:缺少 `code` 时 `run_code()` 抛 `CapabilityNotSupportedError`(E3004)。`sandbox.capabilities` 返回生效的集合。 +先用 `abx template launch ` 启动完整模板,再根据返回的沙箱信息,通过 +`application_client(..., token=...)` 保留 Provider 网关头和应用认证。见 +[模板启动](../guide/template-workflows.md)与[模板 server API](../guide/template-server-api.md)。 +仅用熟悉的模板名创建普通平台沙箱,不会执行这一启动流程。 ---- +规范 5 通过 `provides`、`start_cmd` / `ready_cmd` 声明服务;不使用旧模板 +`capabilities` 列表来保证运行能力。 ## 运行 Agent -只有一个入口:Agents API,即沙箱内 server 上的 HTTP 会话 API(`/v1/agents/**`),驱动 Codex 和 Qwen Code。在模板镜像里安装 Agent CLI,然后调用该 API。指南和完整案例见 [Agents API](../guide/agents-api.md)。 - ---- +沙箱内 Agents API 的 `/v1/agents/**` 使用已配置的 Agent adapter。先在模板中安装、 +配置对应 Agent CLI,再按 [Agents API 指南](../guide/agents-api.md)调用。 +本地协议夹具与真实模型调用是不同的验证范围。 ## 配置 -解析顺序,优先级从高到低: +项目平台配置使用 `ABX_SANDBOX_API_KEY`、`ABX_SANDBOX_BASE_URL`、`ABX_SANDBOX_DOMAIN`。 +ProviderSandbox 单次覆盖使用 `sandbox_api_key`、`sandbox_base_url`、`sandbox_domain`。 +标准客户端保留独立有效的 `api_key` 接口约定。优先级和 Provider 专有配置见 +[配置参考](../reference/configuration.md),不能仅凭可选字段名推断平台能力。 -``` -代码参数 > 环境变量 > .env 文件 > ~/.abx/config.toml > 默认值 -``` - -```bash -export ABX_SANDBOX_API_KEY=your-api-key # 主凭证 -export ALICLOUD_ACCESS_KEY_ID=your-ak # 可选的 AK/SK -export ALICLOUD_ACCESS_KEY_SECRET=your-sk -``` - -单次调用的覆盖项是 `Sandbox.create()` 的关键字参数(`api_key`、`api_url`、`domain`、`access_key_id`、`access_key_secret`)。完整键列表见[配置参考](../reference/configuration.md)和[环境变量](../guide/environment-variables.md)。 +## Sandbox 核心 API ---- +完整签名以 [API 参考](../reference/api-reference.md)和 [SDK 用法](../guide/sdk-usage.md)为准。 +原生生命周期、命令/文件客户端、网络解析和应用客户端与标准解释器 API 是不同入口。 +PTY 等条件能力需要对应 Provider 和真实平台支持。`close()` 释放连接;`kill()` 删除资源, +只应用于自己负责生命周期的沙箱。 -## Sandbox 核心 API +## 错误 -完整签名见 [API 参考](../reference/api-reference.md)。整体结构: +Provider 错误派生自 `SandboxError`,携带 `code`、`message`、`suggestion`、`docs_url`; +标准客户端保留独立的异常约定。以下完整程序捕获真实进程超时 +`CommandTimeoutError`(E3001),在 `finally` 删除自有沙箱,即使删除报错也会关闭连接。 -| 分组 | 成员 | -|------|------| -| 生命周期 | `create`、`connect`、`list`、`kill`、`kill_by_id`、`set_timeout`、`is_running`、`pause`、`resume`、`refresh_info` | -| 属性 | `id`、`provider`、`status`、`url`、`info`、`capabilities` | -| 代码 | `run_code`(需要 `code` 能力) | -| 命令 | `commands.run` / `stream` / `start` / `list` / `kill`(裸 shell 与进程)、`run(name, **kwargs)`、`list_commands()` | -| 文件 | `files.read` / `read_bytes` / `write` / `list` / `remove` / `exists` / `make_dir` / `move` / `upload` / `download` / `watch` | -| 网络 | `network.get_host`、`network.get_url`、`network.get_access_headers`、`network.resolve` | -| 终端 | `get_terminal`(WebSocket 上的 PTY) | -| 沙箱内 server | `server`、`server_at(port)`、`start_server(...)` 返回带类型的 `ServerClient` | +```python +import asyncio -返回值是 Pydantic 模型:`CodeResult`、`ProcessResult`、`ProcessChunk`、`FileInfo`、`SandboxInfo`。沙箱的 `metadata` 在 `sandbox.info` 上。 +from agent_sandbox import ProviderSandbox +from agent_sandbox.models.errors import CommandTimeoutError ---- -## 错误 +async def main(): + sandbox = await ProviderSandbox.create(template="base", timeout=60) + try: + await sandbox.commands.run("sleep 100", timeout=5) + except CommandTimeoutError as error: + print(error.code) # E3001 + finally: + try: + await sandbox.kill() + finally: + await sandbox.close() -所有异常都派生自 `SandboxError`,携带 `code`、`message`、`suggestion` 和 `docs_url`。 -```python -from agent_sandbox import ProviderSandbox as Sandbox, SandboxError -from agent_sandbox.models.errors import TemplateNotFoundError, TimeoutError - -try: - sb = await Sandbox.create(template="code-interpreter") - result = await sb.run_code("import time; time.sleep(100)", timeout=5) -except TemplateNotFoundError as e: - print(f"[{e.code}] {e.message}") -except TimeoutError as e: - print(f"[{e.code}] {e.message}") -except SandboxError as e: - print(f"[{e.code}] {e.message}") - print(f"修复建议: {e.suggestion}") +asyncio.run(main()) ``` -完整错误码见[错误码](../reference/error-codes.md)。 - ---- +完整类型见[错误码](../reference/error-codes.md),不要导入不存在的通用 SDK `TimeoutError`。 ## SDK 不提供的能力 -| 不提供 | 替代方案 | -|--------|----------| -| 模板构建、推送、部署(`Image`、`DockerBuilder`、`Sandbox.deploy`) | `abx template build` 与 `abx template deploy` | -| 自然语言创建沙箱、`Sandbox.plan()` | CLI 的 `abx create --prompt "DESCRIPTION"` 与 `abx template scaffold "DESCRIPTION"` | -| `sb.agent.*` 辅助方法、`Agent(...)` | [Agents API](../guide/agents-api.md) | -| `Sandbox.custom()`、`run_command()`、`template.yaml` 中的 `custom_commands` | `sandbox.run(name, **kwargs)` 调用 server 注册的命令 | -| `SandboxPool` | 不计划提供;按任务创建沙箱 | -| 单沙箱级的 VPC、OSS 挂载、自定义域名(`agent_sandbox.extensions`) | 平台没有对应的单沙箱 API:VPC 是模板的 `vpcConfig`,存储卷是控制面资源,自定义域名是账号级配置 | +Python ProviderSandbox 不构建、推送源码镜像,也不直接按自然语言创建沙箱;使用 CLI +现有 launch/scaffold/create 指南。没有全局 `Config.set(...)` 自动清理策略或兼容别名。 +使用明确的自有资源清理和 Provider 实际支持的超时能力。 diff --git a/docs/zh/guide/getting-started.md b/docs/zh/guide/getting-started.md index 4f99b8ae..7e79db34 100644 --- a/docs/zh/guide/getting-started.md +++ b/docs/zh/guide/getting-started.md @@ -89,6 +89,10 @@ assert result.exit_code == 0 and result.stdout.strip() == "2" `from agent_sandbox import Template, AsyncTemplate, wait_for_port, default_build_logger`。 这些名称不需要另装 `e2b`;标准模板 API 仍有其文档规定的平台、账号前提和公开参数。 +标准沙箱类使用 `from agent_sandbox import Sandbox, AsyncSandbox`;解释器类使用 +`from agent_sandbox.code_interpreter import Sandbox, AsyncSandbox`,二者是不同入口。 +这些导入不需要另装上游 SDK。 + [2026-10-25 公开 a4 示例记录](../../../.agents/evidence/verify/2026-10-25-public-a4-examples.md) 区分了 14 个真实云端示例、本地协议夹具、超时控制和独立清理检查。 示例通过不代表整个模板目录都已验证。 diff --git a/server-sdk/src/agent_sandbox/server/_version.py b/server-sdk/src/agent_sandbox/server/_version.py index b147458b..48b9a7eb 100644 --- a/server-sdk/src/agent_sandbox/server/_version.py +++ b/server-sdk/src/agent_sandbox/server/_version.py @@ -1 +1 @@ -__version__ = "0.4.0a5" +__version__ = "0.4.0a6"