docpull 6.0.1__tar.gz → 6.2.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 (238) hide show
  1. {docpull-6.0.1/src/docpull.egg-info → docpull-6.2.0}/PKG-INFO +58 -8
  2. {docpull-6.0.1 → docpull-6.2.0}/README.md +46 -2
  3. {docpull-6.0.1 → docpull-6.2.0}/pyproject.toml +14 -7
  4. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/__init__.py +41 -1
  5. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/benchmark.py +2 -2
  6. docpull-6.2.0/src/docpull/change_events.py +209 -0
  7. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/cli.py +65 -1
  8. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/__init__.py +11 -0
  9. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/_legacy_cli.py +3 -4
  10. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/common.py +298 -33
  11. docpull-6.2.0/src/docpull/context_packs/policy_pack.py +354 -0
  12. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/product.py +131 -25
  13. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/visuals.py +0 -1
  14. docpull-6.2.0/src/docpull/context_packs/workflow_cli.py +52 -0
  15. docpull-6.2.0/src/docpull/contracts.py +505 -0
  16. docpull-6.2.0/src/docpull/contracts_cli.py +40 -0
  17. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/special_cases.py +148 -8
  18. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/trafilatura_extractor.py +1 -1
  19. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/core/fetcher.py +12 -0
  20. docpull-6.2.0/src/docpull/document_parse.py +1219 -0
  21. docpull-6.2.0/src/docpull/document_worker.py +137 -0
  22. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/eval_grade.py +5 -4
  23. docpull-6.2.0/src/docpull/evidence.py +154 -0
  24. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/mcp/server.py +430 -4
  25. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/mcp/tools.py +17 -3
  26. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/models/config.py +22 -0
  27. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pack_tools.py +340 -12
  28. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/convert.py +76 -26
  29. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/fetch.py +6 -1
  30. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/metadata.py +4 -0
  31. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/project.py +63 -1
  32. docpull-6.2.0/src/docpull/schemas/artifact-manifest.v1.schema.json +99 -0
  33. docpull-6.2.0/src/docpull/schemas/basis.v2.schema.json +86 -0
  34. docpull-6.2.0/src/docpull/schemas/change-event.v1.schema.json +243 -0
  35. docpull-6.2.0/src/docpull/schemas/citation-map.v1.schema.json +30 -0
  36. docpull-6.2.0/src/docpull/schemas/document.v3.schema.json +275 -0
  37. docpull-6.2.0/src/docpull/schemas/intelligence-bundle.v1.schema.json +428 -0
  38. docpull-6.2.0/src/docpull/schemas/pack.v3.schema.json +44 -0
  39. docpull-6.2.0/src/docpull/schemas/provenance.v1.schema.json +32 -0
  40. docpull-6.2.0/src/docpull/schemas/rights.v1.schema.json +34 -0
  41. docpull-6.2.0/src/docpull/schemas/run-identity.v1.schema.json +121 -0
  42. docpull-6.2.0/src/docpull/schemas/workflow-request.v1.schema.json +95 -0
  43. docpull-6.2.0/src/docpull/schemas/workflow-result.v1.schema.json +381 -0
  44. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/security/download_policy.py +24 -3
  45. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/surface.py +44 -21
  46. docpull-6.2.0/src/docpull/workflows.py +262 -0
  47. {docpull-6.0.1 → docpull-6.2.0/src/docpull.egg-info}/PKG-INFO +58 -8
  48. {docpull-6.0.1 → docpull-6.2.0}/src/docpull.egg-info/SOURCES.txt +23 -1
  49. {docpull-6.0.1 → docpull-6.2.0}/src/docpull.egg-info/requires.txt +10 -3
  50. {docpull-6.0.1 → docpull-6.2.0}/tests/test_ci_policy.py +46 -2
  51. {docpull-6.0.1 → docpull-6.2.0}/tests/test_cli.py +12 -0
  52. {docpull-6.0.1 → docpull-6.2.0}/tests/test_context_packs.py +5 -2
  53. {docpull-6.0.1 → docpull-6.2.0}/tests/test_convert_step_new.py +75 -0
  54. docpull-6.2.0/tests/test_document_parse.py +634 -0
  55. {docpull-6.0.1 → docpull-6.2.0}/tests/test_mcp_server.py +9 -0
  56. {docpull-6.0.1 → docpull-6.2.0}/tests/test_release_a_plus_check_script.py +18 -0
  57. docpull-6.2.0/tests/test_reproducible_release_build.py +179 -0
  58. {docpull-6.0.1 → docpull-6.2.0}/tests/test_security_hardening.py +40 -0
  59. {docpull-6.0.1 → docpull-6.2.0}/tests/test_special_cases.py +142 -0
  60. {docpull-6.0.1 → docpull-6.2.0}/tests/test_surface_contract.py +3 -0
  61. docpull-6.2.0/tests/test_workflow_contracts.py +249 -0
  62. docpull-6.0.1/src/docpull/document_parse.py +0 -643
  63. docpull-6.0.1/tests/test_document_parse.py +0 -133
  64. {docpull-6.0.1 → docpull-6.2.0}/LICENSE +0 -0
  65. {docpull-6.0.1 → docpull-6.2.0}/setup.cfg +0 -0
  66. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/__main__.py +0 -0
  67. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/accounting.py +0 -0
  68. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/agent_publish.py +0 -0
  69. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/auth_cli.py +0 -0
  70. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/basis.py +0 -0
  71. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/cache/__init__.py +0 -0
  72. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/cache/frontier.py +0 -0
  73. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/cache/manager.py +0 -0
  74. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/cache/streaming_dedup.py +0 -0
  75. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_aliases.py +0 -0
  76. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_ci.py +0 -0
  77. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/brand.py +0 -0
  78. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/cli.py +0 -0
  79. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/dataset.py +0 -0
  80. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/feed.py +0 -0
  81. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/openapi.py +0 -0
  82. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/package.py +0 -0
  83. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/paper.py +0 -0
  84. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/repo.py +0 -0
  85. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/schema_extract.py +0 -0
  86. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/search.py +0 -0
  87. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/standards.py +0 -0
  88. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/styleguide.py +0 -0
  89. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/transcript.py +0 -0
  90. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/typed.py +0 -0
  91. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/typed_models.py +0 -0
  92. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/context_packs/wiki.py +0 -0
  93. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/__init__.py +0 -0
  94. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/article_cleanup.py +0 -0
  95. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/chunking.py +0 -0
  96. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/ensemble.py +0 -0
  97. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/extractor.py +0 -0
  98. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/filings.py +0 -0
  99. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/markdown.py +0 -0
  100. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/conversion/protocols.py +0 -0
  101. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/core/__init__.py +0 -0
  102. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/__init__.py +0 -0
  103. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/_fetch.py +0 -0
  104. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/composite.py +0 -0
  105. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/contracts.py +0 -0
  106. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/crawler.py +0 -0
  107. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/filters.py +0 -0
  108. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
  109. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
  110. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
  111. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/link_extractors/static.py +0 -0
  112. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/protocols.py +0 -0
  113. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery/sitemap.py +0 -0
  114. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/discovery_cli.py +0 -0
  115. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/doctor.py +0 -0
  116. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/evidence_pack.py +0 -0
  117. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/exports.py +0 -0
  118. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/fixtures/__init__.py +0 -0
  119. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/fixtures/parallel-search-extract.json +0 -0
  120. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/free_core.py +0 -0
  121. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/free_core_smoke.py +0 -0
  122. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/graph.py +0 -0
  123. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/hosted.py +0 -0
  124. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/http/__init__.py +0 -0
  125. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/http/client.py +0 -0
  126. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/http/protocols.py +0 -0
  127. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/http/rate_limiter.py +0 -0
  128. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/judge.py +0 -0
  129. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/local_workflows.py +0 -0
  130. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/mcp/__init__.py +0 -0
  131. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/mcp/sources.py +0 -0
  132. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/metadata_extractor.py +0 -0
  133. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/models/__init__.py +0 -0
  134. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/models/document.py +0 -0
  135. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/models/events.py +0 -0
  136. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/models/profiles.py +0 -0
  137. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/models/run.py +0 -0
  138. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/monitor.py +0 -0
  139. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/output_contract.py +0 -0
  140. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pack_reader.py +0 -0
  141. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/parallel_workflows.py +0 -0
  142. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/parity.py +0 -0
  143. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/parity_cli.py +0 -0
  144. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/passk.py +0 -0
  145. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/__init__.py +0 -0
  146. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/base.py +0 -0
  147. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/manifest.py +0 -0
  148. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/__init__.py +0 -0
  149. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/chunk.py +0 -0
  150. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/dedup.py +0 -0
  151. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/render.py +0 -0
  152. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/save.py +0 -0
  153. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/save_json.py +0 -0
  154. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/save_ndjson.py +0 -0
  155. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/save_okf.py +0 -0
  156. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
  157. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/pipeline/steps/validate.py +0 -0
  158. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/policy.py +0 -0
  159. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/policy_cli.py +0 -0
  160. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/provider_adapters.py +0 -0
  161. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/provider_capabilities.py +0 -0
  162. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/provider_cli.py +0 -0
  163. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/provider_keys.py +0 -0
  164. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/provider_probes.py +0 -0
  165. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/py.typed +0 -0
  166. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/redaction.py +0 -0
  167. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/rendering.py +0 -0
  168. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/scraper.py +0 -0
  169. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/security/__init__.py +0 -0
  170. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/security/robots.py +0 -0
  171. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/security/url_validator.py +0 -0
  172. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/server.py +0 -0
  173. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/share.py +0 -0
  174. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/skill_export.py +0 -0
  175. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/source_scoring.py +0 -0
  176. {docpull-6.0.1 → docpull-6.2.0}/src/docpull/time_utils.py +0 -0
  177. {docpull-6.0.1 → docpull-6.2.0}/src/docpull.egg-info/dependency_links.txt +0 -0
  178. {docpull-6.0.1 → docpull-6.2.0}/src/docpull.egg-info/entry_points.txt +0 -0
  179. {docpull-6.0.1 → docpull-6.2.0}/src/docpull.egg-info/top_level.txt +0 -0
  180. {docpull-6.0.1 → docpull-6.2.0}/tests/test_accounting.py +0 -0
  181. {docpull-6.0.1 → docpull-6.2.0}/tests/test_auth_cli.py +0 -0
  182. {docpull-6.0.1 → docpull-6.2.0}/tests/test_basis.py +0 -0
  183. {docpull-6.0.1 → docpull-6.2.0}/tests/test_benchmark.py +0 -0
  184. {docpull-6.0.1 → docpull-6.2.0}/tests/test_cache_conditional_get.py +0 -0
  185. {docpull-6.0.1 → docpull-6.2.0}/tests/test_chunking.py +0 -0
  186. {docpull-6.0.1 → docpull-6.2.0}/tests/test_claim_audit_script.py +0 -0
  187. {docpull-6.0.1 → docpull-6.2.0}/tests/test_context_ci.py +0 -0
  188. {docpull-6.0.1 → docpull-6.2.0}/tests/test_conversion.py +0 -0
  189. {docpull-6.0.1 → docpull-6.2.0}/tests/test_discovery.py +0 -0
  190. {docpull-6.0.1 → docpull-6.2.0}/tests/test_discovery_contracts.py +0 -0
  191. {docpull-6.0.1 → docpull-6.2.0}/tests/test_doctor.py +0 -0
  192. {docpull-6.0.1 → docpull-6.2.0}/tests/test_document_record.py +0 -0
  193. {docpull-6.0.1 → docpull-6.2.0}/tests/test_evidence_pack.py +0 -0
  194. {docpull-6.0.1 → docpull-6.2.0}/tests/test_exports.py +0 -0
  195. {docpull-6.0.1 → docpull-6.2.0}/tests/test_extractor_ensemble.py +0 -0
  196. {docpull-6.0.1 → docpull-6.2.0}/tests/test_feed_pack.py +0 -0
  197. {docpull-6.0.1 → docpull-6.2.0}/tests/test_free_core.py +0 -0
  198. {docpull-6.0.1 → docpull-6.2.0}/tests/test_frontier_resume.py +0 -0
  199. {docpull-6.0.1 → docpull-6.2.0}/tests/test_graph.py +0 -0
  200. {docpull-6.0.1 → docpull-6.2.0}/tests/test_hosted.py +0 -0
  201. {docpull-6.0.1 → docpull-6.2.0}/tests/test_integration.py +0 -0
  202. {docpull-6.0.1 → docpull-6.2.0}/tests/test_judge.py +0 -0
  203. {docpull-6.0.1 → docpull-6.2.0}/tests/test_link_extractors.py +0 -0
  204. {docpull-6.0.1 → docpull-6.2.0}/tests/test_live_web_smoke.py +0 -0
  205. {docpull-6.0.1 → docpull-6.2.0}/tests/test_local_workflows.py +0 -0
  206. {docpull-6.0.1 → docpull-6.2.0}/tests/test_mcp_tools.py +0 -0
  207. {docpull-6.0.1 → docpull-6.2.0}/tests/test_metadata_extractor.py +0 -0
  208. {docpull-6.0.1 → docpull-6.2.0}/tests/test_monitor.py +0 -0
  209. {docpull-6.0.1 → docpull-6.2.0}/tests/test_naming.py +0 -0
  210. {docpull-6.0.1 → docpull-6.2.0}/tests/test_openapi_pack.py +0 -0
  211. {docpull-6.0.1 → docpull-6.2.0}/tests/test_optional_extras_installed.py +0 -0
  212. {docpull-6.0.1 → docpull-6.2.0}/tests/test_output_contract.py +0 -0
  213. {docpull-6.0.1 → docpull-6.2.0}/tests/test_outputs_e2e.py +0 -0
  214. {docpull-6.0.1 → docpull-6.2.0}/tests/test_pack_server.py +0 -0
  215. {docpull-6.0.1 → docpull-6.2.0}/tests/test_pack_tools.py +0 -0
  216. {docpull-6.0.1 → docpull-6.2.0}/tests/test_parallel_workflows.py +0 -0
  217. {docpull-6.0.1 → docpull-6.2.0}/tests/test_parity_workflows.py +0 -0
  218. {docpull-6.0.1 → docpull-6.2.0}/tests/test_passk.py +0 -0
  219. {docpull-6.0.1 → docpull-6.2.0}/tests/test_pipeline.py +0 -0
  220. {docpull-6.0.1 → docpull-6.2.0}/tests/test_policy.py +0 -0
  221. {docpull-6.0.1 → docpull-6.2.0}/tests/test_policy_cli.py +0 -0
  222. {docpull-6.0.1 → docpull-6.2.0}/tests/test_project.py +0 -0
  223. {docpull-6.0.1 → docpull-6.2.0}/tests/test_provider_adapters.py +0 -0
  224. {docpull-6.0.1 → docpull-6.2.0}/tests/test_provider_cli.py +0 -0
  225. {docpull-6.0.1 → docpull-6.2.0}/tests/test_provider_keys.py +0 -0
  226. {docpull-6.0.1 → docpull-6.2.0}/tests/test_provider_probes.py +0 -0
  227. {docpull-6.0.1 → docpull-6.2.0}/tests/test_public_feature_smoke.py +0 -0
  228. {docpull-6.0.1 → docpull-6.2.0}/tests/test_rate_limiter.py +0 -0
  229. {docpull-6.0.1 → docpull-6.2.0}/tests/test_real_feature_smoke_script.py +0 -0
  230. {docpull-6.0.1 → docpull-6.2.0}/tests/test_real_site_regressions.py +0 -0
  231. {docpull-6.0.1 → docpull-6.2.0}/tests/test_rendering.py +0 -0
  232. {docpull-6.0.1 → docpull-6.2.0}/tests/test_save_ndjson.py +0 -0
  233. {docpull-6.0.1 → docpull-6.2.0}/tests/test_save_sqlite.py +0 -0
  234. {docpull-6.0.1 → docpull-6.2.0}/tests/test_share.py +0 -0
  235. {docpull-6.0.1 → docpull-6.2.0}/tests/test_source_scoring.py +0 -0
  236. {docpull-6.0.1 → docpull-6.2.0}/tests/test_time_utils.py +0 -0
  237. {docpull-6.0.1 → docpull-6.2.0}/tests/test_trafilatura_extractor.py +0 -0
  238. {docpull-6.0.1 → docpull-6.2.0}/tests/test_typed_knowledge_packs.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docpull
