zettelforge 2.5.2__tar.gz → 2.6.2__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.6.2/.github/workflows/stale.yml +47 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.gitignore +13 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/CHANGELOG.md +110 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/CONTRIBUTING.md +43 -0
- zettelforge-2.6.2/CONTRIBUTORS.md +17 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/PKG-INFO +9 -2
- {zettelforge-2.5.2 → zettelforge-2.6.2}/README.md +2 -1
- zettelforge-2.6.2/ROADMAP.md +94 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/config.default.yaml +55 -1
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/THREAT_MODEL.md +7 -5
- zettelforge-2.6.2/docs/explanation/llm-budgets-and-timeouts.md +101 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/troubleshoot.md +99 -8
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/upgrade.md +67 -3
- zettelforge-2.6.2/docs/how-to/use-web-interface.md +177 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/index.md +4 -2
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/configuration.md +111 -6
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/module-inventory.md +40 -1
- zettelforge-2.6.2/docs/reference/web-api.md +415 -0
- zettelforge-2.6.2/docs/rfcs/RFC-014-content-limits.md +143 -0
- zettelforge-2.6.2/docs/rfcs/RFC-015-zettelforge-web-gui.md +624 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/mkdocs.yml +1 -0
- zettelforge-2.6.2/pr-body.md +41 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/pyproject.toml +9 -1
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/__init__.py +1 -1
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/config.py +59 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/governance_validator.py +17 -1
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/lance_maintenance.py +8 -8
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_manager.py +60 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_config.py +27 -0
- zettelforge-2.6.2/tests/test_governance.py +125 -0
- zettelforge-2.6.2/tests/test_web_api.py +493 -0
- zettelforge-2.6.2/web/app.py +1005 -0
- zettelforge-2.6.2/web/static/css/design_tokens.css +114 -0
- zettelforge-2.6.2/web/templates/base.html +201 -0
- zettelforge-2.6.2/web/templates/components/remember_panel.html +72 -0
- zettelforge-2.6.2/web/templates/components/result_card.html +98 -0
- zettelforge-2.6.2/web/templates/components/search_bar.html +11 -0
- zettelforge-2.6.2/web/templates/components/synthesis_block.html +58 -0
- zettelforge-2.6.2/web/templates/components/tab_bar.html +42 -0
- zettelforge-2.6.2/web/templates/config_editor.html +799 -0
- zettelforge-2.6.2/web/templates/index.html +279 -0
- zettelforge-2.6.2/web/ui/assets/favicon-16.png +0 -0
- zettelforge-2.6.2/web/ui/assets/favicon-32.png +0 -0
- zettelforge-2.6.2/web/ui/assets/favicon-512.png +0 -0
- zettelforge-2.6.2/web/ui/assets/favicon-apple-touch.png +0 -0
- zettelforge-2.6.2/web/ui/assets/logo.svg +42 -0
- zettelforge-2.6.2/web/ui/assets/threatrecall-lockup.svg +39 -0
- zettelforge-2.6.2/web/ui/assets/zettelforge_architecture.svg +104 -0
- zettelforge-2.6.2/web/ui/colors_and_type.css +286 -0
- zettelforge-2.6.2/web/ui/favicon.svg +33 -0
- zettelforge-2.6.2/web/ui/fonts/Neuropol.otf +0 -0
- zettelforge-2.6.2/web/ui/index.html +55 -0
- zettelforge-2.6.2/web/ui/js/app.js +144 -0
- zettelforge-2.6.2/web/ui/js/components/header.js +95 -0
- zettelforge-2.6.2/web/ui/js/components/result-card.js +69 -0
- zettelforge-2.6.2/web/ui/js/components/sidebar.js +57 -0
- zettelforge-2.6.2/web/ui/js/components/spinner.js +8 -0
- zettelforge-2.6.2/web/ui/js/components/tabs.js +17 -0
- zettelforge-2.6.2/web/ui/js/components/toast.js +35 -0
- zettelforge-2.6.2/web/ui/js/lib/api.js +83 -0
- zettelforge-2.6.2/web/ui/js/lib/state.js +50 -0
- zettelforge-2.6.2/web/ui/js/views/configuration.js +784 -0
- zettelforge-2.6.2/web/ui/js/views/dashboard.js +251 -0
- zettelforge-2.6.2/web/ui/js/views/entities.js +415 -0
- zettelforge-2.6.2/web/ui/js/views/history.js +296 -0
- zettelforge-2.6.2/web/ui/js/views/ingest.js +425 -0
- zettelforge-2.6.2/web/ui/js/views/knowledge-graph.js +606 -0
- zettelforge-2.6.2/web/ui/js/views/logs.js +256 -0
- zettelforge-2.6.2/web/ui/js/views/search.js +343 -0
- zettelforge-2.5.2/docs/superpowers/research/2026-04-09-ctibench-ragas-benchmarks.md +0 -31
- zettelforge-2.5.2/docs/superpowers/research/2026-04-09-fastembed-local-embeddings.md +0 -460
- zettelforge-2.5.2/docs/superpowers/research/2026-04-09-hybrid-typedb-lancedb-architecture.md +0 -855
- zettelforge-2.5.2/docs/superpowers/research/2026-04-09-local-llm-llama-cpp.md +0 -17
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-anti-aversion-cleanup.md +0 -763
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-causal-graph.md +0 -409
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-ctibench-ate-fix.md +0 -211
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-format-stability.md +0 -1029
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-memory-evolution.md +0 -428
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-merge-consolidation.md +0 -372
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-persistence-semantics.md +0 -422
- zettelforge-2.5.2/docs/superpowers/research/2026-04-15-sqlite-migration.md +0 -636
- zettelforge-2.5.2/docs/superpowers/research/2026-04-17-test-suite-audit.md +0 -178
- zettelforge-2.5.2/docs/superpowers/research/2026-04-24-phase-0.5-attribution-prelim.md +0 -123
- zettelforge-2.5.2/docs/superpowers/research/2026-04-25-graph-retriever-silence.md +0 -117
- zettelforge-2.5.2/docs/superpowers/research/2026-04-25-phase-0.5-attribution.md +0 -230
- zettelforge-2.5.2/docs/superpowers/research/README.md +0 -40
- zettelforge-2.5.2/docs/superpowers/specs/2026-04-15-p1-features-prd.md +0 -990
- zettelforge-2.5.2/tests/test_governance.py +0 -32
- zettelforge-2.5.2/web/app.py +0 -530
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/CODEOWNERS +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/SECURITY.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/dependabot.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/pull_request_template.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/ci.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/docs.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/publish.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/snyk-security.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/ARCHITECTURE.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/CODEOWNERS +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/CODE_OF_CONDUCT.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/Dockerfile +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/GOVERNANCE.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/LICENSE +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/MANIFEST.in +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/SECURITY.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/BENCHMARK_REPORT.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/LOCOMO_BENCHMARK_COMPARISON.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/auto_ralph.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/benchmark_harness.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_benchmark_v2.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_retrieval_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_retrieval_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_v2_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ctibench_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ctibench_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/dataset.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/enterprise-attack.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/evolve_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/evolve_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/graph_test.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/locomo_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/locomo_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/locomo_results_v1.3.0_baseline.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/memoryagentbench.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/memoryagentbench_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/mempalace_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/mempalace_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/naive_memory.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/opencti_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ragas_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ragas_cti_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ragas_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/results/benchmark_report.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/results/ralph_optimization_log.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/scale_benchmark.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/scale_results.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/config.example.yaml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docker/docker-compose.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/.well-known/security.txt +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/CNAME +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/architecture-diagram.mmd +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/archive/PACKAGE_SUMMARY.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/archive/README.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/archive/SKILL.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/ZettelForge_Architecture.mmd +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/architecture-overview.mmd +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/architecture-read-path.mmd +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/architecture-write-path.mmd +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/cf-analytics.js +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/demo.gif +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-16.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-32.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-512.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-64.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-apple-touch.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-old.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/logo.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/social-preview.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-lockup-monogram.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-lockup.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-logo-flat.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-logo-philosophy.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-logo.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-mark.png +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/zettelforge_architecture-light.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/zettelforge_architecture.svg +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/brand/brandIdentity.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/brand/colors_and_type.css +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/architecture.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/epistemic-tiers.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/stix-in-zettelforge.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/two-phase-pipeline.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/zettelkasten-philosophy.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-lancedb.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-opencti.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-pii.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-typedb.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/ingest-news-report.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/integrate-llm-agent.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/migrate-jsonl-to-sqlite.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/query-apt-tools.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/reproduce-benchmarks.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/resolve-aliases.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/run-temporal-query.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/store-threat-actor.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/human-evaluation-rubric.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/llms.txt +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/narrative/2026-04-16-the-memory-problem.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/overrides/main.html +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/architecture-deep-dive.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/governance-controls.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/memory-manager-api.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/retrieval-policies.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/stix-schema.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-001-conversational-entity-extractor.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-002-universal-llm-provider.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-003-adversarial-review.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-003-read-path-depth-routing.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-007-operational-telemetry.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-009-enrichment-pipeline-v2.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-010-enrichment-hotfix.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-011-local-llm-backend-config.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-012-litellm-unified-provider.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-013-presidio-pii-detection.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/stylesheets/brand-tokens.css +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/stylesheets/extra.css +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/stylesheets/fonts/Neuropol.otf +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/tutorials/01-quickstart.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/tutorials/02-first-cti-report.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/examples/athf_bridge.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/examples/mcp_claude_code.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/examples/quickstart.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/governance/controls.yaml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/migrate_jsonl_to_sqlite.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/rebuild_index.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/record-demo.sh +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/typedb-setup.sh +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/zettelforge-rebuild.service +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/zettelforge-rebuild.timer +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/server.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/skills/claude-code-skill.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/skills/openclaw-skill.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/__main__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/alias_resolver.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/backend_factory.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/blended_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/cache.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/consolidation.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/demo.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/base.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/consumers.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/explainer.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/edition.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/entity_indexer.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/extensions.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/fact_extractor.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/graph_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/integrations/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/integrations/langchain_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/intent_classifier.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/json_parse.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/knowledge_graph.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_client.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/base.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/litellm_provider.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/local_provider.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/mock_provider.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/ollama_provider.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/registry.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/log.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/mcp/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/mcp/__main__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/mcp/server.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_evolver.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_store.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_updater.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/note_constructor.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/note_schema.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/observability.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/ocsf.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/ontology.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/pii_validator.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/retry.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/compact_lance.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/human_eval_sampler.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/telemetry_aggregator.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/telemetry_dashboard.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/cli.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/entities.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/ingest.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/parser.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/NOTICE.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/sigma-correlation-rules-schema.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/sigma-detection-rule-schema.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/sigma-filters-schema.json +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/tags.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sqlite_backend.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/storage_backend.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/synthesis_generator.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/synthesis_validator.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/telemetry.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/vector_memory.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/vector_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/cccs_metadata.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/cli.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/entities.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/ingest.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/parser.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/CCCS_YARA.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/CCCS_YARA_values.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/NOTICE.md +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/tags.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/__init__.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/benchmark_scale.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/conftest.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/cloud_example.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/correlation_example.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/process_creation_example.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/tagged_example.yml +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/yara/malware_hash.yar +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/yara/technique_loader.yar +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/yara/webshell.yar +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_basic.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_blended_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_causal_extraction.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_consolidation.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_conversational_entities.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_core.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_cti_integration.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_detection_explainer.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_detection_rule_entities.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_edition.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_embedding.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_entity_indexer_races.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_extensions.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_fact_extractor.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_governance_spec_drift.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_graph_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_human_eval_sampler.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_intent_classifier.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_json_parse.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_kg_edge_schema.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_lance_maintenance.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_langchain_retriever.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_llm_client.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_llm_providers.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_logging_compliance.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_mcp_server.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_memory_evolver.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_memory_updater.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_performance.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_pii_validator.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_recall_integration.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sigma_entities.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sigma_ingest.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sigma_parser.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sqlite_backend.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sqlite_integration.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_storage_backend.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_aggregator.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_collector.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_dashboard.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_integration.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_temporal_graph.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_two_phase_e2e.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_typedb_client.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_yara_entities.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_yara_ingest.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_yara_parser.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/web/auth.py +0 -0
- {zettelforge-2.5.2 → zettelforge-2.6.2}/web/mcp_server.py +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Stale issue and PR management
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: "0 9 * * 1" # Every Monday at 09:00 UTC
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
issues: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
stale:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/stale@v9
|
|
16
|
+
with:
|
|
17
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
+
|
|
19
|
+
# Issues
|
|
20
|
+
stale-issue-message: >
|
|
21
|
+
This issue has been inactive for 60 days. It will be closed
|
|
22
|
+
in 14 days unless there is new activity. If this is still
|
|
23
|
+
relevant, please comment to keep it open.
|
|
24
|
+
close-issue-message: >
|
|
25
|
+
This issue has been automatically closed after 14 days of
|
|
26
|
+
inactivity. It can be reopened at any time.
|
|
27
|
+
days-before-issue-stale: 60
|
|
28
|
+
days-before-issue-close: 14
|
|
29
|
+
stale-issue-label: stale
|
|
30
|
+
exempt-issue-labels: "p0,planned,help wanted,good first issue"
|
|
31
|
+
|
|
32
|
+
# PRs
|
|
33
|
+
stale-pr-message: >
|
|
34
|
+
This pull request has been inactive for 60 days. It will be
|
|
35
|
+
closed in 14 days unless there is new activity.
|
|
36
|
+
close-pr-message: >
|
|
37
|
+
This pull request has been automatically closed after 14 days
|
|
38
|
+
of inactivity.
|
|
39
|
+
days-before-pr-stale: 60
|
|
40
|
+
days-before-pr-close: 14
|
|
41
|
+
stale-pr-label: stale
|
|
42
|
+
exempt-pr-labels: "draft,work-in-progress"
|
|
43
|
+
|
|
44
|
+
# General
|
|
45
|
+
operations-per-run: 100
|
|
46
|
+
remove-stale-when-updated: true
|
|
47
|
+
delete-branch: false
|
|
@@ -12,6 +12,17 @@ tasks/
|
|
|
12
12
|
docs/.ralph/
|
|
13
13
|
agentsync.md
|
|
14
14
|
|
|
15
|
+
# Internal research/specs/plans — workspace scratch that contains
|
|
16
|
+
# unredacted incident detail, customer references, and intermediate
|
|
17
|
+
# reasoning. Kept on disk for the maintainer; not published.
|
|
18
|
+
docs/superpowers/
|
|
19
|
+
|
|
20
|
+
# Claude Code session artifacts — agent worktrees, transcripts, and
|
|
21
|
+
# scratch state created by the maintainer's local tooling. Belt-and-
|
|
22
|
+
# suspenders: these were never tracked, but listing them prevents an
|
|
23
|
+
# accidental `git add -A` from sweeping them in.
|
|
24
|
+
.claude/
|
|
25
|
+
|
|
15
26
|
# MkDocs build output (regenerated from docs/)
|
|
16
27
|
site/
|
|
17
28
|
|
|
@@ -28,6 +39,8 @@ downloads/
|
|
|
28
39
|
eggs/
|
|
29
40
|
.eggs/
|
|
30
41
|
lib/
|
|
42
|
+
!web/ui/js/lib/
|
|
43
|
+
!web/ui/js/lib/*.js
|
|
31
44
|
lib64/
|
|
32
45
|
parts/
|
|
33
46
|
sdist/
|
|
@@ -6,6 +6,116 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.6.2] - 2026-04-27
|
|
10
|
+
|
|
11
|
+
UI/UX release. Fixes the `/config` page so the Apply button actually works
|
|
12
|
+
and surfaces enum-style settings as dropdowns instead of free-text inputs.
|
|
13
|
+
No data migration. No config changes. No API contract changes.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **`/config` "Save Changes" button is no longer dead.** The Quick Settings
|
|
18
|
+
panel called `saveConfigForm()` and `reloadConfig()` — neither function
|
|
19
|
+
was defined anywhere, so the button silently no-op'd and the panel
|
|
20
|
+
rendered "Loading schema..." forever. Replaced with a real form-based
|
|
21
|
+
editor whose Apply button PUTs a nested payload to `/api/config` and
|
|
22
|
+
reloads from server on success.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **Form-based config editor with dropdowns.** `/config` now renders a
|
|
27
|
+
grouped settings form alongside the YAML editor. Known enum fields
|
|
28
|
+
(`backend`, `embedding.provider`, `llm.provider`, `llm.local_backend`,
|
|
29
|
+
`logging.level`, `synthesis.default_format`, `governance.pii.action`)
|
|
30
|
+
render as `<select>` controls instead of free-text inputs. Restart-
|
|
31
|
+
required leaves get a "restart required" badge sourced from the same
|
|
32
|
+
set the server uses, so the UI warning is never out of sync with the
|
|
33
|
+
server's classification.
|
|
34
|
+
- **Pending-changes counter and Revert button.** The form tracks dirty
|
|
35
|
+
fields by dotted path, builds a single nested payload on Apply, and
|
|
36
|
+
shows `N pending change(s) (M need restart)` next to the buttons.
|
|
37
|
+
- **YAML editor accepts both YAML and JSON** (was JSON-only despite the
|
|
38
|
+
label) and skips redacted `***` secrets so they aren't PUT back as
|
|
39
|
+
literal strings.
|
|
40
|
+
|
|
41
|
+
### Tests
|
|
42
|
+
|
|
43
|
+
- 4 new tests in `tests/test_web_api.py`: dropdown enum round-trip for
|
|
44
|
+
`logging.level` (restart-required) and `synthesis.default_format` (live);
|
|
45
|
+
multi-section nested payload from a single Apply; and a regression guard
|
|
46
|
+
on the `/config` HTML structure (form tab, dropdown enum declarations,
|
|
47
|
+
restart-leaf flags, and proof the dead `saveConfigForm`/`reloadConfig`
|
|
48
|
+
handlers are gone). 28 passed, 2 skipped (was 24 + 2).
|
|
49
|
+
|
|
50
|
+
## [2.6.1] - 2026-04-25
|
|
51
|
+
|
|
52
|
+
Hotfix release. Resolves three blockers found in code review of the
|
|
53
|
+
RFC-015 web GUI shipped in v2.6.0. No data migration. No config changes.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- **`/config` HTML page now renders.** `_to_dict` was defined as a closure
|
|
58
|
+
inside `get_config_endpoint`, so every render of `/config` raised
|
|
59
|
+
`NameError`, was silently swallowed by a bare `except`, and left the
|
|
60
|
+
YAML body blank on initial server-side render. Promoted to a module-level
|
|
61
|
+
`_config_to_dict` helper used by both routes. (PR #131)
|
|
62
|
+
- **`PUT /api/config` correctly reports nested restart-required fields.**
|
|
63
|
+
The check compared top-level payload keys against a set of dotted-path
|
|
64
|
+
fields, so payloads like `{"embedding": {"provider": "x"}}` were
|
|
65
|
+
reported as `applied: ["embedding"]`, `pending_restart: []`, telling
|
|
66
|
+
operators a restart-required change had taken effect when it had not.
|
|
67
|
+
Added `_flatten_keys` to walk nested payloads to dotted leaf paths;
|
|
68
|
+
`applied` and `pending_restart` now contain accurate dotted paths.
|
|
69
|
+
(PR #131)
|
|
70
|
+
- **`/config` HTML route is now auth-gated.** `/api/config` was protected,
|
|
71
|
+
but the HTML shell (and once the `_to_dict` bug was fixed, its
|
|
72
|
+
server-rendered YAML body) was reachable without an API key. Added
|
|
73
|
+
`Depends(require_api_guard)` and made the YAML body redact secrets
|
|
74
|
+
before serialization. (PR #131)
|
|
75
|
+
|
|
76
|
+
### Tests
|
|
77
|
+
|
|
78
|
+
- Added four regression tests in `tests/test_web_api.py` covering all
|
|
79
|
+
three fixes. 24 passed, 2 skipped (was 20 + 2).
|
|
80
|
+
|
|
81
|
+
## [2.6.0] - 2026-04-25
|
|
82
|
+
|
|
83
|
+
Feature release. Adds configurable content-size limits for DoS mitigation
|
|
84
|
+
(RFC-014) and moves per-call-site LLM token budgets out of hardcoded
|
|
85
|
+
literals into `LLMConfig`, making them overridable at deploy time.
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
|
|
89
|
+
- **Configurable content size limits** (`GovernanceConfig.limits.max_content_length`,
|
|
90
|
+
default 50 MB). `remember()` calls with content exceeding the limit are
|
|
91
|
+
rejected with a clear error message. Set to `0` to disable the check.
|
|
92
|
+
Environment override: `ZETTELFORGE_LIMITS_MAX_CONTENT_LENGTH`.
|
|
93
|
+
(RFC-014, PR #123)
|
|
94
|
+
- **Per-call-site `max_tokens` budgets configurable via `LLMConfig`**.
|
|
95
|
+
Five new fields: `max_tokens_causal` (8000), `max_tokens_synthesis` (2500),
|
|
96
|
+
`max_tokens_fact` (2500), `max_tokens_ner` (2500), `max_tokens_evolution` (2500).
|
|
97
|
+
Defaults match v2.5.2 values. No behavioral change for existing configs.
|
|
98
|
+
(PR #126, issue #125)
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
|
|
102
|
+
- **Docs: config reconciliation for v2.5.2** — `config.default.yaml` gained
|
|
103
|
+
the `lance:` section (RFC-009 Phase 1.5), `docs/reference/configuration.md`
|
|
104
|
+
now covers all v2.5.2 knobs (`lance`, `pii`, per-call-site budgets), and
|
|
105
|
+
`docs/explanation/llm-budgets-and-timeouts.md` explains the reasoning-model
|
|
106
|
+
token-budget tradeoffs. (PR #126)
|
|
107
|
+
- **`_apply_yaml()` now handles `lance:` section** — previously the
|
|
108
|
+
`lance.cleanup_interval_minutes` and `lance.cleanup_older_than_seconds` YAML
|
|
109
|
+
knobs were silently ignored (regression from RFC-009 Phase 1.5 landing in
|
|
110
|
+
v2.4.x without the `_apply_yaml` branch). (PR #126, code review finding)
|
|
111
|
+
|
|
112
|
+
### Internal
|
|
113
|
+
|
|
114
|
+
- **Removed `docs/superpowers/` from version control**. The directory holds
|
|
115
|
+
workspace scratch — internal research and notes with unredacted incident
|
|
116
|
+
detail. 18 files (7319 lines) untracked. No published content lost.
|
|
117
|
+
(PR #128)
|
|
118
|
+
|
|
9
119
|
## [2.5.2] - 2026-04-25
|
|
10
120
|
|
|
11
121
|
Hotfix release. Restores end-to-end functionality of synthesis, causal
|
|
@@ -37,6 +37,49 @@ If your contribution needs TypeDB or OpenCTI, open an issue to discuss.
|
|
|
37
37
|
We keep the extension boundary clear so contributors know their work
|
|
38
38
|
will always remain open source.
|
|
39
39
|
|
|
40
|
+
### For major features: Start with an RFC
|
|
41
|
+
|
|
42
|
+
If you're proposing a significant new feature (new subsystem, new backend,
|
|
43
|
+
breaking API change), open an RFC before writing code. RFCs live in
|
|
44
|
+
`docs/rfcs/` and follow the template from the existing RFCs in that
|
|
45
|
+
directory. Open a Discussion first to socialize the idea, then file a
|
|
46
|
+
draft RFC as a PR. This prevents wasted effort on work that won't be
|
|
47
|
+
accepted.
|
|
48
|
+
|
|
49
|
+
See [ROADMAP.md](ROADMAP.md) for the current priorities and what's
|
|
50
|
+
planned for upcoming releases.
|
|
51
|
+
|
|
52
|
+
### Good first issues
|
|
53
|
+
|
|
54
|
+
Issues tagged [good first issue](https://github.com/rolandpg/zettelforge/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
|
55
|
+
have structured acceptance criteria in the issue body. Check the issue
|
|
56
|
+
for: which files to edit, test expectations, and example input/output.
|
|
57
|
+
If an issue is unclear, ask in the issue comments.
|
|
58
|
+
|
|
59
|
+
## Issue triage
|
|
60
|
+
|
|
61
|
+
This project is maintained by a solo developer (per GOV-006). Here is
|
|
62
|
+
what you can expect:
|
|
63
|
+
|
|
64
|
+
- **New issues**: triaged within 7 days. You will get a response (even
|
|
65
|
+
if it's "not planned, closing").
|
|
66
|
+
- **Bug reports**: severity assessed within 7 days. P0 (crash, data
|
|
67
|
+
loss, security) gets a same-day response.
|
|
68
|
+
- **Feature requests**: tagged with `enhancement` on creation. The
|
|
69
|
+
maintainer will add `planned`, `deferred`, or `won't fix` within 7
|
|
70
|
+
days.
|
|
71
|
+
- **PR reviews**: first review within 14 days of submission. Smaller
|
|
72
|
+
PRs get reviewed faster.
|
|
73
|
+
- **Stale issues**: issues with no activity for 60 days are tagged
|
|
74
|
+
`stale` and closed after 14 more days without response. This keeps
|
|
75
|
+
the tracker manageable for a solo maintainer.
|
|
76
|
+
|
|
77
|
+
## Contributor recognition
|
|
78
|
+
|
|
79
|
+
Every contributor is listed in [CONTRIBUTORS.md](CONTRIBUTORS.md),
|
|
80
|
+
regardless of contribution size. If you submit a PR that gets merged,
|
|
81
|
+
you will be added. If your name is missing, open a PR.
|
|
82
|
+
|
|
40
83
|
## Code Style
|
|
41
84
|
|
|
42
85
|
- Follow PEP 8
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Contributors
|
|
2
|
+
|
|
3
|
+
Thank you to everyone who has contributed to ZettelForge.
|
|
4
|
+
|
|
5
|
+
## Maintainers
|
|
6
|
+
|
|
7
|
+
- **Patrick G. Roland II** — creator and lead maintainer
|
|
8
|
+
|
|
9
|
+
## Contributors
|
|
10
|
+
|
|
11
|
+
This file tracks individuals who have contributed code, documentation, design, or other improvements. The maintainer updates it with each release.
|
|
12
|
+
|
|
13
|
+
If you have contributed and your name is missing, please open a PR or issue.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
*This project uses a "thank you, next" acknowledgment model. All contributors are listed regardless of contribution size. A typo fix counts.*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zettelforge
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.2
|
|
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
|
|
@@ -36,12 +36,16 @@ Requires-Dist: requests>=2.31.0
|
|
|
36
36
|
Requires-Dist: structlog>=24.0.0
|
|
37
37
|
Requires-Dist: tantivy>=0.11.0
|
|
38
38
|
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: jinja2>=3.0.0; extra == 'dev'
|
|
39
41
|
Requires-Dist: langchain-core>=0.2.0; extra == 'dev'
|
|
40
42
|
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: psutil>=5.9.0; extra == 'dev'
|
|
41
44
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
42
45
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
43
46
|
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
44
47
|
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: uvicorn>=0.20.0; extra == 'dev'
|
|
45
49
|
Provides-Extra: extensions
|
|
46
50
|
Requires-Dist: zettelforge-enterprise>=2.1.0; extra == 'extensions'
|
|
47
51
|
Provides-Extra: langchain
|
|
@@ -61,6 +65,8 @@ Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'pii'
|
|
|
61
65
|
Requires-Dist: spacy>=3.5.0; extra == 'pii'
|
|
62
66
|
Provides-Extra: web
|
|
63
67
|
Requires-Dist: fastapi>=0.100.0; extra == 'web'
|
|
68
|
+
Requires-Dist: jinja2>=3.0.0; extra == 'web'
|
|
69
|
+
Requires-Dist: psutil>=5.9.0; extra == 'web'
|
|
64
70
|
Requires-Dist: uvicorn>=0.20.0; extra == 'web'
|
|
65
71
|
Description-Content-Type: text/markdown
|
|
66
72
|
|
|
@@ -79,8 +85,9 @@ It extracts CVEs, threat actors, IOCs, and ATT&CK techniques from analyst notes
|
|
|
79
85
|
[](https://www.python.org/downloads/)
|
|
80
86
|
[](https://opensource.org/licenses/MIT)
|
|
81
87
|
[](https://github.com/rolandpg/zettelforge/actions)
|
|
88
|
+
[](https://github.com/rolandpg/zettelforge/issues)
|
|
82
89
|
|
|
83
|
-
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai)**
|
|
90
|
+
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai) · [🗺️ Roadmap](ROADMAP.md)**
|
|
84
91
|
|
|
85
92
|
<p align="center">
|
|
86
93
|
<a href="https://www.buymeacoffee.com/xypher22pr0" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me a Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
@@ -13,8 +13,9 @@ It extracts CVEs, threat actors, IOCs, and ATT&CK techniques from analyst notes
|
|
|
13
13
|
[](https://www.python.org/downloads/)
|
|
14
14
|
[](https://opensource.org/licenses/MIT)
|
|
15
15
|
[](https://github.com/rolandpg/zettelforge/actions)
|
|
16
|
+
[](https://github.com/rolandpg/zettelforge/issues)
|
|
16
17
|
|
|
17
|
-
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai)**
|
|
18
|
+
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai) · [🗺️ Roadmap](ROADMAP.md)**
|
|
18
19
|
|
|
19
20
|
<p align="center">
|
|
20
21
|
<a href="https://www.buymeacoffee.com/xypher22pr0" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me a Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# ZettelForge Roadmap
|
|
2
|
+
|
|
3
|
+
Last updated: 2026-04-25
|
|
4
|
+
|
|
5
|
+
This document communicates what the maintainer is building, what is on hold, and what is out of scope. It is updated when priorities shift.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Current release: v2.6.1
|
|
10
|
+
|
|
11
|
+
Shipped: 2026-04-25. See `CHANGELOG.md` for details.
|
|
12
|
+
|
|
13
|
+
The v2.6 series (RFC-013 through RFC-015) delivered PII detection (Presidio), configurable content size limits, and the Web Management Interface. The v2.5 series (RFC-009 through RFC-012) delivered the enrichment pipeline v2, local LLM backends, and unified provider config.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## v2.7.0 targets (next release)
|
|
18
|
+
|
|
19
|
+
Target: 2026-05-09. Scope is frozen at the items below. Everything else defers to v2.8.0 unless marked **P0**.
|
|
20
|
+
|
|
21
|
+
### Must ship (P0)
|
|
22
|
+
|
|
23
|
+
- [ ] **Issue #125: Harden reasoning-model LLM budget plumbing.** Regression tests for `max_tokens` at each call site, config-overridable budgets, `thinking` tag stripping in `json_parse.py`, and a `reasoning_model: bool` auto-scaling flag. Post-#124 follow-up. (Est: 2-3 days)
|
|
24
|
+
- [ ] **Issue #73: Tighten CCCS metadata regexes (SEC-6 / SEC-7).** Low hanging security hardening. (Est: 0.5 day)
|
|
25
|
+
- [ ] **Issue #72: MemoryManager.remember(sync=True) dominates bulk ingest.** The YARA p95 plyara tail needs a timeout or chunked processing path. (Est: 1 day)
|
|
26
|
+
|
|
27
|
+
### Nice to have (P1)
|
|
28
|
+
|
|
29
|
+
- [ ] **Issue #71: Add typed DetectionMeta extension to MemoryNote.Metadata.** Paves the way for richer entity metadata downstream. (Est: 0.5 day)
|
|
30
|
+
- [ ] **Issue #51: Ratchet governance coverage threshold from 67% toward 80%.** Incremental. (Est: 1 day)
|
|
31
|
+
|
|
32
|
+
### Community items (open for contribution)
|
|
33
|
+
|
|
34
|
+
See the [good first issue](https://github.com/rolandpg/zettelforge/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. These are pre-scoped with acceptance criteria:
|
|
35
|
+
|
|
36
|
+
- #47 — IPv6 address extraction
|
|
37
|
+
- #46 — YARA rule reference extraction
|
|
38
|
+
- #45 — Sigma rule ID extraction
|
|
39
|
+
- #39 — Threat actor alias mappings for Chinese APT groups
|
|
40
|
+
- #36 — Architecture decision records (ADRs)
|
|
41
|
+
- #44 — Example: MISP JSON feed ingestion
|
|
42
|
+
- #43 — Example: Slack bot for CTI queries
|
|
43
|
+
- #41 — Example: Jupyter notebook CTI analysis workflow
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Next release (v2.7.1+)
|
|
48
|
+
|
|
49
|
+
Target: approximately 2026-05-23. Provisional scope; will be finalized after v2.7.0 ships.
|
|
50
|
+
|
|
51
|
+
- [ ] **CrewAI tool wrapper** (#40). Integration path for CrewAI agents to use ZettelForge as a memory backend.
|
|
52
|
+
- [ ] **OpenCTI sync overhaul**. Improving the bidirectional sync reliability from the initial implementation.
|
|
53
|
+
- [ ] **Detection rules as first-class entities** (#feat/detection-rules-first-class branch). Sigma/YARA rules stored and searchable as knowledge graph nodes.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Backlog / on hold
|
|
58
|
+
|
|
59
|
+
These are tracked but not actively scheduled:
|
|
60
|
+
|
|
61
|
+
- **MCP registry publish** (feat/mcp-registry-publish branch). Publishing the MCP server to the official MCP registry.
|
|
62
|
+
- **Enterprise split**. Separating the current monolithic package into community + enterprise tiers. Governance, license boundary, and packaging work. Not blocked, but deferred until community adoption justifies the overhead.
|
|
63
|
+
- **TypeDB read-path hardening**. Depth routing and schema versioning for the TypeDB backend. Most users run on JSONL/SQLite; TypeDB is a small fraction of the install base.
|
|
64
|
+
- **Conversational entity extractor** (stash: feature/RFC-001-conversational-entity-extractor). Interactive refinement of extracted entities by analyst chat. Requires UX thinking and a frontend update.
|
|
65
|
+
- **Pydantic v3 upgrade prep** (stash: test-fix/pydantic-v3-prep). Preparing internal models for Pydantic v3 migration. Low urgency; no upstream pressure yet.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Out of scope (not building)
|
|
70
|
+
|
|
71
|
+
These have been proposed or discussed and explicitly decided against:
|
|
72
|
+
|
|
73
|
+
- **UI framework migration** (React, Vue, Svelte, etc.). The web GUI is a vanilla JS SPA using the ZettelForge Design System. No npm build step, no JS framework. This is intentional: the SPA must remain maintainable by a solo developer and installable with `pip install zettelforge[web]` without a separate build step. Not changing.
|
|
74
|
+
- **Docker containerization**. Deferred to v2.x post-v1.0 per the tech stack decision. The in-process architecture already makes deployment trivial.
|
|
75
|
+
- **Cloud-hosted memory backend**. ZettelForge is designed for local-first, air-gapped, and on-prem deployments. A cloud sync layer would compromise the security model and is not on the roadmap.
|
|
76
|
+
- **LangChain / LangGraph integration as a default**. Out of scope per the CLAUDE.md rules in this repo. Community wraps are welcome (see the CrewAI issue for how integration should work).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Release cadence
|
|
81
|
+
|
|
82
|
+
- **Minor releases (v2.x.0)**: roughly every 2-3 weeks, bundling feature work and hardening.
|
|
83
|
+
- **Patch releases (v2.x.y)**: as needed for P0 bugs, security fixes, and regressions. No pre-scheduled date.
|
|
84
|
+
- **Major releases (v3.0.0+)**: not yet planned. The API is still evolving. A major version bump will come with a public deprecation notice and migration guide.
|
|
85
|
+
|
|
86
|
+
## Commitment
|
|
87
|
+
|
|
88
|
+
This roadmap is a best-effort forecast, not a contract. Priorities shift based on user feedback, security findings, and the maintainer's availability (solo maintainer, GOV-006 declared).
|
|
89
|
+
|
|
90
|
+
Issues tagged with a target release are actively planned. Issues without a release tag are candidates for the next roadmap refresh.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
[Back to README](README.md)
|
|
@@ -374,6 +374,8 @@ synthesis:
|
|
|
374
374
|
# Env overrides:
|
|
375
375
|
# ZETTELFORGE_PII_ENABLED=true
|
|
376
376
|
# ZETTELFORGE_PII_ACTION=redact
|
|
377
|
+
# ZETTELFORGE_LIMITS_MAX_CONTENT_LENGTH=104857600
|
|
378
|
+
# ZETTELFORGE_LIMITS_RECALL_TIMEOUT=60
|
|
377
379
|
#
|
|
378
380
|
governance:
|
|
379
381
|
enabled: true
|
|
@@ -385,9 +387,32 @@ governance:
|
|
|
385
387
|
entities: []
|
|
386
388
|
language: en
|
|
387
389
|
nlp_model: en_core_web_sm
|
|
390
|
+
limits:
|
|
391
|
+
max_content_length: 52428800 # 50 MB, 0 = unlimited
|
|
392
|
+
recall_timeout_seconds: 30.0 # seconds, 0 = unlimited
|
|
388
393
|
|
|
389
394
|
|
|
390
|
-
# ──
|
|
395
|
+
# ── LanceDB Maintenance (RFC-009 Phase 1.5) ─────────────────────────────────
|
|
396
|
+
# Background daemon that prunes stale LanceDB version-history per shard.
|
|
397
|
+
#
|
|
398
|
+
# Examples:
|
|
399
|
+
# # Default — clean up every hour, keep versions < 1 hour old
|
|
400
|
+
# cleanup_interval_minutes: 60
|
|
401
|
+
# cleanup_older_than_seconds: 3600
|
|
402
|
+
#
|
|
403
|
+
# # Aggressive cleanup (high-ingestion environments)
|
|
404
|
+
# cleanup_interval_minutes: 15
|
|
405
|
+
# cleanup_older_than_seconds: 600
|
|
406
|
+
#
|
|
407
|
+
# # Disable cleanup entirely
|
|
408
|
+
# cleanup_interval_minutes: 0
|
|
409
|
+
#
|
|
410
|
+
lance:
|
|
411
|
+
cleanup_interval_minutes: 60
|
|
412
|
+
cleanup_older_than_seconds: 3600
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# ── Cache
|
|
391
416
|
# In-memory cache for TypeDB query results. Reduces round-trips for
|
|
392
417
|
# frequently accessed entities and relationships.
|
|
393
418
|
#
|
|
@@ -434,3 +459,32 @@ logging:
|
|
|
434
459
|
level: INFO
|
|
435
460
|
log_intents: true
|
|
436
461
|
log_causal: true
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# ── Web UI (RFC-015) ─────────────────────────────────────────────────────────
|
|
465
|
+
# ZettelForge Web Management Interface — SPA served at GET /.
|
|
466
|
+
#
|
|
467
|
+
# Examples:
|
|
468
|
+
# # Default — enabled on 0.0.0.0:8088
|
|
469
|
+
# enabled: true
|
|
470
|
+
# host: 0.0.0.0
|
|
471
|
+
# port: 8088
|
|
472
|
+
#
|
|
473
|
+
# # Custom port (e.g., behind nginx reverse proxy)
|
|
474
|
+
# enabled: true
|
|
475
|
+
# host: 127.0.0.1
|
|
476
|
+
# port: 9000
|
|
477
|
+
#
|
|
478
|
+
# # Disable web UI entirely (library-only mode)
|
|
479
|
+
# enabled: false
|
|
480
|
+
#
|
|
481
|
+
# Env overrides:
|
|
482
|
+
# ZETTELFORGE_WEB_ENABLED=true
|
|
483
|
+
# ZETTELFORGE_WEB_PORT=8088
|
|
484
|
+
# ZETTELFORGE_WEB_UI_DIR=/path/to/ui
|
|
485
|
+
#
|
|
486
|
+
web:
|
|
487
|
+
enabled: true
|
|
488
|
+
host: 0.0.0.0
|
|
489
|
+
port: 8088
|
|
490
|
+
|
|
@@ -137,9 +137,9 @@ TB-1 ─────────────────────────
|
|
|
137
137
|
|
|
138
138
|
| ID | Threat | Component | Risk | Mitigation |
|
|
139
139
|
|----|--------|-----------|------|------------|
|
|
140
|
-
| D-01 | Large content in `remember()` exhausts memory or blocks the enrichment queue | MemoryManager (P1) | **
|
|
140
|
+
| D-01 | Large content in `remember()` exhausts memory or blocks the enrichment queue | MemoryManager (P1) | **Low** — gracefully rejected | `governance.limits.max_content_length` (RFC-014, default 50 MB) blocks oversized content with a clear error. `remember_report()` chunks long documents. Enrichment queue has `maxsize=500` backpressure. |
|
|
141
141
|
| D-02 | LLM provider (ollama, litellm) hangs and blocks `remember()` | LLM Provider (TB-4) | **High** — operation blocks | OllamaProvider has timeout (RFC-010, default 60s). LitellmProvider has timeout + num_retries. `generate()` returns empty string on recoverable failure. Fallback provider (e.g., local -> ollama) gives alternative path. |
|
|
142
|
-
| D-03 | Malicious query triggers deep graph traversal exhausting time/resources | BlendedRetriever | **Medium** —
|
|
142
|
+
| D-03 | Malicious query triggers deep graph traversal exhausting time/resources | BlendedRetriever | **Medium** — bounded, but timeout may still block | `governance.limits.recall_timeout_seconds` (RFC-014, default 30s) applies a wall-clock timeout to the recall pipeline, but the current `ThreadPoolExecutor`-based approach must not be treated as guaranteeing prompt return on timeout. `max_graph_depth` (default 2) limits BFS hops. `default_k` (default 10) limits results. Reclassify to **Low** only after the timeout path is verified to return promptly and log `recall_timed_out` without waiting for the running task to finish. |
|
|
143
143
|
| D-04 | spaCy model download blocks first `remember()` when PII is enabled | PIIValidator (lazy load) | **Low** — delayed first call (~2-3 seconds) | One-time download cost. Matching fastembed pattern. Can be pre-downloaded for air-gapped deployments. |
|
|
144
144
|
|
|
145
145
|
### 2.6 Elevation of Privilege
|
|
@@ -158,8 +158,8 @@ TB-1 ─────────────────────────
|
|
|
158
158
|
|------------|-------|--------------|
|
|
159
159
|
| **Critical** | 2 | T-01 (storage tampering), I-01 (unencrypted data at rest), E-02 (governance bypass via filesystem) |
|
|
160
160
|
| **High** | 7 | S-01 (spoofed MCP client), S-03 (config tampering), T-02 (config security downgrade), R-01 (repudiation without audit), I-02 (PII in stored notes), D-02 (LLM provider hang), E-01 (cross-tenant data access) |
|
|
161
|
-
| **Medium** |
|
|
162
|
-
| **Low** |
|
|
161
|
+
| **Medium** | 7 | S-02 (fake LLM provider), T-04 (retrieval poisoning), R-02, R-03, I-04 (error message leakage), E-03 |
|
|
162
|
+
| **Low** | 3 | D-01, D-03, D-04 (PII model download delay) |
|
|
163
163
|
|
|
164
164
|
### Top 5 Mitigations (Priority Order)
|
|
165
165
|
|
|
@@ -181,6 +181,8 @@ TB-1 ─────────────────────────
|
|
|
181
181
|
| API key redaction | I-03 | `LLMConfig.__repr__` redacts api_key and sensitive extra keys | Unit tests in `test_llm_providers.py` |
|
|
182
182
|
| PII detection + redaction | I-02 | PIIValidator (RFC-013): log/redact/block | Unit tests in `test_pii_validator.py` |
|
|
183
183
|
| LLM provider timeout | D-02 | `OllamaProvider` timeout=60s, `LiteLLMProvider` timeout + num_retries | Unit tests (RFC-010, RFC-012) |
|
|
184
|
+
| Content size limit | D-01 | `governance.limits.max_content_length` (RFC-014, default 50 MB) blocks oversized content | Unit tests in `test_governance.py` |
|
|
185
|
+
| Recall timeout | D-03 | `governance.limits.recall_timeout_seconds` (RFC-014, default 30s) wraps recall in ThreadPoolExecutor with wall-clock timeout | Unit tests in `test_governance.py` |
|
|
184
186
|
| Config env-var resolution | I-03 | `${ENV_VAR}` syntax prevents raw secrets in YAML | Unit tests |
|
|
185
187
|
| Configurable model provider | S-02, E-03 | `provider` key selects backend; no implicit unauthenticated outbound calls | Config validation |
|
|
186
188
|
| Enrichment queue backpressure | D-01 | `maxsize=500` bounded queue | Code review |
|
|
@@ -189,7 +191,6 @@ TB-1 ─────────────────────────
|
|
|
189
191
|
|
|
190
192
|
| Recommendation | Threat(s) | Effort | Priority |
|
|
191
193
|
|---------------|-----------|--------|----------|
|
|
192
|
-
| Add content size limit to `remember()` | D-01 | Small | P3 |
|
|
193
194
|
| Add global exception handler that sanitizes error output | I-04 | Medium | P2 |
|
|
194
195
|
| Add TLS verification option for self-hosted LLM endpoints | S-02 | Small | P2 |
|
|
195
196
|
| Add config file integrity check (SHA-256 of default vs. loaded) | T-02, S-03 | Medium | P3 |
|
|
@@ -231,6 +232,7 @@ Per GOV-021, the following data types exist in the system:
|
|
|
231
232
|
|
|
232
233
|
| Change | RFC/PR | Date | Threat Model Impact |
|
|
233
234
|
|--------|--------|------|---------------------|
|
|
235
|
+
| Content size limits + recall timeout | RFC-014 | 2026-04-25 | Mitigation for D-01 (content size limit, default 50 MB); partial mitigation for D-03 (timeout) |
|
|
234
236
|
| PII detection and redaction | RFC-013 (PR #118) | 2026-04-25 | New control for I-02; new attack surface (D-04); PII text logging fixed |
|
|
235
237
|
| LiteLLM unified provider | RFC-012 (PR #108) | 2026-04-25 | New provider for I-03 (API keys); new outbound traffic pattern (TB-4) |
|
|
236
238
|
| Local LLM backend selection | RFC-011 (PR #104) | 2026-04-25 | No new threat surface — extends existing local provider |
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "LLM budgets, timeouts, and what they cost you"
|
|
3
|
+
description: "How ZettelForge's LLM token budgets and HTTP timeouts trade latency for end-to-end correctness — what each knob controls, what it costs, and when to override."
|
|
4
|
+
diataxis_type: explanation
|
|
5
|
+
audience: "Operator / Developer"
|
|
6
|
+
tags: [llm, configuration, performance, reasoning-models, ollama]
|
|
7
|
+
last_updated: "2026-04-25"
|
|
8
|
+
version: "2.6.0"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# LLM budgets, timeouts, and what they cost you
|
|
12
|
+
|
|
13
|
+
ZettelForge makes five distinct kinds of LLM calls (causal triple extraction, synthesis, fact extraction, conversational NER, neighbor evolution). Each has a hardcoded `max_tokens` budget and shares a single configurable `llm.timeout`. The defaults trade ingest latency for end-to-end correctness on a reference reasoning model (`qwen3.5:9b`, Q4_K_M). This page explains why the defaults look the way they do and when you should override them.
|
|
14
|
+
|
|
15
|
+
If you just want the table of values, see the [Configuration Reference §Per-call-site `max_tokens` budgets](../reference/configuration.md#per-call-site-max_tokens-budgets-hardcoded-v252). This page is the *why*.
|
|
16
|
+
|
|
17
|
+
## The hidden-thinking-token problem
|
|
18
|
+
|
|
19
|
+
Modern reasoning models — qwen3.5+, qwen3.6, nemotron-3, deepseek-r1, gemini-thinking — generate two streams of tokens for any prompt:
|
|
20
|
+
|
|
21
|
+
1. **Reasoning tokens.** Wrapped in `<think>...</think>`, these are the model's internal scratch work. Ollama hides them from the `response` field by default but **they still count against `num_predict`**.
|
|
22
|
+
2. **Answer tokens.** What the model actually emits as the final user-visible output. These appear in `response`.
|
|
23
|
+
|
|
24
|
+
If `num_predict` is 300 tokens and the model uses 280 of them reasoning, you get 20 tokens of answer — usually not enough for valid JSON. If it uses all 300, you get an empty string and Ollama returns `done_reason: "length" eval_count: 300 response: ""`. The pre-2.5.2 budgets (300/400/800/1024) were sized for non-reasoning models and silently failed every call on the reasoning model that ZettelForge defaults to. v2.5.2 raised the per-call-site caps to give reasoning room *and* answer room on the same generation.
|
|
25
|
+
|
|
26
|
+
## Per-call-site budgets — and why each one is what it is
|
|
27
|
+
|
|
28
|
+
### Causal triple extraction (`note_constructor.py`, **8000 tokens**)
|
|
29
|
+
|
|
30
|
+
The largest budget anywhere in the codebase. The prompt asks the model to enumerate *every* causal relation in a passage of up to 2000 characters, validating each relation against an allowlist. Empirical: `qwen3.5:9b` at 4000 tokens succeeded only ~70% of the time (eval_count varied 2.8k–4k+, with the longer reasoning chains hitting the budget cap). 8000 keeps the success rate above 95% on the same model. Wall-clock cost: 60–140 s per call.
|
|
31
|
+
|
|
32
|
+
### Synthesis (`synthesis_generator.py`, **2500 tokens**)
|
|
33
|
+
|
|
34
|
+
Single-answer prompts converge faster than enumerate-everything prompts. 2500 covers reasoning + a paragraph of JSON answer. Wall-clock: 20–50 s per query.
|
|
35
|
+
|
|
36
|
+
### Fact extraction (`fact_extractor.py`, **2500 tokens**)
|
|
37
|
+
|
|
38
|
+
Similar profile to synthesis — bounded JSON output. The pre-2.5.2 cap was 400, which left this silently no-opping on every reasoning-model call.
|
|
39
|
+
|
|
40
|
+
### Conversational NER (`entity_indexer.py`, **2500 tokens**)
|
|
41
|
+
|
|
42
|
+
The regex fast-path covers CTI types (CVE, ATT&CK, IOCs); LLM NER fills in `person`, `location`, `organization`, `event`, `activity`, `temporal`. Output is a small JSON object so 2500 is generous. The retry path uses the same budget.
|
|
43
|
+
|
|
44
|
+
### Neighbor evolution (`memory_evolver.py`, **2500 tokens** × 2)
|
|
45
|
+
|
|
46
|
+
Two-note comparison + ADD/UPDATE/DELETE/NOOP decision. Both the first call and the parse-retry call use 2500. Parse-retry exists because reasoning models occasionally emit prose preamble before the JSON; the second call reasserts JSON-only and usually gets it.
|
|
47
|
+
|
|
48
|
+
## The shared timeout
|
|
49
|
+
|
|
50
|
+
`llm.timeout` (default **180 s** in v2.5.2; was 60 s pre-fix) governs the HTTP read deadline on every Ollama call.
|
|
51
|
+
|
|
52
|
+
The 60 s default fired before causal extraction at 8000 tokens could complete on a 9B model. The fix had to come at both ends: bigger budget *and* longer timeout. If you lower one, lower the other together — bumping the budget without bumping the timeout just trades empty-response failures for ReadTimeout failures.
|
|
53
|
+
|
|
54
|
+
## When to override
|
|
55
|
+
|
|
56
|
+
The defaults are calibrated for `qwen3.5:9b` Q4_K_M on a single GPU. You want to override if:
|
|
57
|
+
|
|
58
|
+
### You're on faster hardware (H100, multi-GPU, large batch)
|
|
59
|
+
|
|
60
|
+
You can lower `llm.timeout` to e.g. 60 s without losing correctness, since each generation completes faster. The budgets themselves are about *how much room the model needs to think*, not how fast it thinks — leave them alone unless you've measured.
|
|
61
|
+
|
|
62
|
+
### You're on a non-reasoning model (gemma4, llama-3.x base, qwen2.5)
|
|
63
|
+
|
|
64
|
+
These don't emit ` thinking` tokens. Your budgets only need to cover the actual answer length, not reasoning + answer. You can set `max_tokens` values to ~25% of the v2.5.2 defaults in config (v2.6.0 moved them to `LLMConfig` — see [issue #125](https://github.com/rolandpg/zettelforge/issues/125)). `llm.timeout: 60` is then enough.
|
|
65
|
+
|
|
66
|
+
### You're on a much larger model (70B, 120B, cloud)
|
|
67
|
+
|
|
68
|
+
Reasoning depth often *increases* with model size. The 8000-token causal cap may not be enough on a 120B reasoning model. Watch the OCSF log for `event=llm_call_empty_response done_reason=length eval_count=8000` — if you see it, raise the `max_tokens=8000` literal inside `NoteConstructor.extract_causal_triples` (in `src/zettelforge/note_constructor.py`) and re-test.
|
|
69
|
+
|
|
70
|
+
### You're triggering `sync=True` or doing bulk ingestion
|
|
71
|
+
|
|
72
|
+
The default async path moves causal extraction off the write hot path; `remember()` returns in ~50 ms while extraction happens later in the enrichment worker. **`sync=True` blocks the caller until the worker finishes.** With v2.5.2 budgets on a 9B reasoning model that's 1–3 minutes per note. For bulk ingestion (1000+ notes), prefer async and let the queue drain at its own pace; for sync use cases (test fixtures, small one-shots), accept the latency or downgrade to a non-reasoning model.
|
|
73
|
+
|
|
74
|
+
## Verifying your budgets are right
|
|
75
|
+
|
|
76
|
+
The OCSF log at `~/.amem/logs/zettelforge.log` carries every LLM call as a structured event. Two events to know:
|
|
77
|
+
|
|
78
|
+
- **`llm_call_empty_response`** — `WARNING` level, fires whenever an Ollama call returns an empty `response`. Always visible at the default `INFO` log level.
|
|
79
|
+
- **`llm_call_complete`** — `DEBUG` level, fires on every successful call with `eval_count`, `response_chars`, `max_tokens`, `duration_ms`, etc. Only visible when `logging.level: DEBUG` is set in `config.yaml` (or via `ZETTELFORGE_LOG_LEVEL=DEBUG`).
|
|
80
|
+
|
|
81
|
+
To spot too-small budgets at the default log level:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
grep '"event":"llm_call_empty_response"' ~/.amem/logs/zettelforge.log \
|
|
85
|
+
| jq -r '"\(.model) eval=\(.eval_count) of max=\(.max_tokens) dur_ms=\(.duration_ms)"' \
|
|
86
|
+
| tail -20
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`eval == max_tokens` with `done_reason: length` is the canonical token-starvation signature — raise the budget for that call site.
|
|
90
|
+
|
|
91
|
+
To verify budgets aren't *too* generous (free wall-clock to claw back), enable DEBUG logging and grep `llm_call_complete` instead. `eval_count << max_tokens` with non-empty `response_chars` means you could safely lower the cap on faster hardware.
|
|
92
|
+
|
|
93
|
+
## Background
|
|
94
|
+
|
|
95
|
+
- v2.5.2 hotfix CHANGELOG entry — full root-cause writeup and per-file diffs.
|
|
96
|
+
- [Issue #125](https://github.com/rolandpg/zettelforge/issues/125) — v2.6.0 plan to make these budgets config-overridable per call site, add `<think>`-tag stripping as a post-processing guard, and a `reasoning_model: bool` auto-scale flag.
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
|
|
100
|
+
- [Configuration Reference](../reference/configuration.md)
|
|
101
|
+
- [Troubleshoot ZettelForge](../how-to/troubleshoot.md)
|