deepstrike 0.2.80__tar.gz → 0.2.82__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 (273) hide show
  1. {deepstrike-0.2.80 → deepstrike-0.2.82}/Cargo.lock +7 -7
  2. {deepstrike-0.2.80 → deepstrike-0.2.82}/Cargo.toml +3 -3
  3. {deepstrike-0.2.80 → deepstrike-0.2.82}/PKG-INFO +1 -1
  4. deepstrike-0.2.82/crates/deepstrike-core/src/context/cache_ledger.rs +383 -0
  5. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/config.rs +5 -0
  6. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/fault.rs +2 -2
  7. deepstrike-0.2.82/crates/deepstrike-core/src/context/graph.rs +147 -0
  8. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/item.rs +95 -1
  9. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/manager.rs +647 -15
  10. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/mod.rs +3 -0
  11. deepstrike-0.2.82/crates/deepstrike-core/src/context/notes.rs +442 -0
  12. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/partitions.rs +70 -12
  13. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/policy.rs +9 -0
  14. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/renderer.rs +1 -1
  15. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/task_state.rs +98 -0
  16. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/observation.rs +32 -2
  17. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/checkpoint.rs +43 -0
  18. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/config.rs +12 -0
  19. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/continuation.rs +11 -1
  20. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/effects.rs +7 -0
  21. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/projection.rs +5 -0
  22. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/syscall.rs +11 -0
  23. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/tests.rs +135 -2
  24. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver.rs +69 -11
  25. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/effect.rs +12 -1
  26. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/root.rs +4 -0
  27. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/syscall.rs +2 -0
  28. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/session.rs +28 -1
  29. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs +11 -1
  30. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/ollama_adapter.py +13 -2
  31. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/openai_chat_adapter.py +74 -7
  32. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/runtime_registry.py +3 -0
  33. deepstrike-0.2.82/deepstrike/providers/text_tool_calls.py +103 -0
  34. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/_session_event_registry.py +1 -1
  35. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/canonical_kernel_step.py +5 -1
  36. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/context_policy.py +7 -0
  37. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/filtered_plane.py +1 -1
  38. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/kernel_event_log.py +6 -3
  39. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/runner.py +1 -0
  40. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/session_log.py +50 -0
  41. {deepstrike-0.2.80 → deepstrike-0.2.82}/pyproject.toml +1 -1
  42. {deepstrike-0.2.80 → deepstrike-0.2.82}/LICENSE +0 -0
  43. {deepstrike-0.2.80 → deepstrike-0.2.82}/README.md +0 -0
  44. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/Cargo.toml +0 -0
  45. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/LICENSE +0 -0
  46. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/benches/kernel_baseline.rs +0 -0
  47. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/benches/wait_index_baseline.rs +0 -0
  48. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/benchmark/mod.rs +0 -0
  49. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/compression.rs +0 -0
  50. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/measurement.rs +0 -0
  51. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/pressure.rs +0 -0
  52. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/renewal.rs +0 -0
  53. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
  54. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
  55. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/text.rs +0 -0
  56. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
  57. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/units.rs +0 -0
  58. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/context/utility.rs +0 -0
  59. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
  60. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/mod.rs +0 -0
  61. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/permission.rs +0 -0
  62. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
  63. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/quota.rs +0 -0
  64. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
  65. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/repeat_fuse.rs +0 -0
  66. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/governance/veto.rs +0 -0
  67. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/harness/eval.rs +0 -0
  68. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/harness/mod.rs +0 -0
  69. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/lexical.rs +0 -0
  70. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/lib.rs +0 -0
  71. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/memory/curator.rs +0 -0
  72. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/memory/durable.rs +0 -0
  73. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/memory/mod.rs +0 -0
  74. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/mm/handle.rs +0 -0
  75. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/mm/memory.rs +0 -0
  76. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/mm/memory_candidate.rs +0 -0
  77. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/mm/mod.rs +0 -0
  78. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/mm/value.rs +0 -0
  79. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
  80. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
  81. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/orchestration/workflow/mod.rs +0 -0
  82. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/orchestration/workflow/run.rs +0 -0
  83. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/proc/mod.rs +0 -0
  84. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/projection_pairs.rs +0 -0
  85. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
  86. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/binding.rs +0 -0
  87. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/command.rs +0 -0
  88. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/crash_point.rs +0 -0
  89. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/events.rs +0 -0
  90. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/planning.rs +0 -0
  91. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/driver/provider.rs +0 -0
  92. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/envelope.rs +0 -0
  93. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/event.rs +0 -0
  94. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/fault.rs +0 -0
  95. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/memory_authority.rs +0 -0
  96. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/projection.rs +0 -0
  97. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/record.rs +0 -0
  98. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/restore.rs +0 -0
  99. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/scalar.rs +0 -0
  100. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/terminal.rs +0 -0
  101. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/tests.rs +0 -0
  102. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire/transaction.rs +0 -0
  103. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel/wire.rs +0 -0
  104. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/kernel.rs +0 -0
  105. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
  106. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
  107. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
  108. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/session_registry_tests.rs +0 -0
  109. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/runtime/session_wire.rs +0 -0
  110. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/budget_grant.rs +0 -0
  111. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/cross_operation.rs +0 -0
  112. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/entropy.rs +0 -0
  113. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/mailbox.rs +0 -0
  114. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
  115. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
  116. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
  117. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
  118. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/runnable.rs +0 -0
  119. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/cancellation.rs +0 -0
  120. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/capability.rs +0 -0
  121. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/eviction.rs +0 -0
  122. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs +0 -0
  123. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/milestone_exec.rs +0 -0
  124. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/process.rs +0 -0
  125. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/signal.rs +0 -0
  126. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs +0 -0
  127. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/state_machine/workflow.rs +0 -0
  128. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
  129. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/scheduler/wait_index.rs +0 -0
  130. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/signals/attention.rs +0 -0
  131. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/signals/mod.rs +0 -0
  132. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/signals/queue.rs +0 -0
  133. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/signals/router.rs +0 -0
  134. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
  135. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/agent.rs +0 -0
  136. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/capability.rs +0 -0
  137. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/contract.rs +0 -0
  138. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/durable_content.rs +0 -0
  139. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/error.rs +0 -0
  140. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/message.rs +0 -0
  141. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/milestone.rs +0 -0
  142. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/mod.rs +0 -0
  143. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/policy.rs +0 -0
  144. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/result.rs +0 -0
  145. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/signal.rs +0 -0
  146. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/skill.rs +0 -0
  147. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-core/src/types/task.rs +0 -0
  148. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-py/Cargo.toml +0 -0
  149. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-py/LICENSE +0 -0
  150. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-py/src/lib.rs +0 -0
  151. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-tokenizer/Cargo.toml +0 -0
  152. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-tokenizer/LICENSE +0 -0
  153. {deepstrike-0.2.80 → deepstrike-0.2.82}/crates/deepstrike-tokenizer/src/lib.rs +0 -0
  154. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/__init__.py +0 -0
  155. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/agent.py +0 -0
  156. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/collaboration/__init__.py +0 -0
  157. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/collaboration/contract.py +0 -0
  158. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/collaboration/handoff.py +0 -0
  159. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/collaboration/harness.py +0 -0
  160. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/collaboration/modes.py +0 -0
  161. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/collaboration/pool.py +0 -0
  162. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/governance.py +0 -0
  163. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/harness/__init__.py +0 -0
  164. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/harness/harness.py +0 -0
  165. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/harness/judge.py +0 -0
  166. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/kernel/__init__.py +0 -0
  167. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/kernel/canonical.py +0 -0
  168. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/knowledge/__init__.py +0 -0
  169. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/knowledge/source.py +0 -0
  170. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/__init__.py +0 -0
  171. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/agent.py +0 -0
  172. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/attestation.py +0 -0
  173. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/authority.py +0 -0
  174. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/candidate_sink.py +0 -0
  175. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/durable.py +0 -0
  176. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/durable_candidate_sink.py +0 -0
  177. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/durable_lifecycle_store.py +0 -0
  178. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/in_memory_store.py +0 -0
  179. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/lifecycle.py +0 -0
  180. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/protocols.py +0 -0
  181. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/ranking.py +0 -0
  182. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/recall_projection.py +0 -0
  183. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/retention.py +0 -0
  184. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/runtime_policy.py +0 -0
  185. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/working.py +0 -0
  186. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/memory/writer.py +0 -0
  187. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/__init__.py +0 -0
  188. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/anthropic.py +0 -0
  189. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/anthropic_adapter.py +0 -0
  190. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/anthropic_compaction.py +0 -0
  191. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/anthropic_compatible.py +0 -0
  192. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/base.py +0 -0
  193. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/capability_router.py +0 -0
  194. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/credentials.py +0 -0
  195. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/embedding.py +0 -0
  196. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/factories.py +0 -0
  197. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/fallback.py +0 -0
  198. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/gemini.py +0 -0
  199. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/gemini_adapter.py +0 -0
  200. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/model_catalog.py +0 -0
  201. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/model_registry.py +0 -0
  202. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/ollama.py +0 -0
  203. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/openai.py +0 -0
  204. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/openai_responses.py +0 -0
  205. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/prepared_request.py +0 -0
  206. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/protocol_adapter.py +0 -0
  207. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/protocols.py +0 -0
  208. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/provider_error.py +0 -0
  209. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/qwen.py +0 -0
  210. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/replay.py +0 -0
  211. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/replay_validator.py +0 -0
  212. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/request_plan.py +0 -0
  213. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/stop_reason.py +0 -0
  214. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/stream.py +0 -0
  215. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/usage.py +0 -0
  216. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/providers/vendor_profiles.py +0 -0
  217. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/__init__.py +0 -0
  218. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/agent_declaration.py +0 -0
  219. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/archive.py +0 -0
  220. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/credential_vault.py +0 -0
  221. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/durable_content.py +0 -0
  222. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/dynamic_workflow.py +0 -0
  223. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/eval.py +0 -0
  224. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/eval_dataset.py +0 -0
  225. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/event_stream.py +0 -0
  226. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/execution_evidence.py +0 -0
  227. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/execution_plane.py +0 -0
  228. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/facade.py +0 -0
  229. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/kernel_journal.py +0 -0
  230. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/kernel_step.py +0 -0
  231. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/loop_driver.py +0 -0
  232. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
  233. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/os_profile.py +0 -0
  234. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/os_snapshot.py +0 -0
  235. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/output_schema.py +0 -0
  236. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/payload_store.py +0 -0
  237. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/process_sandbox_plane.py +0 -0
  238. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/provider_replay.py +0 -0
  239. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/reaction_checkpoint.py +0 -0
  240. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/reactive_session.py +0 -0
  241. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/reliability.py +0 -0
  242. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/remote_vpc_plane.py +0 -0
  243. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/replay_fixture.py +0 -0
  244. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/replay_provider.py +0 -0
  245. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/replay_sanitize.py +0 -0
  246. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/run_group.py +0 -0
  247. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/session_repair.py +0 -0
  248. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/sub_agent_orchestrator.py +0 -0
  249. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/tool_arguments.py +0 -0
  250. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/turn_policy.py +0 -0
  251. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/workflow_replay.py +0 -0
  252. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/workflow_store.py +0 -0
  253. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/runtime/worktree_plane.py +0 -0
  254. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/safety/__init__.py +0 -0
  255. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/safety/permissions.py +0 -0
  256. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/signals/__init__.py +0 -0
  257. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/signals/gateway.py +0 -0
  258. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/signals/scheduled.py +0 -0
  259. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/signals/types.py +0 -0
  260. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/skills/__init__.py +0 -0
  261. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/skills/loader.py +0 -0
  262. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/skills/registry.py +0 -0
  263. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/skills/watcher.py +0 -0
  264. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/tools/__init__.py +0 -0
  265. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/tools/builtin/__init__.py +0 -0
  266. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/tools/builtin/read_file.py +0 -0
  267. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/tools/errors.py +0 -0
  268. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/tools/execution.py +0 -0
  269. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/tools/registry.py +0 -0
  270. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/types/__init__.py +0 -0
  271. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/types/agent.py +0 -0
  272. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/types/content.py +0 -0
  273. {deepstrike-0.2.80 → deepstrike-0.2.82}/deepstrike/types/content_policy.py +0 -0
