xp-libs-analytics 0.4.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.github/pull_request_template.md +2 -2
  2. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.github/workflows/ci.yml +1 -0
  3. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/CHANGELOG.md +7 -1
  4. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/PKG-INFO +45 -4
  5. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/README.md +44 -3
  6. xp_libs_analytics-0.5.0/SECURITY.md +15 -0
  7. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/architecture.md +2 -0
  8. xp_libs_analytics-0.5.0/docs/registered-queries.md +64 -0
  9. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/security-review.md +18 -4
  10. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/yaml-security.md +2 -2
  11. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/README.md +2 -2
  12. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/README.md +1 -1
  13. xp_libs_analytics-0.5.0/examples/registered_queries.py +60 -0
  14. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/pyproject.toml +1 -1
  15. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/smoke.py +20 -2
  16. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/__init__.py +5 -1
  17. xp_libs_analytics-0.5.0/src/xp_libs_analytics/engine/native.py +202 -0
  18. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/engine/worker.py +22 -8
  19. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/errors/catalog.json +6 -0
  20. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/errors/codes.py +6 -0
  21. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/mcp/server.py +138 -14
  22. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/models/__init__.py +2 -1
  23. xp_libs_analytics-0.5.0/src/xp_libs_analytics/registered.py +144 -0
  24. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/sdk/__init__.py +147 -27
  25. xp_libs_analytics-0.5.0/tests/test_import_separation.py +75 -0
  26. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_packaging_contract.py +1 -1
  27. xp_libs_analytics-0.5.0/tests/test_parameterized_queries.py +238 -0
  28. xp_libs_analytics-0.5.0/tests/test_registered_queries.py +132 -0
  29. xp_libs_analytics-0.4.0/SECURITY.md +0 -15
  30. xp_libs_analytics-0.4.0/tests/test_import_separation.py +0 -31
  31. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.coveragerc +0 -0
  32. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.dockerignore +0 -0
  33. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.github/CODEOWNERS.template +0 -0
  34. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.github/dependabot.yml +0 -0
  35. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.github/workflows/release.yml +0 -0
  36. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.github/workflows/security.yml +0 -0
  37. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/.gitignore +0 -0
  38. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/CONTRIBUTING.md +0 -0
  39. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/Dockerfile +0 -0
  40. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/LICENSE +0 -0
  41. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/THIRD_PARTY_NOTICES.md +0 -0
  42. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/GITHUB_SETUP.md +0 -0
  43. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/adr/ADR-001-schema.md +0 -0
  44. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/adr/ADR-002-errors.md +0 -0
  45. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/adr/ADR-003-snapshots.md +0 -0
  46. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/adr/ADR-004-execution.md +0 -0
  47. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/adr/ADR-005-YAML-DSL.md +0 -0
  48. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/adr/entity-inference-deferred.md +0 -0
  49. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/errors.md +0 -0
  50. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/query-language.md +0 -0
  51. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/releasing.md +0 -0
  52. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/docs/string-matching.md +0 -0
  53. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/__init__.py +0 -0
  54. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/__init__.py +0 -0
  55. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/cli.py +0 -0
  56. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/empty.jsonl +0 -0
  57. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/epoch.jsonl +0 -0
  58. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/events.jsonl +0 -0
  59. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/extreme.jsonl +0 -0
  60. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/injection.jsonl +0 -0
  61. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/invalid_time.jsonl +0 -0
  62. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/matching.jsonl +0 -0
  63. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/mixed.jsonl +0 -0
  64. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/nested_records.jsonl +0 -0
  65. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/sales.jsonl +0 -0
  66. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/sparse_records.jsonl +0 -0
  67. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/telemetry.jsonl +0 -0
  68. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/datasets/timezone.jsonl +0 -0
  69. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/endpoint_security.py +0 -0
  70. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/fixtures.py +0 -0
  71. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/generate.py +0 -0
  72. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/prompts/agent_system.md +0 -0
  73. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/recovery.py +0 -0
  74. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/reporting.py +0 -0
  75. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/reports/.gitignore +0 -0
  76. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios/adversarial.yaml +0 -0
  77. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios/negative.yaml +0 -0
  78. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios/positive.yaml +0 -0
  79. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios/recovery.yaml +0 -0
  80. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios/skill_comparison.yaml +0 -0
  81. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios/temporal.yaml +0 -0
  82. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scenarios.py +0 -0
  83. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/scoring.py +0 -0
  84. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/server.py +0 -0
  85. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/skill_loader.py +0 -0
  86. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/evals/agent_skill/task.py +0 -0
  87. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/data/events.json +0 -0
  88. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/data/events.ndjson +0 -0
  89. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/data/matching_events.json +0 -0
  90. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/data/mixed_types.json +0 -0
  91. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/01_count_all.yaml +0 -0
  92. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/02_filter_and_count.yaml +0 -0
  93. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/03_group_stats_and_topk.yaml +0 -0
  94. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/04_nested_or_predicates.yaml +0 -0
  95. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/05_is_missing.yaml +0 -0
  96. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/06_missing_vs_null.yaml +0 -0
  97. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/07_conditional_aggregation.yaml +0 -0
  98. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/08_ratio_if.yaml +0 -0
  99. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/09_tumbling_window.yaml +0 -0
  100. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/10_sliding_window.yaml +0 -0
  101. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/11_explicit_cast.yaml +0 -0
  102. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/12_text_range_distinct.yaml +0 -0
  103. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/13_summary_statistics.yaml +0 -0
  104. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/14_nested_and_not.yaml +0 -0
  105. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/15_conditional_sum.yaml +0 -0
  106. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/16_mixed_types_cast.yaml +0 -0
  107. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/17_relative_last_hour.yaml +0 -0
  108. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/18_string_exact.yaml +0 -0
  109. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/19_string_normalization.yaml +0 -0
  110. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/20_prefix_suffix.yaml +0 -0
  111. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/21_whole_word.yaml +0 -0
  112. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/22_ilike.yaml +0 -0
  113. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/23_rust_regex.yaml +0 -0
  114. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/24_fuzzy.yaml +0 -0
  115. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/25_multi_field_matching.yaml +0 -0
  116. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/26_conditional_fuzzy.yaml +0 -0
  117. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/27_temporal_fuzzy.yaml +0 -0
  118. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/queries/28_safe_matching_diagnostics.yaml +0 -0
  119. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/examples/run_examples.py +0 -0
  120. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/requirements-lock.txt +0 -0
  121. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/benchmark.py +0 -0
  122. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/benchmark_matching.py +0 -0
  123. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/check_critical_coverage.py +0 -0
  124. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/check_name.py +0 -0
  125. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/inventory.py +0 -0
  126. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/resource_probe.py +0 -0
  127. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/scripts/verify_dist.py +0 -0
  128. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/skills/generic-analytics/SKILL.md +0 -0
  129. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/skills/generic-analytics/references/errors.md +0 -0
  130. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/skills/generic-analytics/references/query-language.md +0 -0
  131. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/skills/generic-analytics/references/string-matching.md +0 -0
  132. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/engine/__init__.py +0 -0
  133. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/engine/backend.py +0 -0
  134. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/engine/planner.py +0 -0
  135. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/errors/__init__.py +0 -0
  136. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/ingest/__init__.py +0 -0
  137. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/interfaces.py +0 -0
  138. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/matching/__init__.py +0 -0
  139. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/matching/compiler.py +0 -0
  140. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/matching/diagnostics.py +0 -0
  141. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/matching/models.py +0 -0
  142. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/matching/normalize.py +0 -0
  143. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/matching/patterns.py +0 -0
  144. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/mcp/__init__.py +0 -0
  145. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/parser/__init__.py +0 -0
  146. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/policy/__init__.py +0 -0
  147. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/py.typed +0 -0
  148. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/src/xp_libs_analytics/registry.py +0 -0
  149. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_live/__init__.py +0 -0
  150. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_live/conftest.py +0 -0
  151. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_live/test_live.py +0 -0
  152. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/__init__.py +0 -0
  153. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_contract_failures.py +0 -0
  154. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_controls.py +0 -0
  155. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_endpoint_privacy.py +0 -0
  156. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_equivalence.py +0 -0
  157. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_fixture_oracles.py +0 -0
  158. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_native_agent.py +0 -0
  159. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_preflight_failures.py +0 -0
  160. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_privacy_runtime.py +0 -0
  161. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_recovery_seeding.py +0 -0
  162. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_runtime_failures.py +0 -0
  163. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_scenario_contracts.py +0 -0
  164. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_scoring.py +0 -0
  165. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_skill_loading.py +0 -0
  166. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/agent_eval_offline/test_variations.py +0 -0
  167. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/conftest.py +0 -0
  168. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/fixtures/matching_events.jsonl +0 -0
  169. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/fixtures/query-schema.json +0 -0
  170. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/__init__.py +0 -0
  171. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/support.py +0 -0
  172. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/test_agent_contract.py +0 -0
  173. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/test_budgets_diagnostics.py +0 -0
  174. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/test_components_security.py +0 -0
  175. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/test_composition.py +0 -0
  176. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/test_operators.py +0 -0
  177. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/matching/test_properties.py +0 -0
  178. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/oracle.py +0 -0
  179. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_agent_workflows.py +0 -0
  180. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_contracts.py +0 -0
  181. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_edge_cases.py +0 -0
  182. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_ingest_policy.py +0 -0
  183. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_mcp.py +0 -0
  184. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_operations.py +0 -0
  185. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_registry.py +0 -0
  186. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_security.py +0 -0
  187. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_telemetry.py +0 -0
  188. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_time.py +0 -0
  189. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_validation.py +0 -0
  190. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_worker_limits.py +0 -0
  191. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_workflows.py +0 -0
  192. {xp_libs_analytics-0.4.0 → xp_libs_analytics-0.5.0}/tests/test_yaml_security.py +0 -0
