hyperforge 1.0.0.post414__tar.gz → 1.0.0.post421__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 (142) hide show
  1. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/PKG-INFO +3 -2
  2. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/pyproject.toml +3 -2
  3. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/pyproject.toml.orig +3 -2
  4. hyperforge-1.0.0.post421/src/hyperforge/errors.py +105 -0
  5. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/session.py +11 -5
  6. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/README.md +0 -0
  7. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/VERSION +0 -0
  8. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/__init__.py +0 -0
  9. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/__init__.py +0 -0
  10. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/auth.py +0 -0
  11. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/card.py +0 -0
  12. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/context.py +0 -0
  13. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/executor.py +0 -0
  14. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/run.py +0 -0
  15. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/server.py +0 -0
  16. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/settings.py +0 -0
  17. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/a2a/task_store.py +0 -0
  18. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/agent.py +0 -0
  19. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/README +0 -0
  20. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/env.py +0 -0
  21. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/script.py.mako +0 -0
  22. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/049a4aed19e1_create_generation_table.py +0 -0
  23. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/1416cb41bd49_add_downloads_table.py +0 -0
  24. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/2847934e2d59_unique_driver.py +0 -0
  25. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/4d89a36c8bda_memory.py +0 -0
  26. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/6a23d8ef74c1_add_sync_oauth_credentials.py +0 -0
  27. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/72b997edb05d_identifier_for_drivers.py +0 -0
  28. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/8c7d2e4f91ab_structure_legacy_llm_fields.py +0 -0
  29. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/9c6f6a1b4e7f_workflows_default_fk.py +0 -0
  30. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/b8edb72295e0_.py +0 -0
  31. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic/versions/f3d2c1b0a9e8_soft_delete_workflows.py +0 -0
  32. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/alembic.ini +0 -0
  33. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/__init__.py +0 -0
  34. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/app.py +0 -0
  35. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/authentication.py +0 -0
  36. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/commands.py +0 -0
  37. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/logging.py +0 -0
  38. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/mcp_server_pool.py +0 -0
  39. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/models.py +0 -0
  40. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/session.py +0 -0
  41. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/settings.py +0 -0
  42. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/utils.py +0 -0
  43. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/__init__.py +0 -0
  44. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/agents.py +0 -0
  45. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/interaction.py +0 -0
  46. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/mcp_content.py +0 -0
  47. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/mcp_interaction.py +0 -0
  48. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/oauth.py +0 -0
  49. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/prompt.py +0 -0
  50. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/router.py +0 -0
  51. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/schema.py +0 -0
  52. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/session.py +0 -0
  53. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/utils.py +0 -0
  54. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/api/v1/workflows.py +0 -0
  55. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/arag.py +0 -0
  56. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/broker/__init__.py +0 -0
  57. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/broker/local.py +0 -0
  58. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/broker/redis.py +0 -0
  59. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/codemode/__init__.py +0 -0
  60. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/codemode/model.py +0 -0
  61. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/codemode/sandbox.py +0 -0
  62. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/codemode/worker.py +0 -0
  63. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/completions.py +0 -0
  64. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/configure.py +0 -0
  65. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/context/__init__.py +0 -0
  66. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/context/agent.py +0 -0
  67. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/context/config.py +0 -0
  68. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/database.py +0 -0
  69. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/__init__.py +0 -0
  70. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/agents.py +0 -0
  71. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/encryption.py +0 -0
  72. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/exceptions.py +0 -0
  73. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/legacy_llm_migration.py +0 -0
  74. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/llm_migration_utils.py +0 -0
  75. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/settings.py +0 -0
  76. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/db/workflow_cleanup.py +0 -0
  77. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/definition.py +0 -0
  78. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/driver.py +0 -0
  79. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/dummy.py +0 -0
  80. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/engine.py +0 -0
  81. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/exceptions.py +0 -0
  82. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/feature_flag.py +0 -0
  83. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/fixtures.py +0 -0
  84. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness/__init__.py +0 -0
  85. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness/agent.py +0 -0
  86. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness/config.py +0 -0
  87. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/__init__.py +0 -0
  88. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/agents.py +0 -0
  89. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/clients.py +0 -0
  90. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/context.py +0 -0
  91. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/harness.py +0 -0
  92. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/models.py +0 -0
  93. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/schema.py +0 -0
  94. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/storage.py +0 -0
  95. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/tools/__init__.py +0 -0
  96. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/tools/codemode.py +0 -0
  97. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/tools/core.py +0 -0
  98. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/tools/utils.py +0 -0
  99. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/harness_sdk/usage.py +0 -0
  100. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/interaction.py +0 -0
  101. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/json_schema.py +0 -0
  102. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/llm.py +0 -0
  103. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/llm_config.py +0 -0
  104. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/manager.py +0 -0
  105. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/memory/__init__.py +0 -0
  106. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/memory/memory.py +0 -0
  107. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/minimal_fixtures.py +0 -0
  108. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/models.py +0 -0
  109. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/openapi.py +0 -0
  110. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/prompts.py +0 -0
  111. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/pubsub.py +0 -0
  112. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/py.typed +0 -0
  113. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/redis_utils.py +0 -0
  114. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/result_payload.py +0 -0
  115. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/retrieval/__init__.py +0 -0
  116. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/retrieval/agent.py +0 -0
  117. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/retrieval/config.py +0 -0
  118. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/__init__.py +0 -0
  119. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/cache.py +0 -0
  120. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/run.py +0 -0
  121. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/sandbox.py +0 -0
  122. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/settings.py +0 -0
  123. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/utils.py +0 -0
  124. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/server/web.py +0 -0
  125. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/settings.py +0 -0
  126. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/__init__.py +0 -0
  127. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/agent.py +0 -0
  128. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/app.py +0 -0
  129. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/config.py +0 -0
  130. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/const.py +0 -0
  131. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/oauth.py +0 -0
  132. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/run.py +0 -0
  133. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/settings.py +0 -0
  134. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/standalone/ui_router.py +0 -0
  135. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/static/assets/index-DLejS5jC.js +0 -0
  136. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/static/assets/index-MoxkZ1_3.css +0 -0
  137. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/static/favicon.svg +0 -0
  138. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/static/index.html +0 -0
  139. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/trace.py +0 -0
  140. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/utils/__init__.py +0 -0
  141. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/src/hyperforge/utils/http.py +0 -0
  142. {hyperforge-1.0.0.post414 → hyperforge-1.0.0.post421}/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.post414
