deepstrike 0.2.22__tar.gz → 0.2.24__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 (174) hide show
  1. {deepstrike-0.2.22 → deepstrike-0.2.24}/Cargo.lock +5 -5
  2. {deepstrike-0.2.22 → deepstrike-0.2.24}/Cargo.toml +2 -2
  3. {deepstrike-0.2.22 → deepstrike-0.2.24}/PKG-INFO +1 -1
  4. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/__init__.py +10 -1
  5. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/governance.py +32 -0
  6. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/harness/harness.py +45 -22
  7. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/anthropic.py +35 -0
  8. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/stream.py +16 -0
  9. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/execution_plane.py +78 -3
  10. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/mcp_proxy_plane.py +2 -1
  11. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/process_sandbox_plane.py +2 -1
  12. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/remote_vpc_plane.py +2 -1
  13. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/runner.py +75 -5
  14. deepstrike-0.2.24/deepstrike/tools/__init__.py +9 -0
  15. deepstrike-0.2.24/deepstrike/tools/errors.py +135 -0
  16. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/tools/execution.py +2 -1
  17. {deepstrike-0.2.22 → deepstrike-0.2.24}/pyproject.toml +1 -1
  18. deepstrike-0.2.22/deepstrike/tools/__init__.py +0 -5
  19. {deepstrike-0.2.22 → deepstrike-0.2.24}/README.md +0 -0
  20. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/Cargo.toml +0 -0
  21. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/compression.rs +0 -0
  22. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/config.rs +0 -0
  23. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/dashboard.rs +0 -0
  24. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/manager.rs +0 -0
  25. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/mod.rs +0 -0
  26. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/partitions.rs +0 -0
  27. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/pressure.rs +0 -0
  28. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/renderer.rs +0 -0
  29. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/renewal.rs +0 -0
  30. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/sections.rs +0 -0
  31. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
  32. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/snapshot.rs +0 -0
  33. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
  34. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/task_state.rs +0 -0
  35. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/text.rs +0 -0
  36. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
  37. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/audit.rs +0 -0
  38. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
  39. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/mod.rs +0 -0
  40. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/permission.rs +0 -0
  41. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
  42. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/quota.rs +0 -0
  43. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
  44. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/sandbox.rs +0 -0
  45. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/tool_decision.rs +0 -0
  46. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/governance/veto.rs +0 -0
  47. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/harness/eval.rs +0 -0
  48. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/harness/mod.rs +0 -0
  49. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/lib.rs +0 -0
  50. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/curator.rs +0 -0
  51. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/durable.rs +0 -0
  52. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/extractor.rs +0 -0
  53. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/idle_pipeline.rs +0 -0
  54. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/mod.rs +0 -0
  55. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/runtime.rs +0 -0
  56. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/semantic.rs +0 -0
  57. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/session.rs +0 -0
  58. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/synthesis.rs +0 -0
  59. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/trace_analyzer.rs +0 -0
  60. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/memory/working.rs +0 -0
  61. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/mm/handle.rs +0 -0
  62. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/mm/memory.rs +0 -0
  63. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/mm/mod.rs +0 -0
  64. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
  65. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
  66. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/orchestration/tournament.rs +0 -0
  67. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/orchestration/workflow/mod.rs +0 -0
  68. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/orchestration/workflow/run.rs +0 -0
  69. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/proc/mod.rs +0 -0
  70. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
  71. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/kernel.rs +0 -0
  72. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
  73. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
  74. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
  75. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/session.rs +0 -0
  76. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/runtime/snapshot.rs +0 -0
  77. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
  78. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
  79. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
  80. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
  81. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/capability.rs +0 -0
  82. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/eviction.rs +0 -0
  83. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs +0 -0
  84. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/milestone_exec.rs +0 -0
  85. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs +0 -0
  86. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/process.rs +0 -0
  87. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/signal.rs +0 -0
  88. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs +0 -0
  89. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/state_machine/workflow.rs +0 -0
  90. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
  91. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/signals/attention.rs +0 -0
  92. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/signals/mod.rs +0 -0
  93. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/signals/queue.rs +0 -0
  94. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/signals/router.rs +0 -0
  95. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
  96. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/agent.rs +0 -0
  97. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/capability.rs +0 -0
  98. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/contract.rs +0 -0
  99. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/error.rs +0 -0
  100. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/message.rs +0 -0
  101. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/milestone.rs +0 -0
  102. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/mod.rs +0 -0
  103. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/model.rs +0 -0
  104. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/policy.rs +0 -0
  105. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/result.rs +0 -0
  106. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/signal.rs +0 -0
  107. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/skill.rs +0 -0
  108. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-core/src/types/task.rs +0 -0
  109. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-py/Cargo.toml +0 -0
  110. {deepstrike-0.2.22 → deepstrike-0.2.24}/crates/deepstrike-py/src/lib.rs +0 -0
  111. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/collaboration/__init__.py +0 -0
  112. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/collaboration/contract.py +0 -0
  113. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/collaboration/handoff.py +0 -0
  114. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/collaboration/harness.py +0 -0
  115. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/collaboration/modes.py +0 -0
  116. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/collaboration/pool.py +0 -0
  117. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/harness/__init__.py +0 -0
  118. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/kernel/__init__.py +0 -0
  119. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/knowledge/__init__.py +0 -0
  120. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/knowledge/source.py +0 -0
  121. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/memory/__init__.py +0 -0
  122. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/memory/agent.py +0 -0
  123. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/memory/in_memory_store.py +0 -0
  124. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/memory/protocols.py +0 -0
  125. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/memory/working.py +0 -0
  126. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/__init__.py +0 -0
  127. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/base.py +0 -0
  128. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/deepseek.py +0 -0
  129. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/gemini.py +0 -0
  130. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/glm.py +0 -0
  131. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/kimi.py +0 -0
  132. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/minimax.py +0 -0
  133. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/ollama.py +0 -0
  134. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/openai.py +0 -0
  135. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/openai_responses.py +0 -0
  136. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/qwen.py +0 -0
  137. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/replay.py +0 -0
  138. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/providers/replay_validator.py +0 -0
  139. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/__init__.py +0 -0
  140. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/archive.py +0 -0
  141. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/credential_vault.py +0 -0
  142. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/eval.py +0 -0
  143. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/filtered_plane.py +0 -0
  144. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/kernel_event_log.py +0 -0
  145. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/kernel_step.py +0 -0
  146. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/large_result_spool.py +0 -0
  147. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/os_profile.py +0 -0
  148. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/os_snapshot.py +0 -0
  149. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/output_schema.py +0 -0
  150. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/provider_replay.py +0 -0
  151. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/reducers.py +0 -0
  152. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/replay_fixture.py +0 -0
  153. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/replay_provider.py +0 -0
  154. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/replay_sanitize.py +0 -0
  155. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/session_log.py +0 -0
  156. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/session_repair.py +0 -0
  157. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/sub_agent_orchestrator.py +0 -0
  158. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/workflow_control_flow.py +0 -0
  159. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/workflow_store.py +0 -0
  160. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/runtime/worktree_plane.py +0 -0
  161. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/safety/__init__.py +0 -0
  162. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/safety/permissions.py +0 -0
  163. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/signals/__init__.py +0 -0
  164. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/signals/gateway.py +0 -0
  165. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/signals/scheduled.py +0 -0
  166. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/signals/types.py +0 -0
  167. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/skills/__init__.py +0 -0
  168. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/skills/registry.py +0 -0
  169. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/skills/watcher.py +0 -0
  170. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/tools/builtin/__init__.py +0 -0
  171. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/tools/builtin/read_file.py +0 -0
  172. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/tools/registry.py +0 -0
  173. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/types/__init__.py +0 -0
  174. {deepstrike-0.2.22 → deepstrike-0.2.24}/deepstrike/types/agent.py +0 -0
