Teach builder sandbox rootfs capture
All checks were successful
build / build (push) Successful in 25s

This commit is contained in:
robert
2026-05-19 13:21:32 -03:00
parent 9f56a15303
commit b802e54549
7 changed files with 34 additions and 28 deletions

View File

@@ -94,17 +94,18 @@ runtime:
``` ```
For render/media/data agents, commands that create user-visible files MUST run For render/media/data agents, commands that create user-visible files MUST run
through ``await ctx.workspace_shell(...)`` or ``await ctx.workspace_python(...)`` through ``await ctx.workspace_shell(...)`` or ``await ctx.workspace_python(...)``.
and write under ``/workspace/outputs/...``. Do not use in-process The platform sandbox persists ``/workspace`` writes directly and captures changed
``asyncio.create_subprocess_exec`` for durable outputs; the agent container is files elsewhere in the guest rootfs under ``outputs/rootfs-captures/...``. Do
not workspace-mounted, so files created in ``/tmp`` or the image filesystem can not use in-process ``asyncio.create_subprocess_exec`` for durable outputs; the
vanish after the request. Commands must still be bounded with timeouts and broad agent container is not workspace-mounted or rootfs-captured, so files created in
exception handling, and failures must return structured JSON instead of ``/tmp`` or the image filesystem can vanish after the request. Commands must
crashing the worker. Use ``render: bool = False`` by default and expose every still be bounded with timeouts and broad exception handling, and failures must
public skill argument with concrete type hints so the live ``input_schema`` return structured JSON instead of crashing the worker. Use ``render: bool = False``
contains the full call surface. If a render command returns nonzero after by default and expose every public skill argument with concrete type hints so
emitting a usable file, preserve the file under ``/workspace/outputs/...`` and the live ``input_schema`` contains the full call surface. If a render command
include the command failure as a warning instead of dropping the result. returns nonzero after emitting a usable file, preserve the file and include the
command failure as a warning instead of dropping the result.
For Manim presentation agents specifically, generate a stable PDF fallback For Manim presentation agents specifically, generate a stable PDF fallback
that does not require Manim rendering. If HTML/video rendering is requested, that does not require Manim rendering. If HTML/video rendering is requested,

View File

@@ -130,9 +130,10 @@ If a skill calls `ctx.workspace_backend()` or `ctx.workspace`, declare
If a skill runs code, renderers, converters, or data tools that create If a skill runs code, renderers, converters, or data tools that create
downloadable files, run them through `await ctx.workspace_shell(...)` or downloadable files, run them through `await ctx.workspace_shell(...)` or
`await ctx.workspace_python(...)` and make the command write under `await ctx.workspace_python(...)`. The sandbox persists `/workspace` writes
`/workspace/outputs/...`. In-process subprocesses write inside the agent pod, directly and mirrors changed files elsewhere in the guest rootfs under
not the caller's mounted workspace. `outputs/rootfs-captures/...`. In-process subprocesses write inside the agent
pod, not the caller's mounted or rootfs-captured workspace.
When invoking an inner DeepAgents graph, pass When invoking an inner DeepAgents graph, pass
`config={"recursion_limit": 500}` to `graph.ainvoke(...)` or `config={"recursion_limit": 500}` to `graph.ainvoke(...)` or

View File

@@ -38,8 +38,9 @@ Check these items:
- Deterministic tools do exact work; no fake canned-response tools. - Deterministic tools do exact work; no fake canned-response tools.
- File-producing skills call `ctx.write_artifact` and `ctx.emit_artifact`. - File-producing skills call `ctx.write_artifact` and `ctx.emit_artifact`.
- Commands that create downloadable outputs run through `ctx.workspace_shell` - Commands that create downloadable outputs run through `ctx.workspace_shell`
or `ctx.workspace_python` and write under `/workspace/outputs/...`. Plain or `ctx.workspace_python`; `/workspace` writes persist directly and other
subprocesses in the agent container are not durable workspace writes. changed sandbox rootfs files are mirrored under `outputs/rootfs-captures/...`.
Plain subprocesses in the agent container are not durable workspace writes.
- External calls, secrets, workspace access, pricing, and resources are - External calls, secrets, workspace access, pricing, and resources are
declared on the class. declared on the class.

View File

@@ -25,7 +25,8 @@ RUNTIME_SKILLS_ROOT = "/outputs/research-agent/.deepagents/skills/"
SYSTEM_PROMPT = """\ SYSTEM_PROMPT = """\
You are a careful research agent. Use project skills for workflow rules and You are a careful research agent. Use project skills for workflow rules and
write durable files under /workspace/outputs/. use workspace-backed tools for durable files. Prefer /workspace/outputs/ for
stable output paths.
""" """