3
- Version: 6.0.1
3
+ Version: 6.2.0
4
4
  Summary: Declare, sync, diff, and lock context dependencies for AI agents
5
5
  Author-email: Zachary Roth <support@raintree.technology>
6
6
  Maintainer-email: Raintree Technology <support@raintree.technology>
7
7
  License-Expression: MIT
8
- Project-URL: Homepage, https://docpull.raintree.technology
9
- Project-URL: Documentation, https://docpull.raintree.technology
8
+ Project-URL: Homepage, https://github.com/raintree-technology/docpull
9
+ Project-URL: Documentation, https://github.com/raintree-technology/docpull#readme
10
10
  Project-URL: Repository, https://github.com/raintree-technology/docpull
11
11
  Project-URL: Source Code, https://github.com/raintree-technology/docpull
12
12
  Project-URL: Bug Tracker, https://github.com/raintree-technology/docpull/issues
@@ -71,11 +71,16 @@ Requires-Dist: uvicorn>=0.30.0; extra == "serve"
71
71
  Provides-Extra: parquet
72
72
  Requires-Dist: pyarrow>=16.0.0; extra == "parquet"
73
73
  Provides-Extra: markitdown
74
- Requires-Dist: markitdown>=0.1.0; extra == "markitdown"
74
+ Requires-Dist: pypdf>=6.14.2; extra == "markitdown"
75
+ Requires-Dist: markitdown[docx,pdf]>=0.1.0; extra == "markitdown"
75
76
  Provides-Extra: unstructured
