docpull 6.3.0__tar.gz → 6.4.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 (251) hide show
  1. {docpull-6.3.0/src/docpull.egg-info → docpull-6.4.0}/PKG-INFO +6 -2
  2. {docpull-6.3.0 → docpull-6.4.0}/README.md +5 -1
  3. {docpull-6.3.0 → docpull-6.4.0}/pyproject.toml +1 -1
  4. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/__init__.py +9 -1
  5. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/cli.py +3 -0
  6. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/__init__.py +3 -0
  7. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/brand.py +23 -9
  8. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/common.py +74 -1
  9. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/policy_pack.py +16 -7
  10. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/product.py +165 -37
  11. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/styleguide.py +14 -7
  12. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/visuals.py +6 -2
  13. docpull-6.4.0/src/docpull/context_packs/website.py +1238 -0
  14. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/workflow_cli.py +44 -0
  15. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/contracts.py +141 -0
  16. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/mcp/server.py +35 -1
  17. docpull-6.4.0/src/docpull/schemas/website-snapshot.v1.schema.json +567 -0
  18. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/surface.py +8 -0
  19. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/workflows.py +36 -1
  20. {docpull-6.3.0 → docpull-6.4.0/src/docpull.egg-info}/PKG-INFO +6 -2
  21. {docpull-6.3.0 → docpull-6.4.0}/src/docpull.egg-info/SOURCES.txt +4 -0
  22. {docpull-6.3.0 → docpull-6.4.0}/tests/test_ci_policy.py +3 -5
  23. docpull-6.4.0/tests/test_local_pack_workflows.py +151 -0
  24. {docpull-6.3.0 → docpull-6.4.0}/tests/test_release_a_plus_check_script.py +6 -14
  25. {docpull-6.3.0 → docpull-6.4.0}/tests/test_surface_contract.py +0 -32
  26. docpull-6.4.0/tests/test_website_snapshot.py +352 -0
  27. {docpull-6.3.0 → docpull-6.4.0}/LICENSE +0 -0
  28. {docpull-6.3.0 → docpull-6.4.0}/setup.cfg +0 -0
  29. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/__main__.py +0 -0
  30. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/accounting.py +0 -0
  31. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/acquisition_workflows.py +0 -0
  32. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/agent_publish.py +0 -0
  33. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/auth_cli.py +0 -0
  34. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/basis.py +0 -0
  35. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/benchmark.py +0 -0
  36. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/cache/__init__.py +0 -0
  37. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/cache/frontier.py +0 -0
  38. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/cache/manager.py +0 -0
  39. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/cache/streaming_dedup.py +0 -0
  40. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/change_events.py +0 -0
  41. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_aliases.py +0 -0
  42. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_ci.py +0 -0
  43. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/_legacy_cli.py +0 -0
  44. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/cli.py +0 -0
  45. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/dataset.py +0 -0
  46. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/feed.py +0 -0
  47. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/openapi.py +0 -0
  48. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/package.py +0 -0
  49. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/paper.py +0 -0
  50. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/relationship.py +0 -0
  51. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/repo.py +0 -0
  52. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/schema_extract.py +0 -0
  53. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/search.py +0 -0
  54. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/standards.py +0 -0
  55. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/transcript.py +0 -0
  56. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/typed.py +0 -0
  57. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/typed_models.py +0 -0
  58. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/context_packs/wiki.py +0 -0
  59. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/contracts_cli.py +0 -0
  60. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/__init__.py +0 -0
  61. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/article_cleanup.py +0 -0
  62. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/chunking.py +0 -0
  63. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/ensemble.py +0 -0
  64. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/extractor.py +0 -0
  65. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/filings.py +0 -0
  66. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/markdown.py +0 -0
  67. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/protocols.py +0 -0
  68. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/special_cases.py +0 -0
  69. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/conversion/trafilatura_extractor.py +0 -0
  70. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/core/__init__.py +0 -0
  71. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/core/fetcher.py +0 -0
  72. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/__init__.py +0 -0
  73. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/_fetch.py +0 -0
  74. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/composite.py +0 -0
  75. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/contracts.py +0 -0
  76. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/crawler.py +0 -0
  77. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/filters.py +0 -0
  78. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
  79. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
  80. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
  81. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/link_extractors/static.py +0 -0
  82. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/protocols.py +0 -0
  83. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery/sitemap.py +0 -0
  84. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/discovery_cli.py +0 -0
  85. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/doctor.py +0 -0
  86. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/document_parse.py +0 -0
  87. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/document_worker.py +0 -0
  88. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/eval_grade.py +0 -0
  89. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/evidence.py +0 -0
  90. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/evidence_pack.py +0 -0
  91. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/export_formats.py +0 -0
  92. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/exports.py +0 -0
  93. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/fixtures/__init__.py +0 -0
  94. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/fixtures/parallel-search-extract.json +0 -0
  95. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/free_core.py +0 -0
  96. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/free_core_smoke.py +0 -0
  97. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/graph.py +0 -0
  98. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/hosted.py +0 -0
  99. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/http/__init__.py +0 -0
  100. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/http/client.py +0 -0
  101. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/http/protocols.py +0 -0
  102. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/http/rate_limiter.py +0 -0
  103. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/judge.py +0 -0
  104. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/local_workflows.py +0 -0
  105. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/mcp/__init__.py +0 -0
  106. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/mcp/sources.py +0 -0
  107. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/mcp/tools.py +0 -0
  108. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/metadata_extractor.py +0 -0
  109. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/__init__.py +0 -0
  110. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/config.py +0 -0
  111. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/document.py +0 -0
  112. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/events.py +0 -0
  113. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/profiles.py +0 -0
  114. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/run.py +0 -0
  115. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/models/schema.py +0 -0
  116. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/monitor.py +0 -0
  117. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/native_integrations.py +0 -0
  118. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/output_contract.py +0 -0
  119. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pack_reader.py +0 -0
  120. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pack_tools.py +0 -0
  121. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/parallel_workflows.py +0 -0
  122. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/parity.py +0 -0
  123. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/parity_cli.py +0 -0
  124. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/passk.py +0 -0
  125. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/__init__.py +0 -0
  126. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/base.py +0 -0
  127. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/manifest.py +0 -0
  128. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/__init__.py +0 -0
  129. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/chunk.py +0 -0
  130. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/convert.py +0 -0
  131. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/dedup.py +0 -0
  132. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/fetch.py +0 -0
  133. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/metadata.py +0 -0
  134. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/render.py +0 -0
  135. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/save.py +0 -0
  136. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/save_json.py +0 -0
  137. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/save_ndjson.py +0 -0
  138. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/save_okf.py +0 -0
  139. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
  140. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/pipeline/steps/validate.py +0 -0
  141. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/policy.py +0 -0
  142. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/policy_cli.py +0 -0
  143. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/project.py +0 -0
  144. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/provider_adapters.py +0 -0
  145. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/provider_capabilities.py +0 -0
  146. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/provider_cli.py +0 -0
  147. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/provider_keys.py +0 -0
  148. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/provider_probes.py +0 -0
  149. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/py.typed +0 -0
  150. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/redaction.py +0 -0
  151. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/rendering.py +0 -0
  152. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/artifact-manifest.v1.schema.json +0 -0
  153. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/basis.v2.schema.json +0 -0
  154. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/change-event.v1.schema.json +0 -0
  155. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/citation-map.v1.schema.json +0 -0
  156. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/document.v3.schema.json +0 -0
  157. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/intelligence-bundle.v1.schema.json +0 -0
  158. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/pack.v3.schema.json +0 -0
  159. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/provenance.v1.schema.json +0 -0
  160. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/relationship-pack.v1.schema.json +0 -0
  161. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/rights.v1.schema.json +0 -0
  162. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/run-identity.v1.schema.json +0 -0
  163. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/workflow-request.v1.schema.json +0 -0
  164. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/schemas/workflow-result.v1.schema.json +0 -0
  165. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/scraper.py +0 -0
  166. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/security/__init__.py +0 -0
  167. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/security/download_policy.py +0 -0
  168. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/security/robots.py +0 -0
  169. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/security/url_validator.py +0 -0
  170. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/server.py +0 -0
  171. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/share.py +0 -0
  172. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/skill_export.py +0 -0
  173. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/source_scoring.py +0 -0
  174. {docpull-6.3.0 → docpull-6.4.0}/src/docpull/time_utils.py +0 -0
  175. {docpull-6.3.0 → docpull-6.4.0}/src/docpull.egg-info/dependency_links.txt +0 -0
  176. {docpull-6.3.0 → docpull-6.4.0}/src/docpull.egg-info/entry_points.txt +0 -0
  177. {docpull-6.3.0 → docpull-6.4.0}/src/docpull.egg-info/requires.txt +0 -0
  178. {docpull-6.3.0 → docpull-6.4.0}/src/docpull.egg-info/top_level.txt +0 -0
  179. {docpull-6.3.0 → docpull-6.4.0}/tests/test_accounting.py +0 -0
  180. {docpull-6.3.0 → docpull-6.4.0}/tests/test_acquisition_workflows.py +0 -0
  181. {docpull-6.3.0 → docpull-6.4.0}/tests/test_auth_cli.py +0 -0
  182. {docpull-6.3.0 → docpull-6.4.0}/tests/test_basis.py +0 -0
  183. {docpull-6.3.0 → docpull-6.4.0}/tests/test_benchmark.py +0 -0
  184. {docpull-6.3.0 → docpull-6.4.0}/tests/test_cache_conditional_get.py +0 -0
  185. {docpull-6.3.0 → docpull-6.4.0}/tests/test_chunking.py +0 -0
  186. {docpull-6.3.0 → docpull-6.4.0}/tests/test_claim_audit_script.py +0 -0
  187. {docpull-6.3.0 → docpull-6.4.0}/tests/test_cli.py +0 -0
  188. {docpull-6.3.0 → docpull-6.4.0}/tests/test_context_ci.py +0 -0
  189. {docpull-6.3.0 → docpull-6.4.0}/tests/test_context_packs.py +0 -0
  190. {docpull-6.3.0 → docpull-6.4.0}/tests/test_conversion.py +0 -0
  191. {docpull-6.3.0 → docpull-6.4.0}/tests/test_convert_step_new.py +0 -0
  192. {docpull-6.3.0 → docpull-6.4.0}/tests/test_discovery.py +0 -0
  193. {docpull-6.3.0 → docpull-6.4.0}/tests/test_discovery_contracts.py +0 -0
  194. {docpull-6.3.0 → docpull-6.4.0}/tests/test_doctor.py +0 -0
  195. {docpull-6.3.0 → docpull-6.4.0}/tests/test_document_parse.py +0 -0
  196. {docpull-6.3.0 → docpull-6.4.0}/tests/test_document_record.py +0 -0
  197. {docpull-6.3.0 → docpull-6.4.0}/tests/test_evidence_pack.py +0 -0
  198. {docpull-6.3.0 → docpull-6.4.0}/tests/test_exports.py +0 -0
  199. {docpull-6.3.0 → docpull-6.4.0}/tests/test_extractor_ensemble.py +0 -0
  200. {docpull-6.3.0 → docpull-6.4.0}/tests/test_feed_pack.py +0 -0
  201. {docpull-6.3.0 → docpull-6.4.0}/tests/test_free_core.py +0 -0
  202. {docpull-6.3.0 → docpull-6.4.0}/tests/test_frontier_resume.py +0 -0
  203. {docpull-6.3.0 → docpull-6.4.0}/tests/test_graph.py +0 -0
  204. {docpull-6.3.0 → docpull-6.4.0}/tests/test_hosted.py +0 -0
  205. {docpull-6.3.0 → docpull-6.4.0}/tests/test_http_efficiency.py +0 -0
  206. {docpull-6.3.0 → docpull-6.4.0}/tests/test_import_efficiency.py +0 -0
  207. {docpull-6.3.0 → docpull-6.4.0}/tests/test_integration.py +0 -0
  208. {docpull-6.3.0 → docpull-6.4.0}/tests/test_judge.py +0 -0
  209. {docpull-6.3.0 → docpull-6.4.0}/tests/test_link_extractors.py +0 -0
  210. {docpull-6.3.0 → docpull-6.4.0}/tests/test_live_web_smoke.py +0 -0
  211. {docpull-6.3.0 → docpull-6.4.0}/tests/test_local_workflows.py +0 -0
  212. {docpull-6.3.0 → docpull-6.4.0}/tests/test_mcp_server.py +0 -0
  213. {docpull-6.3.0 → docpull-6.4.0}/tests/test_mcp_tools.py +0 -0
  214. {docpull-6.3.0 → docpull-6.4.0}/tests/test_metadata_extractor.py +0 -0
  215. {docpull-6.3.0 → docpull-6.4.0}/tests/test_monitor.py +0 -0
  216. {docpull-6.3.0 → docpull-6.4.0}/tests/test_naming.py +0 -0
  217. {docpull-6.3.0 → docpull-6.4.0}/tests/test_native_integrations.py +0 -0
  218. {docpull-6.3.0 → docpull-6.4.0}/tests/test_openapi_pack.py +0 -0
  219. {docpull-6.3.0 → docpull-6.4.0}/tests/test_optional_extras_installed.py +0 -0
  220. {docpull-6.3.0 → docpull-6.4.0}/tests/test_output_contract.py +0 -0
  221. {docpull-6.3.0 → docpull-6.4.0}/tests/test_outputs_e2e.py +0 -0
  222. {docpull-6.3.0 → docpull-6.4.0}/tests/test_pack_server.py +0 -0
  223. {docpull-6.3.0 → docpull-6.4.0}/tests/test_pack_tools.py +0 -0
  224. {docpull-6.3.0 → docpull-6.4.0}/tests/test_parallel_workflows.py +0 -0
  225. {docpull-6.3.0 → docpull-6.4.0}/tests/test_parity_workflows.py +0 -0
  226. {docpull-6.3.0 → docpull-6.4.0}/tests/test_passk.py +0 -0
  227. {docpull-6.3.0 → docpull-6.4.0}/tests/test_pipeline.py +0 -0
  228. {docpull-6.3.0 → docpull-6.4.0}/tests/test_policy.py +0 -0
  229. {docpull-6.3.0 → docpull-6.4.0}/tests/test_policy_cli.py +0 -0
  230. {docpull-6.3.0 → docpull-6.4.0}/tests/test_project.py +0 -0
  231. {docpull-6.3.0 → docpull-6.4.0}/tests/test_provider_adapters.py +0 -0
  232. {docpull-6.3.0 → docpull-6.4.0}/tests/test_provider_cli.py +0 -0
  233. {docpull-6.3.0 → docpull-6.4.0}/tests/test_provider_keys.py +0 -0
  234. {docpull-6.3.0 → docpull-6.4.0}/tests/test_provider_probes.py +0 -0
  235. {docpull-6.3.0 → docpull-6.4.0}/tests/test_public_feature_smoke.py +0 -0
  236. {docpull-6.3.0 → docpull-6.4.0}/tests/test_rate_limiter.py +0 -0
  237. {docpull-6.3.0 → docpull-6.4.0}/tests/test_real_feature_smoke_script.py +0 -0
  238. {docpull-6.3.0 → docpull-6.4.0}/tests/test_real_site_regressions.py +0 -0
  239. {docpull-6.3.0 → docpull-6.4.0}/tests/test_relationship_workflow.py +0 -0
  240. {docpull-6.3.0 → docpull-6.4.0}/tests/test_rendering.py +0 -0
  241. {docpull-6.3.0 → docpull-6.4.0}/tests/test_reproducible_release_build.py +0 -0
  242. {docpull-6.3.0 → docpull-6.4.0}/tests/test_save_ndjson.py +0 -0
  243. {docpull-6.3.0 → docpull-6.4.0}/tests/test_save_sqlite.py +0 -0
  244. {docpull-6.3.0 → docpull-6.4.0}/tests/test_security_hardening.py +0 -0
  245. {docpull-6.3.0 → docpull-6.4.0}/tests/test_share.py +0 -0
  246. {docpull-6.3.0 → docpull-6.4.0}/tests/test_source_scoring.py +0 -0
  247. {docpull-6.3.0 → docpull-6.4.0}/tests/test_special_cases.py +0 -0
  248. {docpull-6.3.0 → docpull-6.4.0}/tests/test_time_utils.py +0 -0
  249. {docpull-6.3.0 → docpull-6.4.0}/tests/test_trafilatura_extractor.py +0 -0
  250. {docpull-6.3.0 → docpull-6.4.0}/tests/test_typed_knowledge_packs.py +0 -0
  251. {docpull-6.3.0 → docpull-6.4.0}/tests/test_workflow_contracts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docpull
