feat: describe the sandbox to the model in a tagged <code-mode> block - #14
Merged
Conversation
The sandbox now reports its Python environment after boot so models can use packages baked into the image without host-side configuration. - Add Python version and deterministic import-to-distribution version mappings to `ReadyFrame` for both TCP and WebSocket transports. - Cache reported environments by backend identity and render sorted `<python-version>` and `<installed-packages>` metadata. - Add full, names-only, and discovery-pointer catalog tiers so oversized tool surfaces retain environment metadata. - Rename the metadata-related `ExecuteCodeTool` options and keep the tool description aligned with the configured prompt surface. - Document the tagged block and add coverage for protocol compatibility, cache isolation, deterministic rendering, and tier boundaries.
benclarkeio
force-pushed
the
feat/code-mode-metadata-block
branch
from
July 23, 2026 23:04
9beab96 to
0978036
Compare
<code-mode> block<code-mode> block
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The model was never told which Python version or third-party packages the sandbox image has. Worse,
execute_code's description actively claimed "The Python Standard Library and a set of custom tools are available" — so a model withpandasbaked into the image would still hand-roll CSV parsing.The README already documents
RUN pip install pandasas the supported way to extend the image; this closes the loop to the prompt.How
The sandbox reports its own environment on connect, so anything baked into the image is advertised with no host-side configuration.
Wire protocol
ReadyFramegainspython_versionandpackages, populated by a newready_frame()helper used by both the TCP and WebSocket transports.packagesmaps each top-level import name to the distribution or distributions that provide it and their versions, so names such asyaml/PyYAMLare unambiguous.These are additive fields with defaults, so no
PROTOCOL_VERSIONbump:decodealready drops unknown keys, and a sandbox predating the fields just leaves them empty. Mixed host/sandbox pins keep working.Caching
adk_code_mode.metadatacaches each sandbox's reported environment keyed by backend identity (RemoteBackend.url/UnsafeLocalDockerBackend.image). Module-level rather than onExecuteCodeTool, so one warm process serves every agent pointing at the same image and the value survives a tool being rebuilt.The report only arrives once a container has booted, so the block changes exactly once per process — at the second model call of the first turn that runs code. Recording is monotonic, while import names and their providing distributions are both sorted before rendering so the block stays byte-stable from then on. This keeps the provider's cached prompt prefix intact.
Block structure
Tags are indented, their content is not — the
<tools-package>body is Python source, where leading whitespace is meaningful. Unknown or empty tags are omitted rather than rendered blank, since an empty tag reads as "none exist".Tiering
The all-or-nothing size gate is gone. A block over budget now degrades:
render_catalog(detail="names"))/tools/— unconditional, so an oversized tool surface no longer costs the model the Python version and package listTwo surfaces, kept apart
The tool description is now the invariant contract of calling
execute_code; the block is the per-deployment inventory. Its discovery pointer is swapped at construction so it never references a block that wasn't appended.API renames
append_function_stubs_to_system_instructionappend_code_mode_metadata_to_system_instructionmax_catalog_charsmax_code_mode_metadata_chars(now budgets the whole block, tags included)Testing
tests/test_metadata.py: cache isolation per identity, monotonic recording, deterministic rendering regardless of input order, omitted empty/unknown tags, all three tiers, exact-budget boundary, indentation.tests/test_sandbox_entry.py: top-level import mapping, multiple providers, deterministic provider ordering, runtime-package filtering.tests/test_docker_integration.py: the Python version reaches the block only after a real container boots.make cigreen locally: 148 passed, Ruff and mypy clean.🤖 Generated with Claude Code