@@ -1,13 +1,13 @@
1
1
  ## Description and motivation
2
2
 
3
3
  ## Affected contracts / V1 operators / public API
4
- - [ ] Updated `QUERY_LANGUAGE_V1.md` / semantic version decision if applicable
4
+ - [ ] Updated `docs/query-language.md` and reviewed versioning if the query contract changed
5
5
  - [ ] New/changed error codes and catalog parity (single `catalog.json`)
6
6
  - [ ] Updated agent skill and machine-valid examples if behavior changed
7
7
 
8
8
  ## Safety and threat model
9
9
  - [ ] No unsafe SQL/Python/UDF/network/file access introduced
10
- - [ ] Authorization and handle ownership unchanged or explicitly reviewed
10
+ - [ ] Reviewed authorization and context-local dataset access
11
11
  - [ ] Resource budgets and input/output leakage considered
12
12
  - [ ] No real records/secrets in fixtures, logs or artifacts
13
13
 
@@ -44,6 +44,7 @@ jobs:
44
44
  - run: python scripts/check_critical_coverage.py coverage.json
45
45
  - run: python examples/run_examples.py --lint-only
46
46
  - run: python examples/run_examples.py --all
47
+ - run: python -m examples.registered_queries
47
48
  - run: python scripts/resource_probe.py
48
49
  - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
49
50
  if: always()
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 — Parameterized registered queries
4
+
5
+ - Add key-only MCP execution for host-registered YAML or typed queries. Add a typed SDK entry point that bypasses YAML parsing while retaining snapshot planning and authorization.
6
+ - Bind strict runtime parameters into fixed YAML value positions and support host-owned Polars transformations over authorized projections.
7
+ - Expose parameter schema discovery and optional generated MCP tools. Support native execution in the OS-limited worker using a host-provided transformation registry.
8
+
3
9
 
4
10
  ## 0.4.0 — Bounded string and fuzzy query matching
5
11
 
@@ -24,7 +30,7 @@
24
30
  ## 0.2.0 — local build
25
31
 
26
32
  - Rename the distribution/CLI to `xp-libs-analytics` and imports to `xp_libs_analytics`.