3
+ Version: 1.0.0.post421
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>
@@ -20,11 +20,12 @@ Requires-Dist: mcp>=1.29.1,<2
20
20
  Requires-Dist: prometheus-client
21
21
  Requires-Dist: mrflagly>=0.2.11
22
22
  Requires-Dist: aiodns
23
+ Requires-Dist: anyio>=4.15.1
23
24
  Requires-Dist: httpx>=0.28.1,<0.29
24
25
  Requires-Dist: httpcore>=1.0.9,<1.1
25
26
  Requires-Dist: jinja2
26
27
  Requires-Dist: jsonschema
27
- Requires-Dist: restrictedpython
28
+ Requires-Dist: restrictedpython>=8.5
28
29
  Requires-Dist: databases[asyncpg]
29
30
  Requires-Dist: sqlalchemy
30
31
  Requires-Dist: cryptography
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "hyperforge"
7
- version = "1.0.0.post414"
7
+ version = "1.0.0.post421"
8
8
  description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -27,11 +27,12 @@ dependencies = [
27
27
  "prometheus_client",
28
28
  "mrflagly>=0.2.11",
29
29
  "aiodns",
30
+ "anyio>=4.15.1",
30
31
  "httpx>=0.28.1,<0.29",
31
32
  "httpcore>=1.0.9,<1.1",
32
33
  "jinja2",
33
34
  "jsonschema",
34
- "RestrictedPython",
35
+ "RestrictedPython>=8.5",
35
36
  "databases[asyncpg]",
36
37
  "sqlalchemy",
37
38
  "cryptography",
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "hyperforge"
7
- version = "1.0.0.post414"
7
+ version = "1.0.0.post421"
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"
@@ -28,11 +28,12 @@ dependencies = [
28
28
  "prometheus_client",
29
29
  "mrflagly>=0.2.11",
30
30
  "aiodns",
31
+ "anyio>=4.15.1",
31
32
  "httpx>=0.28.1,<0.29",
32
33
  "httpcore>=1.0.9,<1.1",
33
34
  "jinja2",
34
35
  "jsonschema",
35
- "RestrictedPython",
36
+ "RestrictedPython>=8.5",
36
37
  "databases[asyncpg]",
37
38
  "sqlalchemy",
38
39
  "cryptography",
@@ -0,0 +1,105 @@
1
+ import asyncio
2
+ import re
3
+ from collections.abc import Iterator
4
+
5
+ _AUTH_CREDENTIAL = re.compile(r"(?i)\b(Bearer|Basic)\s+[^\s,;]+")
6
+ _AUTHORIZATION_VALUE = re.compile(
7
+ r"(?i)\b(authorization)(\s*[:=]\s*)"
8
+ r"(?:\"[^\"]*\"|'[^']*'|(?:(?:Basic|Bearer)\s+)?[^\s,;]+)"
9
+ )
10
+ _COOKIE_VALUE = re.compile(r"(?i)\b(set-cookie|cookie)(\s*:\s*)[^\r\n]+")
11
+ _SECRET_KEYS = (
12
+ r"api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|"
13
+ r"credential|password|private[_-]?key|refresh[_-]?token|secret|session[_-]?id|"
14
+ r"oauth[_-]?token|token"
15
+ )
16
+ _QUOTED_SECRET_VALUE = re.compile(
17
+ rf"(?i)\b({_SECRET_KEYS})\b(\s*[\"']?\s*[:=]\s*)([\"'])[^\"']*\3"
18
+ )
19
+ _SECRET_VALUE = re.compile(
20
+ rf"(?i)\b({_SECRET_KEYS})\b(\s*[\"']?\s*[:=]\s*)([^\"',\s;&}}]+)"
21
+ )
22
+ _URL_CREDENTIALS = re.compile(r"(?i)(https?://)[^\s/:@]+:[^\s/@]+@")
23
+
24
+
25
+ def _redact_secrets(detail: str) -> str:
26
+ detail = _URL_CREDENTIALS.sub(r"\1[REDACTED]:[REDACTED]@", detail)
27
+ detail = _AUTHORIZATION_VALUE.sub(r"\1\2[REDACTED]", detail)
28
+ detail = _AUTH_CREDENTIAL.sub(r"\1 [REDACTED]", detail)
29
+ detail = _COOKIE_VALUE.sub(r"\1\2[REDACTED]", detail)
30
+ detail = _QUOTED_SECRET_VALUE.sub(r"\1\2\3[REDACTED]\3", detail)
31
+ return _SECRET_VALUE.sub(r"\1\2[REDACTED]", detail)
32
+
33
+
34
+ def _leaf_exceptions(exc: BaseException) -> Iterator[BaseException]:
35
+ if isinstance(exc, BaseExceptionGroup):
36
+ for nested in exc.exceptions:
37
+ yield from _leaf_exceptions(nested)
38
+ return
39
+
40
+ yield exc
41
+
42
+
43
+ def actionable_exception_group(exc: BaseExceptionGroup) -> BaseExceptionGroup:
44
+ """Return regular exceptions while allowing only cancellation to be discarded."""
45
+ actionable, remainder = exc.split(Exception)
46
+ if actionable is None or (
47
+ remainder is not None
48
+ and any(
49
+ not isinstance(item, asyncio.CancelledError)
50
+ for item in _leaf_exceptions(remainder)
51
+ )
52
+ ):
53
+ raise exc
54
+ return actionable
55
+
56
+
57
+ def _http_status(exc: BaseException) -> int | None:
58
+ response = getattr(exc, "response", None)
59
+ status = getattr(response, "status_code", None)
60
+ if isinstance(status, int):
61
+ return status
62
+
63
+ status = getattr(exc, "status", None)
64
+ return status if isinstance(status, int) else None
65
+
66
+
67
+ def _exception_detail(exc: BaseException) -> str:
68
+ detail = _redact_secrets(str(exc).strip() or type(exc).__name__)
69
+ status = _http_status(exc)
70
+ if status == 401:
71
+ return f"Authentication failed (HTTP 401): {detail}"
72
+ if status == 403:
73
+ return f"Authorization failed (HTTP 403): {detail}"
74
+ return detail
75
+
76
+
77
+ def _exception_priority(exc: BaseException) -> int:
78
+ status = _http_status(exc)
79
+ if status == 401:
80
+ return 0
81
+ if status == 403:
82
+ return 1
83
+ return 2
84
+
85
+
86
+ def exception_detail(exc: BaseException) -> str:
87
+ """Return useful, bounded details from an exception or exception group."""
88
+ leaves = list(_leaf_exceptions(exc))
89
+ actionable = [
90
+ item for item in leaves if not isinstance(item, asyncio.CancelledError)
91
+ ]
92
+ if actionable:
93
+ leaves = actionable
94
+
95
+ leaves.sort(key=_exception_priority)
96
+ details = list(dict.fromkeys(_exception_detail(item) for item in leaves))
97
+ if len(details) == 1:
98
+ return details[0]
99
+
100
+ visible_details = details[:3]
101
+ if len(details) > len(visible_details):
102
+ visible_details.append(
103
+ f"and {len(details) - len(visible_details)} more error(s)"
104
+ )
105
+ return "Multiple errors: " + "; ".join(visible_details)
@@ -19,6 +19,7 @@ from hyperforge.broker import Broker
19
19
  from hyperforge.configure import load_all_configurations, scan
20
20
  from hyperforge.db.agents import AgentManager
21
21
  from hyperforge.engine import State, get_state
22
+ from hyperforge.errors import actionable_exception_group, exception_detail
22
23
  from hyperforge.interaction import (
23
24
  AnswerOperation,
24
25
  AragAnswer,
@@ -375,10 +376,16 @@ class SessionManager:
375
376
  async with asyncio.timeout(self.settings.question_timeout_seconds):
376
377
  await state.agent(question_memory, state.manager)
377
378
 
379
+ except BaseExceptionGroup as exc:
380
+ error_group = actionable_exception_group(exc)
381
+ logger.exception("Answering exception")
382
+ errors.capture_exception(error_group)
383
+ error = ARAGException(detail=exception_detail(error_group))
384
+ observation.set_status("error")
378
385
  except Exception as e:
379
386
  logger.exception("Answering exception")
380
387
  errors.capture_exception(e)
381
- error = ARAGException(detail=str(e))
388
+ error = ARAGException(detail=exception_detail(e))
382
389
  observation.set_status("error")
383
390
  finally:
384
391
  if state.manager is not None:
@@ -387,10 +394,9 @@ class SessionManager:
387
394
  except Exception as e:
388
395
  logger.exception("Error closing Manager")
389
396
  errors.capture_exception(e)
390
-
391
- observation.end()
392
- answer_running.dec()
393
- keepalive.cancel()
397
+ observation.end()
398
+ answer_running.dec()
399
+ keepalive.cancel()
394
400
 
395
401
  await self.callback(
396
402
  topic,