77
+ Requires-Dist: pypdf>=6.14.2; extra == "unstructured"
76
78
  Requires-Dist: unstructured[all-docs]>=0.16.0; extra == "unstructured"
79
+ Provides-Extra: pdf
80
+ Requires-Dist: pypdf>=6.14.2; extra == "pdf"
77
81
  Provides-Extra: parse
78
- Requires-Dist: markitdown>=0.1.0; extra == "parse"
82
+ Requires-Dist: pypdf>=6.14.2; extra == "parse"
83
+ Requires-Dist: markitdown[docx,pdf]>=0.1.0; extra == "parse"
79
84
  Requires-Dist: unstructured[all-docs]>=0.16.0; extra == "parse"
80
85
  Provides-Extra: presidio
81
86
  Requires-Dist: presidio-analyzer>=2.2.0; extra == "presidio"
@@ -96,7 +101,8 @@ Requires-Dist: python-multipart>=0.0.27; extra == "all"
96
101
  Requires-Dist: starlette>=1.3.1; extra == "all"
97
102
  Requires-Dist: uvicorn>=0.30.0; extra == "all"
98
103
  Requires-Dist: pyarrow>=16.0.0; extra == "all"
99
- Requires-Dist: markitdown>=0.1.0; extra == "all"
104
+ Requires-Dist: pypdf>=6.14.2; extra == "all"
105
+ Requires-Dist: markitdown[docx,pdf]>=0.1.0; extra == "all"
100
106
  Requires-Dist: unstructured[all-docs]>=0.16.0; extra == "all"