27
- - Preserve the independently usable YAML/analytics core and optional FastMCP adapter; add a bring-your-own-MCP example and import-isolation regression test.
33
+ - Preserve the independently usable YAML analytics core and optional FastMCP adapter. Add a custom MCP adapter example and an import isolation regression test.
28
34
  - Add optional Inspect AI agent evaluations, deterministic scenario/oracle contracts, real MCP recovery seeding, semantic/result scorers, memory-only Inspect logs, sanitized reporting and skill/baseline comparison.
29
35
  - Add 105 synthetic scenarios and offline/live test separation. Live results are recorded separately from deterministic test evidence.
30
36
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: xp-libs-analytics
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Schema-on-read, in-memory analytics for agent-generated YAML queries
5
5
  Project-URL: Source, https://github.com/rrodrigoa/xp-libs-analytics
6
6
  Project-URL: Issues, https://github.com/rrodrigoa/xp-libs-analytics/issues
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
45
45
 
46
46
  # XP Libs Analytics
47
47
 
48
- A Python library for bounded, in-memory snapshot analytics over JSON records with unknown, heterogeneous schemas. Queries are restricted YAML, validated with strict Pydantic models and compiled to fixed Polars expressions. The optional standalone FastMCP adapter supplies tools, an agent skill, and 28 read-only query examples.
48
+ A Python library for bounded, in-memory snapshot analytics over JSON records with unknown, heterogeneous schemas. The engine accepts restricted YAML and host-authored typed query definitions, then plans and executes them using fixed Polars expressions. The optional standalone FastMCP adapter supports YAML authoring or execution by registered query key.
49
49
 
50
50
  Python 3.12+ · MIT · Import: `xp_libs_analytics` · Distribution: `xp-libs-analytics`
51
51
 
@@ -109,7 +109,7 @@ Input accepts JSON objects, arrays of objects, NDJSON, or Python iterables of JS
109
109
  | Grouping | Up to four scalar dimensions; missing and explicit null groups remain separate |
110
110
  | Time | Explicit RFC3339/epoch casts, absolute or relative UTC ranges, epoch-aligned tumbling/sliding windows |
111
111
  | Results | HAVING, stable sorting, limit/truncation, strict JSON and quality/provenance evidence |
112
- | Errors | 73 catalog codes, one canonical packaged message catalog, safe diagnostic locations |
112
+ | Errors | 79 catalog codes, one canonical packaged message catalog, safe diagnostic locations |
113
113
 
114
114
  See the [query language](docs/query-language.md), [examples](examples/README.md), and [agent skill](skills/generic-analytics/SKILL.md). `NO_DATA` reports absent matching observations. `INSUFFICIENT_DATA` applies the host's minimum sample policy. Unknown sampling cadence yields `coverage: UNKNOWN`; counts do not imply duration.
115
115
 
@@ -166,7 +166,7 @@ def validation_tool(query_yaml: str) -> dict:
166
166
  return analytics.validate_query(query_yaml)
167
167
  ```
168
168
 
169
- Register those callables with your chosen server. Select the authorized execution context and identity in the host, enforce transport authentication, and use `isolated=True` for untrusted execution. The bundled FastMCP adapter is an optional convenience layer.
169
+ A custom MCP adapter can register these callables. The host selects the authorized execution context and identity, enforces transport authentication, and uses `isolated=True` for untrusted execution. The library also includes an optional FastMCP adapter.
170
170
 
171
171
  ```sh
172
172
  xp-libs-analytics --help
