agentmetry 0.8.0__tar.gz → 0.9.1__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 (258) hide show
  1. {agentmetry-0.8.0 → agentmetry-0.9.1}/PKG-INFO +1 -1
  2. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/routes/audit.py +15 -0
  3. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/cli/__init__.py +60 -1
  4. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/disposition.py +10 -4
  5. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/loader.py +8 -1
  6. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/scanner.py +7 -4
  7. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/evidence_pack.py +2 -1
  8. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/external.py +9 -7
  9. agentmetry-0.9.1/agentmetry/core/audit/otel_ingest.py +526 -0
  10. agentmetry-0.9.1/agentmetry/core/audit/policy_settings.py +162 -0
  11. agentmetry-0.9.1/agentmetry/core/audit/run_context.py +197 -0
  12. agentmetry-0.9.1/agentmetry/core/audit/tool_policy/capability.py +42 -0
  13. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/tool_policy/evaluator.py +24 -15
  14. agentmetry-0.9.1/agentmetry/core/audit/tool_policy/loader.py +89 -0
  15. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/tool_policy/models.py +4 -1
  16. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/doctor.py +37 -0
  17. agentmetry-0.9.1/agentmetry/core/operator_identity.py +80 -0
  18. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/version.py +1 -1
  19. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/hooks/ingest.py +159 -33
  20. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/conftest.py +37 -0
  21. agentmetry-0.9.1/tests/fixtures/otel_claude_code_session.json +902 -0
  22. agentmetry-0.9.1/tests/test_ask_gate_phase_a.py +289 -0
  23. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cli_commands.py +6 -1
  24. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_scanner.py +10 -7
  25. agentmetry-0.9.1/tests/test_hook_decision_format.py +198 -0
  26. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_enforcement_timing.py +7 -2
  27. agentmetry-0.9.1/tests/test_hook_import_cost.py +179 -0
  28. agentmetry-0.9.1/tests/test_hook_real_process.py +100 -0
  29. agentmetry-0.9.1/tests/test_operator_attribution.py +231 -0
  30. agentmetry-0.9.1/tests/test_otel_ingest.py +354 -0
  31. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_readme_claims.py +22 -3
  32. agentmetry-0.9.1/tests/test_suite_leaves_real_home_alone.py +40 -0
  33. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_tool_policy_agent_cli.py +7 -1
  34. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_tool_policy_git_hooks.py +7 -1
  35. agentmetry-0.8.0/agentmetry/core/audit/run_context.py +0 -129
  36. agentmetry-0.8.0/agentmetry/core/audit/tool_policy/loader.py +0 -44
  37. {agentmetry-0.8.0 → agentmetry-0.9.1}/.dockerignore +0 -0
  38. {agentmetry-0.8.0 → agentmetry-0.9.1}/.env.agentmetry-demo +0 -0
  39. {agentmetry-0.8.0 → agentmetry-0.9.1}/.env.example +0 -0
  40. {agentmetry-0.8.0 → agentmetry-0.9.1}/.gitignore +0 -0
  41. {agentmetry-0.8.0 → agentmetry-0.9.1}/Dockerfile +0 -0
  42. {agentmetry-0.8.0 → agentmetry-0.9.1}/README.md +0 -0
  43. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/__init__.py +0 -0
  44. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/__init__.py +0 -0
  45. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/main.py +0 -0
  46. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/routes/__init__.py +0 -0
  47. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/websocket.py +0 -0
  48. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/ws_bridge.py +0 -0
  49. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/cli/__main__.py +0 -0
  50. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/__init__.py +0 -0
  51. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/__init__.py +0 -0
  52. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/__init__.py +0 -0
  53. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/agt.py +0 -0
  54. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/chronicle.py +0 -0
  55. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/cloudevents.py +0 -0
  56. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/ecs.py +0 -0
  57. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/splunk.py +0 -0
  58. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/alerts.py +0 -0
  59. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/atlas.py +0 -0
  60. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/canonical.py +0 -0
  61. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/compliance_digest.py +0 -0
  62. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/__init__.py +0 -0
  63. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/benchmark.py +0 -0
  64. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_approval_denied_then_executed.jsonl +0 -0
  65. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_arbitrary_host_stage_execute.jsonl +0 -0
  66. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_autonomous_unapproved_write.jsonl +0 -0
  67. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_credential_exfil.jsonl +0 -0
  68. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_credential_then_cloud_api.jsonl +0 -0
  69. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_destructive_delete_burst.jsonl +0 -0
  70. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_discovery_then_collect.jsonl +0 -0
  71. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_dotfile_then_git_push.jsonl +0 -0
  72. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_encoded_command_download.jsonl +0 -0
  73. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_env_credential_exfil.jsonl +0 -0
  74. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_hashed_only_no_command.jsonl +0 -0
  75. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_interpreter_egress.jsonl +0 -0
  76. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_pr_merged_without_review.jsonl +0 -0
  77. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_proc_substitution_cradle.jsonl +0 -0
  78. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_remote_pipe_to_shell.jsonl +0 -0
  79. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_remote_staging_then_execute.jsonl +0 -0
  80. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_secret_manager_then_egress.jsonl +0 -0
  81. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_session_tool_burst.jsonl +0 -0
  82. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_single_command_exfil.jsonl +0 -0
  83. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_single_quoted_interpreter_egress.jsonl +0 -0
  84. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_ssh_directory_exfil.jsonl +0 -0
  85. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_subagent_swarm.jsonl +0 -0
  86. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_timestamp_collision.jsonl +0 -0
  87. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_untrusted_input_then_action.jsonl +0 -0
  88. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_authoring_merge_fixtures.jsonl +0 -0
  89. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_autonomous_after_approval.jsonl +0 -0
  90. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_build_artifact_cleanup.jsonl +0 -0
  91. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_ci_artifact_download.jsonl +0 -0
  92. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_commit_message_names_cloud_clis.jsonl +0 -0
  93. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_database_migration.jsonl +0 -0
  94. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_dependency_install_and_build.jsonl +0 -0
  95. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_download_release_archive.jsonl +0 -0
  96. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_env_example_read.jsonl +0 -0
  97. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_data_then_run_repo_script.jsonl +0 -0
  98. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_dataset_then_analyse.jsonl +0 -0
  99. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_lockfile_then_install.jsonl +0 -0
  100. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_piped_into_own_script.jsonl +0 -0
  101. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_git_review_and_push.jsonl +0 -0
  102. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_github_raw_docs_then_script.jsonl +0 -0
  103. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_human_driven_deletes.jsonl +0 -0
  104. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_local_api_probing.jsonl +0 -0
  105. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_long_but_calm_session.jsonl +0 -0
  106. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_loopback_is_not_egress.jsonl +0 -0
  107. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_loopback_pipe_to_interpreter.jsonl +0 -0
  108. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_mcp_text_mentions_credential_path.jsonl +0 -0
  109. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_ordinary_development.jsonl +0 -0
  110. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_package_manager_after_fetch.jsonl +0 -0
  111. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_reading_config_that_is_not_secret.jsonl +0 -0
  112. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_remote_api_call_no_credentials.jsonl +0 -0
  113. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_research_then_docs.jsonl +0 -0
  114. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_reversed_order_is_not_exfil.jsonl +0 -0
  115. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_secret_names_and_docs.jsonl +0 -0
  116. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_test_and_fix_loop.jsonl +0 -0
  117. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_writing_about_credentials.jsonl +0 -0
  118. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/corpus.yaml +0 -0
  119. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/engine.py +0 -0
  120. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/live.py +0 -0
  121. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/live_store.py +0 -0
  122. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/models.py +0 -0
  123. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/rules.py +0 -0
  124. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/traits.py +0 -0
  125. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/yaml_config.py +0 -0
  126. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/yaml_rules.py +0 -0
  127. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/__init__.py +0 -0
  128. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/models.py +0 -0
  129. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dogfood.py +0 -0
  130. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/hashing.py +0 -0
  131. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/heartbeat.py +0 -0
  132. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/hook_bootstrap.py +0 -0
  133. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/identity.py +0 -0
  134. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/ingest.py +0 -0
  135. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/migrate.py +0 -0
  136. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/mitre.py +0 -0
  137. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/policy.py +0 -0
  138. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/redaction.py +0 -0
  139. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/replay.py +0 -0
  140. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/sinks.py +0 -0
  141. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/spool.py +0 -0
  142. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/tool_policy/__init__.py +0 -0
  143. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_anchor.py +0 -0
  144. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_chain.py +0 -0
  145. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_db.py +0 -0
  146. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_merkle.py +0 -0
  147. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/auth.py +0 -0
  148. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/__init__.py +0 -0
  149. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/audit_exporter.py +0 -0
  150. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/bridges.py +0 -0
  151. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/bus.py +0 -0
  152. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/events.py +0 -0
  153. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/outbox.py +0 -0
  154. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/config.py +0 -0
  155. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/__init__.py +0 -0
  156. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/autostart.py +0 -0
  157. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/driver_paths.py +0 -0
  158. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/env_file.py +0 -0
  159. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/hook_coverage.py +0 -0
  160. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/mcp_inventory.py +0 -0
  161. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/mcp_schema.py +0 -0
  162. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/__init__.py +0 -0
  163. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/host.py +0 -0
  164. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/permissions.py +0 -0
  165. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/spec.py +0 -0
  166. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/extensions.py +0 -0
  167. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/health.py +0 -0
  168. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/hooks/__init__.py +0 -0
  169. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/detection/manifest.yaml +0 -0
  170. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/dlp/manifest.yaml +0 -0
  171. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/opa/agent_rules.rego +0 -0
  172. {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/tool/manifest.yaml +0 -0
  173. {agentmetry-0.8.0 → agentmetry-0.9.1}/pyproject.toml +0 -0
  174. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/fixtures/agt_filesink_exfil.jsonl +0 -0
  175. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/fixtures/agt_filesink_mixed.jsonl +0 -0
  176. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/fixtures/fake_mcp_server.py +0 -0
  177. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_agentmetry_audit.py +0 -0
  178. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_agentmetry_ingest_client.py +0 -0
  179. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_agt_adapter.py +0 -0
  180. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_atlas_detection_enrichment.py +0 -0
  181. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_atlas_mapping.py +0 -0
  182. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_audit_sinks.py +0 -0
  183. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_audit_stats.py +0 -0
  184. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_audit_tail.py +0 -0
  185. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_auth.py +0 -0
  186. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_autostart.py +0 -0
  187. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_boot_sequence.py +0 -0
  188. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_burst_windows.py +0 -0
  189. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chinese_agent_hooks.py +0 -0
  190. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chinese_agent_sprint_b.py +0 -0
  191. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chinese_agent_sprint_c.py +0 -0
  192. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chronicle_adapter.py +0 -0
  193. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cli_backup.py +0 -0
  194. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cli_env_var_names.py +0 -0
  195. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cloudevents_adapter.py +0 -0
  196. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_compliance_digest.py +0 -0
  197. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_crewai_adapter.py +0 -0
  198. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_adi.py +0 -0
  199. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_benchmark.py +0 -0
  200. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_default_config.py +0 -0
  201. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_disposition.py +0 -0
  202. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_disposition_api.py +0 -0
  203. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_duplicate_emission.py +0 -0
  204. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_emit_durability.py +0 -0
  205. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_engine.py +0 -0
  206. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_evasion.py +0 -0
  207. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_hf_incident.py +0 -0
  208. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_quoted_command_words.py +0 -0
  209. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_rule_identity.py +0 -0
  210. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_rules_v2.py +0 -0
  211. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_agent_env_override.py +0 -0
  212. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_invisible_unicode.py +0 -0
  213. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_markdown_exfil.py +0 -0
  214. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_doctor.py +0 -0
  215. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dogfood.py +0 -0
  216. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dogfood_freeze.py +0 -0
  217. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_driver_paths.py +0 -0
  218. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_ecs_threat.py +0 -0
  219. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_event_bus.py +0 -0
  220. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_evidence_pack.py +0 -0
  221. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_extensions.py +0 -0
  222. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_external_ingest.py +0 -0
  223. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_forwarding_shape.py +0 -0
  224. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_funding_manifest.py +0 -0
  225. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_heartbeat.py +0 -0
  226. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_bootstrap.py +0 -0
  227. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_coverage.py +0 -0
  228. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_spool.py +0 -0
  229. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_identity_enrich.py +0 -0
  230. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_ingest_roundtrip.py +0 -0
  231. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_isolation.py +0 -0
  232. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_launch_targets.py +0 -0
  233. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_live_detection.py +0 -0
  234. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_live_detection_store.py +0 -0
  235. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_audit_proxy.py +0 -0
  236. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_concealed_text.py +0 -0
  237. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_inventory.py +0 -0
  238. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_meta_fingerprint.py +0 -0
  239. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_schema.py +0 -0
  240. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mitre_dlp.py +0 -0
  241. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_opensre_adapter.py +0 -0
  242. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_powershell_encoding.py +0 -0
  243. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_redaction.py +0 -0
  244. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_replay.py +0 -0
  245. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_sigma_pack.py +0 -0
  246. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_static_serving.py +0 -0
  247. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_subagent_lifecycle.py +0 -0
  248. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_tool_policy.py +0 -0
  249. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_anchor.py +0 -0
  250. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_chain.py +0 -0
  251. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_concurrency.py +0 -0
  252. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_merkle.py +0 -0
  253. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_unattended_agent_policy.py +0 -0
  254. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_version.py +0 -0
  255. {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_yaml_detection_rules.py +0 -0
  256. {agentmetry-0.8.0 → agentmetry-0.9.1}/tools/generate_sigma_pack.py +0 -0
  257. {agentmetry-0.8.0 → agentmetry-0.9.1}/tools/mcp_audit_proxy.py +0 -0
  258. {agentmetry-0.8.0 → agentmetry-0.9.1}/tools/vault_fs_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentmetry
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
  Summary: Local-first flight recorder for AI coding agents: hash-chained audit trail, MITRE-mapped sequence detection, DLP
5
5
  Project-URL: Homepage, https://agentmetry.ai
6
6
  Project-URL: Source, https://github.com/blitzcrieg1/agentmetry
@@ -70,6 +70,19 @@ class IngestInitiatorBody(BaseModel):
70
70
  return v if v in _ACTOR_TYPES else "agent"
71
71
 
72
72
 
73
+ class IngestOperatorBody(BaseModel):
74
+ """Which account the capture process ran as, and how it knew.
75
+
76
+ Separate from `initiator`, which says what kind of actor acted. A claim,
77
+ like everything a client sends: `run_context.resolve_operator` decides what
78
+ is recorded, and an orchestrator-side AGENTMETRY_OPERATOR_ID overrides an
79
+ id the hook only read from the operating system.
80
+ """
81
+
82
+ id: str = ""
83
+ source: str = ""
84
+
85
+
73
86
  class ExternalIngestBody(BaseModel):
74
87
  """Adapter payload — normalized to canonical v1.2 on ingest.
75
88
 
@@ -113,6 +126,8 @@ class ExternalIngestBody(BaseModel):
113
126
  tool_policy: dict[str, Any] | None = None
114
127
  # Who the capture surface says triggered this. Honoured by external.py.
115
128
  initiator: IngestInitiatorBody | None = None
129
+ # Which account ran the agent, resolved by the hook as the developer.
130
+ operator: IngestOperatorBody | None = None
116
131
  # Compact `tools/list` fingerprint from mcp_audit_proxy. Hash only: the
117
132
  # description never leaves the proxy process.
118
133
  schema_fingerprint: str = ""
@@ -284,6 +284,15 @@ def cmd_detections(args: argparse.Namespace) -> int:
284
284
  return 0
285
285
 
286
286
 
287
+ def _triage_operator() -> str:
288
+ configured = os.environ.get("AGENTMETRY_OPERATOR_ID", "").strip()
289
+ if configured:
290
+ return configured
291
+ from agentmetry.core.operator_identity import os_operator
292
+
293
+ return os_operator()
294
+
295
+
287
296
  def cmd_disposition(args: argparse.Namespace) -> int:
288
297
  """Close a detection from the shell, using the same API as the dashboard."""
289
298
  note = args.note.strip()
@@ -299,7 +308,8 @@ def cmd_disposition(args: argparse.Namespace) -> int:
299
308
  "rule_id": args.rule_id,
300
309
  "status": args.status,
301
310
  "note": note,
302
- "decided_by": args.decided_by,
311
+ # The CLI runs as the person triaging; the orchestrator may not.
312
+ "decided_by": args.decided_by or _triage_operator(),
303
313
  },
304
314
  headers=_api_headers(),
305
315
  timeout=10.0,
@@ -896,6 +906,35 @@ def cmd_mcp(args: argparse.Namespace) -> int:
896
906
  return 0
897
907
 
898
908
 
909
+ def cmd_otel(args: argparse.Namespace) -> int:
910
+ """Receive Claude Code's native OpenTelemetry stream and record it.
911
+
912
+ The other way in for Claude Code, with no hooks installed. It sees what ran,
913
+ after it ran, so it records and detects but enforces nothing.
914
+ """
915
+ from agentmetry.core.audit import otel_ingest
916
+
917
+ port = args.listen_port or otel_ingest.default_port()
918
+ if args.print_mapping:
919
+ for line in otel_ingest.mapping_lines():
920
+ print(line)
921
+ return 0
922
+ if args.print_env:
923
+ for line in otel_ingest.claude_env_lines(port):
924
+ print(line)
925
+ return 0
926
+
927
+ # The receiver forwards through the hook client, which reads its target and
928
+ # its command-text setting from the environment. Setting them here, in this
929
+ # process only, is what makes --port and --keep-command mean the same thing
930
+ # they mean everywhere else.
931
+ orchestrator = _api_base_url(args.port)
932
+ os.environ["AGENTMETRY_URL"] = orchestrator
933
+ if args.keep_command:
934
+ os.environ["AGENTMETRY_LOG_COMMANDS"] = "1"
935
+ return otel_ingest.serve(port, orchestrator)
936
+
937
+
899
938
  def cmd_anchor(args: argparse.Namespace) -> int:
900
939
  """Publish, list, or check the checkpoints that commit the trail externally.
901
940
 
@@ -1308,6 +1347,25 @@ def main(argv: list[str] | None = None) -> int:
1308
1347
  "--digest", action="store_true",
1309
1348
  help="also print the config digest, which names no server and is safe to publish",
1310
1349
  )
1350
+ otel = sub.add_parser(
1351
+ "otel", help="receive Claude Code's native OpenTelemetry stream (no hooks needed)"
1352
+ )
1353
+ otel.add_argument(
1354
+ "--listen-port", type=int, default=None,
1355
+ help="port on 127.0.0.1 to receive OTLP on (default AGENTMETRY_OTEL_PORT or 4318)",
1356
+ )
1357
+ otel.add_argument(
1358
+ "--keep-command", action="store_true",
1359
+ help="keep scrubbed command text, as AGENTMETRY_LOG_COMMANDS=1 does for hooks",
1360
+ )
1361
+ otel.add_argument(
1362
+ "--print-mapping", action="store_true",
1363
+ help="list which events are mapped, counted, or unmapped, and exit",
1364
+ )
1365
+ otel.add_argument(
1366
+ "--print-env", action="store_true",
1367
+ help="print the environment Claude Code needs to send here, and exit",
1368
+ )
1311
1369
  verify = sub.add_parser("verify", help="verify evidence pack or JSONL trail chain")
1312
1370
  verify.add_argument(
1313
1371
  "path",
@@ -1374,6 +1432,7 @@ def main(argv: list[str] | None = None) -> int:
1374
1432
  "prove": cmd_prove,
1375
1433
  "anchor": cmd_anchor,
1376
1434
  "mcp": cmd_mcp,
1435
+ "otel": cmd_otel,
1377
1436
  "import-agt": cmd_import_agt,
1378
1437
  "doctor": cmd_doctor,
1379
1438
  "benchmark": cmd_benchmark,
@@ -431,8 +431,14 @@ def build_disposition_event(
431
431
  `action.type:detection_disposition AND action.outcome:risk_accepted`
432
432
  without understanding Agentmetry's vocabulary. The note is operator-written
433
433
  text, never captured command content, so it is safe to forward.
434
+
435
+ Who decided falls back to the resolved operator, not to the raw setting:
436
+ with AGENTMETRY_OPERATOR_ID unset that was an empty string, so a triage
437
+ record could not say who triaged.
434
438
  """
435
- from agentmetry.core.config import settings
439
+ from agentmetry.core.audit.run_context import resolve_operator
440
+
441
+ decided_by = decided_by or resolve_operator()[0]
436
442
 
437
443
  return {
438
444
  "schema_version": SCHEMA_VERSION,
@@ -443,8 +449,8 @@ def build_disposition_event(
443
449
  **identity_fields(),
444
450
  "source_topic": f"disposition/{rule_id}",
445
451
  "source": {"tier": "detection", "app": "agentmetry"},
446
- "actor": {"id": decided_by or settings.operator_id, "type": "human"},
447
- "initiator": {"type": "human", "id": decided_by or settings.operator_id},
452
+ "actor": {"id": decided_by, "type": "human"},
453
+ "initiator": {"type": "human", "id": decided_by},
448
454
  "action": {
449
455
  "type": DISPOSITION_EVENT_TYPE,
450
456
  "outcome": status,
@@ -459,7 +465,7 @@ def build_disposition_event(
459
465
  "previous_status": previous_status,
460
466
  "assignee": assignee,
461
467
  "note": note,
462
- "decided_by": decided_by or settings.operator_id,
468
+ "decided_by": decided_by,
463
469
  },
464
470
  }
465
471
 
@@ -4,6 +4,13 @@ from typing import List
4
4
 
5
5
  from .models import RuleMeta
6
6
 
7
+ #: libyaml where it is installed. The pure-Python parser was most of the hook's
8
+ #: runtime: the DLP and tool manifests are re-read by every hook process, and
9
+ #: parsing them took ~55 ms of an ~85 ms tool call (#171). Same SafeConstructor,
10
+ #: so the same Python objects, which `test_hook_import_cost.py` checks.
11
+ SAFE_LOADER = getattr(yaml, "CSafeLoader", yaml.SafeLoader)
12
+
13
+
7
14
  def load_dlp_rules(manifest_path: Path | str) -> List[RuleMeta]:
8
15
  """Load DLP rules from a YAML manifest file."""
9
16
  path = Path(manifest_path)
@@ -11,7 +18,7 @@ def load_dlp_rules(manifest_path: Path | str) -> List[RuleMeta]:
11
18
  return []
12
19
 
13
20
  with open(path, "r", encoding="utf-8") as f:
14
- data = yaml.safe_load(f)
21
+ data = yaml.load(f, Loader=SAFE_LOADER) # noqa: S506 - a safe loader
15
22
 
16
23
  if not data or "rules" not in data:
17
24
  return []
@@ -5,7 +5,9 @@ from typing import Any, Dict
5
5
 
6
6
  from .models import DlpVerdict, DlpMatch
7
7
  from .loader import load_dlp_rules
8
- from ...config import settings
8
+ # Not core.config: that import is pydantic, and the hook paid ~220 ms for it
9
+ # on every tool call to read three values (#171).
10
+ from ..policy_settings import policy_settings
9
11
 
10
12
  logger = logging.getLogger(__name__)
11
13
 
@@ -21,9 +23,10 @@ def reset_rules() -> None:
21
23
  def _init_rules() -> None:
22
24
  if _COMPILED_RULES:
23
25
  return
24
- rules = load_dlp_rules(settings.dlp_rules_path)
26
+ cfg = policy_settings()
27
+ rules = load_dlp_rules(cfg.dlp_rules_path)
25
28
  for r in rules:
26
- if not settings.dlp_pii and r.category == "pii":
29
+ if not cfg.dlp_pii and r.category == "pii":
27
30
  continue
28
31
  try:
29
32
  _COMPILED_RULES.append((re.compile(r.pattern), r))
@@ -59,7 +62,7 @@ def scan(tool_qualified: str, arguments: Dict[str, Any] | str, mode: str | None
59
62
  metadata is returned — never the matched value.
60
63
  """
61
64
  if mode is None:
62
- mode = settings.dlp_mode
65
+ mode = policy_settings().dlp_mode
63
66
  if mode == "disable":
64
67
  return DlpVerdict(matched=False, mode=mode)
65
68
 
@@ -23,6 +23,7 @@ from pathlib import Path
23
23
  from typing import Any
24
24
 
25
25
  from agentmetry.core.audit.detection.disposition import extract_dispositions
26
+ from agentmetry.core.audit.run_context import resolve_operator
26
27
  from agentmetry.core.config import settings
27
28
  from agentmetry.core.version import __version__
28
29
 
@@ -290,7 +291,7 @@ def _controls_snapshot() -> dict[str, Any]:
290
291
  "detection": {
291
292
  "off_hours_enabled": settings.detect_off_hours,
292
293
  },
293
- "operator_id": settings.operator_id.strip() or "local",
294
+ "operator_id": resolve_operator()[0],
294
295
  }
295
296
 
296
297
 
@@ -10,7 +10,11 @@ from agentmetry.core.audit.canonical import SCHEMA_VERSION
10
10
  from agentmetry.core.audit.identity import identity_fields
11
11
  from agentmetry.core.audit.hashing import arguments_sha256
12
12
  from agentmetry.core.audit.redaction import scrub_arg_values, scrub_secrets
13
- from agentmetry.core.audit.run_context import actor_from_initiator, build_initiator
13
+ from agentmetry.core.audit.run_context import (
14
+ actor_from_initiator,
15
+ build_initiator,
16
+ operator_from_payload,
17
+ )
14
18
  from agentmetry.core.config import settings
15
19
 
16
20
  ExternalApp = Literal[
@@ -34,10 +38,6 @@ _ACTION_MAP = {
34
38
  }
35
39
 
36
40
 
37
- def _operator_id() -> str:
38
- return settings.operator_id.strip() or "local"
39
-
40
-
41
41
  def _utc_now() -> str:
42
42
  return datetime.now(timezone.utc).isoformat()
43
43
 
@@ -63,16 +63,18 @@ def build_external_canonical(payload: dict[str, Any]) -> dict[str, Any]:
63
63
  correlation_id = str(payload.get("correlation_id") or payload.get("thread_id") or "")
64
64
  session_id = str(payload.get("session_id") or "")
65
65
 
66
+ operator = operator_from_payload(payload)
66
67
  initiator_raw = payload.get("initiator")
67
68
  if isinstance(initiator_raw, dict) and initiator_raw.get("actor_type"):
68
69
  initiator = {
69
70
  "actor_type": str(initiator_raw.get("actor_type") or "human"),
70
71
  "trigger": str(initiator_raw.get("trigger") or "manual"),
71
- "operator_id": str(initiator_raw.get("operator_id") or _operator_id()),
72
+ "operator_id": operator[0],
73
+ "operator_source": operator[1],
72
74
  }
73
75
  else:
74
76
  trigger = str(payload.get("triggered_by") or "manual")
75
- initiator = build_initiator(trigger)
77
+ initiator = build_initiator(trigger, operator)
76
78
 
77
79
  tool_block = payload.get("tool") if isinstance(payload.get("tool"), dict) else {}
78
80
  tool_qualified = str(