101
107
  Requires-Dist: presidio-analyzer>=2.2.0; extra == "all"
102
108
  Requires-Dist: e2b>=2.0.0; extra == "all"
@@ -133,6 +139,11 @@ web sources an agent depends on, sync them into cited context packs, diff what
133
139
  changed, and export reproducible context for Cursor, Claude, OpenAI,
134
140
  LlamaIndex, LangChain, MCP clients, and RAG pipelines.
135
141
 
142
+ Architecturally, DocPull is the evidence and acquisition engine: it acquires,
143
+ versions, cites, hashes, and replays evidence. Downstream products own
144
+ scheduling, review, approved claims, legal conclusions, and notifications. See
145
+ the [architecture decision](docs/adr/0001-evidence-acquisition-engine.md).
146
+
136
147
  The core workflow is a `docpull.yaml` plus a `.docpull/context.lock.json`,
137
148
  similar in spirit to code dependency manifests and lockfiles:
138
149
 
@@ -181,6 +192,12 @@ pages such as blogs, API references, OpenAPI specs, changelogs, vendor pages,
181
192
  product pages, filings, docs sites, and other pages where the useful content is
182
193
  available in HTML or embedded page data.
183
194
 
195
+ Direct fetches also recognize standards-style plain text and common textual
196
+ formats from their response media type, including extensionless URLs. Remote
197
+ documents remain denied by default; explicitly opt into local PDF parsing with
198
+ `docpull URL --remote-documents pdf` after installing `docpull[markitdown]` or
199
+ `docpull[parse]`. This does not enable a browser, cloud parser, or paid route.
200
+
184
201
  DocPull is browser-free by default. JS-only pages are skipped with a clear
185
202
  reason unless you explicitly opt into a local renderer. See
186
203
  [Web Source Boundary](docs/scraping-boundary.md) and
@@ -282,6 +299,11 @@ docpull standards-pack rfc:9110 -o packs/standard
282
299
  docpull dataset-pack ./metrics.csv -o packs/dataset
283
300
  docpull transcript-pack ./meeting.vtt -o packs/transcript
284
301
  docpull wiki-pack wiki:Web_scraping -o packs/wiki
302
+ docpull brand-pack example.com -o packs/brand
303
+ docpull product-pack https://example.com/pricing -o packs/product
304
+ docpull styleguide-pack example.com -o packs/styleguide
305
+ docpull image-pack example.com -o packs/visuals
306
+ docpull policy-pack example.com -o packs/policies
285
307
  docpull pack prepare packs/docs --eval-grade
286
308
  docpull pack validate packs/docs --level eval
287
309
  docpull export packs/docs --format openai-vector-jsonl -o exports/openai.jsonl
@@ -556,6 +578,11 @@ special handling for common web, documentation, and API surfaces.
556
578
  | Local datasets | `docpull dataset-pack` emits bounded schema, exact row counts where streamable, column, null-count, and sample summaries |
557
579
  | Transcripts | `docpull transcript-pack` emits timestamped segment records from VTT, SRT, text, JSON, or direct transcript URLs |
558
580
  | Wikimedia / Wikipedia | `docpull wiki-pack` emits MediaWiki REST page metadata, license/revision metadata, and section-level records |
581
+ | Brand evidence | `docpull brand-pack` emits cited identity, firmographic, social, logo, and color observations |
582
+ | Product and pricing | `docpull product-pack` emits plans, currencies, intervals, trials, feature gates, and page-text provenance |
583
+ | Styleguide | `docpull styleguide-pack` emits cited CSS variables, colors, fonts, spacing, and component samples |
584
+ | Visual assets | `docpull image-pack` emits bounded image candidates and optional validated local assets |
585
+ | Policy documents | `docpull policy-pack` discovers policy/security pages and emits effective dates, stable clauses, and textual change candidates without legal conclusions |
559
586
  | Docusaurus / Sphinx / MkDocs | Extracts static article or document regions |
560
587
  | VitePress / VuePress / Astro Starlight | Extracts static content regions |
561
588
  | GitBook / ReadMe.io | Extracts available article or content regions |
@@ -592,10 +619,32 @@ MarkItDown or Unstructured parsers for complex office/PDF files when installed.
592
619
  Install `docpull[markitdown]`, `docpull[unstructured]`, or `docpull[parse]`
593
620
  for those backends.
594
621
 
622
+ An HTTPS PDF can enter the ordinary fetch contract only with explicit consent:
623
+
624
+ ```bash
625
+ docpull https://example.com/handbook.pdf --remote-documents pdf \
626
+ --remote-document-backend auto -o ./packs/handbook
627
+ ```
628
+
629
+ The response must identify as PDF and contain a PDF signature. DocPull parses
630
+ it locally through a mode-`0600` temporary file, records the source hash and
631
+ parser provenance, and removes the temporary source. Other remote download
632
+ types remain blocked.
633
+
595
634
  Every file-backed run writes `corpus.manifest.json` with stable document IDs,
596
635
  chunk IDs, hashes, output paths, and chunk counts. See
597
636
  [Corpus Manifest](docs/corpus-manifest.md).
598
637
 