@@ -178,6 +178,47 @@ Tools: `analytics_capabilities`, `analytics_list_datasets`, `analytics_load_json
178
178
 
179
179
  The local stdio server inherits its launcher's authority; one process is one local context. The default backend uses isolated workers. HTTP access fails closed unless a host supplies an authenticated `principal_resolver` and configures FastMCP transport authentication. A host-supplied `engine` deliberately selects that engine's context for local use. Hosted sessions can supply `context_resolver`, a trusted callable returning their authorized engine/context. No tool accepts a context ID or principal. The identity resolver creates distinct namespaces per authenticated principal and reapplies current field/scopes policy per request. The adapter caps cached principal contexts at 128; restarting clears local state. It is not a turnkey public multi-tenant service.
180
180
 
181
+ ### Registered query keys
182
+
183
+ Hosts can expose a fixed set of queries through `create_server(registered_queries=...)`.
184
+ The host registers the datasets and supplies the query definitions at startup.
185
+
186
+ ```python
187
+ from xp_libs_analytics import AnalyticsEngine, AnalyticsQuery
188
+ from xp_libs_analytics.mcp.server import create_server
189
+
190
+ if __name__ == "__main__":
191
+ engine = AnalyticsEngine(isolated=True)
192
+ engine.register_records("observations", [{"value": 2}, {"value": 4}])
193
+
194
+ count_yaml = """version: analytics.query/v1
195
+ dataset: {name: observations}
196
+ aggregations: [{op: count, as: rows}]
197
+ """
198
+ sum_query = AnalyticsQuery.model_validate(
199
+ {
200
+ "version": "analytics.query/v1",
201
+ "dataset": {"name": "observations"},
202
+ "aggregations": [{"op": "sum", "field": "/value", "as": "total"}],
203
+ }
204
+ )
205
+
206
+ server = create_server(engine, registered_queries={"count": count_yaml, "sum": sum_query})
207
+ server.run(transport="stdio")
208
+ ```
209
+
210
+ The client calls `analytics_query_by_key` with `{"key": "sum"}` and receives the standard analytics result or error. Keys are case-sensitive, start with an ASCII letter, and contain up to 64 ASCII letters, digits, underscores, or hyphens. The host distributes the allowed keys to clients. For HTTP, the host configures transport authentication and supplies a trusted context resolver that selects an engine with the registered datasets.
211
+
212
+ YAML entries use restricted parsing on each call. Typed entries and direct SDK calls to `engine.query_typed(sum_query)` bypass the analytics YAML parser. FastMCP independently imports PyYAML for its skill provider. Both query forms resolve the current dataset snapshot and run authorization, cost planning, and execution on each request.
213
+
214
+ Registered queries can also accept a host-defined Pydantic parameter model. Use
215
+ `ParameterizedYamlQuery` for fixed YAML or `PolarsQuery` for a trusted Polars
216
+ transformation. Clients pass values as `{"key": "sum", "parameters": {"minimum": 3}}`
217
+ and can retrieve the input schema with `analytics_describe_query`. Set
218
+ `generated_tools=True` to expose each query as an `analytics_registered_<key>` tool
219
+ with named arguments. See [registered query configuration](docs/registered-queries.md)
220
+ and run `python -m examples.registered_queries` for an end-to-end example.
221
+
181
222
  ## Development and evidence
182
223
 
183
224
  Optional real-model evaluations use Inspect AI and the actual MCP adapter. From this checkout, install `python -m pip install -e '.[dev,mcp,agent-eval]'`, then run `python -m evals.agent_skill.cli validate`. Live evaluation accepts the model connection only at execution time; see [evaluation usage](evals/agent_skill/README.md). Normal CI runs offline evaluator tests and excludes live model tests.
@@ -1,6 +1,6 @@
1
1
  # XP Libs Analytics
2
2
 
3
- A Python library for bounded, in-memory snapshot analytics over JSON records with unknown, heterogeneous schemas. Queries are restricted YAML, validated with strict Pydantic models and compiled to fixed Polars expressions. The optional standalone FastMCP adapter supplies tools, an agent skill, and 28 read-only query examples.
3
+ A Python library for bounded, in-memory snapshot analytics over JSON records with unknown, heterogeneous schemas. The engine accepts restricted YAML and host-authored typed query definitions, then plans and executes them using fixed Polars expressions. The optional standalone FastMCP adapter supports YAML authoring or execution by registered query key.
4
4
 
5
5
  Python 3.12+ · MIT · Import: `xp_libs_analytics` · Distribution: `xp-libs-analytics`
6
6
 
@@ -64,7 +64,7 @@ Input accepts JSON objects, arrays of objects, NDJSON, or Python iterables of JS
64
64
  | Grouping | Up to four scalar dimensions; missing and explicit null groups remain separate |
65
65
  | Time | Explicit RFC3339/epoch casts, absolute or relative UTC ranges, epoch-aligned tumbling/sliding windows |
66
66
  | Results | HAVING, stable sorting, limit/truncation, strict JSON and quality/provenance evidence |
67
- | Errors | 73 catalog codes, one canonical packaged message catalog, safe diagnostic locations |
67
+ | Errors | 79 catalog codes, one canonical packaged message catalog, safe diagnostic locations |
68
68
 
69
69
  See the [query language](docs/query-language.md), [examples](examples/README.md), and [agent skill](skills/generic-analytics/SKILL.md). `NO_DATA` reports absent matching observations. `INSUFFICIENT_DATA` applies the host's minimum sample policy. Unknown sampling cadence yields `coverage: UNKNOWN`; counts do not imply duration.
70
70
 
@@ -121,7 +121,7 @@ def validation_tool(query_yaml: str) -> dict:
121
121
  return analytics.validate_query(query_yaml)
122
122
  ```
123
123
 
124
- Register those callables with your chosen server. Select the authorized execution context and identity in the host, enforce transport authentication, and use `isolated=True` for untrusted execution. The bundled FastMCP adapter is an optional convenience layer.
124
+ A custom MCP adapter can register these callables. The host selects the authorized execution context and identity, enforces transport authentication, and uses `isolated=True` for untrusted execution. The library also includes an optional FastMCP adapter.
125
125
 
126
126
  ```sh
127
127
  xp-libs-analytics --help
@@ -133,6 +133,47 @@ Tools: `analytics_capabilities`, `analytics_list_datasets`, `analytics_load_json
133
133
 
134
134
  The local stdio server inherits its launcher's authority; one process is one local context. The default backend uses isolated workers. HTTP access fails closed unless a host supplies an authenticated `principal_resolver` and configures FastMCP transport authentication. A host-supplied `engine` deliberately selects that engine's context for local use. Hosted sessions can supply `context_resolver`, a trusted callable returning their authorized engine/context. No tool accepts a context ID or principal. The identity resolver creates distinct namespaces per authenticated principal and reapplies current field/scopes policy per request. The adapter caps cached principal contexts at 128; restarting clears local state. It is not a turnkey public multi-tenant service.
135
135
 
136
+ ### Registered query keys
137
+
138
+ Hosts can expose a fixed set of queries through `create_server(registered_queries=...)`.
139
+ The host registers the datasets and supplies the query definitions at startup.
140
+
141
+ ```python
142
+ from xp_libs_analytics import AnalyticsEngine, AnalyticsQuery
143
+ from xp_libs_analytics.mcp.server import create_server
144
+
145
+ if __name__ == "__main__":
146
+ engine = AnalyticsEngine(isolated=True)
147
+ engine.register_records("observations", [{"value": 2}, {"value": 4}])
148
+
149
+ count_yaml = """version: analytics.query/v1
150
+ dataset: {name: observations}
151
+ aggregations: [{op: count, as: rows}]
152
+ """
153
+ sum_query = AnalyticsQuery.model_validate(
154
+ {
155
+ "version": "analytics.query/v1",
156
+ "dataset": {"name": "observations"},
157
+ "aggregations": [{"op": "sum", "field": "/value", "as": "total"}],
158
+ }
159
+ )
160
+
161
+ server = create_server(engine, registered_queries={"count": count_yaml, "sum": sum_query})
162
+ server.run(transport="stdio")
163
+ ```
164
+
165
+ The client calls `analytics_query_by_key` with `{"key": "sum"}` and receives the standard analytics result or error. Keys are case-sensitive, start with an ASCII letter, and contain up to 64 ASCII letters, digits, underscores, or hyphens. The host distributes the allowed keys to clients. For HTTP, the host configures transport authentication and supplies a trusted context resolver that selects an engine with the registered datasets.
166
+
167
+ YAML entries use restricted parsing on each call. Typed entries and direct SDK calls to `engine.query_typed(sum_query)` bypass the analytics YAML parser. FastMCP independently imports PyYAML for its skill provider. Both query forms resolve the current dataset snapshot and run authorization, cost planning, and execution on each request.
168
+
169
+ Registered queries can also accept a host-defined Pydantic parameter model. Use
170
+ `ParameterizedYamlQuery` for fixed YAML or `PolarsQuery` for a trusted Polars
171
+ transformation. Clients pass values as `{"key": "sum", "parameters": {"minimum": 3}}`
172
+ and can retrieve the input schema with `analytics_describe_query`. Set
173
+ `generated_tools=True` to expose each query as an `analytics_registered_<key>` tool
174
+ with named arguments. See [registered query configuration](docs/registered-queries.md)
175
+ and run `python -m examples.registered_queries` for an end-to-end example.
176
+
136
177
  ## Development and evidence
