adk-code-mode 1.0.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. adk_code_mode-1.2.0/.release-please-manifest.json +3 -0
  2. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/CHANGELOG.md +14 -0
  3. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/PKG-INFO +1 -1
  4. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/__about__.py +1 -1
  5. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/__about__.py +1 -1
  6. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/_artifact_tools.py +4 -1
  7. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tool.py +14 -0
  8. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_artifact_tools.py +42 -8
  9. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_tool.py +10 -0
  10. adk_code_mode-1.0.0/.release-please-manifest.json +0 -3
  11. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.githooks/pre-commit +0 -0
  12. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.githooks/pre-push +0 -0
  13. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.github/workflows/ci.yml +0 -0
  14. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.github/workflows/release-please.yml +0 -0
  15. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.gitignore +0 -0
  16. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.prettierrc +0 -0
  17. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.vscode/settings.json +0 -0
  18. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/.zed/settings.json +0 -0
  19. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/AGENTS.md +0 -0
  20. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/CLAUDE.md +0 -0
  21. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/LICENSE +0 -0
  22. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/Makefile +0 -0
  23. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/README.md +0 -0
  24. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/docker/Dockerfile +0 -0
  25. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/pyproject.toml +0 -0
  26. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/release-please-config.json +0 -0
  27. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/README.md +0 -0
  28. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/pyproject.toml +0 -0
  29. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/__init__.py +0 -0
  30. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/__main__.py +0 -0
  31. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/_entry.py +0 -0
  32. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/_http_server.py +0 -0
  33. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/_rpc_client.py +0 -0
  34. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/sandbox-wheel/src/adk_code_mode_sandbox/protocol.py +0 -0
  35. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/scripts/install-git-hooks.sh +0 -0
  36. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/__init__.py +0 -0
  37. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/output.py +0 -0
  38. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/py.typed +0 -0
  39. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/runtime/__init__.py +0 -0
  40. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/runtime/base.py +0 -0
  41. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/runtime/docker.py +0 -0
  42. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/runtime/protocol.py +0 -0
  43. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/runtime/remote.py +0 -0
  44. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tool_result_artifacts.py +0 -0
  45. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tools/__init__.py +0 -0
  46. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tools/catalog.py +0 -0
  47. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tools/dispatcher.py +0 -0
  48. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tools/namespacing.py +0 -0
  49. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tools/normaliser.py +0 -0
  50. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/tools/stubs.py +0 -0
  51. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/workspace/__init__.py +0 -0
  52. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/src/adk_code_mode/workspace/files.py +0 -0
  53. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/__init__.py +0 -0
  54. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/_docker_helpers.py +0 -0
  55. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/_fake_runtime.py +0 -0
  56. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/conftest.py +0 -0
  57. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_agent_with_fake_llm.py +0 -0
  58. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_catalog.py +0 -0
  59. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_dispatcher.py +0 -0
  60. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_docker_image.py +0 -0
  61. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_docker_integration.py +0 -0
  62. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_host_loop.py +0 -0
  63. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_integration.py +0 -0
  64. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_namespacing.py +0 -0
  65. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_normaliser.py +0 -0
  66. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_output.py +0 -0
  67. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_protocol.py +0 -0
  68. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_remote_backend.py +0 -0
  69. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_remote_integration.py +0 -0
  70. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_runtime_docker.py +0 -0
  71. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_sandbox_rpc_client.py +0 -0
  72. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_stubs.py +0 -0
  73. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_tool_result_artifacts.py +0 -0
  74. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_version.py +0 -0
  75. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/tests/test_workspace_files.py +0 -0
  76. {adk_code_mode-1.0.0 → adk_code_mode-1.2.0}/uv.lock +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.2.0"
