deepstrike 0.2.7__tar.gz → 0.2.8__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 (160) hide show
  1. {deepstrike-0.2.7 → deepstrike-0.2.8}/Cargo.lock +5 -5
  2. {deepstrike-0.2.7 → deepstrike-0.2.8}/Cargo.toml +2 -2
  3. {deepstrike-0.2.7 → deepstrike-0.2.8}/PKG-INFO +1 -1
  4. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/__init__.py +8 -0
  5. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/base.py +14 -0
  6. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/deepseek.py +2 -2
  7. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/minimax.py +2 -2
  8. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/openai.py +24 -10
  9. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/replay.py +18 -0
  10. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/replay_validator.py +52 -21
  11. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/__init__.py +4 -0
  12. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/provider_replay.py +13 -0
  13. {deepstrike-0.2.7 → deepstrike-0.2.8}/pyproject.toml +1 -1
  14. {deepstrike-0.2.7 → deepstrike-0.2.8}/README.md +0 -0
  15. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/Cargo.toml +0 -0
  16. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/compression.rs +0 -0
  17. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/config.rs +0 -0
  18. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/dashboard.rs +0 -0
  19. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/llm_summarizer.rs +0 -0
  20. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/manager.rs +0 -0
  21. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/mod.rs +0 -0
  22. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/partitions.rs +0 -0
  23. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/pressure.rs +0 -0
  24. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/renderer.rs +0 -0
  25. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/renewal.rs +0 -0
  26. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/sections.rs +0 -0
  27. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
  28. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/snapshot.rs +0 -0
  29. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
  30. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/task_state.rs +0 -0
  31. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/text.rs +0 -0
  32. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
  33. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/audit.rs +0 -0
  34. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
  35. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/mod.rs +0 -0
  36. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/permission.rs +0 -0
  37. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
  38. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/quota.rs +0 -0
  39. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
  40. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/sandbox.rs +0 -0
  41. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/tool_decision.rs +0 -0
  42. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/governance/veto.rs +0 -0
  43. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/harness/eval_pipeline.rs +0 -0
  44. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/harness/mod.rs +0 -0
  45. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/lib.rs +0 -0
  46. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/curator.rs +0 -0
  47. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/durable.rs +0 -0
  48. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/extractor.rs +0 -0
  49. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/idle_pipeline.rs +0 -0
  50. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/mod.rs +0 -0
  51. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/runtime.rs +0 -0
  52. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/semantic.rs +0 -0
  53. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/session.rs +0 -0
  54. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/synthesis.rs +0 -0
  55. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/trace_analyzer.rs +0 -0
  56. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/memory/working.rs +0 -0
  57. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/mm/handle.rs +0 -0
  58. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/mm/memory.rs +0 -0
  59. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/mm/mod.rs +0 -0
  60. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/executor.rs +0 -0
  61. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/gen_eval.rs +0 -0
  62. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/loop_until_done.rs +0 -0
  63. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
  64. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/planner.rs +0 -0
  65. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
  66. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/tournament.rs +0 -0
  67. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/orchestration/workflow.rs +0 -0
  68. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/proc/mod.rs +0 -0
  69. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
  70. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/runtime/kernel.rs +0 -0
  71. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
  72. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
  73. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
  74. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/runtime/session.rs +0 -0
  75. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
  76. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
  77. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
  78. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
  79. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/state_machine.rs +0 -0
  80. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/state_machine_tests.rs +0 -0
  81. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
  82. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/scheduler/workflow_run.rs +0 -0
  83. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/signals/attention.rs +0 -0
  84. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/signals/mod.rs +0 -0
  85. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/signals/queue.rs +0 -0
  86. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/signals/router.rs +0 -0
  87. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
  88. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/agent.rs +0 -0
  89. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/capability.rs +0 -0
  90. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/contract.rs +0 -0
  91. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/error.rs +0 -0
  92. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/message.rs +0 -0
  93. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/milestone.rs +0 -0
  94. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/mod.rs +0 -0
  95. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/model.rs +0 -0
  96. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/policy.rs +0 -0
  97. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/result.rs +0 -0
  98. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/signal.rs +0 -0
  99. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/skill.rs +0 -0
  100. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-core/src/types/task.rs +0 -0
  101. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-py/Cargo.toml +0 -0
  102. {deepstrike-0.2.7 → deepstrike-0.2.8}/crates/deepstrike-py/src/lib.rs +0 -0
  103. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/__init__.py +0 -0
  104. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/collaboration/__init__.py +0 -0
  105. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/collaboration/contract.py +0 -0
  106. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/collaboration/handoff.py +0 -0
  107. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/collaboration/harness.py +0 -0
  108. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/collaboration/modes.py +0 -0
  109. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/collaboration/pool.py +0 -0
  110. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/governance.py +0 -0
  111. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/harness/__init__.py +0 -0
  112. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/harness/harness.py +0 -0
  113. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/kernel/__init__.py +0 -0
  114. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/knowledge/__init__.py +0 -0
  115. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/knowledge/source.py +0 -0
  116. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/memory/__init__.py +0 -0
  117. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/memory/agent.py +0 -0
  118. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/memory/protocols.py +0 -0
  119. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/memory/working.py +0 -0
  120. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/anthropic.py +0 -0
  121. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/gemini.py +0 -0
  122. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/glm.py +0 -0
  123. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/kimi.py +0 -0
  124. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/ollama.py +0 -0
  125. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/qwen.py +0 -0
  126. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/providers/stream.py +0 -0
  127. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/archive.py +0 -0
  128. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/credential_vault.py +0 -0
  129. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/execution_plane.py +0 -0
  130. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/filtered_plane.py +0 -0
  131. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/kernel_event_log.py +0 -0
  132. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/kernel_step.py +0 -0
  133. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/large_result_spool.py +0 -0
  134. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
  135. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/os_profile.py +0 -0
  136. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/os_snapshot.py +0 -0
  137. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/process_sandbox_plane.py +0 -0
  138. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/remote_vpc_plane.py +0 -0
  139. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/replay_sanitize.py +0 -0
  140. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/runner.py +0 -0
  141. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/session_log.py +0 -0
  142. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/session_repair.py +0 -0
  143. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/runtime/sub_agent_orchestrator.py +0 -0
  144. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/safety/__init__.py +0 -0
  145. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/safety/permissions.py +0 -0
  146. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/signals/__init__.py +0 -0
  147. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/signals/gateway.py +0 -0
  148. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/signals/scheduled.py +0 -0
  149. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/signals/types.py +0 -0
  150. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/skills/__init__.py +0 -0
  151. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/skills/registry.py +0 -0
  152. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/skills/watcher.py +0 -0
  153. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/tools/__init__.py +0 -0
  154. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/tools/builtin/__init__.py +0 -0
  155. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/tools/builtin/read_file.py +0 -0
  156. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/tools/execution.py +0 -0
  157. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/tools/registry.py +0 -0
  158. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/types/__init__.py +0 -0
  159. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/types/agent.py +0 -0
  160. {deepstrike-0.2.7 → deepstrike-0.2.8}/deepstrike/workflow.py +0 -0