137
178
 
138
179
  Optional real-model evaluations use Inspect AI and the actual MCP adapter. From this checkout, install `python -m pip install -e '.[dev,mcp,agent-eval]'`, then run `python -m evals.agent_skill.cli validate`. Live evaluation accepts the model connection only at execution time; see [evaluation usage](evals/agent_skill/README.md). Normal CI runs offline evaluator tests and excludes live model tests.
@@ -0,0 +1,15 @@
1
+ # Security Policy
2
+
3
+ XP Libs Analytics executes analytical queries over potentially sensitive JSON records. Vulnerability reports should use synthetic records and omit secrets, access tokens, personal information, private schema, and cross-context dataset metadata.
4
+
5
+ ## Reporting vulnerabilities
6
+
7
+ Use the repository's private security advisory form (Security → Advisories → Report a vulnerability) when private reporting is available.
8
+
9
+ ## Supported versions
10
+
11
+ Security fixes are distributed as new package versions. The [changelog](CHANGELOG.md) records version-specific changes.
12
+
13
+ ## Threat model priorities
14
+
15
+ The library's threat model covers cross-context name resolution, snapshot identity, untrusted tool input, resource exhaustion, type and cast handling, metadata disclosure, dependency and package integrity, and unauthorized output. See the [end-to-end security review](docs/security-review.md) for controls and test evidence.
@@ -16,6 +16,8 @@ The worker memory ceiling is absolute. POSIX address-space limits include native
16
16
 
17
17
  MCP resources are read-only and allowlisted. Under modern MCP, local requests inherit launcher identity rather than a per-request connection ID. Remote hosts must supply transport authentication and a trusted principal or execution-context resolver; default HTTP data operations are denied.
18
18
 
19
+ The optional key-only MCP surface resolves caller-supplied keys through a host-configured mapping. A YAML entry enters restricted parsing. A typed entry enters through strict model validation and skips the analytics YAML parser. Both paths bind the current snapshot under the trusted context, plan against its schema and budgets, and use the shared execution and result path.
20
+
19
21
  MCP never accepts agent-supplied identity or context selectors. Authenticated principal contexts are isolated and policy views preserve names while applying current scopes/field restrictions. A context resolver can instead select a host-managed session/request context. Names in other contexts are indistinguishable from nonexistent names. Local stdio deliberately has one launcher context per process. Reserved register_stream rejects without consuming sources; no continuous semantics are implied.
20
22
 
21
23
  The shared matching package owns strict rule models, Unicode normalization, bounded LIKE/Rust regex validation, per-query distinct RapidFuzz scoring and privacy-cleared diagnostics. Matching plans compile to native Polars expressions/membership without row UDFs. Typed datetime equality bypasses string matching after explicit casts. Safe independent nonfuzzy conjunctions can bound candidate work; OR/NOT branches are never pushed independently. There is no persistent derived-value cache. Regex/fuzzy and requested diagnostics default to the OS-limited worker; explain/validate counts candidates without running scorers. Optional diagnostics have an explicitly narrower post-all-filters/pre-aggregation leaf-hit scope. Entity inference is deferred; see docs/adr/entity-inference-deferred.md.
@@ -0,0 +1,64 @@
1
+ # Parameterized registered queries
2
+
3
+ The host supplies `registered_queries` to `create_server`. Each entry accepts fixed
4
+ YAML text, an `AnalyticsQuery`, a `ParameterizedYamlQuery`, or a `PolarsQuery`. The
5
+ adapter resolves the key within the server and selects the engine through the
6
+ trusted context. Dataset bindings remain local to that engine.
7
+
8
+ Use `analytics_describe_query(key)` to obtain the parameter JSON Schema. Call
9
+ `analytics_query_by_key(key, parameters)` with a JSON object. Omitting the object
10
+ applies the model defaults and still enforces required fields. Fixed queries accept
11
+ an omitted or empty object. With `generated_tools=True`, the tool
12
+ `analytics_registered_<key>` accepts the same values as named arguments. Both
13
+ surfaces validate requests against the same model.
14
+
15
+ Parameter models use strict JSON validation. Integer and boolean values require
16
+ their JSON types. Enums use their declared JSON values and datetimes use RFC 3339
17
+ strings. The engine forbids undeclared fields and applies byte, depth, node,
18
+ collection, and string limits before model validation. Effective values,
19
+ including defaults, are also subject to the resource limits. Host models can
20
+ enable Pydantic's `validate_default` setting to validate defaults against field
21
+ constraints. Model validators and serializers execute as trusted host code.
22
+
23
+ ## Fixed YAML
24
+
25
+ Create `ParameterizedYamlQuery(yaml, parameters=Model)` and invoke it directly with
26
+ `engine.query_parameterized(definition, values)` or register it with the MCP
27
+ adapter. Place `{$param: minimum}` where the query expects a whole literal value.
28
+ Supported positions include predicate values, membership lists, range bounds,
29
+ matching pattern values and thresholds, and time range bounds. Placeholder names
30
+ refer to Python model fields. A bound string is a literal even if it contains YAML
31
+ syntax. The dataset name, field paths, operators, aggregate aliases, and other
32
+ structural parts come from the fixed template. The resulting query goes through
33
+ the normal typed planner.
34
+
35
+ ## Native Polars
36
+
37
+ Create `PolarsQuery(dataset, fields, transform, parameters, output_schema)`
38
+ and invoke it with `engine.query_polars(definition, values)` or register it with the
39
+ MCP adapter. The host supplies the dataset name, JSON Pointer paths, and a callable
40
+ that takes a Polars DataFrame and the validated Pydantic model, then returns a
41
+ Polars DataFrame. The input contains every row from the leased snapshot, a
42
+ `row_index`, one column per authorized scalar path, and one Boolean
43
+ `present:<path>` column per path. Missing values have a false presence flag and a
44
+ null cell. Explicit nulls have a true presence flag and a null cell. Container and
45
+ mixed-type projections are rejected.
46
+
47
+ The output must match the declared column names, order, and types. Supported
48
+ output types are `integer` (Int64), `float` (Float64), `boolean`, `string`,
49
+ `datetime` (timezone-aware), and `null`. Values can be nullable. Oversized outputs
50
+ return a structured error. `max_output_rows` limits returned rows after the
51
+ transformation, and the engine enforces its result byte limit.
52
+
53
+ For isolated execution, construct the engine with `isolated=True` and an explicit
54
+ `native_transforms={"count": count_values}` registry. The transformation and the
55
+ parameter model must be top-level definitions in importable host modules. The
56
+ worker resolves the host-selected references after setting its resource limits.
57
+ MCP arguments supply parameter values only. Native transformations have the
58
+ privileges of the host or worker process and must come from trusted application
59
+ code. Use operating-system or container controls to restrict process access as
60
+ needed. The OS worker limits apply to execution, and the host bounds projection
61
+ and parameter processing.
62
+
63
+ Run `python -m examples.registered_queries` from the source checkout with the MCP
64
+ extra installed. The example uses one parameter model with both query types.
@@ -1,15 +1,29 @@
1
- # V1 security review
1
+ # End-to-end security review for V1
2
2
 
