adk-code-mode 1.6.1__tar.gz → 2.0.1__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 (86) hide show
  1. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.github/workflows/release-please.yml +2 -2
  2. adk_code_mode-2.0.1/.release-please-manifest.json +3 -0
  3. adk_code_mode-2.0.1/CHANGELOG.md +169 -0
  4. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/PKG-INFO +15 -14
  5. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/README.md +10 -9
  6. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/docker/Dockerfile +1 -1
  7. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/pyproject.toml +4 -4
  8. adk_code_mode-2.0.1/sandbox-wheel/README.md +14 -0
  9. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/pyproject.toml +4 -4
  10. adk_code_mode-2.0.1/sandbox-wheel/src/adk_code_mode_sandbox/__about__.py +4 -0
  11. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/src/adk_code_mode_sandbox/__init__.py +1 -1
  12. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/src/adk_code_mode_sandbox/__main__.py +1 -1
  13. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/src/adk_code_mode_sandbox/_entry.py +57 -7
  14. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/src/adk_code_mode_sandbox/_http_server.py +5 -3
  15. adk_code_mode-2.0.1/sandbox-wheel/src/adk_code_mode_sandbox/_interrupt.py +67 -0
  16. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/src/adk_code_mode_sandbox/_rpc_client.py +20 -2
  17. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/sandbox-wheel/src/adk_code_mode_sandbox/protocol.py +19 -2
  18. adk_code_mode-2.0.1/src/adk_code_mode/__about__.py +4 -0
  19. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/__init__.py +1 -1
  20. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/_artifact_tools.py +1 -1
  21. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/metadata.py +1 -1
  22. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/output.py +1 -1
  23. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/runtime/__init__.py +1 -1
  24. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/runtime/base.py +2 -1
  25. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/runtime/docker.py +2 -2
  26. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/runtime/protocol.py +19 -2
  27. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/runtime/remote.py +1 -1
  28. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tool.py +141 -43
  29. adk_code_mode-2.0.1/src/adk_code_mode/tool_failures.py +54 -0
  30. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tool_result_artifacts.py +4 -1
  31. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tools/__init__.py +1 -1
  32. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tools/catalog.py +1 -1
  33. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tools/dispatcher.py +16 -2
  34. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tools/namespacing.py +1 -1
  35. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tools/normaliser.py +1 -1
  36. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/tools/stubs.py +15 -9
  37. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/workspace/__init__.py +1 -1
  38. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/workspace/files.py +1 -1
  39. adk_code_mode-2.0.1/tests/__init__.py +3 -0
  40. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/_docker_helpers.py +1 -1
  41. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/_fake_runtime.py +1 -1
  42. adk_code_mode-2.0.1/tests/_rest_tool.py +49 -0
  43. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/conftest.py +1 -1
  44. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_agent_with_fake_llm.py +1 -1
  45. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_artifact_tools.py +1 -1
  46. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_catalog.py +1 -1
  47. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_dispatcher.py +107 -1
  48. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_docker_image.py +4 -1
  49. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_docker_integration.py +1 -1
  50. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_host_loop.py +66 -4
  51. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_integration.py +91 -8
  52. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_metadata.py +1 -1
  53. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_namespacing.py +1 -1
  54. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_normaliser.py +1 -1
  55. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_output.py +1 -1
  56. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_protocol.py +16 -1
  57. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_remote_backend.py +1 -1
  58. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_remote_integration.py +115 -4
  59. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_runtime_docker.py +1 -1
  60. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_sandbox_entry.py +29 -1
  61. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_sandbox_rpc_client.py +51 -2
  62. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_stubs.py +8 -6
  63. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_stubs_json_schema.py +1 -1
  64. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_tool.py +7 -1
  65. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_tool_result_artifacts.py +1 -1
  66. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_version.py +1 -1
  67. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/tests/test_workspace_files.py +1 -1
  68. adk_code_mode-1.6.1/.release-please-manifest.json +0 -3
  69. adk_code_mode-1.6.1/CHANGELOG.md +0 -150
  70. adk_code_mode-1.6.1/sandbox-wheel/README.md +0 -14
  71. adk_code_mode-1.6.1/sandbox-wheel/src/adk_code_mode_sandbox/__about__.py +0 -4
  72. adk_code_mode-1.6.1/src/adk_code_mode/__about__.py +0 -4
  73. adk_code_mode-1.6.1/tests/__init__.py +0 -3
  74. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.githooks/pre-commit +0 -0
  75. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.githooks/pre-push +0 -0
  76. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.github/workflows/ci.yml +0 -0
  77. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.gitignore +0 -0
  78. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.prettierrc +0 -0
  79. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.vscode/settings.json +0 -0
  80. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/.zed/settings.json +0 -0
  81. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/LICENSE +0 -0
  82. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/Makefile +0 -0
  83. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/release-please-config.json +0 -0
  84. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/scripts/install-git-hooks.sh +0 -0
  85. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/src/adk_code_mode/py.typed +0 -0
  86. {adk_code_mode-1.6.1 → adk_code_mode-2.0.1}/uv.lock +0 -0
@@ -112,5 +112,5 @@ jobs:
112
112
  file: docker/Dockerfile
113
113
  push: true
114
114
  tags: |
