hyperforge 1.0.0.post326__tar.gz → 1.0.0.post332__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 (136) hide show
  1. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/PKG-INFO +1 -1
  2. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/pyproject.toml +1 -1
  3. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/pyproject.toml.orig +1 -1
  4. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/codemode/sandbox.py +53 -21
  5. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/__init__.py +2 -1
  6. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/agents.py +5 -3
  7. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/harness.py +6 -4
  8. hyperforge-1.0.0.post332/src/hyperforge/harness_sdk/tools/__init__.py +9 -0
  9. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/tools/codemode.py +8 -3
  10. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/tools/core.py +22 -19
  11. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/tools/utils.py +21 -7
  12. hyperforge-1.0.0.post326/src/hyperforge/harness_sdk/tools/__init__.py +0 -8
  13. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/README.md +0 -0
  14. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/VERSION +0 -0
  15. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/__init__.py +0 -0
  16. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/__init__.py +0 -0
  17. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/auth.py +0 -0
  18. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/card.py +0 -0
  19. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/context.py +0 -0
  20. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/executor.py +0 -0
  21. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/run.py +0 -0
  22. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/server.py +0 -0
  23. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/settings.py +0 -0
  24. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/a2a/task_store.py +0 -0
  25. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/agent.py +0 -0
  26. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/README +0 -0
  27. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/env.py +0 -0
  28. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/script.py.mako +0 -0
  29. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/049a4aed19e1_create_generation_table.py +0 -0
  30. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/1416cb41bd49_add_downloads_table.py +0 -0
  31. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/2847934e2d59_unique_driver.py +0 -0
  32. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/4d89a36c8bda_memory.py +0 -0
  33. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/72b997edb05d_identifier_for_drivers.py +0 -0
  34. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/9c6f6a1b4e7f_workflows_default_fk.py +0 -0
  35. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/b8edb72295e0_.py +0 -0
  36. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic/versions/f3d2c1b0a9e8_soft_delete_workflows.py +0 -0
  37. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/alembic.ini +0 -0
  38. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/__init__.py +0 -0
  39. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/app.py +0 -0
  40. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/authentication.py +0 -0
  41. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/commands.py +0 -0
  42. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/logging.py +0 -0
  43. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/models.py +0 -0
  44. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/session.py +0 -0
  45. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/settings.py +0 -0
  46. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/utils.py +0 -0
  47. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/__init__.py +0 -0
  48. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/agents.py +0 -0
  49. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/interaction.py +0 -0
  50. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/mcp_content.py +0 -0
  51. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/mcp_interaction.py +0 -0
  52. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/oauth.py +0 -0
  53. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/prompt.py +0 -0
  54. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/router.py +0 -0
  55. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/schema.py +0 -0
  56. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/session.py +0 -0
  57. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/utils.py +0 -0
  58. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/api/v1/workflows.py +0 -0
  59. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/arag.py +0 -0
  60. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/broker/__init__.py +0 -0
  61. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/broker/local.py +0 -0
  62. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/broker/redis.py +0 -0
  63. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/codemode/__init__.py +0 -0
  64. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/codemode/model.py +0 -0
  65. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/codemode/worker.py +0 -0
  66. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/configure.py +0 -0
  67. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/context/__init__.py +0 -0
  68. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/context/agent.py +0 -0
  69. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/context/config.py +0 -0
  70. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/database.py +0 -0
  71. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/__init__.py +0 -0
  72. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/agents.py +0 -0
  73. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/encryption.py +0 -0
  74. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/exceptions.py +0 -0
  75. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/llm_migration_utils.py +0 -0
  76. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/settings.py +0 -0
  77. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/db/workflow_cleanup.py +0 -0
  78. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/definition.py +0 -0
  79. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/driver.py +0 -0
  80. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/dummy.py +0 -0
  81. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/engine.py +0 -0
  82. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/exceptions.py +0 -0
  83. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/feature_flag.py +0 -0
  84. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/fixtures.py +0 -0
  85. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness/__init__.py +0 -0
  86. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness/agent.py +0 -0
  87. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness/config.py +0 -0
  88. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/clients.py +0 -0
  89. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/context.py +0 -0
  90. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/models.py +0 -0
  91. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/schema.py +0 -0
  92. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/storage.py +0 -0
  93. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/harness_sdk/usage.py +0 -0
  94. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/interaction.py +0 -0
  95. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/llm.py +0 -0
  96. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/llm_config.py +0 -0
  97. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/manager.py +0 -0
  98. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/memory/__init__.py +0 -0
  99. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/memory/memory.py +0 -0
  100. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/minimal_fixtures.py +0 -0
  101. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/models.py +0 -0
  102. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/openapi.py +0 -0
  103. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/prompts.py +0 -0
  104. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/pubsub.py +0 -0
  105. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/py.typed +0 -0
  106. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/redis_utils.py +0 -0
  107. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/result_payload.py +0 -0
  108. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/retrieval/__init__.py +0 -0
  109. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/retrieval/agent.py +0 -0
  110. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/retrieval/config.py +0 -0
  111. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/__init__.py +0 -0
  112. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/cache.py +0 -0
  113. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/run.py +0 -0
  114. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/sandbox.py +0 -0
  115. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/session.py +0 -0
  116. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/settings.py +0 -0
  117. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/utils.py +0 -0
  118. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/server/web.py +0 -0
  119. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/settings.py +0 -0
  120. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/__init__.py +0 -0
  121. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/agent.py +0 -0
  122. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/app.py +0 -0
  123. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/config.py +0 -0
  124. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/const.py +0 -0
  125. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/oauth.py +0 -0
  126. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/run.py +0 -0
  127. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/settings.py +0 -0
  128. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/standalone/ui_router.py +0 -0
  129. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/static/assets/index-DLejS5jC.js +0 -0
  130. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/static/assets/index-MoxkZ1_3.css +0 -0
  131. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/static/favicon.svg +0 -0
  132. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/static/index.html +0 -0
  133. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/trace.py +0 -0
  134. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/utils/__init__.py +0 -0
  135. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/utils/http.py +0 -0
  136. {hyperforge-1.0.0.post326 → hyperforge-1.0.0.post332}/src/hyperforge/workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperforge
3
- Version: 1.0.0.post326
3
+ Version: 1.0.0.post332
4
4
  Summary: Agentic Framework for Orchestrated Runtime, Governance, and Execution
5
5
  Author: AI Data Team
6
6
  Author-email: AI Data Team <learning@nuclia.com>
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "hyperforge"
7
- version = "1.0.0.post326"
7
+ version = "1.0.0.post332"
8
8
  description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "hyperforge"
7
- version = "1.0.0.post326"
7
+ version = "1.0.0.post332"
8
8
  authors = [{ name = "AI Data Team", email = "learning@nuclia.com" }]
9
9
  description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
10
10
  readme = "README.md"
@@ -8,7 +8,7 @@ from concurrent.futures import Executor, ThreadPoolExecutor
8
8
  from concurrent.futures import Future as ConcurrentFuture
9
9
  from multiprocessing import Pipe, Process
10
10
  from multiprocessing.connection import Connection
11
- from typing import Any, Callable, Coroutine
11
+ from typing import Any, Awaitable, Callable, Coroutine
12
12
 
13
13
  import httpx
14
14
  import nucliadb_telemetry.metrics
@@ -25,7 +25,6 @@ from .model import (
25
25
  from .worker import PythonAgentWorker
26
26
 
27
27
  logger = logging.getLogger("hyperforge_codemode_sandbox")
28
- WORKER_CPU_LIMIT = 1
29
28
  WORKER_EXIT_TIMEOUT = 1
30
29
  MAX_PACKET_BYTES = 16 * 1024 * 1024
31
30
 
@@ -35,6 +34,7 @@ class SandboxSettings(BaseSettings):
35
34
  sandbox_socket: str | None = None
36
35
  sandbox_metrics_port: int = 8091
37
36
  sandbox_token: str | None = None
37
+ sandbox_callback_wait_seconds: float = 1.0
38
38
 
39
39
 
40
40
  settings = SandboxSettings()
@@ -60,8 +60,16 @@ class SandboxRunner:
60
60
  socket: str,
61
61
  callback: Callable[[RestrictedPythonTask], Coroutine[Any, Any, WorkerTypes]],
62
62
  debug: bool = False,
63
+ *,
64
+ token: str | Callable[[], str] | None = None,
63
65
  ) -> "SandboxRunner":
64
- return cls(pool=None, socket=socket, callback=callback, debug=debug)
66
+ return cls(
67
+ pool=None,
68
+ socket=socket,
69
+ callback=callback,
70
+ debug=debug,
71
+ token=token,
72
+ )
65
73
 