3
+ }
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0](https://github.com/a2anet/adk-code-mode/compare/adk-code-mode-v1.1.0...adk-code-mode-v1.2.0) (2026-07-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * add `custom_metadata` to the `save_artifact` tool ([dfd9a59](https://github.com/a2anet/adk-code-mode/commit/dfd9a59bf1718084a1d5c02cc1ea453269223f16))
9
+
10
+ ## [1.1.0](https://github.com/a2anet/adk-code-mode/compare/adk-code-mode-v1.0.0...adk-code-mode-v1.1.0) (2026-07-15)
11
+
12
+
13
+ ### Features
14
+
15
+ * expose `tools` and `backend` as public properties on `ExecuteCodeTool` ([acfb704](https://github.com/a2anet/adk-code-mode/commit/acfb704d388da5d66e318775e03fe055467679c2))
16
+
3
17
  ## [1.0.0](https://github.com/a2anet/adk-code-mode/compare/adk-code-mode-v0.4.1...adk-code-mode-v1.0.0) (2026-07-15)
4
18
 
5
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adk-code-mode
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: A "Code Mode" sandboxed code-execution tool for ADK agents to interact with tools, files, and custom packages with Python
5
5
  Project-URL: Documentation, https://github.com/A2ANet/adk-code-mode#readme
6
6
  Project-URL: Issues, https://github.com/A2ANet/adk-code-mode/issues
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = "1.0.0" # x-release-please-version
4
+ __version__ = "1.2.0" # x-release-please-version
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = "1.0.0" # x-release-please-version
4
+ __version__ = "1.2.0" # x-release-please-version
@@ -43,6 +43,7 @@ async def save_artifact(
43
43
  filename: str,
44
44
  content: str,
45
45
  mime_type: str | None = None,
46
+ custom_metadata: dict[str, Any] | None = None,
46
47
  tool_context: ToolContext,
47
48
  ) -> int:
48
49
  """Save an artifact to the session. Returns the new version number.
@@ -62,6 +63,8 @@ async def save_artifact(
62
63
  content=base64.b64encode(image_bytes).decode("ascii"),
63
64
  mime_type="image/png",
64
65
  )
66
+
67
+ ``custom_metadata`` is custom metadata to associate with the artifact.
65
68
  """
66
69
  if _is_text_mime(mime_type):
67
70
  part = genai_types.Part(
@@ -77,7 +80,7 @@ async def save_artifact(
77
80
  mime_type=mime_type or "application/octet-stream",
78
81
  )
79
82
  )
80
- return await tool_context.save_artifact(filename, part)
83
+ return await tool_context.save_artifact(filename, part, custom_metadata=custom_metadata)
81
84
 
82
85
 
83
86
  async def load_artifact(
@@ -294,6 +294,20 @@ class ExecuteCodeTool(BaseTool):
294
294
  if eager_resolved:
295
295
  namespacing.build(eager_resolved)
296
296
 
297
+ @property
298
+ def tools(self) -> Sequence[BaseTool | BaseToolset]:
299
+ """Tools and toolsets available inside the sandbox, including built-in artifact tools.
300
+
301
+ Host apps that own a toolset's lifecycle (e.g. an ``McpToolset`` needing
302
+ ``close()`` on shutdown) need this to find it again after construction.
303
+ """
304
+ return self._tools
305
+
306
+ @property
307
+ def backend(self) -> SandboxBackend:
308
+ """The configured sandbox runtime backend."""
309
+ return self._backend
310
+
297
311
  def _get_declaration(self) -> genai_types.FunctionDeclaration | None:
298
312
  return genai_types.FunctionDeclaration(
299
313
  name=self.name,
@@ -34,12 +34,17 @@ class _StubToolContext:
34
34
  ) -> None:
35
35
  self._load_result = load_result
36
36
  self._list_result = list_result or []
37
- self.saved: list[tuple[str, genai_types.Part]] = []
37
+ self.saved: list[tuple[str, genai_types.Part, dict[str, Any] | None]] = []
38
38
  self.loaded: list[tuple[str, int | None]] = []
39
39
  self.listed = 0
40
40
 
41
- async def save_artifact(self, filename: str, part: genai_types.Part) -> int:
42
- self.saved.append((filename, part))
41
+ async def save_artifact(
42
+ self,
43
+ filename: str,
44
+ part: genai_types.Part,
45
+ custom_metadata: dict[str, Any] | None = None,
46
+ ) -> int:
47
+ self.saved.append((filename, part, custom_metadata))
43
48
  return len(self.saved)
44
49
 
45
50
  async def load_artifact(
@@ -65,7 +70,7 @@ async def test_save_artifact_encodes_text_mime_as_utf8() -> None:
65
70
 
66
71
  assert version == 1
67
72
  assert len(ctx.saved) == 1
68
- name, part = ctx.saved[0]
73
+ name, part, _ = ctx.saved[0]
69
74
  assert name == "report.json"
70
75
  assert part.inline_data is not None
71
76
  assert part.inline_data.mime_type == "application/json"
@@ -82,7 +87,7 @@ async def test_save_artifact_treats_text_plus_subtype_as_text() -> None:
82
87
  tool_context=ctx, # type: ignore[arg-type]
83
88
  )
84
89
 
85
- _, part = ctx.saved[0]
90
+ _, part, _ = ctx.saved[0]
86
91
  assert part.inline_data is not None
87
92
  assert part.inline_data.data == b"<svg/>"
88
93
 
@@ -98,7 +103,7 @@ async def test_save_artifact_decodes_base64_for_binary_mime() -> None:
98
103
  tool_context=ctx, # type: ignore[arg-type]
99
104
  )
100
105
 
101
- _, part = ctx.saved[0]
106
+ _, part, _ = ctx.saved[0]
102
107
  assert part.inline_data is not None
103
108
  assert part.inline_data.data == raw
104
109
  assert part.inline_data.mime_type == "application/octet-stream"
@@ -114,12 +119,41 @@ async def test_save_artifact_defaults_mime_when_none() -> None:
114
119
  tool_context=ctx, # type: ignore[arg-type]
115
120
  )
116
121
 
117
- _, part = ctx.saved[0]
122
+ _, part, _ = ctx.saved[0]
118
123
  assert part.inline_data is not None
119
124
  assert part.inline_data.mime_type == "application/octet-stream"
120
125
  assert part.inline_data.data == raw
121
126
 
122
127
 
128
+ @pytest.mark.asyncio
129
+ async def test_save_artifact_forwards_custom_metadata() -> None:
130
+ ctx = _StubToolContext()
131
+ await save_artifact(
132
+ filename="report.md",
133
+ content="# Report",
134
+ mime_type="text/markdown",
135
+ custom_metadata={"a2anet_agent_run.send_as_a2a_artifact": "true"},
136
+ tool_context=ctx, # type: ignore[arg-type]
137
+ )
138
+
139
+ _, _, custom_metadata = ctx.saved[0]
140
+ assert custom_metadata == {"a2anet_agent_run.send_as_a2a_artifact": "true"}
141
+
142
+
143
+ @pytest.mark.asyncio
144
+ async def test_save_artifact_defaults_custom_metadata_to_none() -> None:
145
+ ctx = _StubToolContext()
146
+ await save_artifact(
147
+ filename="scratch.txt",
148
+ content="notes",
149
+ mime_type="text/plain",
150
+ tool_context=ctx, # type: ignore[arg-type]
151
+ )
152
+
153
+ _, _, custom_metadata = ctx.saved[0]
154
+ assert custom_metadata is None
155
+
156
+
123
157
  @pytest.mark.asyncio
124
158
  async def test_load_artifact_returns_text_kind_for_text_mime() -> None:
125
159
  part = genai_types.Part(
@@ -196,4 +230,4 @@ def test_artifact_tools_declarations_omit_tool_context_param() -> None:
196
230
  assert save_decl is not None
197
231
  properties: dict[str, Any] = save_decl.parameters.properties or {} # type: ignore[union-attr]
198
232
  assert "tool_context" not in properties
199
- assert {"filename", "content"}.issubset(properties.keys())
233
+ assert {"filename", "content", "custom_metadata"}.issubset(properties.keys())
@@ -69,6 +69,16 @@ def test_declaration_exposes_a_single_code_parameter() -> None:
69
69
  assert decl.parameters_json_schema["required"] == ["code"]
70
70
 
71
71
 
72
+ def test_tools_and_backend_properties_expose_the_configured_surface() -> None:
73
+ schema_tool = _SchemaTool("ping", "Ping.")
74
+ backend = FakeRuntime()
75
+
76
+ tool = ExecuteCodeTool(tools=[schema_tool], backend=backend)
77
+
78
+ assert schema_tool in tool.tools
79
+ assert tool.backend is backend
80
+
81
+
72
82
  @pytest.mark.asyncio
73
83
  async def test_process_llm_request_declares_the_tool() -> None:
74
84
  tool = _make_tool([])
@@ -1,3 +0,0 @@
1
- {
2
- ".": "1.0.0"
3
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes