zettelforge 2.7.0__tar.gz → 2.8.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.
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/ci.yml +54 -18
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/docs.yml +2 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/publish.yml +6 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/snyk-security.yml +3 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/ARCHITECTURE.md +13 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/CHANGELOG.md +48 -0
- zettelforge-2.8.0/GOVERNANCE.md +49 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/PKG-INFO +7 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/BENCHMARK_REPORT.md +73 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_retrieval_benchmark.py +2 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_retrieval_results.json +21 -21
- zettelforge-2.8.0/benchmarks/instrument_lookups.py +62 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/locomo_benchmark.py +40 -11
- zettelforge-2.8.0/benchmarks/locomo_results.json +962 -0
- zettelforge-2.8.0/benchmarks/mine_phase_timings.py +56 -0
- zettelforge-2.8.0/benchmarks/profile_recall.py +61 -0
- zettelforge-2.8.0/benchmarks/rerank_grid.py +43 -0
- zettelforge-2.8.0/benchmarks/results/session_2026-06-09/locomo_results_optimized.json +962 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/config.default.yaml +13 -0
- zettelforge-2.8.0/docs/explanation/design-philosophy-dual-hemisphere.md +159 -0
- zettelforge-2.8.0/docs/how-to/passive-osint-enrichment.md +70 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/index.md +2 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/configuration.md +21 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/governance-controls.md +47 -4
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-016-osint-layer.md +13 -11
- zettelforge-2.8.0/docs/rfcs/RFC-018-threatrecall-next-improvements.md +255 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/mkdocs.yml +1 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/pyproject.toml +11 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/__init__.py +1 -1
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/config.py +108 -0
- zettelforge-2.8.0/src/zettelforge/enrichment_ledger.py +60 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/entity_indexer.py +96 -7
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/fact_extractor.py +11 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/governance_validator.py +9 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/graph_retriever.py +37 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/intent_classifier.py +6 -1
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/json_parse.py +7 -1
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_client.py +13 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_defense.py +127 -3
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_evolver.py +9 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_manager.py +337 -73
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_updater.py +24 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/note_constructor.py +11 -10
- zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/AGE-120-pip-audit.md +51 -0
- zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/LICENSE-Apache-2.0.txt +201 -0
- zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/NOTICE +32 -0
- zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/PROVENANCE.md +65 -0
- zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/THIRD_PARTY_NOTICES.md +37 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/__init__.py +39 -10
- zettelforge-2.8.0/src/zettelforge/osint/collectors/breach/hibp_collector.py +167 -0
- zettelforge-2.8.0/src/zettelforge/osint/collectors/financial/__init__.py +15 -0
- zettelforge-2.8.0/src/zettelforge/osint/collectors/financial/wallet_collector.py +194 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/bgp_collector.py +4 -5
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/dns_collector.py +69 -6
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/whois_collector.py +58 -14
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/people/__init__.py +2 -0
- zettelforge-2.8.0/src/zettelforge/osint/collectors/people/holehe_collector.py +44 -0
- zettelforge-2.8.0/src/zettelforge/osint/collectors/people/maigret_collector.py +145 -0
- zettelforge-2.8.0/src/zettelforge/osint/entity_resolver.py +285 -0
- zettelforge-2.8.0/src/zettelforge/osint/executor.py +543 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/ontology.py +159 -0
- zettelforge-2.8.0/src/zettelforge/prompt_injection_guard.py +117 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/ingest.py +11 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sqlite_backend.py +174 -1
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/storage_backend.py +21 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/synthesis_generator.py +29 -10
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/vector_memory.py +56 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/vector_retriever.py +24 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/cccs_metadata.py +13 -10
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/ingest.py +20 -2
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/parser.py +20 -6
- zettelforge-2.8.0/tests/test_cccs_metadata.py +51 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_conversational_entities.py +36 -0
- zettelforge-2.8.0/tests/test_defense_reference_window.py +87 -0
- zettelforge-2.8.0/tests/test_embedding_cache.py +71 -0
- zettelforge-2.8.0/tests/test_enrichment_switch.py +76 -0
- zettelforge-2.8.0/tests/test_graph_scoping.py +147 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_json_parse.py +18 -1
- zettelforge-2.8.0/tests/test_max_tokens_budgets.py +202 -0
- zettelforge-2.8.0/tests/test_memory_defense_equivalence.py +223 -0
- zettelforge-2.8.0/tests/test_memory_manager_flush.py +96 -0
- zettelforge-2.8.0/tests/test_osint_age119_gap_types.py +94 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_osint_collectors.py +4 -2
- zettelforge-2.8.0/tests/test_osint_enrichers_age120.py +458 -0
- zettelforge-2.8.0/tests/test_osint_entity_resolver.py +84 -0
- zettelforge-2.8.0/tests/test_osint_executor.py +274 -0
- zettelforge-2.8.0/tests/test_prompt_injection_guard.py +104 -0
- zettelforge-2.8.0/tests/test_remember_chunked.py +72 -0
- zettelforge-2.8.0/tests/test_rerank_policy.py +103 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sigma_ingest.py +49 -6
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sqlite_backend.py +54 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_yara_ingest.py +45 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_yara_parser.py +14 -0
- zettelforge-2.7.0/GOVERNANCE.md +0 -24
- zettelforge-2.7.0/benchmarks/locomo_results.json +0 -287
- zettelforge-2.7.0/src/zettelforge/osint/collectors/breach/hibp_collector.py +0 -46
- zettelforge-2.7.0/src/zettelforge/osint/collectors/people/holehe_collector.py +0 -47
- zettelforge-2.7.0/src/zettelforge/osint/entity_resolver.py +0 -164
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/CODEOWNERS +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/SECURITY.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/dependabot.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/pull_request_template.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/stale.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/.gitignore +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/CODEOWNERS +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/CODE_OF_CONDUCT.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/CONTRIBUTING.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/CONTRIBUTORS.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/Dockerfile +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/LICENSE +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/MANIFEST.in +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/README.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/ROADMAP.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/SCOPING_DOC.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/SECURITY.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/LOCOMO_BENCHMARK_COMPARISON.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/auto_ralph.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/benchmark_harness.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_benchmark_v2.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_v2_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ctibench_benchmark.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ctibench_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/dataset.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/enterprise-attack.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/evolve_benchmark.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/evolve_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/graph_test.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/locomo_results_v1.3.0_baseline.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/memoryagentbench.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/memoryagentbench_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/mempalace_benchmark.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/mempalace_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/naive_memory.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/opencti_benchmark.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ragas_benchmark.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ragas_cti_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ragas_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/results/benchmark_report.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/results/ralph_optimization_log.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/scale_benchmark.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/scale_results.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/config.example.yaml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docker/docker-compose.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/.well-known/security.txt +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/CNAME +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/THREAT_MODEL.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/architecture-diagram.mmd +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/archive/PACKAGE_SUMMARY.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/archive/README.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/archive/SKILL.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/ZettelForge_Architecture.mmd +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/architecture-overview.mmd +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/architecture-read-path.mmd +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/architecture-write-path.mmd +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/cf-analytics.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/demo.gif +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-16.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-32.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-512.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-64.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-apple-touch.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-old.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/logo.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/social-preview.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-lockup-monogram.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-lockup.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-logo-flat.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-logo-philosophy.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-logo.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-mark.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/zettelforge_architecture-light.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/zettelforge_architecture.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/brand/brandIdentity.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/brand/colors_and_type.css +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/architecture.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/epistemic-tiers.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/llm-budgets-and-timeouts.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/stix-in-zettelforge.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/two-phase-pipeline.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/zettelkasten-philosophy.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/build-extensions.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-lancedb.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-opencti.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-pii.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-sigma-ingestion.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-typedb.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-yara-ingestion.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/ingest-news-report.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/integrate-llm-agent.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/integrate-with-crewai.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/integrate-with-langchain.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/maintain-lancedb.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/migrate-jsonl-to-sqlite.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/query-apt-tools.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/reproduce-benchmarks.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/resolve-aliases.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/run-temporal-query.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/set-up-mcp-server.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/store-threat-actor.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/troubleshoot.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/upgrade.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/use-detection-rules.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/use-web-interface.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/human-evaluation-rubric.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/llms.txt +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/marketing/awesome-list-submissions.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/narrative/2026-04-16-the-memory-problem.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/overrides/main.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/architecture-deep-dive.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/detection-rules-schema.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/editions.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/entity-indexer-concurrency.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/kg-edge-schema.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/mcp-protocol.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/memory-manager-api.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/module-inventory.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/retrieval-policies.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/sigma-schema-reference.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/stix-schema.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/web-api.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/yara-schema-reference.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-001-conversational-entity-extractor.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-002-universal-llm-provider.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-002a-retrieval-plumbing.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-003-adversarial-review.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-003-read-path-depth-routing.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-007-operational-telemetry.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-009-enrichment-pipeline-v2.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-010-enrichment-hotfix.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-011-local-llm-backend-config.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-012-litellm-unified-provider.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-013-presidio-pii-detection.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-014-content-limits.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-015-zettelforge-web-gui.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-017-memsad-write-time-defenses.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/stylesheets/brand-tokens.css +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/stylesheets/extra.css +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/stylesheets/fonts/Neuropol.otf +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/tutorials/01-quickstart.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/tutorials/02-first-cti-report.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/athf_bridge.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/crewai_cti_crew.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/cti_analysis.ipynb +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/ingest_misp.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/mcp_claude_code.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/quickstart.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/sample_misp_event.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/governance/controls.yaml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/pr-body.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/migrate_jsonl_to_sqlite.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/rebuild_index.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/record-demo.sh +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/typedb-setup.sh +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/zettelforge-rebuild.service +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/zettelforge-rebuild.timer +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/server.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/skills/claude-code-skill.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/skills/openclaw-skill.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/__main__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/alias_resolver.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/backend_factory.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/blended_retriever.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/cache.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/consolidation.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/demo.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/base.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/consumers.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/explainer.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/edition.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/extensions.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/integrations/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/integrations/crewai.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/integrations/langchain_retriever.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/knowledge_graph.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/lance_maintenance.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/base.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/litellm_provider.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/local_provider.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/mock_provider.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/ollama_provider.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/registry.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/log.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/mcp/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/mcp/__main__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/mcp/server.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_store.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/note_schema.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/observability.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/ocsf.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/ontology.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/breach/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/breach/breach_directory.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/cert_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/port_scanner.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/people/hunter_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/people/namechk_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/social/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/social/hashtag_tracker.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/social/twitter_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/tech/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/tech/builtwith_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/tech/wappalyzer_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/investigation.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/transform_registry.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/pii_validator.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/retry.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/compact_lance.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/human_eval_sampler.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/telemetry_aggregator.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/telemetry_dashboard.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/cli.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/entities.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/parser.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/NOTICE.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/sigma-correlation-rules-schema.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/sigma-detection-rule-schema.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/sigma-filters-schema.json +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/tags.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/synthesis_validator.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/telemetry.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/cli.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/entities.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/CCCS_YARA.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/CCCS_YARA_values.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/NOTICE.md +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/tags.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/__init__.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/benchmark_scale.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/conftest.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/cloud_example.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/correlation_example.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/process_creation_example.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/tagged_example.yml +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/yara/malware_hash.yar +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/yara/technique_loader.yar +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/yara/webshell.yar +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_basic.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_blended_retriever.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_causal_extraction.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_config.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_consolidation.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_core.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_crewai_integration.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_cti_integration.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_detection_explainer.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_detection_rule_entities.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_edition.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_embedding.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_entity_indexer_races.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_extensions.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_fact_extractor.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_governance.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_governance_spec_drift.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_graph_retriever.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_human_eval_sampler.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_intent_classifier.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_kg_edge_schema.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_lance_maintenance.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_langchain_retriever.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_llm_client.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_llm_providers.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_logging_compliance.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_mcp_server.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_memory_defense.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_memory_evolver.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_memory_updater.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_osint_entities.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_performance.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_pii_validator.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_recall_integration.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sigma_entities.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sigma_parser.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sqlite_integration.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_storage_backend.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_aggregator.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_collector.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_dashboard.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_integration.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_temporal_graph.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_two_phase_e2e.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_typedb_client.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_web_api.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_yara_entities.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/app.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/auth.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/mcp_server.py +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/static/css/design_tokens.css +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/base.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/remember_panel.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/result_card.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/search_bar.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/synthesis_block.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/tab_bar.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/config_editor.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/index.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-16.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-32.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-512.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-apple-touch.png +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/logo.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/threatrecall-lockup.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/zettelforge_architecture.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/colors_and_type.css +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/favicon.svg +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/fonts/Neuropol.otf +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/index.html +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/app.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/header.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/result-card.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/sidebar.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/spinner.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/tabs.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/toast.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/lib/api.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/lib/state.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/configuration.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/dashboard.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/entities.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/history.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/ingest.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/knowledge-graph.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/logs.js +0 -0
- {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/search.js +0 -0
|
@@ -13,10 +13,10 @@ jobs:
|
|
|
13
13
|
lint:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
17
17
|
|
|
18
18
|
- name: Set up Python
|
|
19
|
-
uses: actions/setup-python@
|
|
19
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
20
20
|
with:
|
|
21
21
|
python-version: '3.12'
|
|
22
22
|
|
|
@@ -35,13 +35,13 @@ jobs:
|
|
|
35
35
|
pip-audit:
|
|
36
36
|
runs-on: ubuntu-latest
|
|
37
37
|
steps:
|
|
38
|
-
- uses: actions/checkout@
|
|
38
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
39
39
|
- name: Set up Python
|
|
40
|
-
uses: actions/setup-python@
|
|
40
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
41
41
|
with:
|
|
42
42
|
python-version: '3.12'
|
|
43
43
|
- name: Install pip-audit
|
|
44
|
-
run: pip install pip-audit
|
|
44
|
+
run: pip install pip-audit==2.9.0
|
|
45
45
|
- name: Audit dependencies (any reported vuln blocks)
|
|
46
46
|
run: |
|
|
47
47
|
pip install -e ".[dev]" || pip install -e "."
|
|
@@ -49,7 +49,7 @@ jobs:
|
|
|
49
49
|
# --ignore-vuln=CVE-... with a citation when the finding is
|
|
50
50
|
# explicitly accepted per GOV-009 §"Vulnerability Response".
|
|
51
51
|
#
|
|
52
|
-
# CVE-2026-3219: vulnerability in `pip` itself (the package
|
|
52
|
+
# CVE-2026-3219 / PYSEC-2026-196: vulnerability in `pip` itself (the package
|
|
53
53
|
# manager), not a project dependency. The runner's pip is
|
|
54
54
|
# supplied by GitHub's setup-python image and is not something
|
|
55
55
|
# ZettelForge's pyproject can pin or upgrade. Risk-accepted
|
|
@@ -57,25 +57,59 @@ jobs:
|
|
|
57
57
|
# install, not at runtime; CI builds in ephemeral runners with
|
|
58
58
|
# no persistent state. Re-evaluate when GitHub's images ship a
|
|
59
59
|
# patched pip.
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
#
|
|
61
|
+
# CVE-2023-36464 / GHSA-4vvm-4w3v-6mr8: medium-severity
|
|
62
|
+
# infinite-loop DoS in PyPDF2 3.0.1, introduced transitively by
|
|
63
|
+
# Maigret. PyPDF2 has no patched release under that package name
|
|
64
|
+
# (upstream recommends migrating to pypdf>=3.9.0), and ZettelForge's
|
|
65
|
+
# AGE-120 username collector does not parse attacker-supplied PDFs or
|
|
66
|
+
# invoke Maigret report generation. Accepted for AGE-120 because the
|
|
67
|
+
# GOV-009 blocking threshold is HIGH/CRITICAL and the collector
|
|
68
|
+
# lazy-imports/fails closed.
|
|
69
|
+
#
|
|
70
|
+
# --skip-editable: do not audit the editable local zettelforge
|
|
71
|
+
# install. It is the project itself, not a third-party dependency,
|
|
72
|
+
# and on a release-prep PR its version is bumped ahead of PyPI
|
|
73
|
+
# (e.g. 2.8.0 before publish), so pip-audit cannot resolve it.
|
|
74
|
+
#
|
|
75
|
+
# --strict is intentionally NOT used: it fails the run whenever a
|
|
76
|
+
# distribution can't be audited, which includes the editable
|
|
77
|
+
# package we deliberately skip ("distribution marked as editable").
|
|
78
|
+
# pip-audit still exits non-zero on any real reported vulnerability,
|
|
79
|
+
# so the "any reported vuln blocks" gate is preserved; every
|
|
80
|
+
# third-party dependency (and the runner's pip) is still audited.
|
|
81
|
+
pip-audit --skip-editable \
|
|
82
|
+
--ignore-vuln=CVE-2026-3219 \
|
|
83
|
+
--ignore-vuln=PYSEC-2026-196 \
|
|
84
|
+
--ignore-vuln=CVE-2023-36464
|
|
62
85
|
|
|
63
86
|
test:
|
|
64
87
|
runs-on: ubuntu-latest
|
|
65
88
|
needs: lint
|
|
66
89
|
strategy:
|
|
67
90
|
fail-fast: false
|
|
91
|
+
# The fastembed model download is shared across Python versions. Running
|
|
92
|
+
# these jobs in parallel can double-hit HuggingFace and trigger 429s.
|
|
93
|
+
max-parallel: 1
|
|
68
94
|
matrix:
|
|
69
95
|
python-version: ['3.12', '3.13']
|
|
70
96
|
|
|
71
97
|
steps:
|
|
72
|
-
- uses: actions/checkout@
|
|
98
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
73
99
|
|
|
74
100
|
- name: Set up Python ${{ matrix.python-version }}
|
|
75
|
-
uses: actions/setup-python@
|
|
101
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
76
102
|
with:
|
|
77
103
|
python-version: ${{ matrix.python-version }}
|
|
78
104
|
|
|
105
|
+
- name: Cache fastembed model
|
|
106
|
+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
|
|
107
|
+
with:
|
|
108
|
+
path: |
|
|
109
|
+
~/.cache/fastembed
|
|
110
|
+
~/.cache/huggingface
|
|
111
|
+
key: fastembed-nomic-embed-text-v1.5-Q-${{ runner.os }}
|
|
112
|
+
|
|
79
113
|
- name: Install dependencies
|
|
80
114
|
run: |
|
|
81
115
|
python -m pip install --upgrade pip
|
|
@@ -102,19 +136,21 @@ jobs:
|
|
|
102
136
|
|
|
103
137
|
- name: Upload coverage
|
|
104
138
|
if: matrix.python-version == '3.12'
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
continue-on-error: true
|
|
140
|
+
env:
|
|
141
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
142
|
+
run: |
|
|
143
|
+
python -m pip install codecov-cli
|
|
144
|
+
codecovcli upload-process -f ./coverage.xml
|
|
109
145
|
|
|
110
146
|
governance:
|
|
111
147
|
runs-on: ubuntu-latest
|
|
112
148
|
needs: lint
|
|
113
149
|
steps:
|
|
114
|
-
- uses: actions/checkout@
|
|
150
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
115
151
|
|
|
116
152
|
- name: Set up Python
|
|
117
|
-
uses: actions/setup-python@
|
|
153
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
118
154
|
with:
|
|
119
155
|
python-version: '3.12'
|
|
120
156
|
|
|
@@ -139,10 +175,10 @@ jobs:
|
|
|
139
175
|
needs: [test, governance]
|
|
140
176
|
|
|
141
177
|
steps:
|
|
142
|
-
- uses: actions/checkout@
|
|
178
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
143
179
|
|
|
144
180
|
- name: Set up Python
|
|
145
|
-
uses: actions/setup-python@
|
|
181
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
146
182
|
with:
|
|
147
183
|
python-version: '3.12'
|
|
148
184
|
|
|
@@ -14,8 +14,8 @@ jobs:
|
|
|
14
14
|
deploy:
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
18
|
-
- uses: actions/setup-python@
|
|
17
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
18
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
19
19
|
with:
|
|
20
20
|
python-version: '3.12'
|
|
21
21
|
- run: pip install mkdocs-material
|
|
@@ -6,15 +6,19 @@ on:
|
|
|
6
6
|
|
|
7
7
|
jobs:
|
|
8
8
|
publish:
|
|
9
|
+
# Package-only releases (for example packages-v0.1.0) are not
|
|
10
|
+
# Python package releases and must not attempt a PyPI upload.
|
|
11
|
+
if: startsWith(github.event.release.tag_name, 'v')
|
|
9
12
|
runs-on: ubuntu-latest
|
|
10
13
|
permissions:
|
|
14
|
+
contents: read
|
|
11
15
|
id-token: write # trusted publishing
|
|
12
16
|
|
|
13
17
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
18
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
15
19
|
|
|
16
20
|
- name: Set up Python
|
|
17
|
-
uses: actions/setup-python@
|
|
21
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
18
22
|
with:
|
|
19
23
|
python-version: '3.12'
|
|
20
24
|
|
|
@@ -17,10 +17,10 @@ jobs:
|
|
|
17
17
|
actions: read
|
|
18
18
|
runs-on: ubuntu-latest
|
|
19
19
|
steps:
|
|
20
|
-
- uses: actions/checkout@
|
|
20
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
|
21
21
|
|
|
22
22
|
- name: Set up Python
|
|
23
|
-
uses: actions/setup-python@
|
|
23
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
|
|
24
24
|
with:
|
|
25
25
|
python-version: '3.12'
|
|
26
26
|
|
|
@@ -65,7 +65,7 @@ jobs:
|
|
|
65
65
|
|
|
66
66
|
- name: Upload SARIF to GitHub
|
|
67
67
|
if: steps.check_token.outputs.has_token == 'true'
|
|
68
|
-
uses: github/codeql-action/upload-sarif@
|
|
68
|
+
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e
|
|
69
69
|
with:
|
|
70
70
|
sarif_file: snyk-code.sarif
|
|
71
71
|
continue-on-error: true
|
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
Visual diagram: [`docs/architecture-diagram.mmd`](docs/architecture-diagram.mmd)
|
|
4
4
|
Deep explanation: [`docs/explanation/architecture.md`](docs/explanation/architecture.md)
|
|
5
5
|
|
|
6
|
+
## Foundational Model: Two Hemispheres
|
|
7
|
+
|
|
8
|
+
ZettelForge is modeled on the brain. A symbolic left hemisphere (the typed,
|
|
9
|
+
STIX-aligned knowledge graph with schema enforcement and rule-based inference) is
|
|
10
|
+
the precise, explainable authority of record. An associative right hemisphere
|
|
11
|
+
(blended vector and graph retrieval with GraphRAG and agentic synthesis) explores
|
|
12
|
+
and proposes. Underneath both, knowledge is atomic facts connected by directed,
|
|
13
|
+
time-stamped, pairwise associations, with time and provenance as first-class
|
|
14
|
+
primitives (Cognitive Data Model, Pieris 2025). The components below implement
|
|
15
|
+
this model. See [design philosophy](docs/explanation/design-philosophy-dual-hemisphere.md)
|
|
16
|
+
for the rationale and research basis, and [GOVERNANCE.md](GOVERNANCE.md) for the
|
|
17
|
+
binding design commitment.
|
|
18
|
+
|
|
6
19
|
## Storage
|
|
7
20
|
|
|
8
21
|
ZettelForge uses a `StorageBackend` ABC (33 methods) with pluggable
|
|
@@ -6,6 +6,54 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.8.0] - 2026-06-26
|
|
10
|
+
|
|
11
|
+
Feature release. Extends the RFC-016 OSINT layer with passive ingest and
|
|
12
|
+
live AGE-120 enrichers, adds AGE-127 prompt-injection /
|
|
13
|
+
retrieval-poisoning guardrails across the memory pipeline, and lays the
|
|
14
|
+
RFC-018 Phase 0 enrichment job ledger foundation, alongside configurable
|
|
15
|
+
LLM generation budgets and bulk detection-ingest hardening. No data
|
|
16
|
+
migration is required.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Prompt-injection and retrieval-poisoning guardrails** (AGE-127). A
|
|
21
|
+
deterministic guard for untrusted memory and CTI text, wired into the
|
|
22
|
+
remember, recall, synthesis, fact-extraction, entity-indexing,
|
|
23
|
+
note-construction, intent-classification, memory-update, and evolution
|
|
24
|
+
boundaries. LLM prompts are hardened to treat query and context text as
|
|
25
|
+
untrusted evidence. (#166)
|
|
26
|
+
- **Passive OSINT executor** (RFC-016 Phase 1.5). Validates collector
|
|
27
|
+
tuples and persists knowledge-graph writes; scopes OSINT alias caching to
|
|
28
|
+
each `KnowledgeGraph` instance; canonicalizes Organization values to
|
|
29
|
+
avoid WHOIS/RDAP duplicates; treats an explicit empty collector
|
|
30
|
+
allow-list as empty; adds user-facing passive OSINT docs and mkdocs
|
|
31
|
+
navigation. (#163)
|
|
32
|
+
- **Live OSINT enrichers** (AGE-120). Native RFC-016 collectors that feed
|
|
33
|
+
the graph backend: WHOIS/DNS, maigret/sherlock username discovery, HIBP
|
|
34
|
+
breach lookup, and blockchain wallet transactions, with OSINT
|
|
35
|
+
ontology / executor / entity-resolution / graph-persistence extensions.
|
|
36
|
+
The network collectors are an opt-in `[osint]` extra, key-gated and
|
|
37
|
+
fail-closed, and are never auto-triggered by `remember()`. Known
|
|
38
|
+
fast-follow robustness items are tracked in #176. (#167)
|
|
39
|
+
- **RFC-018 Phase 0 enrichment job ledger.** Durable SQLite job-metadata
|
|
40
|
+
foundation with `MemoryManager` queue-state recording and regression
|
|
41
|
+
tests, plus the RFC-018 spec for the next ingestion, enrichment,
|
|
42
|
+
parser-safety, metadata-policy, operator-health, and release-train
|
|
43
|
+
improvements. (#178)
|
|
44
|
+
- Configurable LLM generation budgets for causal extraction, synthesis,
|
|
45
|
+
fact extraction, NER, and memory evolution, plus `reasoning_model` scaling
|
|
46
|
+
floors and `<think>` / `<thinking>` JSON parse stripping. (#153)
|
|
47
|
+
- Bulk Sigma/YARA ingest can defer enrichment and drain once via
|
|
48
|
+
`MemoryManager.flush()`. (#153)
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Hardened CCCS YARA metadata validation against multiline regex injection
|
|
53
|
+
and overly permissive author values. (#153)
|
|
54
|
+
- `MemoryManager.flush()` now waits for in-flight enrichment work, not only
|
|
55
|
+
queued work; cached `Plyara` parsing is serialized for concurrent callers. (#153)
|
|
56
|
+
|
|
9
57
|
## [2.7.0] - 2026-05-26
|
|
10
58
|
|
|
11
59
|
Security and OSINT release. Adds the RFC-016 OSINT layer and the first
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Governance
|
|
2
|
+
|
|
3
|
+
## Maintainer
|
|
4
|
+
|
|
5
|
+
ZettelForge is maintained by Patrick Roland (@rolandpg).
|
|
6
|
+
|
|
7
|
+
## Licensing Commitment
|
|
8
|
+
|
|
9
|
+
ZettelForge is MIT licensed. This will not change.
|
|
10
|
+
|
|
11
|
+
## Design Commitment: Brain-Inspired Memory Architecture
|
|
12
|
+
|
|
13
|
+
ZettelForge is modeled on the brain by deliberate decision, not metaphor. This
|
|
14
|
+
commitment is binding on the architecture and will not be quietly abandoned.
|
|
15
|
+
|
|
16
|
+
The memory engine is dual-hemisphere. A symbolic left hemisphere (a typed,
|
|
17
|
+
STIX/ATT&CK-aligned knowledge graph with schema enforcement and rule-based
|
|
18
|
+
inference) is the authority of record: precise, constraint-checked, and
|
|
19
|
+
explainable. An associative right hemisphere (blended vector and graph retrieval,
|
|
20
|
+
GraphRAG, and agentic synthesis) explores and proposes, and writes back into the
|
|
21
|
+
symbolic layer only through validated paths. The two layers stay distinct.
|
|
22
|
+
|
|
23
|
+
Underneath both, knowledge is represented as atomic facts connected by directed,
|
|
24
|
+
time-stamped, pairwise associations (the Cognitive Data Model, Pieris 2025). Time
|
|
25
|
+
and provenance are first-class primitives: every note and claim carries its
|
|
26
|
+
source, markings (TLP), confidence, and time validity, and every retrieval result
|
|
27
|
+
carries provenance. Ingested content is untrusted and is sanitized or isolated
|
|
28
|
+
before any model call. Quantitative quality or performance claims are reproduced
|
|
29
|
+
on the project's own CTI benchmark suite before being stated as fact.
|
|
30
|
+
|
|
31
|
+
Changes to storage, retrieval, the knowledge graph, or ingestion MUST preserve
|
|
32
|
+
this model, and design decisions SHOULD cite the relevant hemisphere or layer.
|
|
33
|
+
The full rationale and research basis are in
|
|
34
|
+
[docs/explanation/design-philosophy-dual-hemisphere.md](docs/explanation/design-philosophy-dual-hemisphere.md).
|
|
35
|
+
|
|
36
|
+
## Extension Boundary
|
|
37
|
+
|
|
38
|
+
The extension package (`zettelforge-enterprise`) provides features
|
|
39
|
+
that require external infrastructure (TypeDB, OpenCTI, multi-tenant
|
|
40
|
+
OAuth). The open source project will never be degraded to create
|
|
41
|
+
commercial incentive.
|
|
42
|
+
|
|
43
|
+
Rule: if a feature works with JSONL + local embeddings, it belongs
|
|
44
|
+
in this repo.
|
|
45
|
+
|
|
46
|
+
## Contributions
|
|
47
|
+
|
|
48
|
+
Community contributions are reviewed on their technical merits.
|
|
49
|
+
All contributions to this repository remain MIT licensed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zettelforge
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: ZettelForge: Agentic Memory System with vector search, knowledge graph, and synthesis
|
|
5
5
|
Project-URL: Homepage, https://github.com/rolandpg/zettelforge
|
|
6
6
|
Project-URL: Documentation, https://docs.threatrecall.ai
|
|
@@ -39,10 +39,11 @@ Provides-Extra: crewai
|
|
|
39
39
|
Requires-Dist: crewai>=1.14.0; extra == 'crewai'
|
|
40
40
|
Provides-Extra: dev
|
|
41
41
|
Requires-Dist: dnspython>=2.4.0; extra == 'dev'
|
|
42
|
-
Requires-Dist: fastapi
|
|
42
|
+
Requires-Dist: fastapi<0.138.2,>=0.100.0; extra == 'dev'
|
|
43
43
|
Requires-Dist: ipwhois>=1.2.0; extra == 'dev'
|
|
44
44
|
Requires-Dist: jinja2>=3.0.0; extra == 'dev'
|
|
45
45
|
Requires-Dist: langchain-core>=0.2.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: maigret>=0.4.0; extra == 'dev'
|
|
46
47
|
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
47
48
|
Requires-Dist: psutil>=5.9.0; extra == 'dev'
|
|
48
49
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
@@ -50,6 +51,7 @@ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
|
50
51
|
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
51
52
|
Requires-Dist: python-whois>=0.9.0; extra == 'dev'
|
|
52
53
|
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
54
|
+
Requires-Dist: sherlock-project>=0.14.0; extra == 'dev'
|
|
53
55
|
Requires-Dist: uvicorn>=0.20.0; extra == 'dev'
|
|
54
56
|
Provides-Extra: extensions
|
|
55
57
|
Requires-Dist: zettelforge-enterprise>=2.1.0; extra == 'extensions'
|
|
@@ -67,13 +69,15 @@ Requires-Dist: onnxruntime-genai>=0.4.0; extra == 'local-onnx'
|
|
|
67
69
|
Provides-Extra: osint
|
|
68
70
|
Requires-Dist: dnspython>=2.4.0; extra == 'osint'
|
|
69
71
|
Requires-Dist: ipwhois>=1.2.0; extra == 'osint'
|
|
72
|
+
Requires-Dist: maigret>=0.4.0; extra == 'osint'
|
|
70
73
|
Requires-Dist: python-whois>=0.9.0; extra == 'osint'
|
|
74
|
+
Requires-Dist: sherlock-project>=0.14.0; extra == 'osint'
|
|
71
75
|
Provides-Extra: pii
|
|
72
76
|
Requires-Dist: presidio-analyzer>=2.2.0; extra == 'pii'
|
|
73
77
|
Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'pii'
|
|
74
78
|
Requires-Dist: spacy>=3.5.0; extra == 'pii'
|
|
75
79
|
Provides-Extra: web
|
|
76
|
-
Requires-Dist: fastapi
|
|
80
|
+
Requires-Dist: fastapi<0.138.2,>=0.100.0; extra == 'web'
|
|
77
81
|
Requires-Dist: jinja2>=3.0.0; extra == 'web'
|
|
78
82
|
Requires-Dist: psutil>=5.9.0; extra == 'web'
|
|
79
83
|
Requires-Dist: uvicorn>=0.20.0; extra == 'web'
|
|
@@ -28,6 +28,79 @@ ZettelForge was evaluated across five benchmark suites. The system runs with zer
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
+
## 0. Performance session 2026-06-09 (v2.8.0-dev, branch perf/cti-memory-40)
|
|
32
|
+
|
|
33
|
+
All numbers below are same-machine (DGX Spark GB10), same-day, deterministic
|
|
34
|
+
config: enrichment disabled (`ZETTELFORGE_ENRICHMENT_ENABLED=false`), keyword
|
|
35
|
+
judge, heuristic answer extraction (no synthesis LLM installed). The clean
|
|
36
|
+
baseline was measured first on unmodified v2.7.0 source after repairing the
|
|
37
|
+
rotted harnesses (dead `disable_enrichment` kwarg, removed `remember_chunked`
|
|
38
|
+
API). Raw logs: `benchmarks/results/session_2026-06-09/`.
|
|
39
|
+
|
|
40
|
+
| Metric | v2.7.0 baseline | optimized | delta |
|
|
41
|
+
|--------|-----------------|-----------|-------|
|
|
42
|
+
| LoCoMo accuracy (keyword judge) | 7.0% | 11.0% | +57% relative |
|
|
43
|
+
| LoCoMo p50 / p95 latency | 336ms / 387ms | 170ms / 193ms | -49% / -50% |
|
|
44
|
+
| LoCoMo ingest (272 sessions) | 262.5s (1.0/s) | 33.8s (8.0/s) | 7.8x |
|
|
45
|
+
| CTI retrieval accuracy | 75.0% | 75.0% | held |
|
|
46
|
+
| CTI p50 latency (idle machine) | 79ms | 39ms | -51% |
|
|
47
|
+
| recall p95 (profiled, 60 calls) | 258ms | 93ms | -64% |
|
|
48
|
+
| recall mean (profiled) | 117.6ms | 54.8ms | -53% |
|
|
49
|
+
|
|
50
|
+
Note on LoCoMo baselines: the published 22% (v2.1.1) used a local synthesis
|
|
51
|
+
LLM (qwen2.5:3b) that is not installed on this host; both columns above use
|
|
52
|
+
the same deterministic heuristic-extraction path, so the comparison is
|
|
53
|
+
apples to apples. Latency includes harness overhead (keyword boost scan and
|
|
54
|
+
synthesis fallback), not just `recall()`.
|
|
55
|
+
|
|
56
|
+
### What changed
|
|
57
|
+
|
|
58
|
+
1. **Scoped knowledge graph reads.** `_recall_inner` traversed the
|
|
59
|
+
process-global JSONL KG (109MB on this host, mixing every store) while
|
|
60
|
+
writes went to the per-store SQLite KG. Isolated stores saw up to ~2000
|
|
61
|
+
phantom note IDs per entity query and never saw their own graph. Recall
|
|
62
|
+
now reads the store's KG via `StoreGraphSource`.
|
|
63
|
+
2. **MemSAD gate vectorized.** The write-time anomaly gate was 93% of
|
|
64
|
+
remember() latency at 50 references (~1.1s/ingest): O(n^2) pure-Python
|
|
65
|
+
cosines plus n^2 n-gram recounts per ingest. numpy pairwise scoring,
|
|
66
|
+
content-hash counter cache, and a bounded reference fetch
|
|
67
|
+
(`get_recent_notes_by_domain`) brought warm evaluate() to ~3.4ms with
|
|
68
|
+
scores pinned to the original math at 1e-9 by characterization tests.
|
|
69
|
+
3. **Rerank policy.** Cross-encoder rerank is the dominant read cost and is
|
|
70
|
+
worth +15pp CTI accuracy (75% vs 60% without it). Grid-tuned bounds:
|
|
71
|
+
8 candidates, 256 chars/doc (accuracy holds from 50x512 down to 8x128;
|
|
72
|
+
collapses below 8 candidates). `rerank_model` is configurable; the
|
|
73
|
+
model grid kept ms-marco-MiniLM-L-6-v2.
|
|
74
|
+
4. **ONNX thread pinning.** 20-core default oversubscribed small batches:
|
|
75
|
+
8 threads cut rerank 23.7ms to 11.5ms and query embedding 5.9ms to 4.5ms.
|
|
76
|
+
5. **Embedding LRU cache** keyed by (model, sha256(text)) — first
|
|
77
|
+
integration of the dormant cache.py.
|
|
78
|
+
6. **Entity fan-out gate.** Query entities whose KG out-degree exceeds
|
|
79
|
+
`retrieval.entity_max_fanout` (default 25) are skipped by graph and
|
|
80
|
+
entity-augmentation stages (conversational speaker names map to every
|
|
81
|
+
session and flood blended recall).
|
|
82
|
+
7. **Enrichment off-switch** (`ZETTELFORGE_ENRICHMENT_ENABLED`) restoring
|
|
83
|
+
deterministic benchmark ingestion; `remember_chunked()` restored.
|
|
84
|
+
|
|
85
|
+
### Chunked-ingestion configuration (recorded, not default)
|
|
86
|
+
|
|
87
|
+
`LOCOMO_CHUNK_SIZE=800` stores each session as ~800-char chunks
|
|
88
|
+
(MemPalace granularity, no 4000-char truncation): 13.0% accuracy at
|
|
89
|
+
p50 347ms / p95 418ms on a ~1400-note store. Compared to the v2.7.0
|
|
90
|
+
baseline at effectively the same latency (336ms), that is +86%
|
|
91
|
+
relative accuracy; compared to the default optimized config it trades
|
|
92
|
+
2x latency for +2pp. Default stays full-session (11.0% at 170ms).
|
|
93
|
+
|
|
94
|
+
### Negative result (recorded)
|
|
95
|
+
|
|
96
|
+
Free-text person extraction (capitalized tokens in running text) dropped
|
|
97
|
+
LoCoMo from 11% to 5% by reshuffling supersession chains at ingest, with no
|
|
98
|
+
single-hop or multi-hop gain. Reverted same day; regression-locked in
|
|
99
|
+
`tests/test_conversational_entities.py`. Conversational NER should come via
|
|
100
|
+
the RFC-001 LLM path, not regex.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
31
104
|
## 1. CTI Retrieval Benchmark (Domain Benchmark)
|
|
32
105
|
|
|
33
106
|
**Date:** 2026-04-10 | **Corpus:** 8 real-world-style CTI reports | **Queries:** 20
|
|
@@ -22,6 +22,8 @@ from datetime import datetime
|
|
|
22
22
|
from typing import List, Dict, Tuple
|
|
23
23
|
|
|
24
24
|
os.environ["ZETTELFORGE_BACKEND"] = "jsonl"
|
|
25
|
+
# Deterministic ingestion: no background LLM enrichment during benchmarks.
|
|
26
|
+
os.environ.setdefault("ZETTELFORGE_ENRICHMENT_ENABLED", "false")
|
|
25
27
|
|
|
26
28
|
from zettelforge import MemoryManager
|
|
27
29
|
|
|
@@ -1,78 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
|
-
"date": "2026-
|
|
3
|
+
"date": "2026-06-09T13:56:15.802128",
|
|
4
4
|
"reports": 8,
|
|
5
5
|
"queries": 20
|
|
6
6
|
},
|
|
7
7
|
"full_session": {
|
|
8
8
|
"strategy": "full_session",
|
|
9
9
|
"notes": 8,
|
|
10
|
-
"ingest_time_s":
|
|
10
|
+
"ingest_time_s": 3.4,
|
|
11
11
|
"accuracy": 75.0,
|
|
12
|
-
"avg_score": 0.
|
|
13
|
-
"p50_latency_ms":
|
|
14
|
-
"p95_latency_ms":
|
|
12
|
+
"avg_score": 0.85,
|
|
13
|
+
"p50_latency_ms": 39.0,
|
|
14
|
+
"p95_latency_ms": 159.0,
|
|
15
15
|
"by_category": {
|
|
16
16
|
"tool-attribution": {
|
|
17
17
|
"accuracy": 40.0,
|
|
18
18
|
"avg_score": 0.7,
|
|
19
|
-
"p50_latency_ms":
|
|
19
|
+
"p50_latency_ms": 42.0
|
|
20
20
|
},
|
|
21
21
|
"cve-linkage": {
|
|
22
22
|
"accuracy": 75.0,
|
|
23
|
-
"avg_score": 0.
|
|
24
|
-
"p50_latency_ms":
|
|
23
|
+
"avg_score": 0.75,
|
|
24
|
+
"p50_latency_ms": 38.0
|
|
25
25
|
},
|
|
26
26
|
"attribution": {
|
|
27
27
|
"accuracy": 100.0,
|
|
28
28
|
"avg_score": 1.0,
|
|
29
|
-
"p50_latency_ms":
|
|
29
|
+
"p50_latency_ms": 59.0
|
|
30
30
|
},
|
|
31
31
|
"temporal": {
|
|
32
32
|
"accuracy": 66.7,
|
|
33
33
|
"avg_score": 0.833,
|
|
34
|
-
"p50_latency_ms":
|
|
34
|
+
"p50_latency_ms": 41.0
|
|
35
35
|
},
|
|
36
36
|
"multi-hop": {
|
|
37
37
|
"accuracy": 100.0,
|
|
38
38
|
"avg_score": 1.0,
|
|
39
|
-
"p50_latency_ms":
|
|
39
|
+
"p50_latency_ms": 38.0
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"chunked_800": {
|
|
44
44
|
"strategy": "chunked_800",
|
|
45
45
|
"notes": 8,
|
|
46
|
-
"ingest_time_s":
|
|
46
|
+
"ingest_time_s": 0.1,
|
|
47
47
|
"accuracy": 75.0,
|
|
48
|
-
"avg_score": 0.
|
|
49
|
-
"p50_latency_ms":
|
|
50
|
-
"p95_latency_ms":
|
|
48
|
+
"avg_score": 0.85,
|
|
49
|
+
"p50_latency_ms": 52.0,
|
|
50
|
+
"p95_latency_ms": 59.0,
|
|
51
51
|
"by_category": {
|
|
52
52
|
"tool-attribution": {
|
|
53
53
|
"accuracy": 40.0,
|
|
54
54
|
"avg_score": 0.7,
|
|
55
|
-
"p50_latency_ms":
|
|
55
|
+
"p50_latency_ms": 50.0
|
|
56
56
|
},
|
|
57
57
|
"cve-linkage": {
|
|
58
58
|
"accuracy": 75.0,
|
|
59
|
-
"avg_score": 0.
|
|
60
|
-
"p50_latency_ms":
|
|
59
|
+
"avg_score": 0.75,
|
|
60
|
+
"p50_latency_ms": 52.0
|
|
61
61
|
},
|
|
62
62
|
"attribution": {
|
|
63
63
|
"accuracy": 100.0,
|
|
64
64
|
"avg_score": 1.0,
|
|
65
|
-
"p50_latency_ms":
|
|
65
|
+
"p50_latency_ms": 52.0
|
|
66
66
|
},
|
|
67
67
|
"temporal": {
|
|
68
68
|
"accuracy": 66.7,
|
|
69
69
|
"avg_score": 0.833,
|
|
70
|
-
"p50_latency_ms":
|
|
70
|
+
"p50_latency_ms": 54.0
|
|
71
71
|
},
|
|
72
72
|
"multi-hop": {
|
|
73
73
|
"accuracy": 100.0,
|
|
74
74
|
"avg_score": 1.0,
|
|
75
|
-
"p50_latency_ms":
|
|
75
|
+
"p50_latency_ms": 33.0
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Instrument note-lookup volume per recall stage.
|
|
3
|
+
|
|
4
|
+
Counts store.get_note_by_id calls (total vs unique ids) and graph result
|
|
5
|
+
sizes per query to locate the redundant-lookup source the profiler exposed
|
|
6
|
+
(~476 lookups/query on an 8-note corpus).
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python benchmarks/instrument_lookups.py
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
|
|
14
|
+
os.environ.setdefault('ZETTELFORGE_ENRICHMENT_ENABLED', 'false')
|
|
15
|
+
|
|
16
|
+
from cti_retrieval_benchmark import CTI_QUERIES, CTI_REPORTS
|
|
17
|
+
|
|
18
|
+
from zettelforge import MemoryManager
|
|
19
|
+
from zettelforge.graph_retriever import GraphRetriever
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main() -> None:
|
|
23
|
+
tmpdir = tempfile.mkdtemp(prefix='instr_lookups_')
|
|
24
|
+
mm = MemoryManager(jsonl_path=f'{tmpdir}/notes.jsonl', lance_path=f'{tmpdir}/vectordb')
|
|
25
|
+
for report in CTI_REPORTS:
|
|
26
|
+
mm.remember(report['content'], source_type='threat_report', source_ref=report['id'], domain='cti')
|
|
27
|
+
|
|
28
|
+
# Wrap get_note_by_id with a counter
|
|
29
|
+
calls = {'total': 0, 'ids': []}
|
|
30
|
+
orig = mm.store.get_note_by_id
|
|
31
|
+
|
|
32
|
+
def counting(nid):
|
|
33
|
+
calls['total'] += 1
|
|
34
|
+
calls['ids'].append(nid)
|
|
35
|
+
return orig(nid)
|
|
36
|
+
|
|
37
|
+
mm.store.get_note_by_id = counting
|
|
38
|
+
|
|
39
|
+
# Wrap graph retrieval to report result sizes
|
|
40
|
+
orig_retrieve = GraphRetriever.retrieve_note_ids
|
|
41
|
+
graph_sizes = []
|
|
42
|
+
|
|
43
|
+
def counting_retrieve(self, query_entities, max_depth=2):
|
|
44
|
+
res = orig_retrieve(self, query_entities, max_depth=max_depth)
|
|
45
|
+
graph_sizes.append(len(res))
|
|
46
|
+
return res
|
|
47
|
+
|
|
48
|
+
GraphRetriever.retrieve_note_ids = counting_retrieve
|
|
49
|
+
|
|
50
|
+
print(f'{"query":<48} {"lookups":>8} {"unique":>7} {"graph_n":>8}')
|
|
51
|
+
for qa in CTI_QUERIES:
|
|
52
|
+
calls['total'] = 0
|
|
53
|
+
calls['ids'] = []
|
|
54
|
+
graph_sizes.clear()
|
|
55
|
+
mm.recall(qa['question'], k=10, exclude_superseded=False)
|
|
56
|
+
uniq = len(set(calls['ids']))
|
|
57
|
+
gsz = graph_sizes[0] if graph_sizes else 0
|
|
58
|
+
print(f'{qa["question"][:46]:<48} {calls["total"]:>8} {uniq:>7} {gsz:>8}')
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == '__main__':
|
|
62
|
+
main()
|