3
- Version: 6.3.0
3
+ Version: 6.4.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>
@@ -307,6 +307,7 @@ docpull standards-pack rfc:9110 -o packs/standard
307
307
  docpull dataset-pack ./metrics.csv -o packs/dataset
308
308
  docpull dataset-pack 'https://data.example.org/rows.json?$limit=100' -o packs/dataset
309
309
  docpull relationship-pack example.com --subject "Example Brand" -o packs/relationships
310
+ docpull website-pack example.com -o packs/website
310
311
  docpull transcript-pack ./meeting.vtt -o packs/transcript
311
312
  docpull wiki-pack wiki:Web_scraping -o packs/wiki
312
313
  docpull brand-pack example.com -o packs/brand
@@ -320,6 +321,9 @@ docpull export packs/docs --format openai-vector-jsonl -o exports/openai.jsonl
320
321
  docpull export packs/docs --format cursor-rules -o .cursor/rules --skill-name docs
321
322
  ```
322
323
 
324
+ The canonical baseline-aware website acquisition contract is documented in
325
+ [Website snapshots](docs/website-snapshot.md).
326
+
323
327
  Use `docpull ci --prepare` to validate a project or standalone pack in CI.
324
328
 
325
329
  Install optional extras as needed:
@@ -772,7 +776,7 @@ part of the package release contract.
772
776
  `docpull parse`, `docpull openapi-pack`, `docpull feed-pack`,
773
777
  `docpull paper-pack`, `docpull repo-pack`, `docpull package-pack`,
774
778
  `docpull standards-pack`, `docpull dataset-pack`, `docpull transcript-pack`,
775
- `docpull wiki-pack`, `docpull brand-pack`, `docpull product-pack`,
779
+ `docpull wiki-pack`, `docpull website-pack`, `docpull brand-pack`, `docpull product-pack`,
776
780
  `docpull styleguide-pack`, `docpull image-pack`, `docpull policy-pack`,
777
781
  `docpull pack validate`,
778
782
  `docpull pack audit`, `docpull export`,
@@ -183,6 +183,7 @@ docpull standards-pack rfc:9110 -o packs/standard
183
183
  docpull dataset-pack ./metrics.csv -o packs/dataset
184
184
  docpull dataset-pack 'https://data.example.org/rows.json?$limit=100' -o packs/dataset
185
185
  docpull relationship-pack example.com --subject "Example Brand" -o packs/relationships
186
+ docpull website-pack example.com -o packs/website
186
187
  docpull transcript-pack ./meeting.vtt -o packs/transcript
187
188
  docpull wiki-pack wiki:Web_scraping -o packs/wiki
188
189
  docpull brand-pack example.com -o packs/brand
@@ -196,6 +197,9 @@ docpull export packs/docs --format openai-vector-jsonl -o exports/openai.jsonl
196
197
  docpull export packs/docs --format cursor-rules -o .cursor/rules --skill-name docs
197
198
  ```
198
199
 
200
+ The canonical baseline-aware website acquisition contract is documented in
201
+ [Website snapshots](docs/website-snapshot.md).
202
+
199
203
  Use `docpull ci --prepare` to validate a project or standalone pack in CI.
200
204
 
201
205
  Install optional extras as needed:
@@ -648,7 +652,7 @@ part of the package release contract.
648
652
  `docpull parse`, `docpull openapi-pack`, `docpull feed-pack`,
649
653
  `docpull paper-pack`, `docpull repo-pack`, `docpull package-pack`,
650
654
  `docpull standards-pack`, `docpull dataset-pack`, `docpull transcript-pack`,
651
- `docpull wiki-pack`, `docpull brand-pack`, `docpull product-pack`,
655
+ `docpull wiki-pack`, `docpull website-pack`, `docpull brand-pack`, `docpull product-pack`,
652
656
  `docpull styleguide-pack`, `docpull image-pack`, `docpull policy-pack`,
653
657
  `docpull pack validate`,
654
658
  `docpull pack audit`, `docpull export`,
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "docpull"
7
- version = "6.3.0"
7
+ version = "6.4.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"}
@@ -15,7 +15,7 @@ from typing import TYPE_CHECKING, Any
15
15
 
16
16
  from .surface import PUBLIC_SDK_EXPORTS