66
74
  @classmethod
67
75
  def isolated_process(
@@ -78,12 +86,14 @@ class SandboxRunner:
78
86
  socket: str | None,
79
87
  callback: Callable[[RestrictedPythonTask], Coroutine[Any, Any, WorkerTypes]],
80
88
  debug: bool,
89
+ token: str | Callable[[], str] | None = None,
81
90
  ):
82
91
  self.pool = pool
83
92
  self.socket = socket
84
93
  self.callback = callback
85
94
  self.debug = debug
86
95
  self.isolated = False
96
+ self.token = token
87
97
 
88
98
  async def run(self, request: WorkerExecutionRequest):
89
99
  if self.pool is not None:
@@ -137,14 +147,19 @@ class SandboxRunner:
137
147
  await asyncio.gather(controller_task, return_exceptions=True)
138
148
 
139
149
  async def _run_remotely(self, request: WorkerExecutionRequest):
140
- if settings.sandbox_token is None:
150
+ token_source = self.token
151
+ if token_source is None:
152
+ token = SandboxSettings().sandbox_token
153
+ elif isinstance(token_source, str):
154
+ token = token_source
155
+ else:
156
+ token = token_source()
157
+ if token is None:
141
158
  raise RuntimeError("SANDBOX_TOKEN is required for remote codemode")
142
159
  rx, tx = await asyncio.open_unix_connection(self.socket)
143
160
  reader, writer = SandboxReader(rx), SandboxWriter(tx)
144
161
  try:
145
- await writer.write_message(
146
- SandboxMessage.Run(run=request, token=settings.sandbox_token)
147
- )
162
+ await writer.write_message(SandboxMessage.Run(run=request, token=token))
148
163
  while True:
149
164
  try:
150
165
  msg = await reader.read_message()
@@ -291,9 +306,15 @@ class SandboxReader:
291
306
 
292
307
 
293
308
  class SandboxSession:
294
- def __init__(self, reader: SandboxReader, writer: SandboxWriter):
309
+ def __init__(
310
+ self,
311
+ reader: SandboxReader,
312
+ writer: SandboxWriter,
313
+ callback_wait_seconds: float = 1.0,
314
+ ):
295
315
  self.reader = reader
296
316
  self.writer = writer
317
+ self.callback_wait_seconds = callback_wait_seconds
297
318
  self.runner = SandboxRunner(
298
319
  pool=None, socket=None, callback=self._callback, debug=False
299
320
  )
@@ -303,7 +324,7 @@ class SandboxSession:
303
324
  async def run(self, request: WorkerExecutionRequest):
304
325
  self.task, self.process = self.runner.run_in_process(request)
305
326
  try:
306
- await self._start_timeout(WORKER_CPU_LIMIT)
327
+ await self._start_timeout(self.callback_wait_seconds)
307
328
  if request.max_runtime_seconds is None:
308
329
  clean_exit = await self.task
309
330
  else:
@@ -358,7 +379,7 @@ class SandboxSession:
358
379
  self.task.cancel()
359
380
  return None
360
381
  assert isinstance(response, SandboxMessage.Response)
361
- await self._start_timeout(WORKER_CPU_LIMIT)
382
+ await self._start_timeout(self.callback_wait_seconds)
362
383
  return response.result
363
384
 
364
385
  async def _start_timeout(self, timeout: float):