@@ -194,7 +194,7 @@ dependencies = [
194
194
 
195
195
  [[package]]
196
196
  name = "deepstrike-core"
197
- version = "0.2.7"
197
+ version = "0.2.8"
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.7"
209
+ version = "0.2.8"
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.7"
221
+ version = "0.2.8"
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.7"
232
+ version = "0.2.8"
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.7"
266
+ version = "0.2.8"
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.7"
6
+ version = "0.2.8"
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.7" }
12
+ deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.8" }
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.7
3
+ Version: 0.2.8
4
4
  Requires-Dist: httpx>=0.27
5
5
  Requires-Dist: pyyaml>=6.0
6
6
  Requires-Dist: anyio>=4.0
@@ -31,6 +31,12 @@ from .stream import (
31
31
  ToolArgumentRepairedEvent,
32
32
  )
33
33
  from .replay import ReasoningReplayMixin, assistant_replay_key
34
+ from .replay_validator import (
35
+ DEGRADED_REASONING_PLACEHOLDER,
36
+ ProviderReplayValidationError,
37
+ assess_reasoning_replay,
38
+ validate_openai_chat_replay,
39
+ )
34
40
  from .anthropic import AnthropicProvider
35
41
  from .openai import OpenAIProvider
36
42
  from .qwen import QwenProvider
@@ -49,6 +55,8 @@ __all__ = [
49
55
  "to_anthropic_content", "to_anthropic_messages",
50
56
  "to_openai_content", "to_openai_message_params", "ThinkingTagStreamExtractor",
51
57
  "ReasoningReplayMixin", "assistant_replay_key",
58
+ "ProviderReplayValidationError", "validate_openai_chat_replay", "assess_reasoning_replay",
59
+ "DEGRADED_REASONING_PLACEHOLDER",
52
60
  "StreamEvent", "TextDelta", "ThinkingDelta",
53
61
  "ToolCallEvent", "ToolDeltaEvent", "ToolSuspendEvent", "ToolResultEvent", "DoneEvent", "ErrorEvent",
54
62
  "PermissionRequestEvent", "PermissionResolvedEvent", "PermissionResponse",
@@ -11,6 +11,20 @@ from .stream import StreamEvent
11
11
  logger = logging.getLogger(__name__)
12
12
  T = TypeVar("T")
13
13
 
14
+ # Internal control flags that steer DeepStrike's own serialization/validation
15
+ # and must never be forwarded to any provider's wire request.
16
+ INTERNAL_EXTENSION_KEYS = frozenset({"degrade_missing_reasoning_replay"})
17
+
18
+ # Keys the chat-completions transport sets itself; never echo them from extensions.
19
+ _WIRE_RESERVED_KEYS = frozenset({"model", "messages", "tools", "stream", "stream_options"})
20
+
21
+
22
+ def wire_request_extensions(extensions: dict | None, *, extra_omit: tuple[str, ...] = ()) -> dict:
23
+ """Filter caller extensions down to what is safe to send on the wire,
24
+ dropping transport-reserved keys and DeepStrike-internal control flags."""
25
+ blocked = _WIRE_RESERVED_KEYS | INTERNAL_EXTENSION_KEYS | frozenset(extra_omit)
26
+ return {k: v for k, v in (extensions or {}).items() if k not in blocked}
27
+
14
28
 
15
29
  def parse_tool_arguments(value: Any) -> dict[str, Any]:
16
30
  """Normalize tool arguments to dict, handling str/dict/null."""
@@ -5,7 +5,7 @@ from typing import AsyncIterator
5
5
  import httpx
6
6
  from deepstrike._kernel import Message, ToolCall, ToolSchema
7
7
  from .stream import StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent
8
- from .base import RetryConfig, ProviderDescriptor, RenderedContext, RuntimePolicy, normalize_tool_call
8
+ from .base import RetryConfig, ProviderDescriptor, RenderedContext, RuntimePolicy, normalize_tool_call, wire_request_extensions
9
9
  from .openai import OpenAIProvider
10
10
 
11
11
  logger = logging.getLogger(__name__)
@@ -90,7 +90,7 @@ class DeepSeekProvider(OpenAIProvider):
90
90
  last_exc = None
91
91
  for attempt in range(self._retry.max_retries):
92
92
  try:
93
- request_extensions = {k: v for k, v in (extensions or {}).items() if k not in {"model", "messages", "tools", "stream", "stream_options"}}
93
+ request_extensions = wire_request_extensions(extensions)
94
94
  resp = await self._client.chat.completions.create(
95
95
  **request_extensions,
96
96
  model=self._model,
@@ -5,7 +5,7 @@ from typing import AsyncIterator
5
5
 
6
6
  from deepstrike._kernel import Message, ToolCall, ToolSchema
7
7
  from .anthropic import AnthropicProvider
8
- from .base import ProviderDescriptor, RenderedContext, RetryConfig, RuntimePolicy, normalize_tool_call
8
+ from .base import ProviderDescriptor, RenderedContext, RetryConfig, RuntimePolicy, normalize_tool_call, wire_request_extensions
9
9
  from .openai import OpenAIProvider
10
10
  from .stream import StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, UsageEvent
11
11
 
@@ -119,7 +119,7 @@ class MiniMaxOpenAIProvider(OpenAIProvider):
119
119
  last_exc = None
120
120
  for attempt in range(self._retry.max_retries):
121
121
  try:
122
- request_extensions = {k: v for k, v in ext.items() if k not in {"model", "messages", "tools", "stream", "stream_options"}}
122
+ request_extensions = wire_request_extensions(ext)
123
123
  resp = await self._client.chat.completions.create(
124
124
  **request_extensions,
125
125
  model=self._model,
@@ -5,7 +5,7 @@ from typing import AsyncIterator
5
5
  from openai import AsyncOpenAI
6
6
  from deepstrike._kernel import Message, ToolCall, ToolSchema
7
7
  from .stream import StreamEvent, TextDelta, ToolCallEvent, ThinkingDelta
8
- from .base import RetryConfig, CircuitBreaker, ProviderDescriptor, RenderedContext, RuntimePolicy, normalize_tool_call, to_openai_message_params, ThinkingTagStreamExtractor
8
+ from .base import RetryConfig, CircuitBreaker, ProviderDescriptor, RenderedContext, RuntimePolicy, normalize_tool_call, to_openai_message_params, ThinkingTagStreamExtractor, wire_request_extensions
9
9
  from .replay import ReasoningReplayMixin, assistant_replay_key
10
10
  from .replay_validator import validate_openai_chat_replay
11
11
 
@@ -66,17 +66,35 @@ class OpenAIProvider(ReasoningReplayMixin):
66
66
  def _require_non_empty_reasoning_replay_for_tool_turns(self, extensions: dict | None) -> bool:
67
67
  return False
68
68
 
69
+ def _degrade_missing_reasoning_replay(self, extensions: dict | None) -> bool:
70
+ return (extensions or {}).get("degrade_missing_reasoning_replay") is True
71
+
72
+ def assess_replayability(self, context: RenderedContext, extensions: dict | None = None) -> dict:
73
+ """Pre-flight query: would this history validate against this provider with
74
+ the given extensions, without sending the request? Returns the tool-call
75
+ ids whose turn lacks the reasoning replay this provider requires, so an
76
+ embedder can route around the failure before issuing the request. Seed any
77
+ persisted replay first. ``ok`` is True when no reasoning replay is required."""
78
+ if not self._require_non_empty_reasoning_replay_for_tool_turns(extensions):
79
+ return {"ok": True, "offending_call_ids": []}
80
+ return self.assess_reasoning(context)
81
+
69
82
  def _build_messages(self, context: RenderedContext, extensions: dict | None = None) -> list[dict]:
83
+ require_reasoning = self._require_non_empty_reasoning_replay_for_tool_turns(extensions)
84
+ degrade = self._degrade_missing_reasoning_replay(extensions)
70
85
  validate_openai_chat_replay(
71
86
  context.turns,
72
87
  descriptor=self.descriptor(),
73
- require_non_empty_reasoning_for_tool_calls=self._require_non_empty_reasoning_replay_for_tool_turns(extensions),
88
+ require_non_empty_reasoning_for_tool_calls=require_reasoning,
89
+ degrade_missing_reasoning=degrade,
74
90
  replay_for_assistant=lambda content, tool_calls: self._replay_fields.get(
75
91
  assistant_replay_key(content, tool_calls)
76
92
  ),
77
93
  )
78
94
  serialized = to_openai_message_params(context)
79
- return self._merge_replay_into_openai_messages(serialized, context)
95
+ return self._merge_replay_into_openai_messages(
96
+ serialized, context, degrade_missing_reasoning=require_reasoning and degrade,
97
+ )
80
98
 
81
99
  def _build_tools(self, tools: list[ToolSchema]) -> list[dict] | None:
82
100
  if not tools:
@@ -107,11 +125,7 @@ class OpenAIProvider(ReasoningReplayMixin):
107
125
  extensions: dict | None = None,
108
126
  ) -> dict:
109
127
  body = {
110
- **{
111
- k: v
112
- for k, v in (extensions or {}).items()
113
- if k not in {"model", "messages", "tools", "stream", "stream_options"}
114
- },
128
+ **wire_request_extensions(extensions),
115
129
  "model": self._model,
116
130
  "messages": messages,
117
131
  "stream": stream,
@@ -131,7 +145,7 @@ class OpenAIProvider(ReasoningReplayMixin):
131
145
  last_exc = None
132
146
  for attempt in range(self._retry.max_retries):
133
147
  try:
134
- request_extensions = {k: v for k, v in (extensions or {}).items() if k not in {"model", "messages", "tools", "stream", "stream_options"}}
148
+ request_extensions = wire_request_extensions(extensions)
135
149
  resp = await self._client.chat.completions.create(
136
150
  **request_extensions,
137
151
  model=self._model,
@@ -175,7 +189,7 @@ class OpenAIProvider(ReasoningReplayMixin):
175
189
  accumulated_content = ""
176
190
  final_tool_calls = []
177
191
 
178
- request_extensions = {k: v for k, v in (extensions or {}).items() if k not in {"model", "messages", "tools", "stream", "stream_options"}}
192
+ request_extensions = wire_request_extensions(extensions)
179
193
  stream = await self._client.chat.completions.create(
180
194
  **request_extensions,
181
195
  model=self._model,
@@ -6,6 +6,7 @@ from typing import Any
6
6
  from deepstrike._kernel import Message, ToolCall
7
7
 
8
8
  from .base import RenderedContext
9
+ from .replay_validator import DEGRADED_REASONING_PLACEHOLDER, assess_reasoning_replay
9
10
 
10
11
 
11
12
  def _sort_dict_keys(val: Any) -> Any:
@@ -83,6 +84,7 @@ class ReasoningReplayMixin:
83
84
  self,
84
85
  serialized: list[dict[str, Any]],
85
86
  context: RenderedContext,
87
+ degrade_missing_reasoning: bool = False,
86
88
  ) -> list[dict[str, Any]]:
87
89
  cursor = 1 if context.system_text else 0
88
90
  for source in context.turns:
@@ -97,11 +99,27 @@ class ReasoningReplayMixin:
97
99
  assistant_replay_key(source.content, source.tool_calls or [])
98
100
  )
99
101
  wire = openai_chat_wire_replay_fields(replay)
102
+ if not wire and degrade_missing_reasoning and source.tool_calls:
103
+ # Reasoning-requiring provider, no stored reasoning for this
104
+ # tool-call turn, caller opted into degradation: inject a
105
+ # placeholder so the wire message stays well-formed instead of
106
+ # failing the whole request.
107
+ wire = {"reasoning_content": DEGRADED_REASONING_PLACEHOLDER}
100
108
  if wire:
101
109
  serialized[cursor] = {**serialized[cursor], **wire}
102
110
  cursor += 1
103
111
  return serialized
104
112
 
113
+ def assess_reasoning(self, context: RenderedContext) -> dict:
114
+ """Raise-free pre-flight check: which assistant tool-call turns in
115
+ ``context`` lack the non-empty reasoning replay this provider needs."""
116
+ return assess_reasoning_replay(
117
+ context.turns,
118
+ lambda content, tool_calls: self._replay_fields.get(
119
+ assistant_replay_key(content, tool_calls)
120
+ ),
121
+ )
122
+
105
123
  def remember_reasoning_for_turn(
106
124
  self,
107
125
  content: str,
@@ -4,6 +4,12 @@ from typing import Any, Callable
4
4
 
5
5
  from deepstrike._kernel import Message
6
6
 
7
+ # Placeholder reasoning injected for an assistant tool-call turn that has no
8
+ # stored reasoning replay when the caller opted into graceful degradation
9
+ # (``degrade_missing_reasoning``). Keeps the wire message well-formed for a
10
+ # thinking-on provider without fabricating substantive reasoning.
11
+ DEGRADED_REASONING_PLACEHOLDER = "[reasoning unavailable on replay]"
12
+
7
13
 
8
14
  class ProviderReplayValidationError(Exception):
9
15
  """Raised before dispatch when a rebuilt history would violate the
@@ -15,11 +21,42 @@ def validate_openai_chat_replay(
15
21
  *,
16
22
  descriptor: Any = None,
17
23
  require_non_empty_reasoning_for_tool_calls: bool = False,
24
+ degrade_missing_reasoning: bool = False,
18
25
  replay_for_assistant: Callable[[str, list], dict | None] | None = None,
19
26
  ) -> None:
20
27
  _validate_strict_tool_result_pairing(turns)
21
- if require_non_empty_reasoning_for_tool_calls:
22
- _validate_reasoning_replay_for_assistant_tool_calls(turns, descriptor, replay_for_assistant)
28
+ if require_non_empty_reasoning_for_tool_calls and not degrade_missing_reasoning:
29
+ assessment = assess_reasoning_replay(turns, replay_for_assistant)
30
+ if not assessment["ok"]:
31
+ raise _reasoning_replay_error(assessment["offending_call_ids"], descriptor)
32
+
33
+
34
+ def assess_reasoning_replay(
35
+ turns: list[Message],
36
+ replay_for_assistant: Callable[[str, list], dict | None] | None,
37
+ ) -> dict:
38
+ """Pure, raise-free assessment: which assistant tool-call turns lack the
39
+ non-empty reasoning replay a reasoning-requiring provider needs. Lets an
40
+ embedder decide per-candidate whether to keep thinking on, disable it, or
41
+ skip the candidate — before sending."""
42
+ offending_call_ids: list[str] = []
43
+ for message in turns:
44
+ if message.role != "assistant" or not getattr(message, "tool_calls", None):
45
+ continue
46
+ replay = replay_for_assistant(message.content, message.tool_calls) if replay_for_assistant else None
47
+ reasoning = replay.get("reasoning_content") if isinstance(replay, dict) else None
48
+ if not (isinstance(reasoning, str) and reasoning.strip()):
49
+ offending_call_ids.extend(tc.id for tc in message.tool_calls)
50
+ return {"ok": not offending_call_ids, "offending_call_ids": offending_call_ids}
51
+
52
+
53
+ def _reasoning_replay_error(call_ids: list[str], descriptor: Any) -> ProviderReplayValidationError:
54
+ who = f"{descriptor.provider}/{descriptor.model}" if descriptor is not None else "provider"
55
+ return ProviderReplayValidationError(
56
+ f"{who} replay requires non-empty reasoning_content for assistant tool call turn {', '.join(call_ids)}. "
57
+ "Disable thinking, rebuild this history with provider replay, switch to a provider that can replay this turn, "
58
+ "or pass extensions['degrade_missing_reasoning_replay'] to send a degraded turn."
59
+ )
23
60
 
24
61
 
25
62
  def _tool_result_parts(message: Message) -> list:
@@ -30,14 +67,26 @@ def _validate_strict_tool_result_pairing(turns: list[Message]) -> None:
30
67
  pending_ids: set[str] | None = None
31
68
  completed_ids: set[str] = set()
32
69
 
70
+ def assert_all_completed() -> None:
71
+ if not pending_ids:
72
+ return
73
+ missing = [cid for cid in pending_ids if cid not in completed_ids]
74
+ if missing:
75
+ raise ProviderReplayValidationError(
76
+ f"OpenAI-compatible replay has assistant tool_calls with no tool result for {', '.join(missing)}: "
77
+ "every tool_call must be answered by a tool message before the next assistant or user turn."
78
+ )
79
+
33
80
  for message in turns:
34
81
  if message.role == "assistant":
82
+ assert_all_completed()
35
83
  tool_calls = getattr(message, "tool_calls", None) or []
36
84
  pending_ids = {tc.id for tc in tool_calls} if tool_calls else None
37
85
  completed_ids = set()
38
86
  continue
39
87
 
40
88
  if message.role != "tool":
89
+ assert_all_completed()
41
90
  pending_ids = None
42
91
  completed_ids = set()
43
92
  continue
@@ -54,22 +103,4 @@ def _validate_strict_tool_result_pairing(turns: list[Message]) -> None:
54
103
  )
55
104
  completed_ids.add(part.call_id)
56
105
 
57
-
58
- def _validate_reasoning_replay_for_assistant_tool_calls(
59
- turns: list[Message],
60
- descriptor: Any,
61
- replay_for_assistant: Callable[[str, list], dict | None] | None,
62
- ) -> None:
63
- for message in turns:
64
- if message.role != "assistant" or not getattr(message, "tool_calls", None):
65
- continue
66
- replay = replay_for_assistant(message.content, message.tool_calls) if replay_for_assistant else None
67
- reasoning = replay.get("reasoning_content") if isinstance(replay, dict) else None
68
- if not (isinstance(reasoning, str) and reasoning.strip()):
69
- call_ids = ", ".join(tc.id for tc in message.tool_calls)
70
- who = f"{descriptor.provider}/{descriptor.model}" if descriptor is not None else "provider"
71
- raise ProviderReplayValidationError(
72
- f"{who} replay requires non-empty reasoning_content for assistant tool call turn {call_ids}. "
73
- "Disable thinking, rebuild this history with provider replay, or switch to a provider "
74
- "that can replay this turn."
75
- )
106
+ assert_all_completed()
@@ -18,7 +18,9 @@ from deepstrike.runtime.session_log import (
18
18
  )
19
19
  from deepstrike.runtime.provider_replay import (
20
20
  ProviderReplay,
21
+ assess_provider_replayability,
21
22
  assistant_replay_key,
23
+ is_replay_compatible_with_provider,
22
24
  peek_provider_replay,
23
25
  seed_provider_replay_from_events,
24
26
  )
@@ -65,6 +67,8 @@ __all__ = [
65
67
  "assistant_replay_key",
66
68
  "peek_provider_replay",
67
69
  "seed_provider_replay_from_events",
70
+ "assess_provider_replayability",
71
+ "is_replay_compatible_with_provider",
68
72
  "CredentialVault",
69
73
  "EnvCredentialVault",
70
74
  "InMemoryCredentialVault",
@@ -11,6 +11,7 @@ __all__ = [
11
11
  "is_replay_compatible_with_provider",
12
12
  "seed_provider_replay_from_events",
13
13
  "peek_provider_replay",
14
+ "assess_provider_replayability",
14
15
  ]
15
16
 
16
17
  class ProviderReplay(TypedDict, total=False):
@@ -77,3 +78,15 @@ def peek_provider_replay(provider: Any, content: str, tool_calls: list[ToolCall]
77
78
  if not callable(peek):
78
79
  return None
79
80
  return peek(content, tool_calls)
81
+
82
+
83
+ def assess_provider_replayability(provider: Any, context: Any, extensions: dict | None = None) -> dict:
84
+ """Pre-flight query for fallback routing: would ``context`` validate against
85
+ ``provider`` (with ``extensions``) before the request is sent? Seed any
86
+ persisted replay first so the assessment reflects what the provider can
87
+ actually replay. Providers without ``assess_replayability`` (no reasoning-
88
+ replay requirement) are reported as ``ok``."""
89
+ assess = getattr(provider, "assess_replayability", None)
90
+ if not callable(assess):
91
+ return {"ok": True, "offending_call_ids": []}
92
+ return assess(context, extensions)
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "deepstrike"
7
- version = "0.2.7"
7
+ version = "0.2.8"
8
8
  requires-python = ">=3.10"
9
9
  readme = "README.md"
10
10
  dependencies = [
File without changes