dhee 7.2.7__tar.gz → 7.3.0__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 (541) hide show
  1. {dhee-7.2.7/dhee.egg-info → dhee-7.3.0}/PKG-INFO +1 -1
  2. {dhee-7.2.7 → dhee-7.3.0}/dhee/__init__.py +1 -1
  3. dhee-7.3.0/dhee/corpus/__init__.py +72 -0
  4. dhee-7.3.0/dhee/corpus/extract.py +352 -0
  5. dhee-7.3.0/dhee/corpus/index.py +344 -0
  6. dhee-7.3.0/dhee/corpus/search.py +229 -0
  7. dhee-7.3.0/dhee/corpus/store.py +666 -0
  8. dhee-7.3.0/dhee/corpus/tree.py +376 -0
  9. dhee-7.3.0/dhee/embeddings/openrouter.py +168 -0
  10. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/reranker.py +72 -0
  11. {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/factory.py +4 -0
  12. {dhee-7.2.7 → dhee-7.3.0/dhee.egg-info}/PKG-INFO +1 -1
  13. {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/SOURCES.txt +9 -0
  14. {dhee-7.2.7 → dhee-7.3.0}/pyproject.toml +1 -1
  15. dhee-7.3.0/tests/test_corpus_index.py +333 -0
  16. dhee-7.3.0/tests/test_corpus_tree.py +238 -0
  17. {dhee-7.2.7 → dhee-7.3.0}/LICENSE +0 -0
  18. {dhee-7.2.7 → dhee-7.3.0}/MANIFEST.in +0 -0
  19. {dhee-7.2.7 → dhee-7.3.0}/README.md +0 -0
  20. {dhee-7.2.7 → dhee-7.3.0}/dhee/adapters/__init__.py +0 -0
  21. {dhee-7.2.7 → dhee-7.3.0}/dhee/adapters/gstack.py +0 -0
  22. {dhee-7.2.7 → dhee-7.3.0}/dhee/adapters/gstack_parser.py +0 -0
  23. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/__init__.py +0 -0
  24. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/auth.py +0 -0
  25. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/client.py +0 -0
  26. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/events.py +0 -0
  27. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/models.py +0 -0
  28. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/policy.py +0 -0
  29. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/run.py +0 -0
  30. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/server.py +0 -0
  31. {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/tools.py +0 -0
  32. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/__init__.py +0 -0
  33. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/arc_agi.py +0 -0
  34. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/decades.py +0 -0
  35. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/hippocamp.py +0 -0
  36. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/longmemeval.py +0 -0
  37. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/phase0_context_audit.py +0 -0
  38. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/portability.py +0 -0
  39. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/raw_extractors.py +0 -0
  40. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/replay_corpus.py +0 -0
  41. {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/router_replay.py +0 -0
  42. {dhee-7.2.7 → dhee-7.3.0}/dhee/checkpoint_runtime.py +0 -0
  43. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli.py +0 -0
  44. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_config.py +0 -0
  45. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_mcp.py +0 -0
  46. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_onboard.py +0 -0
  47. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_pretty.py +0 -0
  48. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_setup.py +0 -0
  49. {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_update.py +0 -0
  50. {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/__init__.py +0 -0
  51. {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/active.py +0 -0
  52. {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/base.py +0 -0
  53. {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/presets.py +0 -0
  54. {dhee-7.2.7 → dhee-7.3.0}/dhee/context_ir.py +0 -0
  55. {dhee-7.2.7 → dhee-7.3.0}/dhee/context_kernel.py +0 -0
  56. {dhee-7.2.7 → dhee-7.3.0}/dhee/context_state.py +0 -0
  57. {dhee-7.2.7 → dhee-7.3.0}/dhee/contract_runtime.py +0 -0
  58. {dhee-7.2.7 → dhee-7.3.0}/dhee/contract_supervisor.py +0 -0
  59. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/__init__.py +0 -0
  60. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/alaya.py +0 -0
  61. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/answer_orchestration.py +0 -0
  62. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/artifacts.py +0 -0
  63. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/belief.py +0 -0
  64. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/buddhi.py +0 -0
  65. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/category.py +0 -0
  66. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/codex_artifacts.py +0 -0
  67. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/codex_stream.py +0 -0
  68. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/cognition.py +0 -0
  69. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/cognition_kernel.py +0 -0
  70. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/conflict.py +0 -0
  71. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/context_bootstrap.py +0 -0
  72. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/contrastive.py +0 -0
  73. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/decay.py +0 -0
  74. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/distillation.py +0 -0
  75. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/echo.py +0 -0
  76. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram.py +0 -0
  77. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_consolidator.py +0 -0
  78. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_extractor.py +0 -0
  79. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_tiering.py +0 -0
  80. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_verification.py +0 -0
  81. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/enrichment.py +0 -0
  82. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/episode.py +0 -0
  83. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/episodic_index.py +0 -0
  84. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/evolution.py +0 -0
  85. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/file_baseline.py +0 -0
  86. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/file_read_tracker.py +0 -0
  87. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/forgetting.py +0 -0
  88. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/fusion.py +0 -0
  89. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/graph.py +0 -0
  90. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/handoff_snapshot.py +0 -0
  91. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/heuristic.py +0 -0
  92. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/intent.py +0 -0
  93. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/intention.py +0 -0
  94. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/kernel.py +0 -0
  95. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/learnings.py +0 -0
  96. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/live_context.py +0 -0
  97. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/log_parser.py +0 -0
  98. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/meta_buddhi.py +0 -0
  99. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/pattern_detector.py +0 -0
  100. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/policy.py +0 -0
  101. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/profile.py +0 -0
  102. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/provenance.py +0 -0
  103. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/resolvers.py +0 -0
  104. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/retrieval.py +0 -0
  105. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/samskara.py +0 -0
  106. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/scene.py +0 -0
  107. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/session_tracker.py +0 -0
  108. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/shared_tasks.py +0 -0
  109. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/strategy.py +0 -0
  110. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/task_state.py +0 -0
  111. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/thread_state.py +0 -0
  112. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/traces.py +0 -0
  113. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/trigger.py +0 -0
  114. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/viveka.py +0 -0
  115. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/workspace_line.py +0 -0
  116. {dhee-7.2.7 → dhee-7.3.0}/dhee/core/workspace_line_bus.py +0 -0
  117. {dhee-7.2.7 → dhee-7.3.0}/dhee/db/__init__.py +0 -0
  118. {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite.py +0 -0
  119. {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_analytics.py +0 -0
  120. {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_artifacts.py +0 -0
  121. {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_common.py +0 -0
  122. {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_domains.py +0 -0
  123. {dhee-7.2.7 → dhee-7.3.0}/dhee/debugger_api.py +0 -0
  124. {dhee-7.2.7 → dhee-7.3.0}/dhee/demo.py +0 -0
  125. {dhee-7.2.7 → dhee-7.3.0}/dhee/doctor.py +0 -0
  126. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/__init__.py +0 -0
  127. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/base.py +0 -0
  128. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/gemini.py +0 -0
  129. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/nvidia.py +0 -0
  130. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/ollama.py +0 -0
  131. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/openai.py +0 -0
  132. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/qwen.py +0 -0
  133. {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/simple.py +0 -0
  134. {dhee-7.2.7 → dhee-7.3.0}/dhee/exceptions.py +0 -0
  135. {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/__init__.py +0 -0
  136. {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/mounts.py +0 -0
  137. {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/types.py +0 -0
  138. {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/uri.py +0 -0
  139. {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/workspace.py +0 -0
  140. {dhee-7.2.7 → dhee-7.3.0}/dhee/graph_projection_hardening.py +0 -0
  141. {dhee-7.2.7 → dhee-7.3.0}/dhee/graph_proof_bundle.py +0 -0
  142. {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/__init__.py +0 -0
  143. {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/base.py +0 -0
  144. {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/claude_code.py +0 -0
  145. {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/codex.py +0 -0
  146. {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/install.py +0 -0
  147. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/__init__.py +0 -0
  148. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/__init__.py +0 -0
  149. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/__main__.py +0 -0
  150. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/assembler.py +0 -0
  151. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/chunker.py +0 -0
  152. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/ingest.py +0 -0
  153. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/install.py +0 -0
  154. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/migrate.py +0 -0
  155. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/privacy.py +0 -0
  156. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/renderer.py +0 -0
  157. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/signal.py +0 -0
  158. {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/scene_world.py +0 -0
  159. {dhee-7.2.7 → dhee-7.3.0}/dhee/incremental_context.py +0 -0
  160. {dhee-7.2.7 → dhee-7.3.0}/dhee/install_cleanup.py +0 -0
  161. {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/__init__.py +0 -0
  162. {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/context_sources.py +0 -0
  163. {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/hermes.py +0 -0
  164. {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/hermes_provider/__init__.py +0 -0
  165. {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/hermes_provider/provider.py +0 -0
  166. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/__init__.py +0 -0
  167. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/anthropic.py +0 -0
  168. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/base.py +0 -0
  169. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/dhee.py +0 -0
  170. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/gemini.py +0 -0
  171. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/mock.py +0 -0
  172. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/nvidia.py +0 -0
  173. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/ollama.py +0 -0
  174. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/openai.py +0 -0
  175. {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/teacher_logger.py +0 -0
  176. {dhee-7.2.7 → dhee-7.3.0}/dhee/mcp_registry.py +0 -0
  177. {dhee-7.2.7 → dhee-7.3.0}/dhee/mcp_server.py +0 -0
  178. {dhee-7.2.7 → dhee-7.3.0}/dhee/mcp_slim.py +0 -0
  179. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/__init__.py +0 -0
  180. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/admission.py +0 -0
  181. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/base.py +0 -0
  182. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/core.py +0 -0
  183. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/cost.py +0 -0
  184. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/episodic.py +0 -0
  185. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/main.py +0 -0
  186. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/narrative_scene.py +0 -0
  187. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/orchestration.py +0 -0
  188. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/parallel.py +0 -0
  189. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/projects.py +0 -0
  190. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/quality.py +0 -0
  191. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/retrieval_helpers.py +0 -0
  192. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/scene_profile.py +0 -0
  193. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/scoping.py +0 -0
  194. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/search_pipeline.py +0 -0
  195. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/smart.py +0 -0
  196. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/tasks.py +0 -0
  197. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/utils.py +0 -0
  198. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/vectors.py +0 -0
  199. {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/write_pipeline.py +0 -0
  200. {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/__init__.py +0 -0
  201. {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/karma_evaluator.py +0 -0
  202. {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/progressive_trainer.py +0 -0
  203. {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/replay_gate.py +0 -0
  204. {dhee-7.2.7 → dhee-7.3.0}/dhee/observability.py +0 -0
  205. {dhee-7.2.7 → dhee-7.3.0}/dhee/patch_families.py +0 -0
  206. {dhee-7.2.7 → dhee-7.3.0}/dhee/plugin.py +0 -0
  207. {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/__init__.py +0 -0
  208. {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/elevenlabs.py +0 -0
  209. {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/gemini.py +0 -0
  210. {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/openai.py +0 -0
  211. {dhee-7.2.7 → dhee-7.3.0}/dhee/protocol/__init__.py +0 -0
  212. {dhee-7.2.7 → dhee-7.3.0}/dhee/protocol/v1.py +0 -0
  213. {dhee-7.2.7 → dhee-7.3.0}/dhee/provider_defaults.py +0 -0
  214. {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/__init__.py +0 -0
  215. {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/base.py +0 -0
  216. {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/elevenlabs.py +0 -0
  217. {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/gemini.py +0 -0
  218. {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/openai.py +0 -0
  219. {dhee-7.2.7 → dhee-7.3.0}/dhee/release_hygiene.py +0 -0
  220. {dhee-7.2.7 → dhee-7.3.0}/dhee/replay_branching.py +0 -0
  221. {dhee-7.2.7 → dhee-7.3.0}/dhee/repo_intelligence.py +0 -0
  222. {dhee-7.2.7 → dhee-7.3.0}/dhee/repo_link.py +0 -0
  223. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/__init__.py +0 -0
  224. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/agent_digest.py +0 -0
  225. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/bash_digest.py +0 -0
  226. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/bootstrap.py +0 -0
  227. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/critical_surface.py +0 -0
  228. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/digest.py +0 -0
  229. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/edit_ledger.py +0 -0
  230. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/grep_digest.py +0 -0
  231. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/handlers.py +0 -0
  232. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/install.py +0 -0
  233. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/intent.py +0 -0
  234. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/policy.py +0 -0
  235. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/pre_tool_gate.py +0 -0
  236. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/ptr_store.py +0 -0
  237. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/quality_report.py +0 -0
  238. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/stats.py +0 -0
  239. {dhee-7.2.7 → dhee-7.3.0}/dhee/router/tune.py +0 -0
  240. {dhee-7.2.7 → dhee-7.3.0}/dhee/runtime.py +0 -0
  241. {dhee-7.2.7 → dhee-7.3.0}/dhee/runtime_daemon.py +0 -0
  242. {dhee-7.2.7 → dhee-7.3.0}/dhee/runtime_io.py +0 -0
  243. {dhee-7.2.7 → dhee-7.3.0}/dhee/schemas/__init__.py +0 -0
  244. {dhee-7.2.7 → dhee-7.3.0}/dhee/schemas/narrative.py +0 -0
  245. {dhee-7.2.7 → dhee-7.3.0}/dhee/schemas/scene_card.py +0 -0
  246. {dhee-7.2.7 → dhee-7.3.0}/dhee/secret_store.py +0 -0
  247. {dhee-7.2.7 → dhee-7.3.0}/dhee/simple.py +0 -0
  248. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/__init__.py +0 -0
  249. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/discovery.py +0 -0
  250. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/executor.py +0 -0
  251. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/hashing.py +0 -0
  252. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/miner.py +0 -0
  253. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/outcomes.py +0 -0
  254. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/schema.py +0 -0
  255. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/store.py +0 -0
  256. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/structure.py +0 -0
  257. {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/trajectory.py +0 -0
  258. {dhee-7.2.7 → dhee-7.3.0}/dhee/task_contracts.py +0 -0
  259. {dhee-7.2.7 → dhee-7.3.0}/dhee/temporal_fact_integration.py +0 -0
  260. {dhee-7.2.7 → dhee-7.3.0}/dhee/temporal_fact_ledger.py +0 -0
  261. {dhee-7.2.7 → dhee-7.3.0}/dhee/temporal_scenes.py +0 -0
  262. {dhee-7.2.7 → dhee-7.3.0}/dhee/training/__init__.py +0 -0
  263. {dhee-7.2.7 → dhee-7.3.0}/dhee/training/data_formatter.py +0 -0
  264. {dhee-7.2.7 → dhee-7.3.0}/dhee/training/karma.py +0 -0
  265. {dhee-7.2.7 → dhee-7.3.0}/dhee/training/nididhyasana.py +0 -0
  266. {dhee-7.2.7 → dhee-7.3.0}/dhee/training/smrti.py +0 -0
  267. {dhee-7.2.7 → dhee-7.3.0}/dhee/training/train.py +0 -0
  268. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/__init__.py +0 -0
  269. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/cli.py +0 -0
  270. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/server.py +0 -0
  271. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/assets/CanvasView-Cl1HxIK0.js +0 -0
  272. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/assets/index-BboZhdsv.js +0 -0
  273. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/assets/index-CIsLOp4u.css +0 -0
  274. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/dhee-logo.png +0 -0
  275. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/index.html +0 -0
  276. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/index.html +0 -0
  277. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/package-lock.json +0 -0
  278. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/package.json +0 -0
  279. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/public/dhee-logo.png +0 -0
  280. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/App.js +0 -0
  281. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/App.tsx +0 -0
  282. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/api.js +0 -0
  283. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/api.ts +0 -0
  284. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/AssetDrawer.js +0 -0
  285. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/AssetDrawer.tsx +0 -0
  286. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ChatMessage.js +0 -0
  287. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ChatMessage.tsx +0 -0
  288. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/FirstRunPanel.js +0 -0
  289. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/FirstRunPanel.tsx +0 -0
  290. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/LinePanel.js +0 -0
  291. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/LinePanel.tsx +0 -0
  292. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/NavRail.js +0 -0
  293. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/NavRail.tsx +0 -0
  294. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/OrgDrawer.js +0 -0
  295. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/OrgDrawer.tsx +0 -0
  296. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TopBar.js +0 -0
  297. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TopBar.tsx +0 -0
  298. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TweaksPanel.js +0 -0
  299. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TweaksPanel.tsx +0 -0
  300. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/WorkspaceManagerModal.js +0 -0
  301. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/WorkspaceManagerModal.tsx +0 -0
  302. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasControls.js +0 -0
  303. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasControls.tsx +0 -0
  304. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasSkeleton.js +0 -0
  305. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasSkeleton.tsx +0 -0
  306. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/DirectionHints.js +0 -0
  307. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/DirectionHints.tsx +0 -0
  308. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/Minimap.js +0 -0
  309. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/Minimap.tsx +0 -0
  310. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/NodeCard.js +0 -0
  311. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/NodeCard.tsx +0 -0
  312. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/forceLayout.js +0 -0
  313. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/forceLayout.ts +0 -0
  314. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/layout.js +0 -0
  315. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/layout.ts +0 -0
  316. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/treeLayout.js +0 -0
  317. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/treeLayout.ts +0 -0
  318. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/useInfiniteCanvas.js +0 -0
  319. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/useInfiniteCanvas.ts +0 -0
  320. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/cards/Cards.js +0 -0
  321. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/cards/Cards.tsx +0 -0
  322. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/graph/CanvasNodeCard.js +0 -0
  323. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/graph/CanvasNodeCard.tsx +0 -0
  324. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/DecayBar.js +0 -0
  325. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/DecayBar.tsx +0 -0
  326. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/Markdown.js +0 -0
  327. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/Markdown.tsx +0 -0
  328. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/SectionHeader.js +0 -0
  329. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/SectionHeader.tsx +0 -0
  330. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/StatPill.js +0 -0
  331. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/StatPill.tsx +0 -0
  332. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/TierBadge.js +0 -0
  333. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/TierBadge.tsx +0 -0
  334. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/main.js +0 -0
  335. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/main.tsx +0 -0
  336. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/styles.css +0 -0
  337. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/types.js +0 -0
  338. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/types.ts +0 -0
  339. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/CanvasView.js +0 -0
  340. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/CanvasView.tsx +0 -0
  341. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ChannelView.js +0 -0
  342. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ChannelView.tsx +0 -0
  343. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ConflictView.js +0 -0
  344. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ConflictView.tsx +0 -0
  345. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ContextView.js +0 -0
  346. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ContextView.tsx +0 -0
  347. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/EvolutionView.js +0 -0
  348. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/EvolutionView.tsx +0 -0
  349. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/LaunchView.js +0 -0
  350. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/LaunchView.tsx +0 -0
  351. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/MemoryView.js +0 -0
  352. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/MemoryView.tsx +0 -0
  353. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/NotepadView.js +0 -0
  354. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/NotepadView.tsx +0 -0
  355. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/OrgCanvas.js +0 -0
  356. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/OrgCanvas.tsx +0 -0
  357. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ProductViews.js +0 -0
  358. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ProductViews.tsx +0 -0
  359. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/RouterView.js +0 -0
  360. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/RouterView.tsx +0 -0
  361. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/TasksView.js +0 -0
  362. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/TasksView.tsx +0 -0
  363. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/WorkspaceView.js +0 -0
  364. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/WorkspaceView.tsx +0 -0
  365. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/tsconfig.json +0 -0
  366. {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/vite.config.ts +0 -0
  367. {dhee-7.2.7 → dhee-7.3.0}/dhee/update_capsules.py +0 -0
  368. {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/__init__.py +0 -0
  369. {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/math.py +0 -0
  370. {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/prompts.py +0 -0
  371. {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/repo_identity.py +0 -0
  372. {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/__init__.py +0 -0
  373. {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/base.py +0 -0
  374. {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/memory.py +0 -0
  375. {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/sqlite_vec.py +0 -0
  376. {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/zvec_store.py +0 -0
  377. {dhee-7.2.7 → dhee-7.3.0}/dhee/verification_runner.py +0 -0
  378. {dhee-7.2.7 → dhee-7.3.0}/dhee/verifier_engine.py +0 -0
  379. {dhee-7.2.7 → dhee-7.3.0}/dhee/webhooks/__init__.py +0 -0
  380. {dhee-7.2.7 → dhee-7.3.0}/dhee/webhooks/elevenlabs.py +0 -0
  381. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/__init__.py +0 -0
  382. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/capture_store.py +0 -0
  383. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/causal_graph.py +0 -0
  384. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/encoder.py +0 -0
  385. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/gem_extractor.py +0 -0
  386. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/predictor.py +0 -0
  387. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/schema.py +0 -0
  388. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/service.py +0 -0
  389. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/session_graph.py +0 -0
  390. {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/store.py +0 -0
  391. {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/dependency_links.txt +0 -0
  392. {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/entry_points.txt +0 -0
  393. {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/requires.txt +0 -0
  394. {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/top_level.txt +0 -0
  395. {dhee-7.2.7 → dhee-7.3.0}/dhee_shared/__init__.py +0 -0
  396. {dhee-7.2.7 → dhee-7.3.0}/dhee_shared/model_paths.py +0 -0
  397. {dhee-7.2.7 → dhee-7.3.0}/engram/__init__.py +0 -0
  398. {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/__init__.py +0 -0
  399. {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/bus.py +0 -0
  400. {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/pubsub.py +0 -0
  401. {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/server.py +0 -0
  402. {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/store.py +0 -0
  403. {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/workspace.py +0 -0
  404. {dhee-7.2.7 → dhee-7.3.0}/setup.cfg +0 -0
  405. {dhee-7.2.7 → dhee-7.3.0}/tests/test_accel.py +0 -0
  406. {dhee-7.2.7 → dhee-7.3.0}/tests/test_accel_benchmark.py +0 -0
  407. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_digest.py +0 -0
  408. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_provider_examples.py +0 -0
  409. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_before.py +0 -0
  410. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_http_auth.py +0 -0
  411. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_lifecycle.py +0 -0
  412. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_tool.py +0 -0
  413. {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_voice_policy.py +0 -0
  414. {dhee-7.2.7 → dhee-7.3.0}/tests/test_artifacts.py +0 -0
  415. {dhee-7.2.7 → dhee-7.3.0}/tests/test_auto_lifecycle.py +0 -0
  416. {dhee-7.2.7 → dhee-7.3.0}/tests/test_backward_compat.py +0 -0
  417. {dhee-7.2.7 → dhee-7.3.0}/tests/test_batch.py +0 -0
  418. {dhee-7.2.7 → dhee-7.3.0}/tests/test_belief_debugger.py +0 -0
  419. {dhee-7.2.7 → dhee-7.3.0}/tests/test_causal_scene_memory.py +0 -0
  420. {dhee-7.2.7 → dhee-7.3.0}/tests/test_claude_code_hooks.py +0 -0
  421. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_config.py +0 -0
  422. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_harness_commands.py +0 -0
  423. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_onboard_update.py +0 -0
  424. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_pretty.py +0 -0
  425. {dhee-7.2.7 → dhee-7.3.0}/tests/test_codex_stream.py +0 -0
  426. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cognition_evals.py +0 -0
  427. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cognition_kernel.py +0 -0
  428. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cognition_v3.py +0 -0
  429. {dhee-7.2.7 → dhee-7.3.0}/tests/test_context_state.py +0 -0
  430. {dhee-7.2.7 → dhee-7.3.0}/tests/test_contract_runtime_recovery.py +0 -0
  431. {dhee-7.2.7 → dhee-7.3.0}/tests/test_core_memory.py +0 -0
  432. {dhee-7.2.7 → dhee-7.3.0}/tests/test_cosine_similarity.py +0 -0
  433. {dhee-7.2.7 → dhee-7.3.0}/tests/test_data_dir_paths.py +0 -0
  434. {dhee-7.2.7 → dhee-7.3.0}/tests/test_decades_eval.py +0 -0
  435. {dhee-7.2.7 → dhee-7.3.0}/tests/test_dedup.py +0 -0
  436. {dhee-7.2.7 → dhee-7.3.0}/tests/test_deferred_enrichment.py +0 -0
  437. {dhee-7.2.7 → dhee-7.3.0}/tests/test_demo.py +0 -0
  438. {dhee-7.2.7 → dhee-7.3.0}/tests/test_dhee_init.py +0 -0
  439. {dhee-7.2.7 → dhee-7.3.0}/tests/test_dhee_model_paths.py +0 -0
  440. {dhee-7.2.7 → dhee-7.3.0}/tests/test_dheefs.py +0 -0
  441. {dhee-7.2.7 → dhee-7.3.0}/tests/test_distillation.py +0 -0
  442. {dhee-7.2.7 → dhee-7.3.0}/tests/test_e2e_all_features.py +0 -0
  443. {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_cli.py +0 -0
  444. {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_correction_memory.py +0 -0
  445. {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_no_secret_storage.py +0 -0
  446. {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_profile.py +0 -0
  447. {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_two_call_memory.py +0 -0
  448. {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_webhook.py +0 -0
  449. {dhee-7.2.7 → dhee-7.3.0}/tests/test_engram_tiering.py +0 -0
  450. {dhee-7.2.7 → dhee-7.3.0}/tests/test_engram_tiering_promotion.py +0 -0
  451. {dhee-7.2.7 → dhee-7.3.0}/tests/test_evolution_meta_loop.py +0 -0
  452. {dhee-7.2.7 → dhee-7.3.0}/tests/test_evolution_session_gate.py +0 -0
  453. {dhee-7.2.7 → dhee-7.3.0}/tests/test_forgetting.py +0 -0
  454. {dhee-7.2.7 → dhee-7.3.0}/tests/test_gemini_provider.py +0 -0
  455. {dhee-7.2.7 → dhee-7.3.0}/tests/test_graph_proof_bundle.py +0 -0
  456. {dhee-7.2.7 → dhee-7.3.0}/tests/test_gstack_adapter.py +0 -0
  457. {dhee-7.2.7 → dhee-7.3.0}/tests/test_handoff_snapshot.py +0 -0
  458. {dhee-7.2.7 → dhee-7.3.0}/tests/test_harness_adapters.py +0 -0
  459. {dhee-7.2.7 → dhee-7.3.0}/tests/test_harness_install.py +0 -0
  460. {dhee-7.2.7 → dhee-7.3.0}/tests/test_hashing.py +0 -0
  461. {dhee-7.2.7 → dhee-7.3.0}/tests/test_hermes_provider.py +0 -0
  462. {dhee-7.2.7 → dhee-7.3.0}/tests/test_hippocamp_benchmark.py +0 -0
  463. {dhee-7.2.7 → dhee-7.3.0}/tests/test_incremental_context.py +0 -0
  464. {dhee-7.2.7 → dhee-7.3.0}/tests/test_install_cleanup.py +0 -0
  465. {dhee-7.2.7 → dhee-7.3.0}/tests/test_intent.py +0 -0
  466. {dhee-7.2.7 → dhee-7.3.0}/tests/test_karma_evaluator.py +0 -0
  467. {dhee-7.2.7 → dhee-7.3.0}/tests/test_learnings.py +0 -0
  468. {dhee-7.2.7 → dhee-7.3.0}/tests/test_locomo_plus_runner.py +0 -0
  469. {dhee-7.2.7 → dhee-7.3.0}/tests/test_log_parser.py +0 -0
  470. {dhee-7.2.7 → dhee-7.3.0}/tests/test_longmemeval_config.py +0 -0
  471. {dhee-7.2.7 → dhee-7.3.0}/tests/test_mcp_artifact_tools.py +0 -0
  472. {dhee-7.2.7 → dhee-7.3.0}/tests/test_mcp_tools_slim.py +0 -0
  473. {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_admission.py +0 -0
  474. {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_os_service.py +0 -0
  475. {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_quality_contract.py +0 -0
  476. {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_types.py +0 -0
  477. {dhee-7.2.7 → dhee-7.3.0}/tests/test_meta_buddhi_group_relative.py +0 -0
  478. {dhee-7.2.7 → dhee-7.3.0}/tests/test_migration.py +0 -0
  479. {dhee-7.2.7 → dhee-7.3.0}/tests/test_miner.py +0 -0
  480. {dhee-7.2.7 → dhee-7.3.0}/tests/test_narrative_scene_intelligence.py +0 -0
  481. {dhee-7.2.7 → dhee-7.3.0}/tests/test_nvidia_embedder.py +0 -0
  482. {dhee-7.2.7 → dhee-7.3.0}/tests/test_nvidia_llm.py +0 -0
  483. {dhee-7.2.7 → dhee-7.3.0}/tests/test_openai_provider.py +0 -0
  484. {dhee-7.2.7 → dhee-7.3.0}/tests/test_openclaw.py +0 -0
  485. {dhee-7.2.7 → dhee-7.3.0}/tests/test_orchestration_core.py +0 -0
  486. {dhee-7.2.7 → dhee-7.3.0}/tests/test_oss_61_regressions.py +0 -0
  487. {dhee-7.2.7 → dhee-7.3.0}/tests/test_packaging.py +0 -0
  488. {dhee-7.2.7 → dhee-7.3.0}/tests/test_parallel.py +0 -0
  489. {dhee-7.2.7 → dhee-7.3.0}/tests/test_patch_families.py +0 -0
  490. {dhee-7.2.7 → dhee-7.3.0}/tests/test_plugin_learnings.py +0 -0
  491. {dhee-7.2.7 → dhee-7.3.0}/tests/test_portability_eval.py +0 -0
  492. {dhee-7.2.7 → dhee-7.3.0}/tests/test_power_packages.py +0 -0
  493. {dhee-7.2.7 → dhee-7.3.0}/tests/test_presets.py +0 -0
  494. {dhee-7.2.7 → dhee-7.3.0}/tests/test_profile.py +0 -0
  495. {dhee-7.2.7 → dhee-7.3.0}/tests/test_progressive_trainer.py +0 -0
  496. {dhee-7.2.7 → dhee-7.3.0}/tests/test_projects.py +0 -0
  497. {dhee-7.2.7 → dhee-7.3.0}/tests/test_protocol_v1.py +0 -0
  498. {dhee-7.2.7 → dhee-7.3.0}/tests/test_provider_runtime_profiles.py +0 -0
  499. {dhee-7.2.7 → dhee-7.3.0}/tests/test_query_cache.py +0 -0
  500. {dhee-7.2.7 → dhee-7.3.0}/tests/test_release_hygiene.py +0 -0
  501. {dhee-7.2.7 → dhee-7.3.0}/tests/test_replay_branching.py +0 -0
  502. {dhee-7.2.7 → dhee-7.3.0}/tests/test_replay_corpus_export.py +0 -0
  503. {dhee-7.2.7 → dhee-7.3.0}/tests/test_replay_gate.py +0 -0
  504. {dhee-7.2.7 → dhee-7.3.0}/tests/test_repo_intelligence.py +0 -0
  505. {dhee-7.2.7 → dhee-7.3.0}/tests/test_repo_link.py +0 -0
  506. {dhee-7.2.7 → dhee-7.3.0}/tests/test_reranker_defaults.py +0 -0
  507. {dhee-7.2.7 → dhee-7.3.0}/tests/test_router.py +0 -0
  508. {dhee-7.2.7 → dhee-7.3.0}/tests/test_router_replay.py +0 -0
  509. {dhee-7.2.7 → dhee-7.3.0}/tests/test_router_stats_agents.py +0 -0
  510. {dhee-7.2.7 → dhee-7.3.0}/tests/test_runtime.py +0 -0
  511. {dhee-7.2.7 → dhee-7.3.0}/tests/test_runtime_hardening.py +0 -0
  512. {dhee-7.2.7 → dhee-7.3.0}/tests/test_scene.py +0 -0
  513. {dhee-7.2.7 → dhee-7.3.0}/tests/test_scene_maintenance.py +0 -0
  514. {dhee-7.2.7 → dhee-7.3.0}/tests/test_scene_world_hook.py +0 -0
  515. {dhee-7.2.7 → dhee-7.3.0}/tests/test_secret_store.py +0 -0
  516. {dhee-7.2.7 → dhee-7.3.0}/tests/test_session_tracker.py +0 -0
  517. {dhee-7.2.7 → dhee-7.3.0}/tests/test_shared_tasks.py +0 -0
  518. {dhee-7.2.7 → dhee-7.3.0}/tests/test_simple_zero_config.py +0 -0
  519. {dhee-7.2.7 → dhee-7.3.0}/tests/test_skills.py +0 -0
  520. {dhee-7.2.7 → dhee-7.3.0}/tests/test_smart_memory.py +0 -0
  521. {dhee-7.2.7 → dhee-7.3.0}/tests/test_sqlite_connection_pool.py +0 -0
  522. {dhee-7.2.7 → dhee-7.3.0}/tests/test_sqlite_vec.py +0 -0
  523. {dhee-7.2.7 → dhee-7.3.0}/tests/test_structural.py +0 -0
  524. {dhee-7.2.7 → dhee-7.3.0}/tests/test_structured_resolution.py +0 -0
  525. {dhee-7.2.7 → dhee-7.3.0}/tests/test_task_contracts.py +0 -0
  526. {dhee-7.2.7 → dhee-7.3.0}/tests/test_tasks.py +0 -0
  527. {dhee-7.2.7 → dhee-7.3.0}/tests/test_temporal_fact_integration.py +0 -0
  528. {dhee-7.2.7 → dhee-7.3.0}/tests/test_temporal_fact_ledger.py +0 -0
  529. {dhee-7.2.7 → dhee-7.3.0}/tests/test_temporal_scenes.py +0 -0
  530. {dhee-7.2.7 → dhee-7.3.0}/tests/test_thread_state.py +0 -0
  531. {dhee-7.2.7 → dhee-7.3.0}/tests/test_traces.py +0 -0
  532. {dhee-7.2.7 → dhee-7.3.0}/tests/test_trajectory.py +0 -0
  533. {dhee-7.2.7 → dhee-7.3.0}/tests/test_ui.py +0 -0
  534. {dhee-7.2.7 → dhee-7.3.0}/tests/test_unified_enrichment.py +0 -0
  535. {dhee-7.2.7 → dhee-7.3.0}/tests/test_update_capsules.py +0 -0
  536. {dhee-7.2.7 → dhee-7.3.0}/tests/test_vector_store_factory.py +0 -0
  537. {dhee-7.2.7 → dhee-7.3.0}/tests/test_verification_runner.py +0 -0
  538. {dhee-7.2.7 → dhee-7.3.0}/tests/test_verifier_engine.py +0 -0
  539. {dhee-7.2.7 → dhee-7.3.0}/tests/test_workspace_line_agent_emit.py +0 -0
  540. {dhee-7.2.7 → dhee-7.3.0}/tests/test_workspace_line_bus.py +0 -0
  541. {dhee-7.2.7 → dhee-7.3.0}/tests/test_zvec_store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhee
3
- Version: 7.2.7
3
+ Version: 7.3.0
4
4
  Summary: Dhee - world memory layer and context compiler for AI agents
5
5
  Author: Sankhya AI Labs
6
6
  License-Expression: MIT
@@ -50,7 +50,7 @@ from dhee.memory.admission import (
50
50
  # Default import remains model-free for backwards compatibility.
51
51
  Memory = CoreMemory
52
52
 
53
- __version__ = "7.2.7"
53
+ __version__ = "7.3.0"
54
54
  __all__ = [
55
55
  # Memory classes
56
56
  "Engram",
@@ -0,0 +1,72 @@
1
+ """Dhee's document corpus — a third lane, beside beliefs and world memory.
2
+
3
+ Belief memory answers "what is true about the user and the work". A corpus answers
4
+ "what does this folder of documents say", and the two must not share a store: Dhee's
5
+ quality pipeline deliberately treats document chunks as noise so that a thousand
6
+ fragments of a PDF never drown a handful of real beliefs. This package is where those
7
+ fragments are supposed to live instead.
8
+
9
+ Typical use::
10
+
11
+ from dhee.corpus import CorpusIndex, CorpusSearch, CorpusStore
12
+
13
+ index = CorpusIndex(store=store, vector_store=vectors, embedder=embedder,
14
+ extractor=my_ocr_extractor)
15
+ corpus_id = index.attach("~/Documents/Cases")
16
+ index.sync(corpus_id)
17
+
18
+ hits = CorpusSearch(store=store, vector_store=vectors, embedder=embedder,
19
+ reranker=reranker).search(corpus_id, "what is the penalty clause?")
20
+
21
+ Extraction is injected, never imported: OCR belongs to the host application, so Dhee
22
+ still installs and runs on its own.
23
+ """
24
+
25
+ from dhee.corpus.extract import (
26
+ Chunk,
27
+ ExtractedDoc,
28
+ ExtractedLine,
29
+ ExtractedPage,
30
+ ExtractorChain,
31
+ PlainTextExtractor,
32
+ TextExtractor,
33
+ chunk_document,
34
+ )
35
+ from dhee.corpus.index import CorpusIndex, SyncProgress, SyncReport
36
+ from dhee.corpus.search import Citation, CorpusSearch, SearchHit, SearchResult
37
+ from dhee.corpus.store import ChunkRecord, CorpusRecord, CorpusStore
38
+ from dhee.corpus.tree import (
39
+ BlobEntry,
40
+ FolderDiff,
41
+ FolderSnapshot,
42
+ changed_subtrees,
43
+ diff,
44
+ scan,
45
+ )
46
+
47
+ __all__ = [
48
+ "BlobEntry",
49
+ "Chunk",
50
+ "ChunkRecord",
51
+ "Citation",
52
+ "CorpusIndex",
53
+ "CorpusRecord",
54
+ "CorpusSearch",
55
+ "CorpusStore",
56
+ "ExtractedDoc",
57
+ "ExtractedLine",
58
+ "ExtractedPage",
59
+ "ExtractorChain",
60
+ "FolderDiff",
61
+ "FolderSnapshot",
62
+ "PlainTextExtractor",
63
+ "SearchHit",
64
+ "SearchResult",
65
+ "SyncProgress",
66
+ "SyncReport",
67
+ "TextExtractor",
68
+ "changed_subtrees",
69
+ "chunk_document",
70
+ "diff",
71
+ "scan",
72
+ ]
@@ -0,0 +1,352 @@
1
+ """Turning a file into pages of text, and pages of text into citable chunks.
2
+
3
+ Two responsibilities that stay deliberately separate:
4
+
5
+ **Extraction** is pluggable and lives outside Dhee. Dhee ships a default that reads
6
+ plain text and PDFs that already carry a text layer — enough to be useful alone, which
7
+ is the standing rule for this package. Anything harder (a scan, a photograph of a page,
8
+ a Devanagari order) needs OCR, and OCR is a 220MB bundle that belongs to the host
9
+ application, not to a library. So `TextExtractor` is a Protocol the host implements and
10
+ injects. Dhee never imports the host.
11
+
12
+ **Chunking** is Dhee's, and it is page-aware on purpose. A retrieval hit is only worth
13
+ as much as the citation attached to it: "this is in the folder somewhere" is not an
14
+ answer, "page 4 of the March order" is. So chunks never straddle a page boundary, and
15
+ every chunk carries the page it came from all the way through to the answer.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import hashlib
21
+ import re
22
+ from dataclasses import dataclass, field
23
+ from pathlib import Path
24
+ from typing import Any, Dict, List, Optional, Protocol, Sequence, Tuple, runtime_checkable
25
+
26
+ # Roughly 300 tokens. Small enough that a hit points at a paragraph rather than a page,
27
+ # large enough that a clause keeps the sentences that qualify it.
28
+ DEFAULT_CHUNK_CHARS = 1200
29
+ # One or two sentences of carry-over, so a clause split across a boundary is still
30
+ # retrievable from either side.
31
+ DEFAULT_CHUNK_OVERLAP_CHARS = 160
32
+ MIN_CHUNK_CHARS = 60
33
+
34
+ EXTRACTION_OK = "extracted"
35
+ EXTRACTION_EMPTY = "empty"
36
+ EXTRACTION_UNSUPPORTED = "unsupported"
37
+ EXTRACTION_FAILED = "failed"
38
+
39
+ _PARAGRAPH_BREAK = re.compile(r"\n\s*\n")
40
+ _SENTENCE_END = re.compile(r"(?<=[.!?।])\s+")
41
+ _WHITESPACE = re.compile(r"[ \t]+")
42
+
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class ExtractedLine:
46
+ """One recognised line, and where it sat on the page.
47
+
48
+ The box is optional because a text-layer PDF has no geometry worth keeping, but an
49
+ OCR engine does — and a citation into a scan should be able to highlight the region
50
+ it came from rather than only name the page.
51
+ """
52
+
53
+ text: str
54
+ confidence: float = 1.0
55
+ box: Optional[Tuple[float, float, float, float]] = None
56
+
57
+
58
+ @dataclass(frozen=True, slots=True)
59
+ class ExtractedPage:
60
+ page_no: int
61
+ text: str
62
+ lines: Tuple[ExtractedLine, ...] = ()
63
+
64
+ @property
65
+ def is_empty(self) -> bool:
66
+ return not self.text.strip()
67
+
68
+
69
+ @dataclass(frozen=True, slots=True)
70
+ class ExtractedDoc:
71
+ """Everything one file yielded, plus an honest account of how it went."""
72
+
73
+ pages: Tuple[ExtractedPage, ...] = ()
74
+ engine: str = ""
75
+ status: str = EXTRACTION_OK
76
+ error: str = ""
77
+ was_scanned: bool = False
78
+ metadata: Dict[str, Any] = field(default_factory=dict)
79
+
80
+ @property
81
+ def page_count(self) -> int:
82
+ return len(self.pages)
83
+
84
+ @property
85
+ def text(self) -> str:
86
+ return "\n\n".join(page.text for page in self.pages if page.text.strip())
87
+
88
+ @property
89
+ def is_usable(self) -> bool:
90
+ return self.status == EXTRACTION_OK and bool(self.text.strip())
91
+
92
+ @classmethod
93
+ def failure(cls, reason: str, *, engine: str = "", status: str = EXTRACTION_FAILED) -> "ExtractedDoc":
94
+ return cls(engine=engine, status=status, error=reason)
95
+
96
+
97
+ @runtime_checkable
98
+ class TextExtractor(Protocol):
99
+ """What the host plugs in. One method to decide, one to do."""
100
+
101
+ def supports(self, path: Path) -> bool:
102
+ ...
103
+
104
+ def extract(self, path: Path) -> ExtractedDoc:
105
+ ...
106
+
107
+
108
+ @dataclass(frozen=True, slots=True)
109
+ class Chunk:
110
+ """A retrievable span, addressed by the content it came from.
111
+
112
+ ``chunk_id`` is derived from the blob hash and the chunk's own text, so the same
113
+ passage in two copies of the same document is one chunk with one embedding, and a
114
+ chunk's identity never depends on the path it was found at.
115
+ """
116
+
117
+ chunk_id: str
118
+ blob_sha: str
119
+ chunk_index: int
120
+ page_no: int
121
+ text: str
122
+ char_start: int
123
+ char_end: int
124
+
125
+ @property
126
+ def char_count(self) -> int:
127
+ return len(self.text)
128
+
129
+
130
+ def chunk_id_for(blob_sha: str, chunk_index: int, text: str) -> str:
131
+ digest = hashlib.sha256()
132
+ digest.update(blob_sha.encode("ascii"))
133
+ digest.update(b"\0")
134
+ digest.update(str(chunk_index).encode("ascii"))
135
+ digest.update(b"\0")
136
+ digest.update(text.encode("utf-8"))
137
+ return digest.hexdigest()
138
+
139
+
140
+ def normalise_text(value: str) -> str:
141
+ """Collapse the noise OCR and PDF layers add, keeping paragraph structure.
142
+
143
+ Runs of spaces become one space and trailing space is dropped, because they change
144
+ a chunk's hash without changing its meaning — and a chunk whose hash moves for no
145
+ reason is a chunk that gets re-embedded for no reason.
146
+ """
147
+ lines = [_WHITESPACE.sub(" ", line).strip() for line in value.splitlines()]
148
+ collapsed: List[str] = []
149
+ blank_run = 0
150
+ for line in lines:
151
+ if line:
152
+ blank_run = 0
153
+ collapsed.append(line)
154
+ else:
155
+ blank_run += 1
156
+ if blank_run == 1:
157
+ collapsed.append("")
158
+ return "\n".join(collapsed).strip()
159
+
160
+
161
+ def chunk_page(
162
+ *,
163
+ blob_sha: str,
164
+ page: ExtractedPage,
165
+ start_index: int,
166
+ max_chars: int = DEFAULT_CHUNK_CHARS,
167
+ overlap_chars: int = DEFAULT_CHUNK_OVERLAP_CHARS,
168
+ ) -> List[Chunk]:
169
+ """Split one page, preferring to break where the author already broke.
170
+
171
+ Paragraph boundaries first, then sentence boundaries, then a hard cut. Breaking
172
+ mid-sentence is the worst outcome for retrieval quality, so it is what we fall back
173
+ to rather than what we do.
174
+ """
175
+ text = normalise_text(page.text)
176
+ if len(text) < MIN_CHUNK_CHARS:
177
+ return []
178
+
179
+ spans = _split_spans(text, max_chars=max_chars, overlap_chars=overlap_chars)
180
+ chunks: List[Chunk] = []
181
+ for offset, (start, end) in enumerate(spans):
182
+ body = text[start:end].strip()
183
+ if len(body) < MIN_CHUNK_CHARS:
184
+ continue
185
+ index = start_index + len(chunks)
186
+ chunks.append(
187
+ Chunk(
188
+ chunk_id=chunk_id_for(blob_sha, index, body),
189
+ blob_sha=blob_sha,
190
+ chunk_index=index,
191
+ page_no=page.page_no,
192
+ text=body,
193
+ char_start=start,
194
+ char_end=end,
195
+ )
196
+ )
197
+ return chunks
198
+
199
+
200
+ def chunk_document(
201
+ *,
202
+ blob_sha: str,
203
+ doc: ExtractedDoc,
204
+ max_chars: int = DEFAULT_CHUNK_CHARS,
205
+ overlap_chars: int = DEFAULT_CHUNK_OVERLAP_CHARS,
206
+ ) -> List[Chunk]:
207
+ chunks: List[Chunk] = []
208
+ for page in doc.pages:
209
+ chunks.extend(
210
+ chunk_page(
211
+ blob_sha=blob_sha,
212
+ page=page,
213
+ start_index=len(chunks),
214
+ max_chars=max_chars,
215
+ overlap_chars=overlap_chars,
216
+ )
217
+ )
218
+ return chunks
219
+
220
+
221
+ def _split_spans(text: str, *, max_chars: int, overlap_chars: int) -> List[Tuple[int, int]]:
222
+ if len(text) <= max_chars:
223
+ return [(0, len(text))]
224
+
225
+ boundaries = _candidate_boundaries(text)
226
+ spans: List[Tuple[int, int]] = []
227
+ start = 0
228
+ length = len(text)
229
+
230
+ while start < length:
231
+ hard_end = min(start + max_chars, length)
232
+ if hard_end >= length:
233
+ spans.append((start, length))
234
+ break
235
+
236
+ # The last boundary that fits, provided it leaves a chunk worth having.
237
+ cut = max(
238
+ (pos for pos in boundaries if start + MIN_CHUNK_CHARS < pos <= hard_end),
239
+ default=hard_end,
240
+ )
241
+ spans.append((start, cut))
242
+ next_start = cut - overlap_chars
243
+ start = cut if next_start <= start else next_start
244
+
245
+ return spans
246
+
247
+
248
+ def _candidate_boundaries(text: str) -> List[int]:
249
+ """Offsets it is safe to break at, best first — paragraphs, then sentences."""
250
+ positions = {match.end() for match in _PARAGRAPH_BREAK.finditer(text)}
251
+ positions.update(match.end() for match in _SENTENCE_END.finditer(text))
252
+ positions.add(len(text))
253
+ return sorted(positions)
254
+
255
+
256
+ class PlainTextExtractor:
257
+ """Dhee's built-in floor: text files, and PDFs that already carry a text layer.
258
+
259
+ This exists so the corpus works with no host and no bundle at all. It deliberately
260
+ does not attempt OCR — a scanned page returns ``EXTRACTION_EMPTY`` with a reason
261
+ rather than silently indexing nothing, so the caller can see that a real extractor
262
+ is needed rather than wonder why the document is unsearchable.
263
+ """
264
+
265
+ TEXT_SUFFIXES = frozenset({".txt", ".md", ".markdown", ".rst", ".csv", ".tsv", ".json", ".yaml", ".yml", ".log"})
266
+ PDF_SUFFIXES = frozenset({".pdf"})
267
+
268
+ name = "plain_text"
269
+
270
+ def supports(self, path: Path) -> bool:
271
+ suffix = path.suffix.lower()
272
+ return suffix in self.TEXT_SUFFIXES or suffix in self.PDF_SUFFIXES
273
+
274
+ def extract(self, path: Path) -> ExtractedDoc:
275
+ suffix = path.suffix.lower()
276
+ if suffix in self.TEXT_SUFFIXES:
277
+ return self._extract_text(path)
278
+ if suffix in self.PDF_SUFFIXES:
279
+ return self._extract_pdf(path)
280
+ return ExtractedDoc.failure(f"unsupported suffix {suffix}", engine=self.name, status=EXTRACTION_UNSUPPORTED)
281
+
282
+ def _extract_text(self, path: Path) -> ExtractedDoc:
283
+ try:
284
+ raw = path.read_text(encoding="utf-8", errors="replace")
285
+ except OSError as exc:
286
+ return ExtractedDoc.failure(str(exc), engine=self.name)
287
+ body = normalise_text(raw)
288
+ if not body:
289
+ return ExtractedDoc(engine=self.name, status=EXTRACTION_EMPTY, error="file is empty")
290
+ return ExtractedDoc(pages=(ExtractedPage(page_no=1, text=body),), engine=self.name)
291
+
292
+ def _extract_pdf(self, path: Path) -> ExtractedDoc:
293
+ try:
294
+ from pypdf import PdfReader
295
+ except ImportError:
296
+ return ExtractedDoc.failure(
297
+ "pypdf is not installed, so PDF text extraction is unavailable",
298
+ engine=self.name,
299
+ status=EXTRACTION_UNSUPPORTED,
300
+ )
301
+ try:
302
+ reader = PdfReader(str(path))
303
+ pages: List[ExtractedPage] = []
304
+ for number, page in enumerate(reader.pages, start=1):
305
+ body = normalise_text(page.extract_text() or "")
306
+ if body:
307
+ pages.append(ExtractedPage(page_no=number, text=body))
308
+ except Exception as exc: # pypdf raises a wide range on damaged files
309
+ return ExtractedDoc.failure(f"could not read pdf: {exc}", engine=self.name)
310
+
311
+ if not pages:
312
+ # Almost always a scan. Say so, because the fix is an OCR extractor.
313
+ return ExtractedDoc(
314
+ engine=self.name,
315
+ status=EXTRACTION_EMPTY,
316
+ error="pdf has no text layer — it is probably scanned and needs OCR",
317
+ was_scanned=True,
318
+ )
319
+ return ExtractedDoc(pages=tuple(pages), engine=self.name)
320
+
321
+
322
+ class ExtractorChain:
323
+ """Try each extractor in order and take the first that both supports and succeeds.
324
+
325
+ A registry rather than branching, so the host adds OCR by putting it at the front
326
+ of the tuple. Falling through on an *unusable* result (not merely an unsupported
327
+ one) is what lets a text-layer reader sit ahead of an expensive OCR pass: the cheap
328
+ engine gets first refusal, and the expensive one only runs when the cheap one came
329
+ back empty.
330
+ """
331
+
332
+ def __init__(self, extractors: Sequence[TextExtractor]):
333
+ if not extractors:
334
+ raise ValueError("ExtractorChain needs at least one extractor")
335
+ self._extractors = tuple(extractors)
336
+
337
+ def supports(self, path: Path) -> bool:
338
+ return any(extractor.supports(path) for extractor in self._extractors)
339
+
340
+ def extract(self, path: Path) -> ExtractedDoc:
341
+ last: Optional[ExtractedDoc] = None
342
+ for extractor in self._extractors:
343
+ if not extractor.supports(path):
344
+ continue
345
+ result = extractor.extract(path)
346
+ if result.is_usable:
347
+ return result
348
+ last = last or result
349
+ return last or ExtractedDoc.failure(
350
+ f"no extractor handles {path.suffix or 'this file'}",
351
+ status=EXTRACTION_UNSUPPORTED,
352
+ )