View File

@@ -29,9 +29,9 @@ backend = ctx.workspace_backend()
graph = create_deep_agent(model=model, backend=backend, skills=skill_sources or None) graph = create_deep_agent(model=model, backend=backend, skills=skill_sources or None)
``` ```
Write durable model-created files under `/workspace/outputs/...` in prompts. For stable, human-readable paths, ask model-created files to use
When writing through `ctx.workspace`, use workspace-relative paths such as `/workspace/outputs/...`. When writing through `ctx.workspace`, use
`outputs/report.md`. workspace-relative paths such as `outputs/report.md`.
## Artifact Pattern ## Artifact Pattern
@@ -77,10 +77,12 @@ async def run_checked(script: str, timeout_s: int = 120) -> dict[str, str | int]
} }
``` ```
Commands should write user-visible files under `/workspace/outputs/...`; that Commands may write to `/workspace/outputs/...` for stable paths, or to normal
path is backed by the caller's workspace bucket. Do not use tool defaults such as `/tmp`, `/root`, or `/app`. The platform sandbox mirrors
`asyncio.create_subprocess_exec(...)` for durable outputs. Plain subprocesses changed files outside `/workspace` into `outputs/rootfs-captures/...`. Do not
run inside the agent container, which is not mounted to the caller workspace. use `asyncio.create_subprocess_exec(...)` for durable outputs. Plain subprocesses
run inside the agent container, which is not mounted to the caller workspace and
is not rootfs-captured.
If a nonzero command still produced a usable output, preserve the output and If a nonzero command still produced a usable output, preserve the output and
include the command failure as a warning instead of discarding the file. include the command failure as a warning instead of discarding the file.

View File

@@ -1,4 +1,4 @@
a2a-pack>=0.1.8 a2a-pack>=0.1.9
httpx>=0.27 httpx>=0.27
boto3>=1.34 boto3>=1.34
deepagents>=0.5.0 deepagents>=0.5.0

View File

@@ -38,8 +38,8 @@ class BuilderPromptTests(unittest.TestCase):
def test_prompt_requires_workspace_mounted_execution_for_outputs(self) -> None: def test_prompt_requires_workspace_mounted_execution_for_outputs(self) -> None:
self.assertIn("ctx.workspace_shell", SYSTEM_PROMPT) self.assertIn("ctx.workspace_shell", SYSTEM_PROMPT)
self.assertIn("ctx.workspace_python", SYSTEM_PROMPT) self.assertIn("ctx.workspace_python", SYSTEM_PROMPT)
self.assertIn("/workspace/outputs/...", SYSTEM_PROMPT) self.assertIn("outputs/rootfs-captures/...", SYSTEM_PROMPT)
self.assertIn("not workspace-mounted", SYSTEM_PROMPT) self.assertIn("not workspace-mounted or rootfs-captured", SYSTEM_PROMPT)
def test_prompt_and_backend_load_packaged_builder_skills(self) -> None: def test_prompt_and_backend_load_packaged_builder_skills(self) -> None:
expected = { expected = {
@@ -66,7 +66,7 @@ class BuilderPromptTests(unittest.TestCase):
self.assertIn('config={"recursion_limit": 500}', files["deepagents-implementation-patterns/SKILL.md"]) self.assertIn('config={"recursion_limit": 500}', files["deepagents-implementation-patterns/SKILL.md"])
self.assertIn("ctx.write_artifact", files["workspace-artifact-safety/SKILL.md"]) self.assertIn("ctx.write_artifact", files["workspace-artifact-safety/SKILL.md"])
self.assertIn("ctx.workspace_shell", files["workspace-artifact-safety/SKILL.md"]) self.assertIn("ctx.workspace_shell", files["workspace-artifact-safety/SKILL.md"])
self.assertIn("/workspace/outputs/...", files["workspace-artifact-safety/SKILL.md"]) self.assertIn("outputs/rootfs-captures/...", files["workspace-artifact-safety/SKILL.md"])
self.assertIn("live_skills[].input_schema", files["agent-quality-review/SKILL.md"]) self.assertIn("live_skills[].input_schema", files["agent-quality-review/SKILL.md"])
self.assertIn("ctx.workspace_python", files["agent-quality-review/SKILL.md"]) self.assertIn("ctx.workspace_python", files["agent-quality-review/SKILL.md"])