17
17
 
18
- __version__ = "6.3.0"
18
+ __version__ = "6.4.0"
19
19
 
20
20
  _LAZY_EXPORTS: dict[str, tuple[str, str]] = {
21
21
  **{
@@ -41,9 +41,11 @@ _LAZY_EXPORTS: dict[str, tuple[str, str]] = {
41
41
  "build_repo_pack",
42
42
  "build_standards_pack",
43
43
  "build_styleguide_pack",
44
+ "build_website_pack",
44
45
  "build_transcript_pack",
45
46
  "build_wiki_pack",
46
47
  "capture_screenshot_pack",
48
+ "validate_website_snapshot_pack",
47
49
  )
48
50
  },
49
51
  **{
@@ -59,6 +61,8 @@ _LAZY_EXPORTS: dict[str, tuple[str, str]] = {
59
61
  "SourceAuthority",
60
62
  "WorkflowRequest",
61
63
  "WorkflowResult",
64
+ "WebsiteSnapshot",
65
+ "WebsiteSnapshotDocument",
62
66
  "bundled_schema_path",
63
67
  "write_contract_schemas",
64
68
  )
@@ -198,8 +202,10 @@ if TYPE_CHECKING:
198
202
  build_standards_pack,
199
203
  build_styleguide_pack,
200
204
  build_transcript_pack,
205
+ build_website_pack,
201
206
  build_wiki_pack,
202
207
  capture_screenshot_pack,
208
+ validate_website_snapshot_pack,
203
209
  )
204
210
  from .contracts import (
205
211
  ArtifactManifest,
@@ -210,6 +216,8 @@ if TYPE_CHECKING:
210
216
  RelationshipCandidate,
211
217
  RelationshipPack,
212
218
  SourceAuthority,
219
+ WebsiteSnapshot,
220
+ WebsiteSnapshotDocument,
213
221
  WorkflowRequest,
214
222
  WorkflowResult,
215
223
  bundled_schema_path,
@@ -1661,6 +1661,7 @@ def main(argv: list[str] | None = None) -> int:
1661
1661
  return run_monitor_cli(raw_argv[1:])
1662
1662
  if raw_argv and raw_argv[0] in {
1663
1663
  "brand-pack",
1664
+ "website-pack",
1664
1665
  "product-pack",
1665
1666
  "styleguide-pack",
1666
1667
  "image-pack",
@@ -1676,9 +1677,11 @@ def main(argv: list[str] | None = None) -> int:
1676
1677
  run_relationship_pack_cli,
1677
1678
  run_screenshot_pack_cli,
1678
1679
  run_styleguide_pack_cli,
1680
+ run_website_pack_cli,
1679
1681
  )
1680
1682
 
1681
1683
  workflow_runners = {
1684
+ "website-pack": run_website_pack_cli,
1682
1685
  "brand-pack": run_brand_pack_cli,
1683
1686
  "product-pack": run_product_pack_cli,
1684
1687
  "styleguide-pack": run_styleguide_pack_cli,
@@ -25,6 +25,8 @@ _LAZY_EXPORTS = {
25
25
  **{name: (".transcript", name) for name in ("async_build_transcript_pack", "build_transcript_pack")},
26
26
  **{name: (".wiki", name) for name in ("async_build_wiki_pack", "build_wiki_pack")},
27
27
  "capture_screenshot_pack": (".visuals", "capture_screenshot_pack"),
28
+ "build_website_pack": (".website", "build_website_pack"),
29
+ "validate_website_snapshot_pack": (".website", "validate_website_snapshot_pack"),
28
30
  }
29
31
 
30
32
  __all__ = list(PUBLIC_CONTEXT_PACK_EXPORTS)
@@ -59,6 +61,7 @@ if TYPE_CHECKING:
59
61
  from .styleguide import build_styleguide_pack
60
62
  from .transcript import async_build_transcript_pack, build_transcript_pack
61
63
  from .visuals import build_image_pack, capture_screenshot_pack
64
+ from .website import build_website_pack, validate_website_snapshot_pack
62
65
  from .wiki import async_build_wiki_pack, build_wiki_pack
63
66
 
64
67
 
@@ -29,6 +29,7 @@ from .common import (
29
29
  homepage_url_for_domain,
30
30
  is_free_or_disposable_email_domain,
31
31
  likely_internal_pages,
32
+ pages_from_local_pack,
32
33
  public_url,
33
34
  quote_markdown,
34
35
  resolve_url,
@@ -92,7 +93,14 @@ def build_brand_pack(
92
93
  max_pages: int = MAX_BRAND_PAGES,
93
94
  ) -> dict[str, Any]:
94
95
  """Build a local brand profile from public website evidence."""
95
- domain = _resolve_brand_domain(domain_or_url, email=email, allow_free_email=allow_free_email)
96
+ local_pages = pages_from_local_pack(domain_or_url)
97
+ domain = (
98
+ domain_from_input(local_pages[0].url)
99
+ if local_pages
100
+ else _resolve_brand_domain(domain_or_url, email=email, allow_free_email=allow_free_email)
101
+ )
102
+ if not domain:
103
+ raise ContextPackError("Could not resolve a domain from local brand-pack evidence.")
96
104
  policy = ensure_policy_for_domain(policy, domain)
97
105
  output_dir = output_dir.resolve()
98
106
  run = ContextPackRun(
@@ -101,14 +109,20 @@ def build_brand_pack(
101
109
  policy=policy,
102
110
  input_value=email or domain_or_url,
103
111
  )
104
- home_url = homepage_url_for_domain(domain)
105
- home_pages = fetch_pages_blocking([home_url], run=run, max_pages=1)
106
- if not home_pages:
107
- raise ContextPackError(f"Could not fetch homepage for {domain}.")
108
- candidate_urls = likely_internal_pages(home_pages[0], domain, max_pages=max_pages)
109
- pages = fetch_pages_blocking(candidate_urls, run=run, max_pages=max_pages)
110
- if not pages:
111
- pages = home_pages
112
+ if local_pages is not None:
113
+ pages = local_pages[:max_pages]
114
+ if not pages:
115
+ raise ContextPackError("Local brand-pack input contains no readable documents.")
116
+ download_assets = False
117
+ else:
118
+ home_url = homepage_url_for_domain(domain)
119
+ home_pages = fetch_pages_blocking([home_url], run=run, max_pages=1)
120
+ if not home_pages:
121
+ raise ContextPackError(f"Could not fetch homepage for {domain}.")
122
+ candidate_urls = likely_internal_pages(home_pages[0], domain, max_pages=max_pages)
123
+ pages = fetch_pages_blocking(candidate_urls, run=run, max_pages=max_pages)
124
+ if not pages:
125
+ pages = home_pages
112
126
 
113
127
  profile = _extract_brand_profile(pages, domain=domain, name_hint=name, ticker=ticker)
114
128
  assets = _extract_brand_assets(
@@ -40,6 +40,7 @@ from ..models.document import DocumentRecord
40
40
  from ..models.events import SkipReason
41
41
  from ..models.run import RunIdentity
42
42
  from ..output_contract import OUTPUT_CONTRACT_SCHEMA_VERSION, write_raw_contract_sidecars
43
+ from ..pack_reader import PackReadError, load_pack, resolve_pack_path
43
44
  from ..pack_tools import build_citation_map
44
45
  from ..policy import PolicyConfig, policy_domain_matches
45
46
  from ..security.download_policy import SafeDownloadPolicy, UnsafeDownloadError, content_type_base
@@ -118,6 +119,8 @@ class PageSnapshot:
118
119
  metadata: dict[str, Any]
119
120
  extraction: dict[str, Any]
120
121
  source_type: str | None = None
122
+ etag: str | None = None
123
+ last_modified: str | None = None
121
124
 
122
125
 
123
126
  @dataclass(frozen=True)
@@ -214,6 +217,9 @@ class ContextPackRun:
214
217
  http_request_count: int = 0
215
218
  cache_hit_count: int = 0
216
219
  progress_events: list[dict[str, Any]] = field(default_factory=list)
220
+ fetch_output_dir: Path | None = None
221
+ cache_dir: Path | None = None
222
+ unchanged_urls: set[str] = field(default_factory=set)
217
223
 
218
224
  def __post_init__(self) -> None:
219
225
  self.progress("run", "started", message=f"Started {self.workflow}", timestamp=self.started_at)
@@ -444,10 +450,17 @@ async def fetch_pages(
444
450
  run.progress("acquisition", "started", total=len(selected), message="Acquiring source pages")
445
451
  config = DocpullConfig(url=selected[0], profile=ProfileName.CUSTOM)
446
452
  config.network.log_retry_warnings = False
453
+ if run.fetch_output_dir is not None:
454
+ config.output.directory = run.fetch_output_dir
455
+ if run.cache_dir is not None:
456
+ config.cache.enabled = True
457
+ config.cache.directory = run.cache_dir
458
+ config.cache.ttl_days = None
459
+ config.cache.skip_unchanged = True
447
460
  snapshots: list[PageSnapshot] = []
448
461
  async with Fetcher(config) as fetcher:
449
462
  for index, url in enumerate(selected, start=1):
450
- ctx = await fetcher.fetch_one(url, save=False)
463
+ ctx = await fetcher.fetch_one(url, save=run.fetch_output_dir is not None)
451
464
  run.http_request_count += 1
452
465
  if ctx.error:
453
466
  run.errors.append(
@@ -462,6 +475,10 @@ async def fetch_pages(
462
475
  )
463
476
  continue
464
477
  if ctx.should_skip:
478
+ if ctx.skip_code == SkipReason.CACHE_UNCHANGED:
479
+ run.cache_hit_count += 1
480
+ run.unchanged_urls.add(public_url(url))
481
+ continue
465
482
  if ctx.skip_code in {
466
483
  SkipReason.HTTP_ERROR,
467
484
  SkipReason.ROBOTS_DISALLOWED,
@@ -503,6 +520,8 @@ async def fetch_pages(
503
520
  metadata=dict(ctx.metadata or {}),
504
521
  extraction=dict(ctx.extraction_info or {}),
505
522
  source_type=ctx.source_type,
523
+ etag=getattr(ctx, "etag", None),
524
+ last_modified=getattr(ctx, "last_modified", None),
506
525
  )
507
526
  )
508
527
  run.progress(
@@ -531,6 +550,60 @@ def fetch_pages_blocking(
531
550
  return asyncio.run(fetch_pages(urls, run=run, max_pages=max_pages))
532
551
 
533
552
 
553
+ def pages_from_local_pack(value: str | Path) -> list[PageSnapshot] | None:
554
+ """Load an existing DocPull pack without permitting any network fallback."""
555
+
556
+ path = Path(value).expanduser()
557
+ if not path.exists():
558
+ return None
559
+ try:
560
+ pack = load_pack(path)
561
+ except PackReadError as err:
562
+ raise ContextPackError(f"Could not read local DocPull pack {path}: {err}") from err
563
+ raw_by_identity: dict[tuple[str, str], str] = {}
564
+ website_metadata_by_identity: dict[tuple[str, str], dict[str, Any]] = {}
565
+ records_path = path.resolve() / "documents.ndjson"
566
+ if records_path.is_file():
567
+ for line in records_path.read_text(encoding="utf-8").splitlines():
568
+ if not line.strip():
569
+ continue
570
+ try:
571
+ row = json.loads(line)
572
+ except json.JSONDecodeError:
573
+ continue
574
+ if not isinstance(row, dict):
575
+ continue
576
+ identity = (str(row.get("document_id") or ""), str(row.get("content_hash") or ""))
577
+ website_metadata_by_identity[identity] = {
578
+ key: row[key]
579
+ for key in ("page_role", "entity_reference", "authority", "representations")
580
+ if row.get(key) is not None
581
+ }
582
+ representations = row.get("representations")
583
+ raw = representations.get("raw") if isinstance(representations, dict) else None
584
+ raw_path = resolve_pack_path(path.resolve(), raw.get("path") if isinstance(raw, dict) else None)
585
+ if raw_path and raw_path.is_file():
586
+ raw_by_identity[identity] = raw_path.read_text(encoding="utf-8")
587
+ pages: list[PageSnapshot] = []
588
+ for record in pack.documents:
589
+ identity = (record.document_id, record.content_hash)
590
+ pages.append(
591
+ PageSnapshot(
592
+ url=public_url(record.url),
593
+ title=record.title,
594
+ html=raw_by_identity.get(identity, ""),
595
+ markdown=record.content,
596
+ metadata={
597
+ **dict(record.metadata),
598
+ **website_metadata_by_identity.get(identity, {}),
599
+ },
600
+ extraction={**record.extraction, "local_pack": str(path.resolve())},
601
+ source_type=record.source_type or "local-pack",
602
+ )
603
+ )
604
+ return pages
605
+
606
+
534
607
  def soup_for(page: PageSnapshot) -> BeautifulSoup:
535
608
  return BeautifulSoup(page.html or page.markdown, "html.parser")
536
609
 
@@ -24,6 +24,7 @@ from .common import (
24
24
  extract_links,
25
25
  fetch_pages_blocking,
26
26
  homepage_url_for_domain,
27
+ pages_from_local_pack,
27
28
  public_url,
28
29
  quote_markdown,
29
30
  same_policy_domain,
@@ -73,7 +74,8 @@ def build_policy_pack(
73
74
  ) -> dict[str, Any]:
74
75
  """Discover policy pages and emit neutral, clause-level evidence records."""
75
76
 
76
- domain = domain_from_input(domain_or_url)
77
+ local_pages = pages_from_local_pack(domain_or_url)
78
+ domain = domain_from_input(local_pages[0].url) if local_pages else domain_from_input(domain_or_url)
77
79
  if not domain:
78
80
  raise ContextPackError("Could not resolve a domain from policy-pack input.")
79
81
  effective_policy = ensure_policy_for_domain(policy, domain)
@@ -84,14 +86,21 @@ def build_policy_pack(
84
86
  input_value=domain_or_url,
85
87
  )
86
88
  start_url = public_url(domain_or_url if "://" in domain_or_url else homepage_url_for_domain(domain))
87
- discovery_pages = fetch_pages_blocking([start_url], run=run, max_pages=1)
89
+ discovery_pages = (
90
+ local_pages[:max_pages]
91
+ if local_pages is not None
92
+ else fetch_pages_blocking([start_url], run=run, max_pages=1)
93
+ )
88
94
  if not discovery_pages:
89
- raise ContextPackError(f"Could not fetch policy discovery target: {start_url}")
95
+ raise ContextPackError(f"Could not load policy discovery target: {start_url}")
90
96
 
91
- urls = _policy_urls(discovery_pages[0], domain=domain, max_pages=max_pages)
92
- if _policy_target_hint(discovery_pages[0]) and start_url not in urls:
93
- urls.insert(0, start_url)
94
- pages = fetch_pages_blocking(urls, run=run, max_pages=max_pages) if urls else []
97
+ if local_pages is not None:
98
+ pages = local_pages[:max_pages]
99
+ else:
100
+ urls = _policy_urls(discovery_pages[0], domain=domain, max_pages=max_pages)
101
+ if _policy_target_hint(discovery_pages[0]) and start_url not in urls:
102
+ urls.insert(0, start_url)
103
+ pages = fetch_pages_blocking(urls, run=run, max_pages=max_pages) if urls else []
95
104
  if not pages:
96
105
  run.warn(
97
106
  "policy_documents_not_found",