115
- ghcr.io/a2anet/adk-code-mode:${{ needs.release-please.outputs.version }}
116
- ghcr.io/a2anet/adk-code-mode:latest
115
+ ghcr.io/buildagenta/adk-code-mode:${{ needs.release-please.outputs.version }}
116
+ ghcr.io/buildagenta/adk-code-mode:latest
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "2.0.1"
3
+ }
@@ -0,0 +1,169 @@
1
+ # Changelog
2
+
3
+ ## [2.0.1](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v2.0.0...adk-code-mode-v2.0.1) (2026-11-02)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * credit Agenta as the author ([#25](https://github.com/buildagenta/adk-code-mode/issues/25)) ([bfe577e](https://github.com/buildagenta/adk-code-mode/commit/bfe577e9aaba31ed9d8f6423485b061deb77b963))
9
+
10
+ ## [2.0.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.6.1...adk-code-mode-v2.0.0) (2026-09-25)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * sandbox code that checked a tool result for an `error` key now gets an exception instead, and the sandbox image must be rebuilt for protocol version 3.
16
+
17
+ ### Features
18
+
19
+ * raise when a tool call fails instead of returning the error ([#22](https://github.com/buildagenta/adk-code-mode/issues/22)) ([489083d](https://github.com/buildagenta/adk-code-mode/commit/489083d988cd7e958e94f3ef6acba6443284abd8))
20
+ * stop a timed-out block without losing the turn's state ([#23](https://github.com/buildagenta/adk-code-mode/issues/23)) ([4720156](https://github.com/buildagenta/adk-code-mode/commit/47201568d0bb06527e32f850b7abb1b465e36920))
21
+
22
+ ## [1.6.1](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.6.0...adk-code-mode-v1.6.1) (2026-09-22)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * exit the sandbox when the host disconnects mid-block ([bd3fb0f](https://github.com/buildagenta/adk-code-mode/commit/bd3fb0fb0d8a0cab9370f2ad6db56dfacf566cb4))
28
+
29
+ ## [1.6.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.5.0...adk-code-mode-v1.6.0) (2026-08-29)
30
+
31
+
32
+ ### Features
33
+
34
+ * bound an execute_code block by default ([1609c0d](https://github.com/buildagenta/adk-code-mode/commit/1609c0d24b8563cba51434cd1214b4299aeefc86))
35
+ * expand object oneOf/anyOf branches in tool stub docstrings ([#20](https://github.com/buildagenta/adk-code-mode/issues/20)) ([7d42cb1](https://github.com/buildagenta/adk-code-mode/commit/7d42cb1ee9d9e217d008776dc74a84034f4b67a9))
36
+
37
+ ## [1.5.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.4.0...adk-code-mode-v1.5.0) (2026-08-10)
38
+
39
+
40
+ ### Features
41
+
42
+ * scope tool-name uniqueness to each namespace ([#17](https://github.com/buildagenta/adk-code-mode/issues/17)) ([437bc0d](https://github.com/buildagenta/adk-code-mode/commit/437bc0dc930521a933e5c25b4bd6103e9434fc27))
43
+
44
+ ## [1.4.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.3.0...adk-code-mode-v1.4.0) (2026-07-28)
45
+
46
+
47
+ ### Features
48
+
49
+ * surface nested schema properties and validation keywords in tool stubs ([608d14b](https://github.com/buildagenta/adk-code-mode/commit/608d14bb115159b2991c7d8d4e3c641977fd0d66))
50
+
51
+
52
+ ### Documentation
53
+
54
+ * document stub schema fidelity and the `JSON_SCHEMA_FOR_FUNC_DECL` flag in `README.md` ([219f02b](https://github.com/buildagenta/adk-code-mode/commit/219f02bd493c2e2d7275a0034ef2a40db933859f))
55
+
56
+ ## [1.3.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.2.0...adk-code-mode-v1.3.0) (2026-07-23)
57
+
58
+
59
+ ### Features
60
+
61
+ * describe the sandbox to the model in a tagged `<code-mode>` block ([#14](https://github.com/buildagenta/adk-code-mode/issues/14)) ([662a281](https://github.com/buildagenta/adk-code-mode/commit/662a28138e8632b434e5f1ecd52793f7d83ee097))
62
+
63
+ ## [1.2.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.1.0...adk-code-mode-v1.2.0) (2026-07-23)
64
+
65
+
66
+ ### Features
67
+
68
+ * add `custom_metadata` to the `save_artifact` tool ([dfd9a59](https://github.com/buildagenta/adk-code-mode/commit/dfd9a59bf1718084a1d5c02cc1ea453269223f16))
69
+
70
+ ## [1.1.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v1.0.0...adk-code-mode-v1.1.0) (2026-07-15)
71
+
72
+
73
+ ### Features
74
+
75
+ * expose `tools` and `backend` as public properties on `ExecuteCodeTool` ([acfb704](https://github.com/buildagenta/adk-code-mode/commit/acfb704d388da5d66e318775e03fe055467679c2))
76
+
77
+ ## [1.0.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.4.1...adk-code-mode-v1.0.0) (2026-07-15)
78
+
79
+
80
+ ### ⚠ BREAKING CHANGES
81
+
82
+ * `CodeModeCodeExecutor`, `code_mode_before_model_callback`, and `CODE_MODE_SYSTEM_INSTRUCTION` no longer exist. Add `ExecuteCodeTool` to the agent's `tools=[...]` instead of wiring `code_executor=`; drop `before_model_callback=`, `generate_content_config=`, and `CODE_MODE_SYSTEM_INSTRUCTION` entirely; `release_invocation` is now `async`. See the README's migration section.
83
+
84
+ ### Features
85
+
86
+ * replace `CodeModeCodeExecutor` with `ExecuteCodeTool` ([#10](https://github.com/buildagenta/adk-code-mode/issues/10)) ([b78572e](https://github.com/buildagenta/adk-code-mode/commit/b78572e72446930548760756255390d1a3cc388a))
87
+
88
+ ## [0.4.1](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.4.0...adk-code-mode-v0.4.1) (2026-07-10)
89
+
90
+
91
+ ### Documentation
92
+
93
+ * note the sandbox writes to `/tools` in remote deployment guide ([96bb45e](https://github.com/buildagenta/adk-code-mode/commit/96bb45e4e18d9d54709f4c10b6b09cf3567ee385))
94
+
95
+ ## [0.4.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.3.0...adk-code-mode-v0.4.0) (2026-07-09)
96
+
97
+
98
+ ### Features
99
+
100
+ * save tool results as artifacts with optional model-supplied naming ([76a3671](https://github.com/buildagenta/adk-code-mode/commit/76a36713b2c52bd5b8dc51d3669226a47e7108e5))
101
+
102
+
103
+ ### Bug Fixes
104
+
105
+ * align code-mode example with the `<code-mode>` catalog and fix fences ([4a0cc6d](https://github.com/buildagenta/adk-code-mode/commit/4a0cc6d0fc99d5425f6682cc489e76c9ddef7d46))
106
+ * clarify code-mode tools are a Python library, not function tools ([48d1018](https://github.com/buildagenta/adk-code-mode/commit/48d1018152a2ed2a8e7361de6ce0f43b31b429ce))
107
+ * nudge the model to call tools via Python in the code-mode block ([141baf3](https://github.com/buildagenta/adk-code-mode/commit/141baf3a44976e82cf437b2064666be893d6d409))
108
+ * update `CODE_MODE_SYSTEM_INSTRUCTION` and `README.md` ([933d42c](https://github.com/buildagenta/adk-code-mode/commit/933d42cb237b9d6deeb6611511cba28a0d5e9261))
109
+
110
+
111
+ ### Documentation
112
+
113
+ * streamline `README.md` and fix broken code fences ([c237514](https://github.com/buildagenta/adk-code-mode/commit/c2375149df48e21318967090b6c85d0b1f411ea2))
114
+
115
+ ## [0.3.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.2.2...adk-code-mode-v0.3.0) (2026-07-06)
116
+
117
+
118
+ ### Features
119
+
120
+ * hold one sandbox container open per turn (protocol v2) ([#6](https://github.com/buildagenta/adk-code-mode/issues/6)) ([aebe17d](https://github.com/buildagenta/adk-code-mode/commit/aebe17d2bbbf21cc0f05852efaf910ff6adc3154))
121
+
122
+
123
+ ### Documentation
124
+
125
+ * lower startup probe period in `README.md` for faster cold starts ([e251af2](https://github.com/buildagenta/adk-code-mode/commit/e251af2cf7307c9944361e1a0e37c7191822a9e1))
126
+ * note HTTP `/health` startup probe in `README.md` Cloud Run section ([d58fb10](https://github.com/buildagenta/adk-code-mode/commit/d58fb10f7aeda95e5c8cf29263a13e1333f661dd))
127
+
128
+ ## [0.2.2](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.2.1...adk-code-mode-v0.2.2) (2026-05-08)
129
+
130
+
131
+ ### Bug Fixes
132
+
133
+ * use `python` fences instead of `tool_code` in `CodeModeCodeExecutor` ([efcc11e](https://github.com/buildagenta/adk-code-mode/commit/efcc11e1020c7544223074e00a8649f9c85d4ad6))
134
+
135
+ ## [0.2.1](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.2.0...adk-code-mode-v0.2.1) (2026-05-05)
136
+
137
+
138
+ ### Bug Fixes
139
+
140
+ * rename `<tools>` tag to `<code-mode>` and update `CODE_MODE_SYSTEM_INSTRUCTION` ([0a06bfb](https://github.com/buildagenta/adk-code-mode/commit/0a06bfbdf22db6e0226e477d0ed074a6cc4d1515))
141
+
142
+
143
+ ### Documentation
144
+
145
+ * update `README.md` with deployment instructions ([1dd7a95](https://github.com/buildagenta/adk-code-mode/commit/1dd7a95197fd56231ba97182526a825baf18508a))
146
+
147
+ ## [0.2.0](https://github.com/buildagenta/adk-code-mode/compare/adk-code-mode-v0.1.0...adk-code-mode-v0.2.0) (2026-05-03)
148
+
149
+
150
+ ### Features
151
+
152
+ * add `RemoteBackend`, sandbox hardening, and make `README.md` more concise ([a1a70ee](https://github.com/buildagenta/adk-code-mode/commit/a1a70eec0686f164ce3796ba1d82bb82b7fbda59))
153
+
154
+
155
+ ### Bug Fixes
156
+
157
+ * improve `CODE_MODE_SYSTEM_INSTRUCTION` ([0d00863](https://github.com/buildagenta/adk-code-mode/commit/0d008636e2e273b438192f737e20e207c165a610))
158
+
159
+ ## 0.1.0 (2026-04-29)
160
+
161
+
162
+ ### Features
163
+
164
+ * ADK Code Mode: `CodeModeExecutor`, `DockerRuntime`, and more ([6ab1ac2](https://github.com/buildagenta/adk-code-mode/commit/6ab1ac28236d21ecb740d8e920e08e4d9bd969c5))
165
+
166
+
167
+ ### Documentation
168
+
169
+ * shorten ADK code executor comparison table headers ([28116cf](https://github.com/buildagenta/adk-code-mode/commit/28116cf9f40b3623c31782a55885fa5c4f8c6bbf))
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: adk-code-mode
3
- Version: 1.6.1
3
+ Version: 2.0.1
4
4
  Summary: A "Code Mode" sandboxed code-execution tool for ADK agents to interact with tools, files, and custom packages with Python
5
- Project-URL: Documentation, https://github.com/A2ANet/adk-code-mode#readme
6
- Project-URL: Issues, https://github.com/A2ANet/adk-code-mode/issues
7
- Project-URL: Source, https://github.com/A2ANet/adk-code-mode
8
- Author-email: A2A Net <hello@a2anet.com>
5
+ Project-URL: Documentation, https://github.com/buildagenta/adk-code-mode#readme
6
+ Project-URL: Issues, https://github.com/buildagenta/adk-code-mode/issues
7
+ Project-URL: Source, https://github.com/buildagenta/adk-code-mode
8
+ Author-email: Agenta <hello@buildagenta.com>
9
9
  License-Expression: Apache-2.0
10
10
  License-File: LICENSE
11
11
  Keywords: a2a,adk,agents,code-mode,execute-code
@@ -39,6 +39,7 @@ Inspired by Cloudflare's [Code Mode](https://blog.cloudflare.com/code-mode/) and
39
39
  ## ✨ Features
40
40
 
41
41
  - **Call ADK tools from sandbox code** — imports against the `tools` package proxy back to the host and run through ADK's `before_tool` / `after_tool` / `on_error` callbacks and the plugin manager exactly as direct tool calls would.
42
+ - **Failed tool calls raise** — a REST tool whose API answers with an HTTP error raises `HTTPStatusError` (with `status_code` and `body`), and any other failure, including an MCP tool that reports one, raises `ToolError`. ADK returns these as values for a model to read, but sandbox code reads return values, so it would otherwise carry on as if the call had worked.
42
43
  - **Bake any Python package into the image** — extend the published base image with anything the model's code needs to `import`, no runtime `pip install` required. The sandbox reports what it has installed, so the model is told about it automatically.
43
44
  - **Cross-turn persistence via ADK Artifacts** — `save_artifact` / `load_artifact` / `list_artifacts` are auto-injected and route through your configured `ArtifactService`. Files the code creates or changes are saved as artifacts automatically too.
44
45
  - **Tool results saved as artifacts** — on by default; every tool's result is persisted as a `code_mode.tool_result` artifact (with optional model-supplied name/description) so hosts can forward outputs and large results stay out of the prompt. Opt out with `save_tool_results_as_artifacts=False`.
@@ -124,7 +125,7 @@ override_feature_enabled(FeatureName.JSON_SCHEMA_FOR_FUNC_DECL, True)
124
125
 
125
126
  tool = ExecuteCodeTool(
126
127
  tools=[my_fn_tool, McpToolset(...), OpenAPIToolset(...)],
127
- backend=UnsafeLocalDockerBackend(image="ghcr.io/a2anet/adk-code-mode:latest"),
128
+ backend=UnsafeLocalDockerBackend(image="ghcr.io/buildagenta/adk-code-mode:latest"),
128
129
  )
129
130
  ```
130
131
 
@@ -144,8 +145,8 @@ print(send_message(channel="C123", text="hi"))
144
145
  ```bash
145
146
  # Push the sandbox image to Artifact Registry
146
147
  gcloud auth configure-docker <region>-docker.pkg.dev
147
- docker pull --platform linux/amd64 ghcr.io/a2anet/adk-code-mode:latest
148
- docker tag ghcr.io/a2anet/adk-code-mode:latest \
148
+ docker pull --platform linux/amd64 ghcr.io/buildagenta/adk-code-mode:latest
149
+ docker tag ghcr.io/buildagenta/adk-code-mode:latest \
149
150
  <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
150
151
  docker push <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
151
152
 
@@ -279,10 +280,10 @@ ExecuteCodeTool(tools=..., backend=..., save_tool_results_as_artifacts=True)
279
280
 
280
281
  ## 🐳 Sandbox Image
281
282
 
282
- The published base image (`ghcr.io/a2anet/adk-code-mode`) works as-is for tools whose execution is fully host-side. To bake in extra Python packages:
283
+ The published base image (`ghcr.io/buildagenta/adk-code-mode`) works as-is for tools whose execution is fully host-side. To bake in extra Python packages:
283
284
 
284
285
  ```dockerfile
285
- FROM ghcr.io/a2anet/adk-code-mode:latest
286
+ FROM ghcr.io/buildagenta/adk-code-mode:latest
286
287
  RUN pip install --no-cache-dir pandas==2.2.*
287
288
  ```
288
289
 
@@ -300,7 +301,7 @@ All settings are `ExecuteCodeTool` constructor arguments:
300
301
  | `max_code_mode_metadata_chars` | `50_000` | Budget for the whole rendered `<code-mode>` block, tags included. Oversized blocks degrade in tiers rather than disappearing — see [What the model sees](#what-the-model-sees). |
301
302
  | `max_output_chars` | `50_000` | Caps stdout/stderr handed back to the model. Overflow is saved as a session artifact at `code_mode/stdout/<call-id>.txt` and the model sees a head-and-tail view pointing to it. |
302
303
  | `max_code_chars` | `1_000_000` | Rejects oversized code payloads before starting a container. |
303
- | `timeout_seconds` | `60` | Caps overall execution time of one `execute_code` call, and with it every tool the block calls. `None` lifts the cap, leaving the platform request timeout (e.g. Cloud Run `--timeout`) as the only bound. |
304
+ | `timeout_seconds` | `60` | Caps overall execution time of one `execute_code` call, and with it every tool the block calls. A block past the cap is stopped with its output so far, keeping the turn's variables and working directory. If it won't stop within a few seconds or leaves a worker thread or child process running, its sandbox is restarted and its output and state are lost. The model is told the cap. `None` lifts it, leaving the platform request timeout (e.g. Cloud Run `--timeout`) as the only bound. |
304
305
  | `per_tool_timeout_seconds` | `None` | Caps each individual tool call made from within the sandbox. |
305
306
  | `session_idle_timeout_seconds` | `600` | Idle reaper: closes a turn's container once it goes untouched this long. Backstop for turns that never call `release_invocation`. |
306
307
 
@@ -469,9 +470,9 @@ uv run pytest -m docker
469
470
 
470
471
  `adk-code-mode` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
471
472
 
472
- ## 🤝 Join the A2A Net Community
473
+ ## 🤝 Join the Agenta Community
473
474
 
474
- A2A Net is a site to find and share AI agents and open-source community. Join to share your A2A agents, ask questions, stay up-to-date with the latest A2A news, be the first to hear about open-source releases, tutorials, and more!
475
+ Agenta is a site to find and share AI agents and open-source community. Join to share your A2A agents, ask questions, stay up-to-date with the latest A2A news, be the first to hear about open-source releases, tutorials, and more!
475
476
 
476
- - 🌍 Site: [A2A Net](https://a2anet.com)
477
+ - 🌍 Site: [Agenta](https://buildagenta.com)
477
478
  - 🤖 Discord: [Join the Discord](https://discord.gg/674NGXpAjU)
@@ -12,6 +12,7 @@ Inspired by Cloudflare's [Code Mode](https://blog.cloudflare.com/code-mode/) and
12
12
  ## ✨ Features
13
13
 
14
14
  - **Call ADK tools from sandbox code** — imports against the `tools` package proxy back to the host and run through ADK's `before_tool` / `after_tool` / `on_error` callbacks and the plugin manager exactly as direct tool calls would.
15
+ - **Failed tool calls raise** — a REST tool whose API answers with an HTTP error raises `HTTPStatusError` (with `status_code` and `body`), and any other failure, including an MCP tool that reports one, raises `ToolError`. ADK returns these as values for a model to read, but sandbox code reads return values, so it would otherwise carry on as if the call had worked.
15
16
  - **Bake any Python package into the image** — extend the published base image with anything the model's code needs to `import`, no runtime `pip install` required. The sandbox reports what it has installed, so the model is told about it automatically.
16
17
  - **Cross-turn persistence via ADK Artifacts** — `save_artifact` / `load_artifact` / `list_artifacts` are auto-injected and route through your configured `ArtifactService`. Files the code creates or changes are saved as artifacts automatically too.
17
18
  - **Tool results saved as artifacts** — on by default; every tool's result is persisted as a `code_mode.tool_result` artifact (with optional model-supplied name/description) so hosts can forward outputs and large results stay out of the prompt. Opt out with `save_tool_results_as_artifacts=False`.
@@ -97,7 +98,7 @@ override_feature_enabled(FeatureName.JSON_SCHEMA_FOR_FUNC_DECL, True)
97
98
 
98
99
  tool = ExecuteCodeTool(
99
100
  tools=[my_fn_tool, McpToolset(...), OpenAPIToolset(...)],
100
- backend=UnsafeLocalDockerBackend(image="ghcr.io/a2anet/adk-code-mode:latest"),
101
+ backend=UnsafeLocalDockerBackend(image="ghcr.io/buildagenta/adk-code-mode:latest"),
101
102
  )
102
103
  ```
103
104
 
@@ -117,8 +118,8 @@ print(send_message(channel="C123", text="hi"))
117
118
  ```bash
118
119
  # Push the sandbox image to Artifact Registry
119
120
  gcloud auth configure-docker <region>-docker.pkg.dev
120
- docker pull --platform linux/amd64 ghcr.io/a2anet/adk-code-mode:latest
121
- docker tag ghcr.io/a2anet/adk-code-mode:latest \
121
+ docker pull --platform linux/amd64 ghcr.io/buildagenta/adk-code-mode:latest
122
+ docker tag ghcr.io/buildagenta/adk-code-mode:latest \
122
123
  <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
123
124
  docker push <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
124
125
 
@@ -252,10 +253,10 @@ ExecuteCodeTool(tools=..., backend=..., save_tool_results_as_artifacts=True)
252
253
 
253
254
  ## 🐳 Sandbox Image
254
255
 
255
- The published base image (`ghcr.io/a2anet/adk-code-mode`) works as-is for tools whose execution is fully host-side. To bake in extra Python packages:
256
+ The published base image (`ghcr.io/buildagenta/adk-code-mode`) works as-is for tools whose execution is fully host-side. To bake in extra Python packages:
256
257
 
257
258
  ```dockerfile
258
- FROM ghcr.io/a2anet/adk-code-mode:latest
259
+ FROM ghcr.io/buildagenta/adk-code-mode:latest
259
260
  RUN pip install --no-cache-dir pandas==2.2.*
260
261
  ```
261
262
 
@@ -273,7 +274,7 @@ All settings are `ExecuteCodeTool` constructor arguments:
273
274
  | `max_code_mode_metadata_chars` | `50_000` | Budget for the whole rendered `<code-mode>` block, tags included. Oversized blocks degrade in tiers rather than disappearing — see [What the model sees](#what-the-model-sees). |
274
275
  | `max_output_chars` | `50_000` | Caps stdout/stderr handed back to the model. Overflow is saved as a session artifact at `code_mode/stdout/<call-id>.txt` and the model sees a head-and-tail view pointing to it. |
275
276
  | `max_code_chars` | `1_000_000` | Rejects oversized code payloads before starting a container. |
276
- | `timeout_seconds` | `60` | Caps overall execution time of one `execute_code` call, and with it every tool the block calls. `None` lifts the cap, leaving the platform request timeout (e.g. Cloud Run `--timeout`) as the only bound. |
277
+ | `timeout_seconds` | `60` | Caps overall execution time of one `execute_code` call, and with it every tool the block calls. A block past the cap is stopped with its output so far, keeping the turn's variables and working directory. If it won't stop within a few seconds or leaves a worker thread or child process running, its sandbox is restarted and its output and state are lost. The model is told the cap. `None` lifts it, leaving the platform request timeout (e.g. Cloud Run `--timeout`) as the only bound. |
277
278
  | `per_tool_timeout_seconds` | `None` | Caps each individual tool call made from within the sandbox. |
278
279
  | `session_idle_timeout_seconds` | `600` | Idle reaper: closes a turn's container once it goes untouched this long. Backstop for turns that never call `release_invocation`. |
279
280
 
@@ -442,9 +443,9 @@ uv run pytest -m docker
442
443
 
443
444
  `adk-code-mode` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
444
445
 
445
- ## 🤝 Join the A2A Net Community
446
+ ## 🤝 Join the Agenta Community
446
447
 
447
- A2A Net is a site to find and share AI agents and open-source community. Join to share your A2A agents, ask questions, stay up-to-date with the latest A2A news, be the first to hear about open-source releases, tutorials, and more!
448
+ Agenta is a site to find and share AI agents and open-source community. Join to share your A2A agents, ask questions, stay up-to-date with the latest A2A news, be the first to hear about open-source releases, tutorials, and more!
448
449
 
449
- - 🌍 Site: [A2A Net](https://a2anet.com)
450
+ - 🌍 Site: [Agenta](https://buildagenta.com)
450
451
  - 🤖 Discord: [Join the Discord](https://discord.gg/674NGXpAjU)
@@ -1,6 +1,6 @@
1
1
  FROM python:3.13-slim
2
2
 
3
- LABEL org.opencontainers.image.source="https://github.com/a2anet/adk-code-mode"
3
+ LABEL org.opencontainers.image.source="https://github.com/buildagenta/adk-code-mode"
4
4
  LABEL org.opencontainers.image.description="Sandbox base image for ADK Code Mode"
5
5
  LABEL org.opencontainers.image.licenses="Apache-2.0"
6
6
 
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
11
11
  license = "Apache-2.0"
12
12
  keywords = ["adk", "code-mode", "execute-code", "agents", "a2a"]
13
13
  authors = [
14
- { name = "A2A Net", email = "hello@a2anet.com" },
14
+ { name = "Agenta", email = "hello@buildagenta.com" },
15
15
  ]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
@@ -47,9 +47,9 @@ dev = [
47
47
  ]
48
48
 
49
49
  [project.urls]
50
- Documentation = "https://github.com/A2ANet/adk-code-mode#readme"
51
- Issues = "https://github.com/A2ANet/adk-code-mode/issues"
52
- Source = "https://github.com/A2ANet/adk-code-mode"
50
+ Documentation = "https://github.com/buildagenta/adk-code-mode#readme"
51
+ Issues = "https://github.com/buildagenta/adk-code-mode/issues"
52
+ Source = "https://github.com/buildagenta/adk-code-mode"
53
53
 
54
54
  [tool.hatch.version]
55
55
  path = "src/adk_code_mode/__about__.py"
@@ -0,0 +1,14 @@
1
+ # ADK Code Mode Sandbox
2
+
3
+ In-sandbox shim for [`adk-code-mode`](https://github.com/buildagenta/adk-code-mode). The host package launches a container that has this wheel installed, then talks to it over a TCP control connection so model code can call ADK tools that run on the host.
4
+
5
+ ## 📄 License
6
+
7
+ `adk-code-mode-sandbox` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
8
+
9
+ ## 🤝 Join the Agenta Community
10
+
11
+ Agenta is a site to find and share AI agents and open-source community. Join to share your A2A agents, ask questions, stay up-to-date with the latest A2A news, be the first to hear about open-source releases, tutorials, and more!
12
+
13
+ - 🌍 Site: [Agenta](https://buildagenta.com)
14
+ - 🤖 Discord: [Join the Discord](https://discord.gg/674NGXpAjU)
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
11
11
  license = "Apache-2.0"
12
12
  keywords = ["adk", "code-mode", "sandbox"]
13
13
  authors = [
14
- { name = "A2A Net", email = "hello@a2anet.com" },
14
+ { name = "Agenta", email = "hello@buildagenta.com" },
15
15
  ]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
@@ -27,9 +27,9 @@ dependencies = [
27
27
  ]
28
28
 
29
29
  [project.urls]
30
- Documentation = "https://github.com/A2ANet/adk-code-mode#readme"
31
- Issues = "https://github.com/A2ANet/adk-code-mode/issues"
32
- Source = "https://github.com/A2ANet/adk-code-mode"
30
+ Documentation = "https://github.com/buildagenta/adk-code-mode#readme"
31
+ Issues = "https://github.com/buildagenta/adk-code-mode/issues"
32
+ Source = "https://github.com/buildagenta/adk-code-mode"
33
33
 
34
34
  [tool.hatch.version]
35
35
  path = "src/adk_code_mode_sandbox/__about__.py"
@@ -0,0 +1,4 @@
1
+ # SPDX-FileCopyrightText: 2025-present Agenta <hello@buildagenta.com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ __version__ = "2.0.1" # x-release-please-version
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
1
+ # SPDX-FileCopyrightText: 2025-present Agenta <hello@buildagenta.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  from adk_code_mode_sandbox.__about__ import __version__
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
1
+ # SPDX-FileCopyrightText: 2025-present Agenta <hello@buildagenta.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  from adk_code_mode_sandbox._entry import main
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
1
+ # SPDX-FileCopyrightText: 2025-present Agenta <hello@buildagenta.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  """Sandbox entry point.
@@ -28,11 +28,13 @@ import io
28
28
  import os
29
29
  import platform
30
30
  import socket
31
+ import subprocess
31
32
  import sys
33
+ import threading
32
34
  import traceback
33
35
  from typing import Any
34
36
 
35
- from adk_code_mode_sandbox import _rpc_client
37
+ from adk_code_mode_sandbox import _interrupt, _rpc_client
36
38
  from adk_code_mode_sandbox._rpc_client import RpcClient
37
39
  from adk_code_mode_sandbox.protocol import (
38
40
  DoneFrame,
@@ -184,7 +186,8 @@ def _exec_into(code: str, globs: dict[str, Any]) -> int:
184
186
  traceback.print_exc()
185
187
  return 1
186
188
  try:
187
- exec(compiled, globs)
189
+ with _interrupt.running():
190
+ exec(compiled, globs)
188
191
  except SystemExit as exc:
189
192
  code_val = exc.code
190
193
  if code_val is None:
@@ -199,7 +202,54 @@ def _exec_into(code: str, globs: dict[str, Any]) -> int:
199
202
  return 0
200
203
 
201
204
 
202
- def run_block(code: str, globs: dict[str, Any]) -> tuple[str, str, int]:
205
+ def _has_linux_child_processes(task_dir: str = "/proc/self/task") -> bool:
206
+ """Check each Linux thread for child processes still running."""
207
+ checked = False
208
+ try:
209
+ with os.scandir(task_dir) as tasks:
210
+ for task in tasks:
211
+ try:
212
+ with open(f"{task.path}/children") as children:
213
+ checked = True
214
+ if children.read().strip():
215
+ return True
216
+ except FileNotFoundError:
217
+ continue
218
+ except OSError:
219
+ return True
220
+ return not checked
221
+
222
+
223
+ def _has_child_processes() -> bool:
224
+ """Check whether user code left a child process running."""
225
+ if sys.platform == "linux":
226
+ return _has_linux_child_processes()
227
+ try:
228
+ probe = subprocess.Popen(["ps", "-axo", "pid=,ppid="], stdout=subprocess.PIPE, text=True)
229
+ try:
230
+ output, _ = probe.communicate(timeout=2)
231
+ except subprocess.TimeoutExpired:
232
+ probe.kill()
233
+ probe.wait()
234
+ return True
235
+ except OSError:
236
+ return True
237
+ if probe.returncode != 0:
238
+ return True
239
+ return any(
240
+ int(pid) != probe.pid and int(ppid) == os.getpid()
241
+ for pid, ppid in (line.split() for line in output.splitlines())
242
+ )
243
+
244
+
245
+ def _has_background_work() -> bool:
246
+ """Check whether code remains active outside the interrupted execution thread."""
247
+ reader_thread = _rpc_client.get()._reader_thread
248
+ trusted = {threading.current_thread(), threading.main_thread(), reader_thread}
249
+ return any(thread not in trusted for thread in threading.enumerate()) or _has_child_processes()
250
+
251
+
252
+ def run_block(code: str, globs: dict[str, Any]) -> tuple[str, str, int, bool]:
203
253
  """Run one code block with its stdout/stderr captured.
204
254
 
205
255
  Redirects ``sys.stdout`` / ``sys.stderr`` to in-memory buffers for the
@@ -216,7 +266,7 @@ def run_block(code: str, globs: dict[str, Any]) -> tuple[str, str, int]:
216
266
  finally:
217
267
  sys.stdout = old_stdout
218
268
  sys.stderr = old_stderr
219
- return capture_out.getvalue(), capture_err.getvalue(), exit_code
269
+ return capture_out.getvalue(), capture_err.getvalue(), exit_code, _has_background_work()
220
270
 
221
271
 
222
272
  def main() -> int:
@@ -246,8 +296,8 @@ def main() -> int:
246
296
  if isinstance(frame, ShutdownFrame):
247
297
  return 0
248
298
  if isinstance(frame, RunFrame):
249
- stdout_text, stderr_text, exit_code = run_block(frame.code, globs)
250
- client.send(DoneFrame(exit_code=exit_code))
299
+ stdout_text, stderr_text, exit_code, background_work = run_block(frame.code, globs)
300
+ client.send(DoneFrame(exit_code=exit_code, background_work=background_work))
251
301
  client.send(OutputFrame(stdout=stdout_text, stderr=stderr_text, exit_code=exit_code))
252
302
  continue
253
303
  # Any other frame kind is a host protocol error; keep going but log.
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
1
+ # SPDX-FileCopyrightText: 2025-present Agenta <hello@buildagenta.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  """HTTP/WebSocket server mode for the sandbox (one connection per turn).
@@ -258,7 +258,7 @@ async def _run_one_block(
258
258
  _rpc_client.install(client)
259
259
  pump_task = asyncio.create_task(_pump_ws_to_reader(ws, bridge_reader))
260
260
  try:
261
- stdout_text, stderr_text, exit_code = await loop.run_in_executor(
261
+ stdout_text, stderr_text, exit_code, background_work = await loop.run_in_executor(
262
262
  None, run_block, run_frame.code, globs
263
263
  )
264
264
  finally:
@@ -269,7 +269,9 @@ async def _run_one_block(
269
269
  pass
270
270
 
271
271
  # d-f. DoneFrame, OutputFrame, then the updated workspace tar (binary).
272
- await ws.send(encode(DoneFrame(exit_code=exit_code)).decode("utf-8"))
272
+ await ws.send(
273
+ encode(DoneFrame(exit_code=exit_code, background_work=background_work)).decode("utf-8")
274
+ )
273
275
  await ws.send(
274
276
  encode(OutputFrame(stdout=stdout_text, stderr=stderr_text, exit_code=exit_code)).decode(
275
277
  "utf-8"
@@ -0,0 +1,67 @@
1
+ # SPDX-FileCopyrightText: 2025-present Agenta <hello@buildagenta.com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ """Stop the running code block when the host asks, keeping the sandbox's state.
5
+
6
+ The host sends an ``InterruptFrame`` when a block runs past its timeout. Raising
7
+ ``CodeTimeout`` in the thread running the block ends it like any other exception,
8
+ so its output so far, the globals and the working directory all survive for the
9
+ turn's next block. Code stuck outside the interpreter (a C call, a long
10
+ ``time.sleep``) only sees the exception once it returns, which is why the host
11
+ still falls back to restarting the sandbox after a grace period.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import ctypes
17
+ import threading
18
+ from collections.abc import Iterator
19
+ from contextlib import contextmanager
20
+
21
+
22
+ class CodeTimeout(BaseException):
23
+ """Raised in running code when the host stops it at its timeout.
24
+
25
+ A ``BaseException`` so the code's own ``except Exception`` can't swallow it.
26
+ """
27
+
28
+ message = ""
29
+
30
+ def __init__(self, *args: object) -> None:
31
+ # The interpreter raises an asynchronous exception without arguments.
32
+ super().__init__(*(args or (type(self).message,)))
33
+
34
+
35
+ _lock = threading.Lock()
36
+ _running_thread: int | None = None
37
+
38
+
39
+ def _set_async_exc(thread_id: int, exc: type[BaseException] | None) -> None:
40
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(
41
+ ctypes.c_ulong(thread_id), ctypes.py_object(exc) if exc else None
42
+ )
43
+
44
+
45
+ @contextmanager
46
+ def running() -> Iterator[None]:
47
+ """Mark the current thread as the one running a code block."""
48
+ global _running_thread
49
+ with _lock:
50
+ _running_thread = threading.get_ident()
51
+ try:
52
+ yield
53
+ finally:
54
+ with _lock:
55
+ _running_thread = None
56
+ # A stop that arrived as the block finished must not fire in the
57
+ # sandbox's own code afterwards.
58
+ _set_async_exc(threading.get_ident(), None)
59
+
60
+
61
+ def interrupt(message: str) -> None:
62
+ """Raise ``CodeTimeout`` with ``message`` in the running block, if there is one."""
63
+ with _lock:
64
+ if _running_thread is None:
65
+ return
66
+ CodeTimeout.message = message
67
+ _set_async_exc(_running_thread, CodeTimeout)