@@ -194,7 +194,7 @@ dependencies = [
194
194
 
195
195
  [[package]]
196
196
  name = "deepstrike-core"
197
- version = "0.2.22"
197
+ version = "0.2.24"
198
198
  dependencies = [
199
199
  "compact_str",
200
200
  "pretty_assertions",
@@ -206,7 +206,7 @@ dependencies = [
206
206
 
207
207
  [[package]]
208
208
  name = "deepstrike-node"
209
- version = "0.2.22"
209
+ version = "0.2.24"
210
210
  dependencies = [
211
211
  "compact_str",
212
212
  "deepstrike-core",
@@ -218,7 +218,7 @@ dependencies = [
218
218
 
219
219
  [[package]]
220
220
  name = "deepstrike-py"
221
- version = "0.2.22"
221
+ version = "0.2.24"
222
222
  dependencies = [
223
223
  "compact_str",
224
224
  "deepstrike-core",
@@ -229,7 +229,7 @@ dependencies = [
229
229
 
230
230
  [[package]]
231
231
  name = "deepstrike-sdk"
232
- version = "0.2.22"
232
+ version = "0.2.24"
233
233
  dependencies = [
234
234
  "async-stream",
235
235
  "async-trait",
@@ -263,7 +263,7 @@ dependencies = [
263
263
 
264
264
  [[package]]
265
265
  name = "deepstrike-wasm"
266
- version = "0.2.22"
266
+ version = "0.2.24"
267
267
  dependencies = [
268
268
  "compact_str",
269
269
  "deepstrike-core",
@@ -3,13 +3,13 @@ resolver = "2"
3
3
  members = ["crates/deepstrike-core", "crates/deepstrike-py"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.22"
6
+ version = "0.2.24"
7
7
  edition = "2024"
8
8
  license = "MIT"
9
9
  repository = "https://github.com/kongusen/deepstrike"
10
10
 
11
11
  [workspace.dependencies]
12
- deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.22" }
12
+ deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.24" }
13
13
  notify = "6"
14
14
  serde = { version = "1", features = ["derive"] }
15
15
  serde_json = "1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepstrike
3
- Version: 0.2.22
3
+ Version: 0.2.24
4
4
  Requires-Dist: httpx>=0.27
5
5
  Requires-Dist: pyyaml>=6.0
6
6
  Requires-Dist: anyio>=4.0
@@ -64,7 +64,11 @@ from deepstrike.providers import (
64
64
  PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, ToolArgumentRepairedEvent,
65
65
  RetryConfig, CircuitBreaker, TokenUsage, ProviderToolSpec,
66
66
  )
67
- from deepstrike.tools import RegisteredTool, tool, streaming_tool, validate_tool_arguments, execute_tools, read_file
67
+ from deepstrike.tools import (
68
+ RegisteredTool, tool, streaming_tool, validate_tool_arguments, execute_tools, read_file,
69
+ ToolError, safe_tool, ok, fail, format_tool_error,
70
+ )
71
+ from deepstrike.providers.stream import ToolAuditFailedEvent
68
72
  from deepstrike.memory import (
69
73
  WorkingMemory,
70
74
  DreamStore, DreamResult, SessionData, MemoryEntry, CurationResult, CurationStats,
@@ -75,6 +79,9 @@ from deepstrike.harness import (
75
79
  QualityGate,
76
80
  SinglePassHarness, HarnessLoop, EvalLoopHarness,
77
81
  HarnessRequest, HarnessOutcome,
82
+ # I3.1: surface harness types so hosts can construct verdicts and stream-event handlers
83
+ # without importing from deepstrike.harness.harness directly.
84
+ HarnessEvent, CriterionResult,
78
85
  )
79
86
  from deepstrike.skills import SkillRegistry
80
87
  from deepstrike.knowledge import KnowledgeSource
@@ -136,11 +143,13 @@ __all__ = [
136
143
  "PermissionRequestEvent", "PermissionResolvedEvent", "PermissionResponse", "ToolArgumentRepairedEvent",
137
144
  "RetryConfig", "CircuitBreaker", "TokenUsage", "ProviderToolSpec",
138
145
  "RegisteredTool", "tool", "streaming_tool", "validate_tool_arguments", "execute_tools", "read_file",
146
+ "ToolError", "safe_tool", "ok", "fail", "format_tool_error", "ToolAuditFailedEvent",
139
147
  "WorkingMemory",
140
148
  "DreamStore", "DreamResult", "SessionData", "MemoryEntry", "CurationResult", "CurationStats",
141
149
  "PermissionManager", "PermissionMode", "Permission", "PermissionDecision",
142
150
  "QualityGate",
143
151
  "SinglePassHarness", "HarnessLoop", "EvalLoopHarness", "HarnessRequest", "HarnessOutcome",
152
+ "HarnessEvent", "CriterionResult",
144
153
  "SkillRegistry",
145
154
  "KnowledgeSource",
146
155
  "RuntimeSignal", "SignalSource", "ScheduledPrompt", "SignalGateway",
@@ -28,6 +28,38 @@ class GovernancePolicy:
28
28
  vetoes: list[str] = field(default_factory=list)
29
29
  rate_limits: list[GovernanceRateLimit] = field(default_factory=list)
30
30
  constraints: list[dict[str, Any]] = field(default_factory=list)
31
+ # I5: when True (default), the runner pre-filters denied tools out of the schema. Mirrors Node.
32
+ surface_denied_in_system: bool = True
33
+
34
+
35
+ def governance_filter_schema(tools: list, policy: "GovernancePolicy | None") -> tuple[list, list[str]]:
36
+ """I5: bucket tools into (allowed, denied) per the policy. Pure. Mirrors Node ``governanceFilterSchema``."""
37
+ if policy is None:
38
+ return tools, []
39
+ vetoes = set(policy.vetoes or [])
40
+ allowed: list = []
41
+ denied: list[str] = []
42
+ def matches(pat: str, name: str) -> bool:
43
+ return pat == name or (pat.endswith("*") and name.startswith(pat[:-1]))
44
+ for t in tools:
45
+ name = t.name if hasattr(t, "name") else (t.get("name") if isinstance(t, dict) else None)
46
+ if name is None:
47
+ allowed.append(t)
48
+ continue
49
+ if name in vetoes:
50
+ denied.append(name)
51
+ continue
52
+ action = policy.default_action or "allow"
53
+ for r in (policy.rules or []):
54
+ pat = r.pattern if hasattr(r, "pattern") else r.get("pattern")
55
+ act = r.action if hasattr(r, "action") else r.get("action")
56
+ if pat is not None and matches(pat, name):
57
+ action = act
58
+ if action == "deny":
59
+ denied.append(name)
60
+ else:
61
+ allowed.append(t)
62
+ return allowed, denied
31
63
 
32
64
 
33
65
  def governance_policy_to_kernel_event(policy: GovernancePolicy) -> dict[str, Any]:
@@ -16,6 +16,10 @@ class Criterion:
16
16
  text: str
17
17
  required: bool = True
18
18
  weight: float = 1.0
19
+ # I3.3 (A4): optional stable id from the host's contract layer (threaded to verdict_fn).
20
+ id: "str | None" = None
21
+ # I3.3 (A4): host hint — host has a deterministic check for this criterion.
22
+ machine_checkable: "bool | None" = None
19
23
 
20
24
 
21
25
  @dataclass
@@ -160,6 +164,9 @@ class SinglePassHarness:
160
164
 
161
165
 
162
166
  class EvalLoopHarness:
167
+ """I3.4 (A1): deprecated — prefer ``HarnessLoop`` with ``verdict_fn`` for host-defined
168
+ judgment. ``EvalLoopHarness.stream()`` does NOT honor ``gate`` (only ``.run()`` does);
169
+ ``HarnessLoop`` runs the eval loop uniformly across stream and run."""
163
170
  def __init__(self, runner: "RuntimeRunner", gate: "QualityGate", max_attempts: int = 3):
164
171
  self._runner = runner
165
172
  self._gate = gate
@@ -186,11 +193,16 @@ class HarnessLoop:
186
193
  *,
187
194
  max_attempts: int = 3,
188
195
  skill_dir: str | None = None,
196
+ # I3.2 (A2/A3): host-supplied judgment. Receives kwargs (goal, criteria, attempt, result);
197
+ # returns Verdict to short-circuit the LLM eval, or None/Awaitable[None] to defer. Mirrors
198
+ # the Node SDK ``verdictFn``.
199
+ verdict_fn=None,
189
200
  ):
190
201
  self._runner = runner
191
202
  self._eval_provider = eval_provider
192
203
  self._max_attempts = max_attempts
193
204
  self._skill_dir = pathlib.Path(skill_dir) if skill_dir else None
205
+ self._verdict_fn = verdict_fn
194
206
 
195
207
  async def run(self, request: HarnessRequest) -> HarnessOutcome:
196
208
  # R3-1: collect nodes the agent submits while running under the harness (dynamic fan-out in
@@ -251,30 +263,41 @@ class HarnessLoop:
251
263
 
252
264
  yield SupervisingEvent()
253
265
 
254
- # #6 (0.5.0): the eval/verdict compute is the kernel's stateless free functions (was the
255
- # EvalPipeline state machine). Build the eval prompt, call the eval LLM, parse the verdict.
256
- eval_msgs = build_eval_messages(request.goal, criteria, last_result, attempt, True)
257
- eval_text = ""
258
- eval_system = "\n\n".join(m.content for m in eval_msgs if m.role == "system")
259
- eval_turns = [m for m in eval_msgs if m.role != "system"]
260
- eval_context = RenderedContext(system_text=eval_system, turns=eval_turns)
261
- async for evt in self._eval_provider.stream(eval_context, [], extensions=None):
262
- if isinstance(evt, TextDelta):
263
- eval_text += evt.delta
264
-
265
- parsed = parse_verdict(eval_text)
266
- verdict = Verdict(
267
- passed=parsed.passed,
268
- overall_score=parsed.overall_score,
269
- feedback=parsed.feedback,
270
- details=[
271
- CriterionResult(criterion=d.criterion, passed=d.passed, score=d.score, feedback=d.feedback)
272
- for d in (parsed.details or [])
273
- ],
274
- )
266
+ # I3.2 (A2/A3): host-supplied verdict_fn short-circuits the LLM eval. Returning None
267
+ # (or awaitable None) defers to the built-in eval below enables hybrid judgment.
268
+ verdict = None
269
+ skill_candidate = None
270
+ if self._verdict_fn is not None:
271
+ result = self._verdict_fn(goal=request.goal, criteria=criteria, attempt=attempt, result=last_result)
272
+ if hasattr(result, "__await__"):
273
+ result = await result
274
+ verdict = result
275
+ if verdict is None:
276
+ # #6 (0.5.0): the eval/verdict compute is the kernel's stateless free functions (was the
277
+ # EvalPipeline state machine). Build the eval prompt, call the eval LLM, parse the verdict.
278
+ eval_msgs = build_eval_messages(request.goal, criteria, last_result, attempt, True)
279
+ eval_text = ""
280
+ eval_system = "\n\n".join(m.content for m in eval_msgs if m.role == "system")
281
+ eval_turns = [m for m in eval_msgs if m.role != "system"]
282
+ eval_context = RenderedContext(system_text=eval_system, turns=eval_turns)
283
+ async for evt in self._eval_provider.stream(eval_context, [], extensions=None):
284
+ if isinstance(evt, TextDelta):
285
+ eval_text += evt.delta
286
+
287
+ parsed = parse_verdict(eval_text)
288
+ verdict = Verdict(
289
+ passed=parsed.passed,
290
+ overall_score=parsed.overall_score,
291
+ feedback=parsed.feedback,
292
+ details=[
293
+ CriterionResult(criterion=d.criterion, passed=d.passed, score=d.score, feedback=d.feedback)
294
+ for d in (parsed.details or [])
295
+ ],
296
+ )
297
+ skill_candidate = parsed.skill_candidate
275
298
 
276
299
  if verdict.passed:
277
- sc = parsed.skill_candidate
300
+ sc = skill_candidate
278
301
  if sc and self._skill_dir:
279
302
  lines = ["---", f"name: {sc.name}", f"description: {sc.description}"]
280
303
  if sc.when_to_use:
@@ -222,6 +222,8 @@ class AnthropicProvider:
222
222
  system = self._build_system(context, strategy)
223
223
  tool_defs = self._build_tools(tools, anchor_cache=not isinstance(system, list), strategy=strategy)
224
224
  _assert_cache_budget(system, len(tools))
225
+ # I1: capture which slots will carry cache_control for pro-rata attribution at usage time.
226
+ slot_bp = _count_cache_control_slots(system, tool_defs, msgs)
225
227
 
226
228
  native_blocks: dict[int, dict] = {}
227
229
  tool_blocks: dict[int, dict] = {}
@@ -254,12 +256,14 @@ class AnthropicProvider:
254
256
  # input_tokens is the FULL prompt size (uncached + cache
255
257
  # read + cache write) for accurate context accounting.
256
258
  full_input = uncached_input + cache_read + cache_creation
259
+ by_slot = _estimate_cache_read_by_slot(cache_read, slot_bp)
257
260
  yield UsageEvent(
258
261
  total_tokens=full_input + output_tokens,
259
262
  input_tokens=full_input,
260
263
  output_tokens=output_tokens,
261
264
  cache_read_input_tokens=cache_read,
262
265
  cache_creation_input_tokens=cache_creation,
266
+ cache_read_input_tokens_by_slot=by_slot,
263
267
  )
264
268
  elif event.type == "content_block_start":
265
269
  idx = event.index
@@ -341,6 +345,37 @@ def _resolve_cache_breakpoint_strategy(extensions: "dict | None") -> str:
341
345
  return "default"
342
346
 
343
347
 
348
+ def _count_cache_control_slots(system, tool_defs, msgs) -> dict:
349
+ """I1: which slots of the outgoing request carry a ``cache_control`` breakpoint. Mirrors Node."""
350
+ sys_bp = isinstance(system, list) and any((isinstance(b, dict) and b.get("cache_control") is not None) for b in system)
351
+ tool_bp = bool(tool_defs) and any((isinstance(t, dict) and t.get("cache_control") is not None) for t in tool_defs)
352
+ msg_bp = False
353
+ for m in msgs or []:
354
+ content = m.get("content") if isinstance(m, dict) else None
355
+ if isinstance(content, list):
356
+ if any((isinstance(b, dict) and b.get("cache_control") is not None) for b in content):
357
+ msg_bp = True
358
+ break
359
+ return {"system": sys_bp, "tools": tool_bp, "messages": msg_bp}
360
+
361
+
362
+ def _estimate_cache_read_by_slot(cache_read: int, slot_bp: dict) -> "dict | None":
363
+ """I1: split ``cache_read_input_tokens`` evenly across contributing slots. Remainder lands on the
364
+ first contributing slot to keep the sum exact. Returns None when no cache read or no slot
365
+ contributed — consumers see the field absent rather than all zeros. Mirrors Node."""
366
+ if cache_read <= 0:
367
+ return None
368
+ contributors = [s for s in ("system", "tools", "messages") if slot_bp.get(s)]
369
+ if not contributors:
370
+ return None
371
+ share = cache_read // len(contributors)
372
+ remainder = cache_read - share * len(contributors)
373
+ out: dict = {}
374
+ for i, slot in enumerate(contributors):
375
+ out[slot] = share + (remainder if i == 0 else 0)
376
+ return out
377
+
378
+
344
379
  def _apply_message_cache_control(msgs: list[dict], frozen_prefix_len: "int | None" = None, strategy: str = "default") -> None:
345
380
  """Place the (<=2) message-history cache breakpoints. The final message always
346
381
  gets one (writes the current full prefix). The second is placed by one of two
@@ -63,6 +63,9 @@ class UsageEvent:
63
63
  # Cost breakdown (subset of input_tokens): reads bill ~0.1x, writes ~1.25x.
64
64
  cache_read_input_tokens: int = 0
65
65
  cache_creation_input_tokens: int = 0
66
+ # I1: pro-rata per-slot attribution of cache_read_input_tokens (Anthropic only). None when the
67
+ # provider doesn't honor cache_control. Shape: {"system": int?, "tools": int?, "messages": int?}.
68
+ cache_read_input_tokens_by_slot: "dict | None" = None
66
69
 
67
70
 
68
71
  @dataclass
@@ -123,6 +126,18 @@ class ToolDeniedEvent:
123
126
  reason: str = ""
124
127
 
125
128
 
129
+ @dataclass
130
+ class ToolAuditFailedEvent:
131
+ """A tool's ``ctx.audit(label, fn)`` best-effort side-effect threw. The tool itself completed
132
+ successfully (no ``is_error`` flip, no retry); this event lets the host log / monitor that an
133
+ audit-store / metrics-emit / non-essential persistence step failed."""
134
+ type: str = "tool_audit_failed"
135
+ call_id: str = ""
136
+ name: str = ""
137
+ label: str = ""
138
+ error: str = ""
139
+
140
+
126
141
  @dataclass
127
142
  class WorkflowNodesSubmittedEvent:
128
143
  """R3-1: a workflow node's agent called the ``submit_workflow_nodes`` tool. The runner surfaces
@@ -146,5 +161,6 @@ StreamEvent = Union[
146
161
  PermissionResolvedEvent,
147
162
  ToolArgumentRepairedEvent,
148
163
  ToolDeniedEvent,
164
+ ToolAuditFailedEvent,
149
165
  WorkflowNodesSubmittedEvent,
150
166
  ]
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import inspect
4
5
  import json
6
+ import warnings
5
7
  from collections.abc import AsyncIterable, AsyncIterator
6
8
  from dataclasses import dataclass
7
9
  from pathlib import Path
@@ -13,12 +15,14 @@ from deepstrike.providers.stream import (
13
15
  PermissionResolvedEvent,
14
16
  PermissionResponse,
15
17
  StreamEvent,
18
+ ToolAuditFailedEvent,
16
19
  ToolDeltaEvent,
17
20
  ToolDeniedEvent,
18
21
  ToolResultEvent,
19
22
  ToolSuspendEvent,
20
23
  ToolArgumentRepairedEvent,
21
24
  )
25
+ from deepstrike.tools.errors import format_tool_error
22
26
  from deepstrike.tools.registry import RegisteredTool, normalize_tool_chunk, tool_chunk_text, validate_tool_arguments
23
27
 
24
28
  if TYPE_CHECKING:
@@ -33,6 +37,39 @@ def _strip_frontmatter(content: str) -> str:
33
37
  return re.sub(r"^---\n.*?\n---\n?", "", content, count=1, flags=re.DOTALL)
34
38
 
35
39
 
40
+ _WARNED_FAILURE_SHAPES: set[str] = set()
41
+
42
+
43
+ def _maybe_warn_failure_shaped_chunk(tool_name: str, delta_text: str) -> None:
44
+ """One-shot heuristic: detect when a streaming tool yielded text that *looks* like a failure
45
+ envelope. The runtime cannot block the tool from doing it, but we warn (once per tool) so
46
+ the author migrates to raising — the canonical "streaming tool fails" path. Aligns with the
47
+ non-streaming ``tool()`` / ``safe_tool`` contract: failures raise, successes return data."""
48
+ if not delta_text or tool_name in _WARNED_FAILURE_SHAPES:
49
+ return
50
+ trimmed = delta_text.strip()
51
+ if len(trimmed) < 2 or trimmed[0] != "{":
52
+ return
53
+ try:
54
+ parsed = json.loads(trimmed)
55
+ except Exception:
56
+ return
57
+ if not isinstance(parsed, dict):
58
+ return
59
+ looks_like_failure = parsed.get("success") is False or parsed.get("isError") is True or parsed.get("is_error") is True
60
+ if not looks_like_failure:
61
+ return
62
+ _WARNED_FAILURE_SHAPES.add(tool_name)
63
+ warnings.warn(
64
+ f'streaming tool "{tool_name}" yielded a failure-shaped chunk '
65
+ "(success:false / isError:true). Streaming tools should fail by raising; the runtime will "
66
+ "catch and surface the error consistently. Returning a failure-shaped chunk is a foot-gun: "
67
+ "the kernel still sees is_error=False.",
68
+ RuntimeWarning,
69
+ stacklevel=2,
70
+ )
71
+
72
+
36
73
  @dataclass
37
74
  class RunContext:
38
75
  agent_id: str | None = None
@@ -45,6 +82,12 @@ class RunContext:
45
82
  # M3/G4: the working directory a sub-agent's tools should run in (the git worktree created for an
46
83
  # ``isolation: "worktree"`` node). Injected by ``WorktreeExecutionPlane``; a cwd-aware tool reads it.
47
84
  cwd: str | None = None
85
+ # Per-call best-effort side-effect helper (injected by the execution plane). Wrap audit-log
86
+ # writes, metrics emits, or any non-essential persistence in ``await ctx.audit(label, fn)``;
87
+ # if ``fn`` raises, the failure is surfaced as a ``ToolAuditFailedEvent`` and the tool still
88
+ # completes successfully — avoiding the foot-gun where a transient audit-store outage flips
89
+ # an already-committed write into ``is_error=True`` and triggers a duplicate retry.
90
+ audit: Callable[[str, Callable[[], Awaitable[None] | None]], Awaitable[None]] | None = None
48
91
 
49
92
 
50
93
  class ExecutionPlane:
@@ -174,6 +217,29 @@ class LocalExecutionPlane:
174
217
  error_kind="recoverable",
175
218
  )
176
219
  return
220
+ # Per-call ``audit`` helper: failures collected here are surfaced as
221
+ # ``ToolAuditFailedEvent`` rather than flipping the main tool result to ``is_error=True``.
222
+ audit_failures: list[tuple[str, str]] = []
223
+
224
+ async def _audit(label: str, fn: Callable[[], Awaitable[None] | None]) -> None:
225
+ try:
226
+ result = fn()
227
+ if inspect.isawaitable(result):
228
+ await result
229
+ except Exception as ae:
230
+ audit_failures.append((label, format_tool_error(ae)))
231
+
232
+ call_ctx = RunContext(
233
+ agent_id=ctx.agent_id,
234
+ skill_dir=ctx.skill_dir,
235
+ dream_store=ctx.dream_store,
236
+ knowledge_source=ctx.knowledge_source,
237
+ on_tool_suspend=ctx.on_tool_suspend,
238
+ on_permission_request=ctx.on_permission_request,
239
+ result_spool=ctx.result_spool,
240
+ cwd=ctx.cwd,
241
+ audit=_audit,
242
+ )
177
243
  try:
178
244
  kwargs = json.loads(call.arguments or "{}")
179
245
  original_args_str = json.dumps(kwargs)
@@ -195,8 +261,9 @@ class LocalExecutionPlane:
195
261
  original_arguments=original_args_str,
196
262
  repaired_arguments=json.dumps(kwargs),
197
263
  )
198
- # M3/G4: pass the run context (incl. ``cwd``) so cwd-aware tools scope work to the worktree.
199
- output = await registered(_ctx=ctx, **kwargs)
264
+ # M3/G4: pass the run context (incl. ``cwd``, ``audit``) so cwd-aware / audit-aware tools
265
+ # scope work to the worktree and route best-effort side-effects through the plane.
266
+ output = await registered(_ctx=call_ctx, **kwargs)
200
267
  if isinstance(output, AsyncIterable):
201
268
  combined = ""
202
269
  iterator = output.__aiter__()
@@ -234,22 +301,30 @@ class LocalExecutionPlane:
234
301
  continue
235
302
  delta = tool_chunk_text(raw)
236
303
  combined += delta
304
+ if delta:
305
+ _maybe_warn_failure_shaped_chunk(call.name, delta)
237
306
  yield ToolDeltaEvent(
238
307
  call_id=call.id, name=call.name, delta=delta,
239
308
  chunk=None if isinstance(raw, str) else chunk,
240
309
  )
310
+ for label, error in audit_failures:
311
+ yield ToolAuditFailedEvent(call_id=call.id, name=call.name, label=label, error=error)
241
312
  yield ToolResultEvent(call_id=call.id, name=call.name, content=combined, is_error=False)
242
313
  return
314
+ for label, error in audit_failures:
315
+ yield ToolAuditFailedEvent(call_id=call.id, name=call.name, label=label, error=error)
243
316
  yield ToolResultEvent(call_id=call.id, name=call.name, content=str(output), is_error=False)
244
317
  except Exception as exc:
245
318
  is_fatal = getattr(exc, "is_fatal", False)
246
319
  error_kind = getattr(exc, "error_kind", None)
247
320
  if error_kind is None:
248
321
  error_kind = "fatal" if is_fatal else "recoverable"
322
+ for label, error in audit_failures:
323
+ yield ToolAuditFailedEvent(call_id=call.id, name=call.name, label=label, error=error)
249
324
  yield ToolResultEvent(
250
325
  call_id=call.id,
251
326
  name=call.name,
252
- content=str(exc),
327
+ content=format_tool_error(exc),
253
328
  is_error=True,
254
329
  is_fatal=is_fatal,
255
330
  error_kind=error_kind,
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any
10
10
 
11
11
  from deepstrike._kernel import ToolCall, ToolSchema
12
12
  from deepstrike.providers.stream import StreamEvent, ToolResultEvent
13
+ from deepstrike.tools.errors import format_tool_error
13
14
  from deepstrike.tools.registry import RegisteredTool
14
15
  from deepstrike.runtime.execution_plane import ExecutionPlane, LocalExecutionPlane, RunContext
15
16
  from deepstrike.runtime.credential_vault import CredentialVault
@@ -126,7 +127,7 @@ class _McpConnection:
126
127
  )
127
128
  return (text or json.dumps(result)), result.get("isError", False)
128
129
  except Exception as exc:
129
- return str(exc), True
130
+ return format_tool_error(exc), True
130
131
 
131
132
  async def stop(self) -> None:
132
133
  if self._reader_task:
@@ -6,6 +6,7 @@ import os
6
6
  from pathlib import Path
7
7
 
8
8
  from deepstrike._kernel import ToolSchema
9
+ from deepstrike.tools.errors import format_tool_error
9
10
  from deepstrike.tools.registry import RegisteredTool
10
11
  from deepstrike.runtime.execution_plane import LocalExecutionPlane
11
12
 
@@ -72,7 +73,7 @@ class ProcessSandboxPlane(LocalExecutionPlane):
72
73
  combined = combined[: self._max_output_bytes] + b"\n[output truncated]"
73
74
  return combined.decode("utf-8", errors="replace"), proc.returncode != 0
74
75
  except Exception as exc:
75
- return str(exc), True
76
+ return format_tool_error(exc), True
76
77
 
77
78
  def _make_bash_tool(self) -> RegisteredTool:
78
79
  sandbox = self
@@ -9,6 +9,7 @@ import aiohttp
9
9
 
10
10
  from deepstrike._kernel import ToolCall, ToolSchema
11
11
  from deepstrike.providers.stream import StreamEvent, ToolResultEvent
12
+ from deepstrike.tools.errors import format_tool_error
12
13
  from deepstrike.tools.registry import RegisteredTool
13
14
  from deepstrike.runtime.execution_plane import LocalExecutionPlane, RunContext
14
15
  from deepstrike.runtime.credential_vault import CredentialVault
@@ -97,4 +98,4 @@ class RemoteVpcPlane:
97
98
  result: dict = await resp.json()
98
99
  return result.get("output", ""), bool(result.get("isError", False))
99
100
  except Exception as exc:
100
- return str(exc), True
101
+ return format_tool_error(exc), True