sift-engine 0.2.0__tar.gz → 0.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.
- {sift_engine-0.2.0/sift_engine.egg-info → sift_engine-0.3.0}/PKG-INFO +17 -5
- {sift_engine-0.2.0 → sift_engine-0.3.0}/README.md +14 -4
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/plan.py +2 -3
- {sift_engine-0.2.0 → sift_engine-0.3.0}/pyproject.toml +3 -1
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/__init__.py +1 -1
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/cli.py +447 -12
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/config.py +31 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/extract.py +216 -17
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/fetch.py +102 -16
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/integrity.py +83 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/mcp_server.py +722 -26
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/normalize.py +32 -1
- sift_engine-0.3.0/sift/prove.py +242 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/publish.py +282 -11
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/quality.py +61 -0
- sift_engine-0.3.0/sift/robots.py +109 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/__init__.py +44 -2
- sift_engine-0.3.0/sift/sites/cve.py +47 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/stripe.py +4 -0
- sift_engine-0.3.0/sift/sources/frontier.py +125 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sources/impersonate.py +76 -53
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/status.py +15 -2
- sift_engine-0.3.0/sift/timestamp.py +145 -0
- sift_engine-0.3.0/sift/verify_proof.py +101 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0/sift_engine.egg-info}/PKG-INFO +17 -5
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift_engine.egg-info/SOURCES.txt +19 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift_engine.egg-info/requires.txt +2 -0
- sift_engine-0.3.0/tests/test_api_connector.py +261 -0
- sift_engine-0.3.0/tests/test_changelog_continuity.py +81 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_config.py +6 -2
- sift_engine-0.3.0/tests/test_content_admission.py +154 -0
- sift_engine-0.3.0/tests/test_discover.py +92 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_excludes_and_pdf.py +27 -1
- sift_engine-0.3.0/tests/test_extract_json.py +157 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_extract_strategy.py +5 -3
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_fetch_browser.py +81 -0
- sift_engine-0.3.0/tests/test_frontier.py +90 -0
- sift_engine-0.3.0/tests/test_gates.py +321 -0
- sift_engine-0.3.0/tests/test_impersonate_rotation.py +143 -0
- sift_engine-0.3.0/tests/test_mcp_changed_since.py +244 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_mcp_server.py +2 -2
- sift_engine-0.3.0/tests/test_mcp_time_travel.py +231 -0
- sift_engine-0.3.0/tests/test_normalizer_scoping.py +124 -0
- sift_engine-0.3.0/tests/test_prove.py +346 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_publish_integrity.py +26 -0
- sift_engine-0.3.0/tests/test_robots.py +111 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_tiered_transport.py +14 -2
- sift_engine-0.3.0/tests/test_timestamp.py +135 -0
- sift_engine-0.2.0/tests/test_gates.py +0 -135
- {sift_engine-0.2.0 → sift_engine-0.3.0}/LICENSE +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/agent.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/judge.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/questions.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/report.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/runner.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/agent_loop/tools.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/aggregate_report.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/drift.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/fixtures/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/fixtures/sites.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/full_suite.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/commit.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/extract.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/fetch.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/mcp.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/publish.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/per_stage/seed.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/report.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/runner.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/scoring/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/scoring/fidelity.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/scoring/structural.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/bench/scoring/use_case.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/cli.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/determinism.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/efficiency.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/facts_coverage.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/facts_validation.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/llm_judge.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/performance.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/sampler.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/evals/structural.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/setup.cfg +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/_io.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/agent_surface.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/browser.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/classify.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/commit.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/decide.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/extract_code.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/extract_next_state.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/extract_strategy.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/facts.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/index_profile.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/manifest.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/paths.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/plan.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/purge.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/registry.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/ato.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/augov.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/generic.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/generic_browser.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/mdn.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sites/python_docs.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sources/__init__.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sources/firecrawl.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift/sources/sitemap.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift_engine.egg-info/dependency_links.txt +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift_engine.egg-info/entry_points.txt +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/sift_engine.egg-info/top_level.txt +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_agent_loop.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_agent_surface.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_backup.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_browser_contract.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_browser_real.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_classify.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_commit_chain.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_consolidate_md.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_decide.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_eval_bench_b3.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_eval_bench_b5.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_eval_bench_drift.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_eval_bench_scoring.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_evals.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_extract_anchors.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_extract_code.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_extract_next_state.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_facts.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_facts_coverage.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_firecrawl_scrape.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_integrity.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_manifest.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_markdown_passthrough.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_mcp_index.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_mcp_multi_index.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_mcp_verify.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_normalize.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_only_urls.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_pre_freeze_hardening.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_production_hardening.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_purge.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_re_extract.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_registry.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_sites.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_sources_firecrawl.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_sources_sitemap_auto.py +0 -0
- {sift_engine-0.2.0 → sift_engine-0.3.0}/tests/test_user_agent_warnings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sift-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Sift — deterministic website indexing for grep-first LLM agents
|
|
5
5
|
Author: Deval Shah
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,7 +21,9 @@ Requires-Dist: lxml>=5.0
|
|
|
21
21
|
Requires-Dist: selectolax>=0.3
|
|
22
22
|
Requires-Dist: mcp>=1.0
|
|
23
23
|
Requires-Dist: pypdf>=4.0
|
|
24
|
+
Requires-Dist: pdfplumber>=0.11
|
|
24
25
|
Requires-Dist: jsonschema>=4
|
|
26
|
+
Requires-Dist: protego>=0.3
|
|
25
27
|
Provides-Extra: dev
|
|
26
28
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
27
29
|
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
|
|
@@ -62,8 +64,8 @@ Your agent's built-in web-fetch gives you:
|
|
|
62
64
|
|
|
63
65
|
## ✅ With sift
|
|
64
66
|
|
|
65
|
-
- **Always fresh** — you control the crawl; conditional GETs refetch only what changed
|
|
66
|
-
- **Provable** — content-hash + date on every page; cite the source, hash, and snapshot
|
|
67
|
+
- **Always fresh** — you control the crawl; conditional GETs refetch only what changed, and `changed_since` lets your agent pull just the delta instead of re-reading the corpus
|
|
68
|
+
- **Provable** — content-hash + date on every page; cite the source, hash, and snapshot, read any past snapshot (`as_of`), or emit a self-contained `prove` inclusion proof a third party verifies offline (`python -m sift.verify_proof`) without trusting the server — optionally anchored by an RFC-3161 timestamp so the date is witnessed by a third party, not self-asserted
|
|
67
69
|
- **Complete & grep-native** — the whole site on disk, not a handful of retrieved snippets
|
|
68
70
|
- **Self-hosted** — any `http(s)` site you can reach, public or internal; your data stays yours
|
|
69
71
|
|
|
@@ -109,7 +111,7 @@ sift --version && which sift-mcp # confirm it's on PATH
|
|
|
109
111
|
Download the skill so you (the agent) know how to build, operate, and query an index.
|
|
110
112
|
Read .claude/skills/sift/SKILL.md after downloading — it is the source of truth for the rest.
|
|
111
113
|
```bash
|
|
112
|
-
SKILL=https://raw.githubusercontent.com/dvlshah/sift/v0.
|
|
114
|
+
SKILL=https://raw.githubusercontent.com/dvlshah/sift/v0.3.0/.claude/skills/sift
|
|
113
115
|
mkdir -p .claude/skills/sift/reference
|
|
114
116
|
curl -fsSL $SKILL/SKILL.md -o .claude/skills/sift/SKILL.md
|
|
115
117
|
for f in cli config mcp-tools; do curl -fsSL $SKILL/reference/$f.md -o .claude/skills/sift/reference/$f.md; done
|
|
@@ -131,12 +133,22 @@ sift run --root ./sift-index --config sift.toml --limit 25 --coverage-base pl
|
|
|
131
133
|
sift verify --root ./sift-index --skip-signature
|
|
132
134
|
```
|
|
133
135
|
Drop `--limit 25 --coverage-base planned` for a full crawl once extraction looks good.
|
|
136
|
+
Preview exactly which URLs a crawl would touch — and how many — *without fetching a
|
|
137
|
+
byte*: `sift discover --from-domain TARGET_SITE` (writes nothing; emits JSON).
|
|
138
|
+
No `sitemap.xml` (or an incomplete one)? After a run, `sift seed --from-frontier`
|
|
139
|
+
extracts in-scope links from the fetched pages and crawls one hop deeper each pass.
|
|
134
140
|
Hardened / bot-blocked host (Cloudflare/Akamai/Imperva) → add `--impersonate-fallback`
|
|
135
141
|
(free, TLS-fingerprint impersonation; `pip install 'sift-engine[impersonate]'`).
|
|
136
142
|
JS-rendered SPA → `pip install 'sift-engine[browser]' && python -m playwright install chromium`,
|
|
137
143
|
then add `[browser]\nenabled = true` (it joins the ladder as a free render tier).
|
|
144
|
+
JS-rendered SPA that has an official API (eCFR, CVE.org, FederalRegister …) → a
|
|
145
|
+
profile's `api_url(url)` fetches the robots-allowed API instead of the shell —
|
|
146
|
+
cleaner and cheaper than a browser, and the citation stays the human page (see
|
|
147
|
+
the bundled `sift.sites.cve:CVEProfile`).
|
|
138
148
|
Still blocked (JS-challenge edges) → `--firecrawl-fallback` (paid; needs FIRECRAWL_API_KEY).
|
|
139
149
|
These compose into one escalation ladder: native → impersonate → browser → Firecrawl.
|
|
150
|
+
sift respects `robots.txt` `Disallow` at seed by default; set `[crawl]` →
|
|
151
|
+
`respect_robots = false` only for sources you have permission to index.
|
|
140
152
|
|
|
141
153
|
4 — WIRE THE READ-ONLY MCP SERVER
|
|
142
154
|
Use the ABSOLUTE path to ./sift-index. Add this to the project's .mcp.json (Claude Code / Cursor / Codex):
|
|
@@ -204,7 +216,7 @@ Your agent reads the published snapshot **read-only** over MCP: `snapshot_status
|
|
|
204
216
|
|
|
205
217
|
> **Open core, Apache-2.0.** This repo is the full open-source engine (pipeline + MCP server) and runs standalone. A hosted platform built on it is in development.
|
|
206
218
|
|
|
207
|
-
**Status — v0.
|
|
219
|
+
**Status — v0.3.0**; tests green on Python 3.11–3.13. Adds proof-carrying answers (`prove` / `verify-proof` + an RFC-3161 timestamp anchor), time-travel reads (`changed_since`, `diff_md`, `as_of` over retained snapshots), API acquisition/content transports (`api_url` source + `json` extract), robots + SSRF + bot-challenge hardening, digital-PDF tables, a recursive link frontier, and `sift discover` (read-only pre-crawl preview). Known limits: no run-dir GC yet, stdout-only logging, stdio-only MCP transport. Issues & roadmap → [GitHub Issues](https://github.com/dvlshah/sift/issues).
|
|
208
220
|
|
|
209
221
|
## License
|
|
210
222
|
|
|
@@ -24,8 +24,8 @@ Your agent's built-in web-fetch gives you:
|
|
|
24
24
|
|
|
25
25
|
## ✅ With sift
|
|
26
26
|
|
|
27
|
-
- **Always fresh** — you control the crawl; conditional GETs refetch only what changed
|
|
28
|
-
- **Provable** — content-hash + date on every page; cite the source, hash, and snapshot
|
|
27
|
+
- **Always fresh** — you control the crawl; conditional GETs refetch only what changed, and `changed_since` lets your agent pull just the delta instead of re-reading the corpus
|
|
28
|
+
- **Provable** — content-hash + date on every page; cite the source, hash, and snapshot, read any past snapshot (`as_of`), or emit a self-contained `prove` inclusion proof a third party verifies offline (`python -m sift.verify_proof`) without trusting the server — optionally anchored by an RFC-3161 timestamp so the date is witnessed by a third party, not self-asserted
|
|
29
29
|
- **Complete & grep-native** — the whole site on disk, not a handful of retrieved snippets
|
|
30
30
|
- **Self-hosted** — any `http(s)` site you can reach, public or internal; your data stays yours
|
|
31
31
|
|
|
@@ -71,7 +71,7 @@ sift --version && which sift-mcp # confirm it's on PATH
|
|
|
71
71
|
Download the skill so you (the agent) know how to build, operate, and query an index.
|
|
72
72
|
Read .claude/skills/sift/SKILL.md after downloading — it is the source of truth for the rest.
|
|
73
73
|
```bash
|
|
74
|
-
SKILL=https://raw.githubusercontent.com/dvlshah/sift/v0.
|
|
74
|
+
SKILL=https://raw.githubusercontent.com/dvlshah/sift/v0.3.0/.claude/skills/sift
|
|
75
75
|
mkdir -p .claude/skills/sift/reference
|
|
76
76
|
curl -fsSL $SKILL/SKILL.md -o .claude/skills/sift/SKILL.md
|
|
77
77
|
for f in cli config mcp-tools; do curl -fsSL $SKILL/reference/$f.md -o .claude/skills/sift/reference/$f.md; done
|
|
@@ -93,12 +93,22 @@ sift run --root ./sift-index --config sift.toml --limit 25 --coverage-base pl
|
|
|
93
93
|
sift verify --root ./sift-index --skip-signature
|
|
94
94
|
```
|
|
95
95
|
Drop `--limit 25 --coverage-base planned` for a full crawl once extraction looks good.
|
|
96
|
+
Preview exactly which URLs a crawl would touch — and how many — *without fetching a
|
|
97
|
+
byte*: `sift discover --from-domain TARGET_SITE` (writes nothing; emits JSON).
|
|
98
|
+
No `sitemap.xml` (or an incomplete one)? After a run, `sift seed --from-frontier`
|
|
99
|
+
extracts in-scope links from the fetched pages and crawls one hop deeper each pass.
|
|
96
100
|
Hardened / bot-blocked host (Cloudflare/Akamai/Imperva) → add `--impersonate-fallback`
|
|
97
101
|
(free, TLS-fingerprint impersonation; `pip install 'sift-engine[impersonate]'`).
|
|
98
102
|
JS-rendered SPA → `pip install 'sift-engine[browser]' && python -m playwright install chromium`,
|
|
99
103
|
then add `[browser]\nenabled = true` (it joins the ladder as a free render tier).
|
|
104
|
+
JS-rendered SPA that has an official API (eCFR, CVE.org, FederalRegister …) → a
|
|
105
|
+
profile's `api_url(url)` fetches the robots-allowed API instead of the shell —
|
|
106
|
+
cleaner and cheaper than a browser, and the citation stays the human page (see
|
|
107
|
+
the bundled `sift.sites.cve:CVEProfile`).
|
|
100
108
|
Still blocked (JS-challenge edges) → `--firecrawl-fallback` (paid; needs FIRECRAWL_API_KEY).
|
|
101
109
|
These compose into one escalation ladder: native → impersonate → browser → Firecrawl.
|
|
110
|
+
sift respects `robots.txt` `Disallow` at seed by default; set `[crawl]` →
|
|
111
|
+
`respect_robots = false` only for sources you have permission to index.
|
|
102
112
|
|
|
103
113
|
4 — WIRE THE READ-ONLY MCP SERVER
|
|
104
114
|
Use the ABSOLUTE path to ./sift-index. Add this to the project's .mcp.json (Claude Code / Cursor / Codex):
|
|
@@ -166,7 +176,7 @@ Your agent reads the published snapshot **read-only** over MCP: `snapshot_status
|
|
|
166
176
|
|
|
167
177
|
> **Open core, Apache-2.0.** This repo is the full open-source engine (pipeline + MCP server) and runs standalone. A hosted platform built on it is in development.
|
|
168
178
|
|
|
169
|
-
**Status — v0.
|
|
179
|
+
**Status — v0.3.0**; tests green on Python 3.11–3.13. Adds proof-carrying answers (`prove` / `verify-proof` + an RFC-3161 timestamp anchor), time-travel reads (`changed_since`, `diff_md`, `as_of` over retained snapshots), API acquisition/content transports (`api_url` source + `json` extract), robots + SSRF + bot-challenge hardening, digital-PDF tables, a recursive link frontier, and `sift discover` (read-only pre-crawl preview). Known limits: no run-dir GC yet, stdout-only logging, stdio-only MCP transport. Issues & roadmap → [GitHub Issues](https://github.com/dvlshah/sift/issues).
|
|
170
180
|
|
|
171
181
|
## License
|
|
172
182
|
|
|
@@ -57,9 +57,8 @@ def eval_plan_decision_correctness() -> PlanDecisionCorrectnessResult:
|
|
|
57
57
|
from sift.config import IndexConfig, TierConfig
|
|
58
58
|
from sift.classify import CLASSIFIER_VERSION
|
|
59
59
|
from sift.extract import EXTRACTOR_VERSION
|
|
60
|
-
from sift.normalize import
|
|
60
|
+
from sift.normalize import normalizer_version
|
|
61
61
|
from sift import decide as decide_mod
|
|
62
|
-
from sift import sites as sites_mod
|
|
63
62
|
from sift import paths
|
|
64
63
|
|
|
65
64
|
# Set up an in-memory-ish manifest in a temp dir
|
|
@@ -151,7 +150,7 @@ def eval_plan_decision_correctness() -> PlanDecisionCorrectnessResult:
|
|
|
151
150
|
plan_phase(
|
|
152
151
|
conn, plan_path, now=now,
|
|
153
152
|
extractor_version=EXTRACTOR_VERSION,
|
|
154
|
-
normalizer_version=
|
|
153
|
+
normalizer_version=normalizer_version(),
|
|
155
154
|
profile=GenericProfile(),
|
|
156
155
|
cfg=cfg,
|
|
157
156
|
)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sift-engine"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Sift — deterministic website indexing for grep-first LLM agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -24,7 +24,9 @@ dependencies = [
|
|
|
24
24
|
"selectolax>=0.3",
|
|
25
25
|
"mcp>=1.0",
|
|
26
26
|
"pypdf>=4.0",
|
|
27
|
+
"pdfplumber>=0.11",
|
|
27
28
|
"jsonschema>=4",
|
|
29
|
+
"protego>=0.3",
|
|
28
30
|
]
|
|
29
31
|
|
|
30
32
|
[project.optional-dependencies]
|
|
@@ -16,7 +16,7 @@ bumping a version triggers re-derivation from cached raw, not refetch.
|
|
|
16
16
|
See README.md for the full design.
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
__version__ = "0.
|
|
19
|
+
__version__ = "0.3.0"
|
|
20
20
|
# Provenance stamp on manifest rows — a *behavioral* version (like
|
|
21
21
|
# EXTRACTOR_VERSION), bumped only when crawl/fetch behavior changes, not on
|
|
22
22
|
# every package release. this is the initial public release; crawl behavior is unchanged, so this stays.
|