@@ -293,7 +293,7 @@ dependencies = [
293
293
 
294
294
  [[package]]
295
295
  name = "deepstrike-core"
296
- version = "0.2.80"
296
+ version = "0.2.82"
297
297
  dependencies = [
298
298
  "compact_str",
299
299
  "deepstrike-tokenizer",
@@ -307,7 +307,7 @@ dependencies = [
307
307
 
308
308
  [[package]]
309
309
  name = "deepstrike-lab"
310
- version = "0.2.80"
310
+ version = "0.2.82"
311
311
  dependencies = [
312
312
  "serde",
313
313
  "serde_json",
@@ -315,7 +315,7 @@ dependencies = [
315
315
 
316
316
  [[package]]
317
317
  name = "deepstrike-node"
318
- version = "0.2.80"
318
+ version = "0.2.82"
319
319
  dependencies = [
320
320
  "compact_str",
321
321
  "deepstrike-core",
@@ -327,7 +327,7 @@ dependencies = [
327
327
 
328
328
  [[package]]
329
329
  name = "deepstrike-py"
330
- version = "0.2.80"
330
+ version = "0.2.82"
331
331
  dependencies = [
332
332
  "compact_str",
333
333
  "deepstrike-core",
@@ -338,7 +338,7 @@ dependencies = [
338
338
 
339
339
  [[package]]
340
340
  name = "deepstrike-sdk"
341
- version = "0.2.80"
341
+ version = "0.2.82"
342
342
  dependencies = [
343
343
  "async-stream",
344
344
  "async-trait",
@@ -376,7 +376,7 @@ dependencies = [
376
376
 
377
377
  [[package]]
378
378
  name = "deepstrike-tokenizer"
379
- version = "0.2.80"
379
+ version = "0.2.82"
380
380
  dependencies = [
381
381
  "pretty_assertions",
382
382
  "tiktoken-rs",
@@ -384,7 +384,7 @@ dependencies = [
384
384
 
385
385
  [[package]]
386
386
  name = "deepstrike-wasm"
387
- version = "0.2.80"
387
+ version = "0.2.82"
388
388
  dependencies = [
389
389
  "compact_str",
390
390
  "deepstrike-core",
@@ -3,13 +3,13 @@ resolver = "2"
3
3
  members = ["crates/deepstrike-core", "crates/deepstrike-tokenizer", "crates/deepstrike-py"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.80"
6
+ version = "0.2.82"
7
7
  edition = "2024"
8
8
  repository = "https://github.com/kongusen/deepstrike"
9
9
 
10
10
  [workspace.dependencies]
11
- deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.80" }
12
- deepstrike-tokenizer = { path = "crates/deepstrike-tokenizer", version = "0.2.80" }
11
+ deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.82" }
12
+ deepstrike-tokenizer = { path = "crates/deepstrike-tokenizer", version = "0.2.82" }
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.80
3
+ Version: 0.2.82
4
4
  Requires-Dist: httpx>=0.27
5
5
  Requires-Dist: pyyaml>=6.0
6
6
  Requires-Dist: anyio>=4.0
@@ -0,0 +1,383 @@
1
+ //! Plan task 8.0 · the cache ledger: where each provider request broke the previous request's
2
+ //! cacheable prefix, how many tokens that cost, and why.
3
+ //!
4
+ //! A provider caches a request as one prefix: tool schemas, then system[0], system[1], then the
5
+ //! history turns. The volatile state turn is the uncached tail and is left out. Every time a
6
+ //! request goes out the kernel fingerprints that prefix segment by segment and compares it with the
7
+ //! previous request. The first segment that differs is a **prefix break**: every token the previous
8
+ //! request held after it has to be sent uncached again. The ledger records the break (turn, cause,
9
+ //! tokens kept before it, tokens lost after it) and sums the losses into the lifecycle report's
10
+ //! `cache_loss_tokens`.
11
+ //!
12
+ //! The cause comes from the change that made the break. Compaction, renewal, provider rewrites,
13
+ //! tool-result collapse and knowledge promotion announce themselves ([`CacheLedger::note_cause`]);
14
+ //! an unannounced break is classified from the render itself: a dropped item is a budget drop, an
15
+ //! item re-rendered as the narration stub is narration collapse, and a different toolset is a
16
+ //! toolset change.
17
+ //!
18
+ //! The state turn after the history is rebuilt every request, so the previous request's state turn
19
+ //! is never reusable: that is not a break, but a provider's count of uncached input includes it.
20
+ //! The ledger sums it separately as `tail_tokens`, so measured cache loss can be compared with the
21
+ //! kernel's.
22
+ //!
23
+ //! Everything here is derived from the rendered request, so a replay rebuilds the same ledger; it
24
+ //! travels in the checkpoint so a restored run keeps comparing against the request it last sent.
25
+
26
+ use std::collections::VecDeque;
27
+
28
+ use serde::{Deserialize, Serialize};
29
+
30
+ use crate::context::fault::stable_hash;
31
+ use crate::context::item::{HISTORY_ITEM_DETAIL_CAP, ItemId};
32
+
33
+ /// Prefix breaks the ledger lists one by one; older ones are only counted.
34
+ pub const PREFIX_BREAK_DETAIL_CAP: usize = HISTORY_ITEM_DETAIL_CAP;
35
+
36
+ /// Why a request's cacheable prefix differs from the previous request's.
37
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
38
+ #[serde(rename_all = "snake_case")]
39
+ pub enum BreakCause {
40
+ /// The tool schemas changed (a skill narrowed or widened the toolset, a meta tool appeared).
41
+ Toolset,
42
+ /// system[0] changed.
43
+ System,
44
+ /// system[1] changed outside a compaction: staged knowledge promoted, or a boundary sweep.
45
+ Promotion,
46
+ /// Older tool results collapsed to previews under pressure.
47
+ Collapse,
48
+ /// An item re-rendered in place without an announced cause (a page-in, a page-out).
49
+ Reprojection,
50
+ /// Old assistant narration left the protected window and rendered as the narration stub.
51
+ Narration,
52
+ /// The renderer dropped the oldest history over the input budget.
53
+ BudgetDrop,
54
+ Snip,
55
+ Micro,
56
+ Auto,
57
+ Renewal,
58
+ /// The provider compacted or cleared context server-side.
59
+ Provider,
60
+ /// History rolled back, or anything else.
61
+ Other,
62
+ }
63
+
64
+ impl BreakCause {
65
+ /// The cause a compaction tier announces.
66
+ pub fn of_compaction(action: crate::context::pressure::PressureAction) -> Self {
67
+ use crate::context::pressure::PressureAction;
68
+ match action {
69
+ PressureAction::SnipCompact => Self::Snip,
70
+ PressureAction::MicroCompact => Self::Micro,
71
+ PressureAction::ContextCollapse => Self::Collapse,
72
+ PressureAction::AutoCompact => Self::Auto,
73
+ PressureAction::None => Self::Other,
74
+ }
75
+ }
76
+ }
77
+
78
+ /// One prefix break, attributed.
79
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
80
+ pub struct PrefixBreak {
81
+ /// The turn of the request that broke the prefix.
82
+ pub turn: u32,
83
+ pub cause: BreakCause,
84
+ /// Tokens of the previous request's prefix still byte-identical (still cacheable).
85
+ pub kept_tokens: u32,
86
+ /// Tokens of the previous request's prefix after the break: sent uncached again.
87
+ pub lost_tokens: u32,
88
+ }
89
+
90
+ /// The fingerprint and size of one prefix segment.
91
+ #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
92
+ pub struct Segment {
93
+ /// FNV-1a 64 of the segment's wire material, as 16 hex digits (a string, so a JSON host that
94
+ /// reads numbers as doubles cannot round it).
95
+ pub hash: String,
96
+ pub tokens: u32,
97
+ }
98
+
99
+ impl Segment {
100
+ pub fn of(material: &[u8], tokens: u32) -> Self {
101
+ Self {
102
+ hash: format!("{:016x}", stable_hash(material)),
103
+ tokens,
104
+ }
105
+ }
106
+ }
107
+
108
+ /// One history turn of a sent request.
109
+ #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
110
+ pub struct SentTurn {
111
+ #[serde(flatten)]
112
+ pub segment: Segment,
113
+ /// The history item the turn rendered; 0 for a synthetic turn.
114
+ #[serde(default)]
115
+ pub item: ItemId,
116
+ /// The turn rendered as the narration stub.
117
+ #[serde(default, skip_serializing_if = "std::ops::Not::not")]
118
+ pub stub: bool,
119
+ }
120
+
121
+ /// The cacheable prefix of one sent request.
122
+ #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
123
+ pub struct SentPrefix {
124
+ pub tools: Segment,
125
+ pub system: Segment,
126
+ pub knowledge: Segment,
127
+ #[serde(default)]
128
+ pub turns: Vec<SentTurn>,
129
+ /// The uncached state turn after the turns.
130
+ #[serde(default, skip_serializing_if = "crate::context::item::is_zero_u32")]
131
+ pub tail: u32,
132
+ }
133
+
134
+ impl SentPrefix {
135
+ fn header_tokens(&self) -> u32 {
136
+ self.tools
137
+ .tokens
138
+ .saturating_add(self.system.tokens)
139
+ .saturating_add(self.knowledge.tokens)
140
+ }
141
+
142
+ fn total_tokens(&self) -> u32 {
143
+ self.turns
144
+ .iter()
145
+ .fold(self.header_tokens(), |sum, turn| sum.saturating_add(turn.segment.tokens))
146
+ }
147
+
148
+ /// The tokens a turn with this hash had in this request, to avoid recounting it.
149
+ pub fn tokens_of(&self, hash: &str) -> Option<u32> {
150
+ self.turns
151
+ .iter()
152
+ .find(|turn| turn.segment.hash == hash)
153
+ .map(|turn| turn.segment.tokens)
154
+ }
155
+ }
156
+
157
+ #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
158
+ #[serde(deny_unknown_fields)]
159
+ pub struct CacheLedger {
160
+ /// The prefix of the last request sent.
161
+ #[serde(default, skip_serializing_if = "Option::is_none")]
162
+ pub sent: Option<SentPrefix>,
163
+ /// The announced cause of a rewrite since the last request.
164
+ #[serde(default, skip_serializing_if = "Option::is_none")]
165
+ pub pending: Option<BreakCause>,
166
+ /// The latest breaks, oldest first.
167
+ #[serde(default, skip_serializing_if = "VecDeque::is_empty")]
168
+ pub breaks: VecDeque<PrefixBreak>,
169
+ #[serde(default, skip_serializing_if = "crate::context::item::is_zero_u32")]
170
+ pub breaks_omitted: u32,
171
+ /// Lost tokens summed over every break of the run.
172
+ #[serde(default, skip_serializing_if = "crate::context::item::is_zero_u64")]
173
+ pub lost_tokens: u64,
174
+ /// The state-turn tokens of every request a later request followed: never reusable, so not a
175
+ /// break, but part of what a provider counts as uncached.
176
+ #[serde(default, skip_serializing_if = "crate::context::item::is_zero_u64")]
177
+ pub tail_tokens: u64,
178
+ }
179
+
180
+ impl CacheLedger {
181
+ /// A rewrite with `cause` happened; the next request's break, if it has one, is attributed to
182
+ /// it. The first cause announced since the last request is kept: later rewrites in the same
183
+ /// interval (a boundary sweep a compaction triggered) follow from it.
184
+ pub fn note_cause(&mut self, cause: BreakCause) {
185
+ self.pending.get_or_insert(cause);
186
+ }
187
+
188
+ /// The request with prefix `next` is being sent on `turn`. Compares it with the last request,
189
+ /// records the break if there is one, and returns it.
190
+ pub fn observe(&mut self, turn: u32, next: SentPrefix) -> Option<PrefixBreak> {
191
+ let pending = self.pending.take();
192
+ self.tail_tokens += self.sent.as_ref().map_or(0, |prev| u64::from(prev.tail));
193
+ let found = self.sent.as_ref().and_then(|prev| find_break(prev, &next, pending));
194
+ self.sent = Some(next);
195
+ let found = found.map(|(cause, kept_tokens, lost_tokens)| PrefixBreak {
196
+ turn,
197
+ cause,
198
+ kept_tokens,
199
+ lost_tokens,
200
+ })?;
201
+ self.lost_tokens += u64::from(found.lost_tokens);
202
+ self.breaks.push_back(found);
203
+ if self.breaks.len() > PREFIX_BREAK_DETAIL_CAP {
204
+ self.breaks.pop_front();
205
+ self.breaks_omitted += 1;
206
+ }
207
+ Some(found)
208
+ }
209
+ }
210
+
211
+ /// Where `next` stops reusing `prev`, why, and the (kept, lost) tokens of `prev`'s prefix.
212
+ fn find_break(
213
+ prev: &SentPrefix,
214
+ next: &SentPrefix,
215
+ pending: Option<BreakCause>,
216
+ ) -> Option<(BreakCause, u32, u32)> {
217
+ let total = prev.total_tokens();
218
+ let lost = |kept: u32| total.saturating_sub(kept);
219
+ if prev.tools.hash != next.tools.hash {
220
+ return Some((BreakCause::Toolset, 0, total)).filter(|_| total > 0);
221
+ }
222
+ if prev.system.hash != next.system.hash {
223
+ let kept = prev.tools.tokens;
224
+ return Some((pending.unwrap_or(BreakCause::System), kept, lost(kept)));
225
+ }
226
+ if prev.knowledge.hash != next.knowledge.hash {
227
+ let kept = prev.tools.tokens.saturating_add(prev.system.tokens);
228
+ return Some((pending.unwrap_or(BreakCause::Promotion), kept, lost(kept)));
229
+ }
230
+ let same = prev
231
+ .turns
232
+ .iter()
233
+ .zip(&next.turns)
234
+ .take_while(|(a, b)| a.segment.hash == b.segment.hash)
235
+ .count();
236
+ if same == prev.turns.len() {
237
+ return None;
238
+ }
239
+ let kept = prev.turns[..same]
240
+ .iter()
241
+ .fold(prev.header_tokens(), |sum, turn| sum.saturating_add(turn.segment.tokens));
242
+ let lost = lost(kept);
243
+ if lost == 0 {
244
+ return None;
245
+ }
246
+ let cause = pending.unwrap_or_else(|| classify(&prev.turns[same], next, same));
247
+ Some((cause, kept, lost))
248
+ }
249
+
250
+ /// An unannounced turn break: what happened to the first turn that no longer matches.
251
+ fn classify(prev: &SentTurn, next: &SentPrefix, at: usize) -> BreakCause {
252
+ if prev.item != 0 && !next.turns.iter().any(|turn| turn.item == prev.item) {
253
+ return BreakCause::BudgetDrop;
254
+ }
255
+ match next.turns.get(at) {
256
+ Some(turn) if turn.item == prev.item && turn.stub && !prev.stub => BreakCause::Narration,
257
+ Some(turn) if turn.item == prev.item && turn.item != 0 => BreakCause::Reprojection,
258
+ _ => BreakCause::Other,
259
+ }
260
+ }
261
+
262
+ #[cfg(test)]
263
+ mod tests {
264
+ use super::*;
265
+
266
+ fn seg(name: &str, tokens: u32) -> Segment {
267
+ Segment::of(name.as_bytes(), tokens)
268
+ }
269
+
270
+ fn turn(name: &str, item: ItemId, tokens: u32) -> SentTurn {
271
+ SentTurn {
272
+ segment: seg(name, tokens),
273
+ item,
274
+ stub: false,
275
+ }
276
+ }
277
+
278
+ fn prefix(turns: Vec<SentTurn>) -> SentPrefix {
279
+ SentPrefix {
280
+ tools: seg("tools", 100),
281
+ system: seg("system", 50),
282
+ knowledge: seg("knowledge", 20),
283
+ turns,
284
+ tail: 7,
285
+ }
286
+ }
287
+
288
+ #[test]
289
+ fn an_appended_request_breaks_nothing() {
290
+ let mut ledger = CacheLedger::default();
291
+ assert_eq!(ledger.observe(1, prefix(vec![turn("a", 1, 10)])), None);
292
+ let next = prefix(vec![turn("a", 1, 10), turn("b", 2, 30)]);
293
+ assert_eq!(ledger.observe(2, next), None);
294
+ assert_eq!(ledger.lost_tokens, 0);
295
+ assert_eq!(ledger.tail_tokens, 7, "the first request's state turn was not reusable");
296
+ }
297
+
298
+ #[test]
299
+ fn a_dropped_oldest_turn_is_a_budget_drop() {
300
+ let mut ledger = CacheLedger::default();
301
+ ledger.observe(1, prefix(vec![turn("a", 1, 10), turn("b", 2, 30)]));
302
+ let found = ledger.observe(2, prefix(vec![turn("b", 2, 30), turn("c", 3, 5)]));
303
+ assert_eq!(
304
+ found,
305
+ Some(PrefixBreak {
306
+ turn: 2,
307
+ cause: BreakCause::BudgetDrop,
308
+ kept_tokens: 170,
309
+ lost_tokens: 40,
310
+ })
311
+ );
312
+ assert_eq!(ledger.lost_tokens, 40);
313
+ }
314
+
315
+ #[test]
316
+ fn a_turn_rendered_as_the_stub_is_narration() {
317
+ let mut ledger = CacheLedger::default();
318
+ ledger.observe(1, prefix(vec![turn("a", 1, 10), turn("b", 2, 30)]));
319
+ let mut stub = turn("stub", 1, 4);
320
+ stub.stub = true;
321
+ let found = ledger.observe(2, prefix(vec![stub, turn("b", 2, 30)])).unwrap();
322
+ assert_eq!(found.cause, BreakCause::Narration);
323
+ assert_eq!((found.kept_tokens, found.lost_tokens), (170, 40));
324
+ }
325
+
326
+ #[test]
327
+ fn an_announced_cause_wins_and_is_spent_by_one_request() {
328
+ let mut ledger = CacheLedger::default();
329
+ ledger.observe(1, prefix(vec![turn("a", 1, 10)]));
330
+ ledger.note_cause(BreakCause::Auto);
331
+ ledger.note_cause(BreakCause::Promotion);
332
+ let found = ledger.observe(2, prefix(vec![turn("summary", 5, 3)])).unwrap();
333
+ assert_eq!(found.cause, BreakCause::Auto);
334
+ // A compaction that broke nothing leaves no cause behind for a later break.
335
+ ledger.note_cause(BreakCause::Snip);
336
+ assert_eq!(ledger.observe(3, prefix(vec![turn("summary", 5, 3)])), None);
337
+ assert_eq!(ledger.pending, None);
338
+ }
339
+
340
+ #[test]
341
+ fn a_new_toolset_loses_the_whole_prefix() {
342
+ let mut ledger = CacheLedger::default();
343
+ ledger.observe(1, prefix(vec![turn("a", 1, 10)]));
344
+ let mut next = prefix(vec![turn("a", 1, 10)]);
345
+ next.tools = seg("narrowed", 60);
346
+ let found = ledger.observe(2, next).unwrap();
347
+ assert_eq!(found.cause, BreakCause::Toolset);
348
+ assert_eq!((found.kept_tokens, found.lost_tokens), (0, 180));
349
+ }
350
+
351
+ #[test]
352
+ fn changed_knowledge_is_a_promotion_unless_announced() {
353
+ let mut ledger = CacheLedger::default();
354
+ ledger.observe(1, prefix(vec![turn("a", 1, 10)]));
355
+ let mut next = prefix(vec![turn("a", 1, 10)]);
356
+ next.knowledge = seg("promoted", 40);
357
+ let found = ledger.observe(2, next).unwrap();
358
+ assert_eq!(found.cause, BreakCause::Promotion);
359
+ assert_eq!((found.kept_tokens, found.lost_tokens), (150, 30));
360
+ }
361
+
362
+ #[test]
363
+ fn the_detail_is_capped_but_the_total_is_not() {
364
+ let mut ledger = CacheLedger::default();
365
+ for round in 0..(PREFIX_BREAK_DETAIL_CAP as u32 + 3) {
366
+ let name = format!("t{round}");
367
+ ledger.observe(round, prefix(vec![turn(&name, u64::from(round) + 1, 10)]));
368
+ }
369
+ assert_eq!(ledger.breaks.len(), PREFIX_BREAK_DETAIL_CAP);
370
+ assert_eq!(ledger.breaks_omitted, 2);
371
+ assert_eq!(ledger.lost_tokens, 10 * (PREFIX_BREAK_DETAIL_CAP as u64 + 2));
372
+ }
373
+
374
+ #[test]
375
+ fn the_ledger_round_trips_through_json() {
376
+ let mut ledger = CacheLedger::default();
377
+ ledger.observe(1, prefix(vec![turn("a", 1, 10)]));
378
+ ledger.observe(2, prefix(vec![turn("b", 2, 10)]));
379
+ ledger.note_cause(BreakCause::Renewal);
380
+ let json = serde_json::to_string(&ledger).unwrap();
381
+ assert_eq!(serde_json::from_str::<CacheLedger>(&json).unwrap(), ledger);
382
+ }
383
+ }
@@ -103,6 +103,10 @@ pub struct ContextConfig {
103
103
  /// The provider compacts natively, so the kernel runs neither context collapse nor auto-compact.
104
104
  /// Renewal is never delegated: it is decided on the full local history and stays the safety net.
105
105
  pub delegate_compaction: bool,
106
+
107
+ // ── Notes (plan task 9.1) ───────────────────────────────────────────────
108
+ /// Tokens the model's notes may hold in the state turn. `0` turns the `note` tool off.
109
+ pub notes_budget_tokens: u32,
106
110
  }
107
111
 
108
112
  impl Default for ContextConfig {
@@ -127,6 +131,7 @@ impl Default for ContextConfig {
127
131
  knowledge_budget_ratio: 0.25,
128
132
  delegate_tool_result_clearing: false,
129
133
  delegate_compaction: false,
134
+ notes_budget_tokens: 0,
130
135
  }
131
136
  }
132
137
  }
@@ -11,8 +11,8 @@ pub enum ContextFault {
11
11
  InvalidReplay { turn: u32, reason: String },
12
12
  }
13
13
 
14
- /// FNV-1a 64-bit — the render layer's cache-stability test fingerprint dialect.
15
- #[cfg(test)]
14
+ /// FNV-1a 64-bit — the render layer's cache-stability fingerprint dialect (the render tests and
15
+ /// the plan task 8.0 cache ledger).
16
16
  pub(crate) fn stable_hash(bytes: &[u8]) -> u64 {
17
17
  let mut hash = 0xcbf29ce484222325u64;
18
18
  for byte in bytes {
@@ -0,0 +1,147 @@
1
+ //! Plan task 7.1 · the item graph: typed, directed edges between the items of one operation.
2
+ //!
3
+ //! The item table says what each item is; the graph says how items relate: which call a tool result
4
+ //! answers, which history item referenced which knowledge item and by what evidence, what a read
5
+ //! brought back, what a provider summary covers. Edges are recorded from committed input only (D1),
6
+ //! so a replay rebuilds the same graph, and they travel in the checkpoint.
7
+ //!
8
+ //! The graph is bounded. An edge whose two ends are both still resident is always kept; of the
9
+ //! edges that touch an item which has left, only the latest [`RETIRED_EDGE_CAP`] are kept and the
10
+ //! rest are counted in [`ItemGraph::omitted`]. In 0.2.81 the graph only reports: no decision reads it.
11
+
12
+ use std::collections::BTreeSet;
13
+
14
+ use crate::context::item::{ExternalEdge, HISTORY_ITEM_DETAIL_CAP, ItemEdge, ItemId};
15
+
16
+ /// Edges touching a retired item that the graph keeps, newest last.
17
+ pub const RETIRED_EDGE_CAP: usize = HISTORY_ITEM_DETAIL_CAP;
18
+
19
+ /// Edges the graph holds before it prunes against residency.
20
+ pub const EDGE_PRUNE_AT: usize = 1024;
21
+
22
+ #[derive(Debug, Clone, Default, PartialEq, Eq)]
23
+ pub struct ItemGraph {
24
+ edges: Vec<ItemEdge>,
25
+ omitted: u32,
26
+ /// Plan task 7.4 · edges to items of other runs, the latest [`RETIRED_EDGE_CAP`] kept.
27
+ external: Vec<ExternalEdge>,
28
+ }
29
+
30
+ impl ItemGraph {
31
+ /// Record `edge` unless it is degenerate (an untracked end, a self-loop) or already recorded.
32
+ /// Returns whether it was added.
33
+ pub fn add(&mut self, edge: ItemEdge) -> bool {
34
+ if edge.from == 0 || edge.to == 0 || edge.from == edge.to {
35
+ return false;
36
+ }
37
+ if self
38
+ .edges
39
+ .iter()
40
+ .any(|e| e.from == edge.from && e.to == edge.to && e.relation == edge.relation)
41
+ {
42
+ return false;
43
+ }
44
+ self.edges.push(edge);
45
+ true
46
+ }
47
+
48
+ /// Whether the graph has grown past [`EDGE_PRUNE_AT`] and should be pruned.
49
+ pub fn over_limit(&self) -> bool {
50
+ self.edges.len() > EDGE_PRUNE_AT
51
+ }
52
+
53
+ /// Keep every edge whose ends are both in `resident`, and the latest [`RETIRED_EDGE_CAP`] of
54
+ /// the others; count what is dropped. If resident edges alone still exceed
55
+ /// [`EDGE_PRUNE_AT`], the oldest go too.
56
+ pub fn prune(&mut self, resident: &BTreeSet<ItemId>) {
57
+ let live = |edge: &ItemEdge| resident.contains(&edge.from) && resident.contains(&edge.to);
58
+ let retired = self.edges.iter().filter(|edge| !live(edge)).count();
59
+ let mut drop_retired = retired.saturating_sub(RETIRED_EDGE_CAP);
60
+ let before = self.edges.len();
61
+ self.edges.retain(|edge| {
62
+ if live(edge) || drop_retired == 0 {
63
+ return true;
64
+ }
65
+ drop_retired -= 1;
66
+ false
67
+ });
68
+ let overflow = self.edges.len().saturating_sub(EDGE_PRUNE_AT);
69
+ self.edges.drain(..overflow);
70
+ self.omitted = self.omitted.saturating_add((before - self.edges.len()) as u32);
71
+ }
72
+
73
+ /// Record an edge to another run's item; the oldest goes past [`RETIRED_EDGE_CAP`].
74
+ pub fn add_external(&mut self, edge: ExternalEdge) -> bool {
75
+ if edge.from == 0 || self.external.contains(&edge) {
76
+ return false;
77
+ }
78
+ self.external.push(edge);
79
+ if self.external.len() > RETIRED_EDGE_CAP {
80
+ self.external.remove(0);
81
+ self.omitted = self.omitted.saturating_add(1);
82
+ }
83
+ true
84
+ }
85
+
86
+ pub fn external(&self) -> &[ExternalEdge] {
87
+ &self.external
88
+ }
89
+
90
+ pub fn edges(&self) -> &[ItemEdge] {
91
+ &self.edges
92
+ }
93
+
94
+ /// Edges dropped by pruning over the run.
95
+ pub fn omitted(&self) -> u32 {
96
+ self.omitted
97
+ }
98
+
99
+ /// The recorded edges into `to`.
100
+ pub fn in_edges(&self, to: ItemId) -> impl Iterator<Item = &ItemEdge> {
101
+ self.edges.iter().filter(move |edge| edge.to == to)
102
+ }
103
+
104
+ /// Rebuild from a checkpoint.
105
+ pub fn restore(&mut self, edges: Vec<ItemEdge>, external: Vec<ExternalEdge>, omitted: u32) {
106
+ self.edges = edges;
107
+ self.external = external;
108
+ self.omitted = omitted;
109
+ }
110
+ }
111
+
112
+ #[cfg(test)]
113
+ mod tests {
114
+ use super::*;
115
+ use crate::context::item::Relation;
116
+
117
+ fn edge(from: ItemId, to: ItemId) -> ItemEdge {
118
+ ItemEdge { from, to, relation: Relation::ReferencesLexical, turn: 0 }
119
+ }
120
+
121
+ #[test]
122
+ fn degenerate_and_repeated_edges_are_not_recorded() {
123
+ let mut graph = ItemGraph::default();
124
+ assert!(!graph.add(edge(0, 3)));
125
+ assert!(!graph.add(edge(3, 3)));
126
+ assert!(graph.add(edge(4, 3)));
127
+ assert!(!graph.add(edge(4, 3)));
128
+ assert!(graph.add(ItemEdge { relation: Relation::ReferencesStrong, ..edge(4, 3) }));
129
+ assert_eq!(graph.in_edges(3).count(), 2);
130
+ }
131
+
132
+ #[test]
133
+ fn pruning_keeps_resident_edges_and_the_latest_retired_ones() {
134
+ let mut graph = ItemGraph::default();
135
+ let total = RETIRED_EDGE_CAP as u64 + 5;
136
+ for from in 1..=total {
137
+ graph.add(edge(from + 1000, 1));
138
+ }
139
+ graph.add(edge(2, 3));
140
+ let resident = BTreeSet::from([2, 3]);
141
+ graph.prune(&resident);
142
+ assert_eq!(graph.omitted(), 5);
143
+ assert_eq!(graph.edges().len(), RETIRED_EDGE_CAP + 1);
144
+ assert_eq!(graph.edges()[0].from, 1006, "the oldest retired edges go first");
145
+ assert!(graph.edges().contains(&edge(2, 3)));
146
+ }
147
+ }