3
- The local review covered YAML/JSON parsing, scalar typing, field/presence normalization, ownership, metadata, quotas, compilation, native worker failure, tools/resources, and release permissions. Executed tests and OS probes are recorded in `docs/evidence`.
3
+ XP Libs Analytics executes bounded snapshot queries over JSON records. The host controls which records enter the engine, which context handles each request, and what that context can read. YAML expresses the query. The Python SDK enforces the same query contract for direct callers and callers using the optional MCP adapter.
4
+
5
+ ```text
6
+ Trusted host ── records ──► bounded admission ──► immutable snapshot
7
+ │ │
8
+ └──────── context, permissions and limits ───┐ │
9
+ ▼ ▼
10
+ Caller ── YAML via SDK or MCP ──► parse and type ──► authorize and plan
11
+ │
12
+ approved plan
13
+ ▼
14
+ Caller ◄── bounded JSON or coded error ◄──────────── execute
15
+ ```
16
+
17
+ The review covers these boundaries from admission through response, as well as tool and release permissions. Executed tests and OS probes are recorded in [verification evidence](evidence/verification.md).
4
18
 
5
19
  - YAML is byte/event/depth/node/collection/scalar bounded before construction. Tags, directives, anchors, aliases, merges and duplicate keys are rejected. Scalar resolvers are isolated from global PyYAML behavior. Query keys cannot introduce SQL, code, plugins, host paths or network sources.
6
20
  - Strict disjoint AST variants forbid unused and unknown fields. Public errors use only the packaged catalog. Invalid unknown-key locations are redacted; parser excerpts, records and backend exception strings are never returned.
7
21
  - Snapshots copy admitted records, preserve missing versus null and examine all rows for typing/casts. Integer precision/overflow and nonfinite results are checked. Opaque internal column names prevent output alias collisions. Casting precedes WHERE and cannot bypass field permissions.
8
- - Ownership is enforced for describe/validate/explain/query/release. Hidden and nonexistent paths are indistinguishable for field-restricted principals; foreign and unknown handles receive identical denial. Host principal changes select immutable policy configurations instead of mutating shared request authority.
22
+ - Ownership is enforced for dataset discovery, description, query validation, planning, execution, and unregistering. Hidden and nonexistent paths are indistinguishable for field-restricted principals. Dataset names outside the current context are indistinguishable from unknown names. The engine applies the host's current policy without mutating shared request authority.
9
23
  - Input bytes, rows, paths, record size, recursion, store bytes, handle count, result bytes, groups, distinct cardinality, windows, overlap and expanded rows have explicit budgets. Quotas/concurrency are shared by owner across SDK views of the same store. A low result limit cannot bypass planning.
10
24
  - Workers have OS CPU/memory ceilings and parent-enforced deadlines; native abort/pipe loss returns a canonical error. Windows Job Object and POSIX rlimit adapters have fail-closed API tests. OS probes demonstrate memory denial and CPU termination; host/container restrictions remain necessary for host-side admission and egress.
11
25
  - Default MCP local identity follows launcher authority under modern per-request connections. Default HTTP data operations fail closed. Authenticated remote hosting requires a trusted resolver and transport authentication; this package does not claim to configure an identity provider. Upload is disabled by default. Resource names are an allowlist, with no caller filesystem routing.
12
26
  - Observability hooks contain only IDs/hashes/status/timing, never records, query text or field values. Hook failures cannot change query outcomes. The package performs no default payload logging.
13
27
  - CI does not use `pull_request_target`. Actions are commit-pinned. Only protected release jobs have OIDC write authority; TestPyPI promotion compares wheel bytes and checks hashes before PyPI. Account-level protection still requires maintainers.
14
28
 
15
- Scope limits: this is a local code/test review, not an external penetration certification. No live internet-facing authentication deployment or hosted GitHub security scan was performed. The deterministic agent suite verifies tool workflows and evidence handling; it does not measure a live language model's query-generation accuracy. Exact decimal, continuous queries and multi-input semantics are outside V1.
29
+ The evidence for this review comes from source inspection and local tests. The deterministic agent suite verifies tool workflows and result handling. Live model accuracy, internet-facing authentication deployments, and independent penetration testing fall outside this review. Exact decimal arithmetic, continuous queries, and multi-input execution fall outside V1.
@@ -1,6 +1,6 @@
1
1
  # Restricted YAML Query Parser — Normative Security Contract (V1)
2
2
 