@@ -382,12 +403,15 @@ agents_running = prometheus_client.Gauge(
382
403
  execution_observer = nucliadb_telemetry.metrics.Observer("arag_sandbox_execution")
383
404
 
384
405
 
385
- async def run_sandbox_server():
386
- assert settings.sandbox_socket is not None
387
- sandbox_token = settings.sandbox_token
388
- if sandbox_token is None:
406
+ async def run_sandbox_server(
407
+ *, token_verifier: Callable[[str], Awaitable[bool]] | None = None
408
+ ):
409
+ server_settings = SandboxSettings()
410
+ assert server_settings.sandbox_socket is not None
411
+ sandbox_token = server_settings.sandbox_token
412
+ if token_verifier is None and sandbox_token is None:
389
413
  raise RuntimeError("SANDBOX_TOKEN is required for the sandbox server")
390
- if settings.sandbox_verify:
414
+ if server_settings.sandbox_verify:
391
415
  _verify_connectivity()
392
416
 
393
417
  async def handler(rx: asyncio.StreamReader, tx: asyncio.StreamWriter):
@@ -397,10 +421,16 @@ async def run_sandbox_server():
397
421
  async with asyncio.timeout(1):
398
422
  msg = await reader.read_message()
399
423
  assert isinstance(msg, SandboxMessage.Run)
400
- if not hmac.compare_digest(msg.token or "", sandbox_token):
424
+ token = msg.token or ""
425
+ valid_token = (
426
+ await token_verifier(token)
427
+ if token_verifier is not None
428
+ else hmac.compare_digest(token, sandbox_token or "")
429
+ )
430
+ if not valid_token:
401
431
  raise PermissionError("Invalid sandbox token")
402
- except (AssertionError, PermissionError, TimeoutError, ValueError):
403
- logger.warning("Rejected invalid sandbox connection")
432
+ except Exception as exc:
433
+ logger.warning("Rejected invalid sandbox connection: %r", exc)
404
434
  writer.close()
405
435
  await writer.wait_closed()
406
436
  return
@@ -409,7 +439,9 @@ async def run_sandbox_server():
409
439
  observation = execution_observer()
410
440
  observation.start()
411
441
  try:
412
- session = SandboxSession(reader, writer)
442
+ session = SandboxSession(
443
+ reader, writer, SandboxSettings().sandbox_callback_wait_seconds
444
+ )
413
445
  await session.run(msg.run)
414
446
  except Exception as exc:
415
447
  observation.set_status("error")
@@ -420,7 +452,7 @@ async def run_sandbox_server():
420
452
  writer.close()
421
453
  await writer.wait_closed()
422
454
 
423
- server = await asyncio.start_unix_server(handler, settings.sandbox_socket)
424
- os.chmod(settings.sandbox_socket, 0o600)
455
+ server = await asyncio.start_unix_server(handler, server_settings.sandbox_socket)
456
+ os.chmod(server_settings.sandbox_socket, 0o600)
425
457
  async with server:
426
458
  await server.serve_forever()
@@ -39,13 +39,14 @@ from .models import (
39
39
  HarnessToolCall,
40
40
  )
41
41
  from .storage import HarnessStorageProtocol, InMemoryHarnessStorage
42
- from .tools import HarnessTool, tool
42
+ from .tools import HarnessTool, ToolCallContext, tool
43
43
  from .tools.codemode import CodemodeInput, CodemodeOutput, codemode
44
44
  from .usage import HarnessUsage, UsageLimitExceeded, UsageLimits
45
45
 
46
46
  __all__ = [
47
47
  "SYSTEM_PROMPT",
48
48
  "AgentHarness",
49
+ "ToolCallContext",
49
50
  "AgentResult",
50
51
  "ChatCompletionChunk",
51
52
  "ChatCompletionChoice",
@@ -23,7 +23,7 @@ from hyperforge.memory.memory import BaseSessionMemory
23
23
  from hyperforge.models import Context, MemoryConfig, Rules
24
24
 
25
25
  from .models import HarnessContextReference, HarnessContextType
26
- from .tools import HarnessTool
26
+ from .tools import HarnessTool, ToolCallContext
27
27
 
28
28
  if TYPE_CHECKING:
29
29
  from hyperforge.agent import Agent as HyperforgeAgent
@@ -64,8 +64,9 @@ class HarnessAgentToTool:
64
64
 
65
65
  def as_tool(self) -> HarnessTool[AgentToolInput, AgentToolOutput]:
66
66
  async def execute(
67
- harness: AgentHarness, input_value: AgentToolInput
67
+ context: ToolCallContext, input_value: AgentToolInput
68
68
  ) -> AgentToolOutput:
69
+ harness = context.harness
69
70
  memory = self.memory_factory(harness, input_value.prompt)
70
71
  if inspect.isawaitable(memory):
71
72
  memory = await memory
@@ -143,8 +144,9 @@ def _published_function_tool(
143
144
  input_model = _published_input_model(method, function_id, definition)
144
145
 
145
146
  async def execute(
146
- harness: AgentHarness, input_value: BaseModel
147
+ context: ToolCallContext, input_value: BaseModel
147
148
  ) -> PublishedFunctionOutput:
149
+ harness = context.harness
148
150
  compat_manager = manager or _compat_manager(harness)
149
151
  memory = harness.execution_context.get("memory")
150
152
  if memory is None:
@@ -23,7 +23,7 @@ from .models import (
23
23
  HarnessToolCall,
24
24
  )
25
25
  from .storage import HarnessStorageProtocol, InMemoryHarnessStorage
26
- from .tools import HarnessTool
26
+ from .tools import HarnessTool, ToolCallContext
27
27
  from .tools.core import DictOutput, SendMessageInput, SpawnAgentInput, create_core_tools
28
28
  from .usage import HarnessUsage, UsageLimitExceeded, UsageLimits
29
29
 
@@ -955,7 +955,8 @@ class AgentHarness:
955
955
  async def _execute_tool_call(self, call: HarnessToolCall) -> HarnessMessage:
956
956
  assert call.id is not None
957
957
  await self.emit(
958
- HarnessEventType.TOOL_REQUESTED, {"call": call.model_dump(mode="json")}
958
+ HarnessEventType.TOOL_REQUESTED,
959
+ {"call": call.model_dump(mode="json")},
959
960
  )
960
961
  tool = self._tools.get(call.name)
961
962
  try:
@@ -965,7 +966,8 @@ class AgentHarness:
965
966
  elif tool.lazy_load and tool.name not in self._active_lazy_tools:
966
967
  raise ValueError(f"Tool is not active: {call.name}")
967
968
  else:
968
- output = await tool.execute(self, call.arguments)
969
+ context = ToolCallContext(harness=self, name=call.name, id=call.id)
970
+ output = await tool.execute(context, call.arguments)
969
971
  result = output.model_dump(mode="json")
970
972
  reference = tool.context(output)
971
973
  content = format_context(reference)
@@ -981,7 +983,7 @@ class AgentHarness:
981
983
  context=reference,
982
984
  )
983
985
  except Exception as exc:
984
- logger.warning(
986
+ logger.info(
985
987
  "Agent tool execution failed: tool=%s call_id=%s error_type=%s error=%s",
986
988
  call.name,
987
989
  call.id,
@@ -0,0 +1,9 @@
1
+ from .utils import (
2
+ ContextFactory,
3
+ HarnessTool,
4
+ ToolCallContext,
5
+ ToolHandler,
6
+ tool,
7
+ )
8
+
9
+ __all__ = ["ContextFactory", "HarnessTool", "ToolCallContext", "ToolHandler", "tool"]
@@ -10,7 +10,7 @@ from hyperforge.codemode import (
10
10
  from hyperforge.codemode.sandbox import settings
11
11
  from hyperforge.definition import FunctionDefinition
12
12
 
13
- from . import HarnessTool, tool
13
+ from . import HarnessTool, ToolCallContext, tool
14
14
 
15
15
  CODEMODE_TOOL_NAME = "codemode"
16
16
  OUTPUT_FUNCTION_NAME = "output"
@@ -31,7 +31,10 @@ class CodemodeOutput(BaseModel):
31
31
  "call output(value) to return a result."
32
32
  ),
33
33
  )
34
- async def codemode(harness: Any, input_value: CodemodeInput) -> CodemodeOutput:
34
+ async def codemode(
35
+ context: ToolCallContext, input_value: CodemodeInput
36
+ ) -> CodemodeOutput:
37
+ harness = context.harness
35
38
  tools = {
36
39
  tool.name: tool
37
40
  for tool in harness.iter_tools()
@@ -54,7 +57,9 @@ async def codemode(harness: Any, input_value: CodemodeInput) -> CodemodeOutput:
54
57
  harness.usage.tool_calls += 1
55
58
  harness._check_limit("max_tool_calls", harness.usage.tool_calls)
56
59
  arguments = _tool_arguments(tool, task.args, task.keyword_args)
57
- output = await tool.execute(harness, arguments)
60
+ output = await tool.execute(
61
+ ToolCallContext(harness=harness, name=tool.name), arguments
62
+ )
58
63
  return output.model_dump(mode="json")
59
64
 
60
65
  runner = (
@@ -2,15 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import uuid
4
4
  from re import findall
5
- from typing import TYPE_CHECKING, Any
5
+ from typing import Any
6
6
 
7
7
  from pydantic import BaseModel
8
8
 
9
9
  from ..models import HarnessEventType, HarnessMemory, HarnessMessage
10
- from . import HarnessTool, tool
11
-
12
- if TYPE_CHECKING:
13
- from ..harness import AgentHarness
10
+ from . import HarnessTool, ToolCallContext, tool
14
11
 
15
12
 
16
13
  class RememberInput(BaseModel):
@@ -78,8 +75,9 @@ def _search_terms(value: str) -> set[str]:
78
75
 
79
76
  @tool(description="Search for additional tools that can be activated.")
80
77
  async def search_tools(
81
- harness: AgentHarness, input_value: SearchToolsInput
78
+ context: ToolCallContext, input_value: SearchToolsInput
82
79
  ) -> ListOutput:
80
+ harness = context.harness
83
81
  terms = _search_terms(input_value.query)
84
82
  candidates: list[tuple[int, HarnessTool[Any, Any]]] = []
85
83
  for candidate in harness._external_tools:
@@ -104,8 +102,9 @@ async def search_tools(
104
102
 
105
103
  @tool(description="Activate additional tools by their exact names.")
106
104
  async def activate_tools(
107
- harness: AgentHarness, input_value: ActivateToolsInput
105
+ context: ToolCallContext, input_value: ActivateToolsInput
108
106
  ) -> DictOutput:
107
+ harness = context.harness
109
108
  names = list(dict.fromkeys(input_value.names))
110
109
  unknown = [
111
110
  name
@@ -119,7 +118,8 @@ async def activate_tools(
119
118
 
120
119
 
121
120
  @tool()
122
- async def remember(harness: AgentHarness, input_value: RememberInput) -> DictOutput:
121
+ async def remember(context: ToolCallContext, input_value: RememberInput) -> DictOutput:
122
+ harness = context.harness
123
123
  memory = HarnessMemory(
124
124
  id=uuid.uuid4().hex,
125
125
  text=input_value.text,
@@ -135,7 +135,8 @@ async def remember(harness: AgentHarness, input_value: RememberInput) -> DictOut
135
135
 
136
136
 
137
137
  @tool()
138
- async def recall(harness: AgentHarness, input_value: RecallInput) -> ListOutput:
138
+ async def recall(context: ToolCallContext, input_value: RecallInput) -> ListOutput:
139
+ harness = context.harness
139
140
  memories = await harness.storage.recall(
140
141
  scope=input_value.scope,
141
142
  query=input_value.query,
@@ -144,7 +145,8 @@ async def recall(harness: AgentHarness, input_value: RecallInput) -> ListOutput:
144
145
 
145
146
 
146
147
  @tool()
147
- async def forget(harness: AgentHarness, input_value: ForgetInput) -> DictOutput:
148
+ async def forget(context: ToolCallContext, input_value: ForgetInput) -> DictOutput:
149
+ harness = context.harness
148
150
  await harness.storage.forget(input_value.id)
149
151
  await harness.emit(HarnessEventType.MEMORY_FORGOTTEN, {"id": input_value.id})
150
152
  return DictOutput(value={"id": input_value.id})
@@ -152,25 +154,26 @@ async def forget(harness: AgentHarness, input_value: ForgetInput) -> DictOutput:
152
154
 
153
155
  @tool()
154
156
  async def spawn_agent(
155
- harness: AgentHarness, input_value: SpawnAgentInput
157
+ context: ToolCallContext, input_value: SpawnAgentInput
156
158
  ) -> DictOutput:
157
- return await harness._child_agents.spawn(input_value)
159
+ return await context.harness._child_agents.spawn(input_value)
158
160
 
159
161
 
160
162
  @tool()
161
163
  async def send_message(
162
- harness: AgentHarness, input_value: SendMessageInput
164
+ context: ToolCallContext, input_value: SendMessageInput
163
165
  ) -> DictOutput:
164
- return await harness._child_agents.send_message(input_value)
166
+ return await context.harness._child_agents.send_message(input_value)
165
167
 
166
168
 
167
169
  @tool()
168
- async def wait_agent(harness: AgentHarness, input_value: AgentIdInput) -> DictOutput:
169
- return await harness._child_agents.wait(input_value.agent_id)
170
+ async def wait_agent(context: ToolCallContext, input_value: AgentIdInput) -> DictOutput:
171
+ return await context.harness._child_agents.wait(input_value.agent_id)
170
172
 
171
173
 
172
174
  @tool()
173
- async def compact(harness: AgentHarness, input_value: CompactInput) -> DictOutput:
175
+ async def compact(context: ToolCallContext, input_value: CompactInput) -> DictOutput:
176
+ harness = context.harness
174
177
  harness.messages = [
175
178
  HarnessMessage(role="system", content=harness.system_prompt),
176
179
  HarnessMessage(
@@ -185,8 +188,8 @@ async def compact(harness: AgentHarness, input_value: CompactInput) -> DictOutpu
185
188
 
186
189
 
187
190
  @tool()
188
- async def feedback(harness: AgentHarness, input_value: FeedbackInput) -> DictOutput:
189
- response = await harness.request_feedback(
191
+ async def feedback(context: ToolCallContext, input_value: FeedbackInput) -> DictOutput:
192
+ response = await context.harness.request_feedback(
190
193
  question=input_value.question,
191
194
  response_schema={
192
195
  "type": "object",
@@ -16,11 +16,24 @@ if TYPE_CHECKING:
16
16
  from ..harness import AgentHarness
17
17
 
18
18
  type ToolHandler[InputT: BaseModel, OutputT: BaseModel] = Callable[
19
- ["AgentHarness", InputT], Awaitable[OutputT]
19
+ [ToolCallContext, InputT], Awaitable[OutputT]
20
20
  ]
21
21
  type ContextFactory[OutputT: BaseModel] = Callable[[OutputT], HarnessContextReference]
22
22
 
23
23
 
24
+ @dataclass(frozen=True)
25
+ class ToolCallContext:
26
+ """Execution context passed to a tool handler.
27
+
28
+ Provides the harness along with the identity of the tool call
29
+ currently being executed.
30
+ """
31
+
32
+ harness: "AgentHarness"
33
+ name: str
34
+ id: str | None = None
35
+
36
+
24
37
  @dataclass(frozen=True)
25
38
  class HarnessTool[InputT: BaseModel, OutputT: BaseModel]:
26
39
  name: str
@@ -38,10 +51,11 @@ class HarnessTool[InputT: BaseModel, OutputT: BaseModel]:
38
51
  parameters = list(inspect.signature(self.handler).parameters.values())
39
52
  if len(parameters) != 2:
40
53
  raise TypeError(
41
- f"Tool handler {self.name!r} must accept harness and input parameters"
54
+ f"Tool handler {self.name!r} must accept context and input parameters"
42
55
  )
43
56
  globalns = dict(getattr(self.handler, "__globals__", {}))
44
57
  globalns.setdefault("AgentHarness", object)
58
+ globalns.setdefault("ToolCallContext", ToolCallContext)
45
59
  hints = get_type_hints(self.handler, globalns=globalns)
46
60
  input_model = hints.get(parameters[1].name)
47
61
  output_model = hints.get("return")
@@ -65,12 +79,12 @@ class HarnessTool[InputT: BaseModel, OutputT: BaseModel]:
65
79
  return self._parameters
66
80
 
67
81
  def __call__(
68
- self, harness: AgentHarness, input_value: InputT
82
+ self, context: ToolCallContext, input_value: InputT
69
83
  ) -> Awaitable[OutputT]:
70
- return self.handler(harness, input_value)
84
+ return self.handler(context, input_value)
71
85
 
72
86
  async def execute(
73
- self, harness: AgentHarness, arguments: dict[str, Any]
87
+ self, context: ToolCallContext, arguments: dict[str, Any]
74
88
  ) -> OutputT:
75
89
  if error := arguments.get("_tool_error"):
76
90
  raise ValueError(str(error))
@@ -85,7 +99,7 @@ class HarnessTool[InputT: BaseModel, OutputT: BaseModel]:
85
99
  value = self.input_model.model_validate(arguments)
86
100
  except ValidationError as exc:
87
101
  raise ValueError(f"Invalid {self.name} arguments: {exc}") from exc
88
- result = await self.handler(harness, value)
102
+ result = await self.handler(context, value)
89
103
  return self.output_model.model_validate(result)
90
104
 
91
105
  def context(self, output: OutputT) -> HarnessContextReference:
@@ -123,4 +137,4 @@ def _is_model_type(value: Any) -> bool:
123
137
  return isinstance(value, type) and issubclass(value, BaseModel)
124
138
 
125
139
 
126
- __all__ = ["ContextFactory", "HarnessTool", "ToolHandler", "tool"]
140
+ __all__ = ["ContextFactory", "HarnessTool", "ToolCallContext", "ToolHandler", "tool"]
@@ -1,8 +0,0 @@
1
- from .utils import (
2
- ContextFactory,
3
- HarnessTool,
4
- ToolHandler,
5
- tool,
6
- )
7
-
8
- __all__ = ["ContextFactory", "HarnessTool", "ToolHandler", "tool"]