638
+ Evidence-pack workflows also write `workflow.request.json`,
639
+ `workflow.result.json`, and `artifact.manifest.json`. Export their Draft 2020-12
640
+ schemas with `docpull contracts export -o schemas/`. The complete inventory and
641
+ compatibility rules are in [Public Contracts](docs/contracts.md).
642
+
643
+ For tracker imports, run `docpull pack intelligence-bundle PACK`. The canonical
644
+ output is `intelligence.bundle.v1.json`; `company_brain.bundle.json` remains a
645
+ compatibility alias. See the
646
+ [competitor-tracker integration contract](docs/competitor-tracker-integration.md).
647
+
599
648
  ## Profiles
600
649
 
601
650
  ```bash
@@ -712,7 +761,8 @@ part of the package release contract.
712
761
  `docpull parse`, `docpull openapi-pack`, `docpull feed-pack`,
713
762
  `docpull paper-pack`, `docpull repo-pack`, `docpull package-pack`,
714
763
  `docpull standards-pack`, `docpull dataset-pack`, `docpull transcript-pack`,
715
- `docpull wiki-pack`,
764
+ `docpull wiki-pack`, `docpull brand-pack`, `docpull product-pack`,
765
+ `docpull styleguide-pack`, `docpull image-pack`, `docpull policy-pack`,
716
766
  `docpull pack validate`,
717
767
  `docpull pack audit`, `docpull export`,
718
768
  `docpull serve`, `docpull share`, `docpull render`, `docpull auth check`,
@@ -754,7 +804,7 @@ docpull URL --preview-urls
754
804
 
755
805
  ## Links
756
806
 
757
- - [Website](https://docpull.raintree.technology)
807
+ - [Documentation](https://github.com/raintree-technology/docpull#readme)
758
808
  - [PyPI](https://pypi.org/project/docpull/)
759
809
  - [GitHub](https://github.com/raintree-technology/docpull)
760
810
  - [Metrics](METRICS.md)
@@ -15,6 +15,11 @@ web sources an agent depends on, sync them into cited context packs, diff what
15
15
  changed, and export reproducible context for Cursor, Claude, OpenAI,
16
16
  LlamaIndex, LangChain, MCP clients, and RAG pipelines.
17
17
 
18
+ Architecturally, DocPull is the evidence and acquisition engine: it acquires,
19
+ versions, cites, hashes, and replays evidence. Downstream products own
20
+ scheduling, review, approved claims, legal conclusions, and notifications. See
21
+ the [architecture decision](docs/adr/0001-evidence-acquisition-engine.md).
22
+
18
23
  The core workflow is a `docpull.yaml` plus a `.docpull/context.lock.json`,
19
24
  similar in spirit to code dependency manifests and lockfiles:
20
25
 
@@ -63,6 +68,12 @@ pages such as blogs, API references, OpenAPI specs, changelogs, vendor pages,
63
68
  product pages, filings, docs sites, and other pages where the useful content is
64
69
  available in HTML or embedded page data.
65
70
 
71
+ Direct fetches also recognize standards-style plain text and common textual
72
+ formats from their response media type, including extensionless URLs. Remote
73
+ documents remain denied by default; explicitly opt into local PDF parsing with
74
+ `docpull URL --remote-documents pdf` after installing `docpull[markitdown]` or
75
+ `docpull[parse]`. This does not enable a browser, cloud parser, or paid route.
76
+
66
77
  DocPull is browser-free by default. JS-only pages are skipped with a clear
67
78
  reason unless you explicitly opt into a local renderer. See
68
79
  [Web Source Boundary](docs/scraping-boundary.md) and
@@ -164,6 +175,11 @@ docpull standards-pack rfc:9110 -o packs/standard
164
175
  docpull dataset-pack ./metrics.csv -o packs/dataset
165
176
  docpull transcript-pack ./meeting.vtt -o packs/transcript
166
177
  docpull wiki-pack wiki:Web_scraping -o packs/wiki
178
+ docpull brand-pack example.com -o packs/brand
179
+ docpull product-pack https://example.com/pricing -o packs/product
180
+ docpull styleguide-pack example.com -o packs/styleguide
181
+ docpull image-pack example.com -o packs/visuals
182
+ docpull policy-pack example.com -o packs/policies
167
183
  docpull pack prepare packs/docs --eval-grade
168
184
  docpull pack validate packs/docs --level eval
169
185
  docpull export packs/docs --format openai-vector-jsonl -o exports/openai.jsonl
@@ -438,6 +454,11 @@ special handling for common web, documentation, and API surfaces.
438
454
  | Local datasets | `docpull dataset-pack` emits bounded schema, exact row counts where streamable, column, null-count, and sample summaries |
439
455
  | Transcripts | `docpull transcript-pack` emits timestamped segment records from VTT, SRT, text, JSON, or direct transcript URLs |
440
456
  | Wikimedia / Wikipedia | `docpull wiki-pack` emits MediaWiki REST page metadata, license/revision metadata, and section-level records |
457
+ | Brand evidence | `docpull brand-pack` emits cited identity, firmographic, social, logo, and color observations |
458
+ | Product and pricing | `docpull product-pack` emits plans, currencies, intervals, trials, feature gates, and page-text provenance |
459
+ | Styleguide | `docpull styleguide-pack` emits cited CSS variables, colors, fonts, spacing, and component samples |
460
+ | Visual assets | `docpull image-pack` emits bounded image candidates and optional validated local assets |
461
+ | Policy documents | `docpull policy-pack` discovers policy/security pages and emits effective dates, stable clauses, and textual change candidates without legal conclusions |
441
462
  | Docusaurus / Sphinx / MkDocs | Extracts static article or document regions |
442
463
  | VitePress / VuePress / Astro Starlight | Extracts static content regions |
443
464
  | GitBook / ReadMe.io | Extracts available article or content regions |
@@ -474,10 +495,32 @@ MarkItDown or Unstructured parsers for complex office/PDF files when installed.
474
495
  Install `docpull[markitdown]`, `docpull[unstructured]`, or `docpull[parse]`
475
496
  for those backends.
476
497
 
498
+ An HTTPS PDF can enter the ordinary fetch contract only with explicit consent:
499
+
500
+ ```bash
501
+ docpull https://example.com/handbook.pdf --remote-documents pdf \
502
+ --remote-document-backend auto -o ./packs/handbook
503
+ ```
504
+
505
+ The response must identify as PDF and contain a PDF signature. DocPull parses
506
+ it locally through a mode-`0600` temporary file, records the source hash and
507
+ parser provenance, and removes the temporary source. Other remote download
508
+ types remain blocked.
509
+
477
510
  Every file-backed run writes `corpus.manifest.json` with stable document IDs,
478
511
  chunk IDs, hashes, output paths, and chunk counts. See
479
512
  [Corpus Manifest](docs/corpus-manifest.md).
480
513
 
514
+ Evidence-pack workflows also write `workflow.request.json`,
515
+ `workflow.result.json`, and `artifact.manifest.json`. Export their Draft 2020-12
516
+ schemas with `docpull contracts export -o schemas/`. The complete inventory and
517
+ compatibility rules are in [Public Contracts](docs/contracts.md).
518
+
519
+ For tracker imports, run `docpull pack intelligence-bundle PACK`. The canonical
520
+ output is `intelligence.bundle.v1.json`; `company_brain.bundle.json` remains a
521
+ compatibility alias. See the
522
+ [competitor-tracker integration contract](docs/competitor-tracker-integration.md).
523
+
481
524
  ## Profiles
482
525
 
483
526
  ```bash