3
- **Scope:** YAML is the only public query authoring format in V1. JSON/NDJSON records remain the analytics input, and query results/error envelopes remain JSON. This file governs YAML decoding before typed Pydantic validation. It does not permit arbitrary general-purpose YAML.
3
+ This document defines restricted YAML decoding before typed Pydantic validation. It applies to caller-authored YAML and host-registered YAML queries. Host-authored typed queries enter through model validation. Both paths operate on snapshots of JSON or NDJSON records and return JSON results or errors.
4
4
 
5
5
  ## Decision and rationale
6
6
 
@@ -34,7 +34,7 @@
34
34
  Parser fidelity:
35
35
 
36
36
  1. Enforce UTF-8 and raw byte size **before** parser work. Suggested default `max_query_bytes=65_536`, configurable lower by trusted policy. Also bound string length (`max_scalar_chars=1_024` except perhaps approved fixed query metadata), nesting (`max_yaml_depth=16`), total nodes/events (`max_yaml_nodes=4_096`), mapping entries and sequences (`max_yaml_collection_items=1_024`); versioned server defaults are normative once selected.
37
- 2. Use PyYAML's `SafeLoader` as a *base class for a custom loader*. Never use `yaml.Loader`, `FullLoader`, `unsafe_load`, `!!python/*` or arbitrary constructors. `yaml.safe_load` **by itself is NOT enough**: it does not enforce this entire subset, does not provide our duplicate-key rules, and its default implicit scalar resolver differs from the desired contract.
37
+ 2. The restricted loader extends PyYAML's `SafeLoader` with the library's duplicate-key rules, scalar resolution, and allowed syntax. Query parsing excludes `yaml.Loader`, `FullLoader`, `unsafe_load`, `!!python/*`, and arbitrary constructors.
38
38
  3. Preflight parser events **with budgets**. Reject `AliasEvent`, any `anchor` on scalar/sequence/mapping events, explicit tags, directives, multi-documents and unsupported events. Stop after the first permitted document and reject further documents. Parse deterministically, with a hard deadline/worker budget and bounded untrusted text. Avoid regex-only prechecks: anchors, aliases and tags must be detected by parsing, not substring search (quoted `"*alias"` is just a string).
39
39
  4. Clone only the safe scalar resolver table before modifying it; don't mutate global `yaml.SafeLoader` implicit resolvers. Install a narrowly documented set of JSON-compatible scalar recognizers: lowercase true/false; exact null; JSON-style decimal integers and finite decimal/exponent floats; no timestamp or binary auto-resolution; no YAML 1.1 aliases for booleans. Treat leading-zero or plus-prefixed numerals as strings unless explicitly supported by a later version. Validate `int64` bounds and numeric finiteness at the typed semantic layer; never accept Python bool as numeric.
40
40
  5. Override mapping constructor to reject duplicate keys **at every depth**, including flow-style maps, before converting to dict; require string keys, no merge key, and preserve source marks in a safe `{query_path → (line,column)}` index. Convert positions to 1-based line/column. Use local source-mark metadata, never return raw YAML excerpts or tokens. A duplicate-key failure may include the duplicate key's **safe path** and line/column, but must not echo field values.
@@ -18,7 +18,7 @@ For an Ollama deployment, install Ollama separately, start it with `ollama serve
18
18
 
19
19
  ## Supply connection settings only at runtime
20
20
 
21
- Set MODEL_ID in your process/shell to an installed native-tool-capable model ID. There is no model default.
21
+ Set `MODEL_ID` in the evaluation process environment to identify an installed model with native tool support. The evaluation runner requires an explicit model ID.
22
22
 
23
23
  The CLI prompts for an API base URL if `--endpoint` is omitted. It never saves that answer. For example:
24
24
 
@@ -32,7 +32,7 @@ python -m evals.agent_skill.cli run --model "$MODEL_ID" --suite adversarial
32
32
  python -m evals.agent_skill.cli compare --model "$MODEL_ID" --scenario aggregate-count --repetitions 3
33
33
  ```
34
34
 
35
- To pass a runtime variable explicitly, use `--endpoint "$API_BASE"` in your shell. The variable's value is supplied by you, never stored in a config or `.env` file. An optional credential can be supplied through the process-only `XP_ANALYTICS_EVAL_API_KEY` environment variable; clear it after use. Shell history/process listings are controlled by your shell and OS; interactive prompting avoids putting the address in command history.
35
+ Pass the endpoint at runtime with `--endpoint "$API_BASE"` or enter it at the interactive prompt. The prompt keeps the address out of command history. Supply any required credential through the process environment variable `XP_ANALYTICS_EVAL_API_KEY` and clear it after use.
36
36
 
37
37
  Other controls: `--seed`, `--temperature` (default zero), `--max-turns`, `--max-tool-calls`, `--timeout`, `--report`, `--compare-skill`, `--verbose`, and explicit `--traces` for redacted message traces. Runs are serial; concurrency defaults to one. Preflight checks model listing where available, native tool calling with a harmless echo tool, actual analytics MCP discovery, and skill references. It fails without silently emulating tools.
38
38
 
@@ -11,7 +11,7 @@ python examples/run_examples.py --all
11
11
 
12
12
  `--lint-only` applies the restricted parser and strict Pydantic AST. `--all` additionally registers synthetic records, registers them under example_data, semantically validates and executes every query, and unregisters each binding. Runtime errors fail the runner.
13
13
 
14
- Each query is a complete document. `dataset.name: example_data` selects the logical name registered by the runner. You can run the same YAML unchanged after registering your own records as example_data. No internal ID is injected into YAML. Inputs are `data/events.json`, equivalent `events.ndjson`, `mixed_types.json`, and `matching_events.json`. Fields are illustrative discovered paths, never built-in business rules.
14
+ Each query is a complete document. `dataset.name: example_data` selects the logical name registered by the runner. The same YAML can execute against any snapshot registered as `example_data` with compatible fields and types. The YAML uses the logical name throughout. Example inputs are `data/events.json`, its equivalent `events.ndjson`, `mixed_types.json`, and `matching_events.json`. The engine discovers their field paths from the admitted records.
15
15
 