@@ -594,7 +637,8 @@ part of the package release contract.
594
637
  `docpull parse`, `docpull openapi-pack`, `docpull feed-pack`,
595
638
  `docpull paper-pack`, `docpull repo-pack`, `docpull package-pack`,
596
639
  `docpull standards-pack`, `docpull dataset-pack`, `docpull transcript-pack`,
597
- `docpull wiki-pack`,
640
+ `docpull wiki-pack`, `docpull brand-pack`, `docpull product-pack`,
641
+ `docpull styleguide-pack`, `docpull image-pack`, `docpull policy-pack`,
598
642
  `docpull pack validate`,
599
643
  `docpull pack audit`, `docpull export`,
600
644
  `docpull serve`, `docpull share`, `docpull render`, `docpull auth check`,
@@ -636,7 +680,7 @@ docpull URL --preview-urls
636
680
 
637
681
  ## Links
638
682
 
639
- - [Website](https://docpull.raintree.technology)
683
+ - [Documentation](https://github.com/raintree-technology/docpull#readme)
640
684
  - [PyPI](https://pypi.org/project/docpull/)
641
685
  - [GitHub](https://github.com/raintree-technology/docpull)
642
686
  - [Metrics](METRICS.md)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "docpull"
7
- version = "6.0.1"
7
+ version = "6.2.0"
8
8
  dynamic = []
9
9
  description = "Declare, sync, diff, and lock context dependencies for AI agents"
10
10
  readme = {file = "README.md", content-type = "text/markdown"}
@@ -123,13 +123,19 @@ parquet = [
123
123
  "pyarrow>=16.0.0",
124
124
  ]
125
125
  markitdown = [
126
- "markitdown>=0.1.0",
126
+ "pypdf>=6.14.2",
127
+ "markitdown[docx,pdf]>=0.1.0",
127
128
  ]
128
129
  unstructured = [
130
+ "pypdf>=6.14.2",
129
131
  "unstructured[all-docs]>=0.16.0",
130
132
  ]
133
+ pdf = [
134
+ "pypdf>=6.14.2",
135
+ ]
131
136
  parse = [
132
- "markitdown>=0.1.0",
137
+ "pypdf>=6.14.2",
138
+ "markitdown[docx,pdf]>=0.1.0",
133
139
  "unstructured[all-docs]>=0.16.0",
134
140
  ]
135
141
  presidio = [
@@ -154,7 +160,8 @@ all = [
154
160
  "starlette>=1.3.1",
155
161
  "uvicorn>=0.30.0",
156
162
  "pyarrow>=16.0.0",
157
- "markitdown>=0.1.0",
163
+ "pypdf>=6.14.2",
164
+ "markitdown[docx,pdf]>=0.1.0",
158
165
  "unstructured[all-docs]>=0.16.0",
159
166
  "presidio-analyzer>=2.2.0",
160
167
  "e2b>=2.0.0",
@@ -179,8 +186,8 @@ dev = [
179
186
  docpull = "docpull.cli:main"
180
187
 
181
188
  [project.urls]
182
- Homepage = "https://docpull.raintree.technology"
183
- Documentation = "https://docpull.raintree.technology"
189
+ Homepage = "https://github.com/raintree-technology/docpull"
190
+ Documentation = "https://github.com/raintree-technology/docpull#readme"
184
191
  Repository = "https://github.com/raintree-technology/docpull"
185
192
  "Source Code" = "https://github.com/raintree-technology/docpull"
186
193
  "Bug Tracker" = "https://github.com/raintree-technology/docpull/issues"
@@ -195,7 +202,7 @@ where = ["src"]
195
202
  include = ["docpull*"]
196
203
 
197
204
  [tool.setuptools.package-data]
198
- docpull = ["py.typed", "fixtures/*.json"]
205
+ docpull = ["py.typed", "fixtures/*.json", "schemas/*.schema.json"]
199
206
 
200
207
  [tool.ruff]
201
208
  line-length = 110
@@ -14,7 +14,7 @@ Usage:
14
14
  print(event)
15
15
  """
16
16
 
17
- __version__ = "6.0.1"
17
+ __version__ = "6.2.0"
18
18
 
19
19
  from .cache import CacheManager, StreamingDeduplicator
20
20
  from .context_ci import CIThresholds, ContextCIError, run_context_ci
@@ -26,15 +26,32 @@ from .context_packs import (
26
26
  async_build_standards_pack,
27
27
  async_build_transcript_pack,
28
28
  async_build_wiki_pack,
29
+ build_brand_pack,
29
30
  build_dataset_pack,
30
31
  build_feed_pack,
32
+ build_image_pack,
31
33
  build_openapi_pack,
32
34
  build_package_pack,
33
35
  build_paper_pack,
36
+ build_policy_pack,
37
+ build_product_pack,
34
38
  build_repo_pack,
35
39
  build_standards_pack,
40
+ build_styleguide_pack,
36
41
  build_transcript_pack,
37
42
  build_wiki_pack,
43
+ capture_screenshot_pack,
44
+ )
45
+ from .contracts import (
46
+ ArtifactManifest,
47
+ ChangeEvent,
48
+ EvidenceSpan,
49
+ IntelligenceBundle,
50
+ SourceAuthority,
51
+ WorkflowRequest,
52
+ WorkflowResult,
53
+ bundled_schema_path,
54
+ write_contract_schemas,
38
55
  )
39
56
  from .conversion.chunking import Chunk, TokenCounter, chunk_markdown
40
57
  from .core.fetcher import Fetcher, fetch_blocking, fetch_one
@@ -69,6 +86,8 @@ from .output_contract import validate_pack_contract
69
86
  from .pack_reader import LocalPack, PackReadError, PackSource, load_pack
70
87
  from .pack_tools import (
71
88
  build_citation_map,
89
+ build_company_brain_bundle,
90
+ build_intelligence_bundle,
72
91
  build_research_brief,
73
92
  diff_packs,
74
93
  extract_pack_entities,
@@ -100,6 +119,7 @@ from .rendering import (
100
119
  from .server import PackASGIApp, PackServerError, create_pack_app
101
120
  from .share import ReportHTTPServer, ShareError, create_report_server, render_report_document
102
121
  from .surface import PUBLIC_SDK_EXPORTS
122
+ from .workflows import async_run_workflow, create_workflow_request, run_workflow
103
123
 
104
124
  __all__ = [
105
125
  "__version__",
@@ -110,20 +130,38 @@ __all__ = [
110
130
  "async_build_standards_pack",
111
131
  "async_build_transcript_pack",
112
132
  "async_build_wiki_pack",
133
+ "async_run_workflow",
134
+ "ArtifactManifest",
135
+ "ChangeEvent",
136
+ "EvidenceSpan",
137
+ "IntelligenceBundle",
138
+ "SourceAuthority",
139
+ "WorkflowRequest",
140
+ "WorkflowResult",
141
+ "bundled_schema_path",
142
+ "write_contract_schemas",
143
+ "create_workflow_request",
144
+ "run_workflow",
113
145
  "Fetcher",
114
146
  "fetch_blocking",
115
147
  "fetch_one",
116
148
  "refresh_pack",
117
149
  "audit_pack",
118
150
  "build_dataset_pack",
151
+ "build_brand_pack",
119
152
  "build_feed_pack",
120
153
  "build_openapi_pack",
121
154
  "build_package_pack",
122
155
  "build_paper_pack",
156
+ "build_policy_pack",
157
+ "build_product_pack",
123
158
  "build_repo_pack",
124
159
  "build_standards_pack",
160
+ "build_styleguide_pack",
125
161
  "build_transcript_pack",
126
162
  "build_wiki_pack",
163
+ "build_image_pack",
164
+ "capture_screenshot_pack",
127
165
  "parse_documents",
128
166
  "parse_one_document",
129
167
  "DocumentParseError",
@@ -132,6 +170,8 @@ __all__ = [
132
170
  "score_pack_sources",
133
171
  "diff_packs",
134
172
  "build_citation_map",
173
+ "build_intelligence_bundle",
174
+ "build_company_brain_bundle",
135
175
  "extract_pack_entities",
136
176
  "search_pack",
137
177
  "build_research_brief",
@@ -200,8 +200,8 @@ TOOL_DOC_TARGETS: tuple[_BenchmarkTarget, ...] = (
200
200
  _BenchmarkTarget(
201
201
  id="docpull_docs",
202
202
  label="DocPull docs",
203
- url="https://docpull.raintree.technology",
204
- include_domains=("docpull.raintree.technology",),
203
+ url="https://github.com/raintree-technology/docpull",
204
+ include_domains=("github.com",),
205
205
  objective="Build an agent context pack for DocPull documentation",
206
206
  queries=("DocPull documentation CLI provider benchmark docs",),
207
207
  freshness_terms=("changelog", "release", "benchmark"),
@@ -0,0 +1,209 @@
1
+ """Versioned, scheduler-neutral change-event generation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import difflib
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any, Literal
9
+
10
+ from .contracts import ChangeEvent, ReplayConfiguration, canonical_sha256, stable_id
11
+ from .evidence import evidence_span
12
+
13
+ ChangeClassification = Literal["pricing", "positioning", "product", "security", "policy", "other"]
14
+
15
+ _CLASSIFICATION_TERMS: dict[ChangeClassification, tuple[str, ...]] = {
16
+ "pricing": ("price", "pricing", "plan", "$", "usd", "billing", "trial"),
17
+ "positioning": ("positioning", "mission", "leader", "best", "platform for", "built for"),
18
+ "product": ("feature", "product", "launch", "available", "integration", "api"),
19
+ "security": ("security", "encryption", "soc 2", "iso 27001", "vulnerability", "breach"),
20
+ "policy": ("terms", "privacy", "cookie", "dpa", "subprocessor", "refund", "effective date"),
21
+ }
22
+
23
+
24
+ def build_change_events(
25
+ old_records: dict[str, list[dict[str, Any]]],
26
+ new_records: dict[str, list[dict[str, Any]]],
27
+ *,
28
+ workflow: str = "pack-diff",
29
+ ) -> list[dict[str, Any]]:
30
+ """Build idempotent events from already acquired document versions."""
31
+
32
+ events: list[dict[str, Any]] = []
33
+ for url in sorted(set(old_records) | set(new_records)):
34
+ old_items = old_records.get(url, [])
35
+ new_items = new_records.get(url, [])
36
+ old_hashes = sorted(_hashes(old_items))
37
+ new_hashes = sorted(_hashes(new_items))
38
+ if old_hashes == new_hashes and _titles(old_items) == _titles(new_items):
39
+ continue
40
+ old_primary = old_items[0] if old_items else None
41
+ new_primary = new_items[0] if new_items else None
42
+ old_text = _combined_content(old_items)
43
+ new_text = _combined_content(new_items)
44
+ classifications = _classifications(old_text, new_text, url)
45
+ structural = _structural_changes(old_items, new_items)
46
+ textual = _textual_changes(old_text, new_text)
47
+ old_evidence = _record_evidence(old_primary, old_text) if old_primary else []
48
+ new_evidence = _record_evidence(new_primary, new_text) if new_primary else []
49
+ identity = {
50
+ "workflow": workflow,
51
+ "url": url,
52
+ "old_hashes": old_hashes,
53
+ "new_hashes": new_hashes,
54
+ }
55
+ idempotency_key = canonical_sha256(identity)
56
+ semantic_candidates = [
57
+ {
58
+ "classification": item,
59
+ "status": "candidate",
60
+ "confidence": _classification_confidence(item, old_text, new_text, url),
61
+ "requires_review": True,
62
+ }
63
+ for item in classifications
64
+ ]
65
+ event = ChangeEvent(
66
+ event_id=stable_id("change_event", identity),
67
+ idempotency_key=idempotency_key,
68
+ workflow=workflow,
69
+ url=url,
70
+ old_document_id=_string(old_primary, "document_id"),
71
+ new_document_id=_string(new_primary, "document_id"),
72
+ old_hash=canonical_sha256(old_hashes) if old_hashes else None,
73
+ new_hash=canonical_sha256(new_hashes) if new_hashes else None,
74
+ old_evidence=old_evidence,
75
+ new_evidence=new_evidence,
76
+ structural_changes=structural,
77
+ textual_changes=textual,
78
+ semantic_candidates=semantic_candidates,
79
+ classifications=classifications,
80
+ replay_configuration=ReplayConfiguration(
81
+ configuration={
82
+ "workflow": workflow,
83
+ "old_hashes": old_hashes,
84
+ "new_hashes": new_hashes,
85
+ }
86
+ ),
87
+ )
88
+ events.append(event.model_dump(mode="json", exclude_none=True))
89
+ return events
90
+
91
+
92
+ def write_change_events(path: Path, events: list[dict[str, Any]]) -> Path:
93
+ path.parent.mkdir(parents=True, exist_ok=True)
94
+ path.write_text(
95
+ "".join(json.dumps(event, ensure_ascii=False, sort_keys=True) + "\n" for event in events),
96
+ encoding="utf-8",
97
+ )
98
+ return path
99
+
100
+
101
+ def _record_evidence(record: dict[str, Any], content: str) -> list[Any]:
102
+ if not content:
103
+ return []
104
+ excerpt = content[: min(600, len(content))]
105
+ citation_id = str(record.get("source_citation_id") or "S0")
106
+ record_citation_id = record.get("record_citation_id")
107
+ return [
108
+ evidence_span(
109
+ url=str(record.get("url") or ""),
110
+ content=content,
111
+ exact_text=excerpt,
112
+ citation_id=citation_id,
113
+ record_citation_id=str(record_citation_id) if record_citation_id else None,
114
+ )
115
+ ]
116
+
117
+
118
+ def _structural_changes(
119
+ old_items: list[dict[str, Any]],
120
+ new_items: list[dict[str, Any]],
121
+ ) -> list[dict[str, Any]]:
122
+ changes: list[dict[str, Any]] = []
123
+ if not old_items:
124
+ changes.append({"type": "document_added", "new_record_count": len(new_items)})
125
+ elif not new_items:
126
+ changes.append({"type": "document_removed", "old_record_count": len(old_items)})
127
+ if len(old_items) != len(new_items):
128
+ changes.append(
129
+ {
130
+ "type": "record_count_changed",
131
+ "before": len(old_items),
132
+ "after": len(new_items),
133
+ }
134
+ )
135
+ if _titles(old_items) != _titles(new_items):
136
+ changes.append({"type": "title_changed", "before": _titles(old_items), "after": _titles(new_items)})
137
+ return changes
138
+
139
+
140
+ def _textual_changes(old_text: str, new_text: str) -> list[dict[str, Any]]:
141
+ old_lines = old_text.splitlines()
142
+ new_lines = new_text.splitlines()
143
+ matcher = difflib.SequenceMatcher(a=old_lines, b=new_lines, autojunk=False)
144
+ changes: list[dict[str, Any]] = []
145
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
146
+ if tag == "equal":
147
+ continue
148
+ before = "\n".join(old_lines[i1:i2])[:1000]
149
+ after = "\n".join(new_lines[j1:j2])[:1000]
150
+ changes.append(
151
+ {
152
+ "type": tag,
153
+ "old_line_start": i1,
154
+ "old_line_end": i2,
155
+ "new_line_start": j1,
156
+ "new_line_end": j2,
157
+ "before": before,
158
+ "after": after,
159
+ "before_sha256": canonical_sha256(before),
160
+ "after_sha256": canonical_sha256(after),
161
+ }
162
+ )
163
+ if len(changes) >= 100:
164
+ break
165
+ return changes
166
+
167
+
168
+ def _classifications(old_text: str, new_text: str, url: str) -> list[ChangeClassification]:
169
+ haystack = f"{url}\n{old_text}\n{new_text}".lower()
170
+ output = [
171
+ classification
172
+ for classification, terms in _CLASSIFICATION_TERMS.items()
173
+ if any(term in haystack for term in terms)
174
+ ]
175
+ return output or ["other"]
176
+
177
+
178
+ def _classification_confidence(
179
+ classification: ChangeClassification,
180
+ old_text: str,
181
+ new_text: str,
182
+ url: str,
183
+ ) -> float:
184
+ terms = _CLASSIFICATION_TERMS.get(classification, ())
185
+ haystack = f"{url}\n{old_text}\n{new_text}".lower()
186
+ hits = sum(1 for term in terms if term in haystack)
187
+ return min(0.95, 0.5 + hits * 0.1) if terms else 0.4
188
+
189
+
190
+ def _combined_content(items: list[dict[str, Any]]) -> str:
191
+ return "\n\n".join(str(item.get("content") or "") for item in items)
192
+
193
+
194
+ def _hashes(items: list[dict[str, Any]]) -> set[str]:
195
+ return {str(item.get("content_hash")) for item in items if item.get("content_hash")}
196
+
197
+
198
+ def _titles(items: list[dict[str, Any]]) -> list[str]:
199
+ return sorted({str(item.get("title") or "") for item in items})
200
+
201
+
202
+ def _string(item: dict[str, Any] | None, key: str) -> str | None:
203
+ if not item or item.get(key) is None:
204
+ return None
205
+ value = str(item[key]).strip()
206
+ return value or None
207
+
208
+
209
+ __all__ = ["build_change_events", "write_change_events"]