16
16
  | Example | Behavior |
17
17
  |---|---|
@@ -0,0 +1,60 @@
1
+ """Run both parameterized query forms through a local MCP client."""
2
+
3
+ import asyncio
4
+ from typing import Annotated
5
+
6
+ import polars as pl
7
+ from fastmcp import Client
8
+ from pydantic import BaseModel, Field
9
+
10
+ from xp_libs_analytics import AnalyticsEngine, ParameterizedYamlQuery, PolarsQuery
11
+ from xp_libs_analytics.mcp.server import create_server
12
+
13
+
14
+ class Minimum(BaseModel):
15
+ minimum: Annotated[int, Field(ge=0, le=100)] = 2
16
+
17
+
18
+ def count_values(frame: pl.DataFrame, parameters: BaseModel) -> pl.DataFrame:
19
+ assert isinstance(parameters, Minimum) # noqa: S101 -- host-owned example contract
20
+ return frame.filter(pl.col("/value") >= parameters.minimum).select(
21
+ pl.len().cast(pl.Int64).alias("count")
22
+ )
23
+
24
+
25
+ YAML_COUNT = ParameterizedYamlQuery(
26
+ """version: analytics.query/v1
27
+ dataset: {name: observations}
28
+ filters:
29
+ - {field: /value, op: gte, value: {$param: minimum}}
30
+ aggregations:
31
+ - {op: count, as: count}
32
+ """,
33
+ Minimum,
34
+ )
35
+ POLARS_COUNT = PolarsQuery("observations", ("/value",), count_values, Minimum, {"count": "integer"})
36
+
37
+
38
+ async def main() -> None:
39
+ engine = AnalyticsEngine()
40
+ engine.register_records("observations", [{"value": 1}, {"value": 3}, {"value": 4}])
41
+ server = create_server(
42
+ engine,
43
+ registered_queries={"yaml_count": YAML_COUNT, "polars_count": POLARS_COUNT},
44
+ generated_tools=True,
45
+ )
46
+ async with Client(server) as client:
47
+ yaml_result = await client.call_tool(
48
+ "analytics_query_by_key", {"key": "yaml_count", "parameters": {"minimum": 3}}
49
+ )
50
+ polars_result = await client.call_tool("analytics_registered_polars_count", {"minimum": 3})
51
+ for result in (yaml_result, polars_result):
52
+ if result.structured_content is None or result.structured_content["rows"] != [
53
+ {"count": 2}
54
+ ]:
55
+ raise RuntimeError("Unexpected registered query result")
56
+ print(result.structured_content["rows"])
57
+
58
+
59
+ if __name__ == "__main__":
60
+ asyncio.run(main())
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "xp-libs-analytics"
7
- version = "0.4.0"
7
+ version = "0.5.0"
8
8
  description = "Schema-on-read, in-memory analytics for agent-generated YAML queries"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -6,7 +6,9 @@ from dataclasses import replace
6
6
  from importlib.metadata import distribution
7
7
  from importlib.resources import files
8
8
 
9
- from xp_libs_analytics import AnalyticsEngine
9
+ from pydantic import BaseModel
10
+
11
+ from xp_libs_analytics import AnalyticsEngine, ParameterizedYamlQuery
10
12
 
11
13
  assert "fastmcp" not in sys.modules
12
14
  assert any(
@@ -14,7 +16,7 @@ assert any(
14
16
  for p in distribution("xp-libs-analytics").files
15
17
  )
16
18
  catalog = json.loads(files("xp_libs_analytics.errors").joinpath("catalog.json").read_text())
17
- assert len(catalog) == 73
19
+ assert len(catalog) == 79
18
20
  bundle = files("xp_libs_analytics").joinpath("skills/generic-analytics")
19
21
  assert bundle.joinpath("SKILL.md").is_file()
20
22
  assert bundle.joinpath("references/query-language-full.md").is_file()
@@ -22,6 +24,22 @@ assert bundle.joinpath("references/string-matching-full.md").is_file()
22
24
  assert bundle.joinpath("examples/queries/01_count_all.yaml").is_file()
23
25
  assert len(list(bundle.joinpath("examples/queries").iterdir())) == 28
24
26
  engine = AnalyticsEngine()
27
+
28
+
29
+ class Parameters(BaseModel):
30
+ minimum: int = 1
31
+
32
+
33
+ engine.register_records("registered_smoke", [{"value": 0}, {"value": 2}])
34
+ registered = ParameterizedYamlQuery(
35
+ """version: analytics.query/v1
36
+ dataset: {name: registered_smoke}
37
+ filters: [{field: /value, op: gte, value: {$param: minimum}}]
38
+ aggregations: [{op: count, as: rows}]
39
+ """,
40
+ Parameters,
41
+ )
42
+ assert engine.query_parameterized(registered)["rows"] == [{"rows": 1}]
25
43
  handle = engine.register_records("dataset_1", [{"x": 2}, {"x": 4}, {}])
26
44
  text = f"""version: analytics.query/v1
27
45
  dataset: {{name: {handle.name}}}
@@ -1,8 +1,10 @@
1
1
  """Bounded snapshot analytics over arbitrary JSON records and restricted YAML."""
2
2
 
3
+ from xp_libs_analytics.engine.native import PolarsQuery
3
4
  from xp_libs_analytics.errors import AnalyticsError
4
5
  from xp_libs_analytics.models import AnalyticsQuery
5
6
  from xp_libs_analytics.policy import Limits, Principal
7
+ from xp_libs_analytics.registered import ParameterizedYamlQuery
6
8
  from xp_libs_analytics.registry import DatasetInfo, DatasetRegistry
7
9
  from xp_libs_analytics.sdk import AnalyticsEngine
8
10
 
@@ -13,6 +15,8 @@ __all__ = [
13
15
  "DatasetInfo",
14
16
  "DatasetRegistry",
15
17
  "Limits",
18
+ "ParameterizedYamlQuery",
19
+ "PolarsQuery",
16
20
  "Principal",
17
21
  ]
18
- __version__ = "0.4.0"
22
+ __version__ = "0.5.0"