agentmetry 0.9.6__tar.gz → 0.9.8__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 (305) hide show
  1. {agentmetry-0.9.6 → agentmetry-0.9.8}/PKG-INFO +1 -1
  2. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/main.py +33 -8
  3. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/routes/audit.py +38 -2
  4. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/cli/__init__.py +108 -0
  5. agentmetry-0.9.8/agentmetry/core/audit/antigravity_trajectory.py +81 -0
  6. agentmetry-0.9.8/agentmetry/core/audit/cloud_bridge.py +341 -0
  7. agentmetry-0.9.8/agentmetry/core/audit/detection/corpus/benign_container_registry_publish.jsonl +3 -0
  8. agentmetry-0.9.8/agentmetry/core/audit/detection/corpus/benign_dependency_audit_network_burst.jsonl +4 -0
  9. agentmetry-0.9.8/agentmetry/core/audit/detection/corpus/benign_terraform_apply_with_profile.jsonl +4 -0
  10. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/corpus.yaml +28 -0
  11. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/evidence_pack.py +65 -8
  12. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/external.py +4 -2
  13. agentmetry-0.9.8/agentmetry/core/audit/grok_bot_transcript.py +58 -0
  14. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/hook_bootstrap.py +30 -0
  15. agentmetry-0.9.8/agentmetry/core/audit/session_tree.py +190 -0
  16. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/trail_db.py +76 -0
  17. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/doctor.py +49 -4
  18. agentmetry-0.9.8/agentmetry/core/diagnostics/machine_view.py +101 -0
  19. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/version.py +1 -1
  20. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/demo.py +1 -1
  21. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/hooks/ingest.py +10 -1
  22. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_agentmetry_ingest_client.py +43 -0
  23. agentmetry-0.9.8/tests/test_antigravity_trajectory.py +35 -0
  24. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_cli_commands.py +7 -9
  25. agentmetry-0.9.8/tests/test_cloud_bridge.py +163 -0
  26. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_copilot_hooks.py +8 -2
  27. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_doctor.py +78 -0
  28. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_evidence_pack.py +77 -0
  29. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_external_ingest.py +15 -0
  30. agentmetry-0.9.8/tests/test_grok_bot_transcript.py +44 -0
  31. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_bootstrap.py +59 -0
  32. agentmetry-0.9.8/tests/test_machine_view.py +111 -0
  33. agentmetry-0.9.8/tests/test_session_tree.py +188 -0
  34. agentmetry-0.9.8/tests/test_transcript_watcher.py +162 -0
  35. {agentmetry-0.9.6 → agentmetry-0.9.8}/.dockerignore +0 -0
  36. {agentmetry-0.9.6 → agentmetry-0.9.8}/.env.agentmetry-demo +0 -0
  37. {agentmetry-0.9.6 → agentmetry-0.9.8}/.env.example +0 -0
  38. {agentmetry-0.9.6 → agentmetry-0.9.8}/.gitignore +0 -0
  39. {agentmetry-0.9.6 → agentmetry-0.9.8}/Dockerfile +0 -0
  40. {agentmetry-0.9.6 → agentmetry-0.9.8}/README.md +0 -0
  41. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/__init__.py +0 -0
  42. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/__init__.py +0 -0
  43. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/routes/__init__.py +0 -0
  44. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/routes/auth.py +0 -0
  45. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/trusted_host.py +0 -0
  46. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/websocket.py +0 -0
  47. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/api/ws_bridge.py +0 -0
  48. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/cli/__main__.py +0 -0
  49. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/__init__.py +0 -0
  50. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/api_token.py +0 -0
  51. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/__init__.py +0 -0
  52. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/__init__.py +0 -0
  53. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/agt.py +0 -0
  54. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/chronicle.py +0 -0
  55. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/cloudevents.py +0 -0
  56. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/ecs.py +0 -0
  57. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/sentinel.py +0 -0
  58. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/adapters/splunk.py +0 -0
  59. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/alerts.py +0 -0
  60. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/atlas.py +0 -0
  61. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/canonical.py +0 -0
  62. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/compliance_digest.py +0 -0
  63. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/__init__.py +0 -0
  64. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/benchmark.py +0 -0
  65. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_approval_denied_then_executed.jsonl +0 -0
  66. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_arbitrary_host_stage_execute.jsonl +0 -0
  67. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_autonomous_unapproved_write.jsonl +0 -0
  68. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_credential_exfil.jsonl +0 -0
  69. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_credential_then_cloud_api.jsonl +0 -0
  70. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_destructive_delete_burst.jsonl +0 -0
  71. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_discovery_then_collect.jsonl +0 -0
  72. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_dotfile_then_git_push.jsonl +0 -0
  73. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_encoded_command_download.jsonl +0 -0
  74. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_env_credential_exfil.jsonl +0 -0
  75. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_hashed_only_no_command.jsonl +0 -0
  76. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_interpreter_egress.jsonl +0 -0
  77. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_pr_merged_without_review.jsonl +0 -0
  78. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_proc_substitution_cradle.jsonl +0 -0
  79. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_remote_pipe_to_shell.jsonl +0 -0
  80. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_remote_staging_then_execute.jsonl +0 -0
  81. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_secret_manager_then_egress.jsonl +0 -0
  82. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_session_tool_burst.jsonl +0 -0
  83. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_single_command_exfil.jsonl +0 -0
  84. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_single_quoted_interpreter_egress.jsonl +0 -0
  85. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_ssh_directory_exfil.jsonl +0 -0
  86. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_subagent_swarm.jsonl +0 -0
  87. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_timestamp_collision.jsonl +0 -0
  88. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/attack_untrusted_input_then_action.jsonl +0 -0
  89. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_authoring_merge_fixtures.jsonl +0 -0
  90. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_autonomous_after_approval.jsonl +0 -0
  91. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_build_artifact_cleanup.jsonl +0 -0
  92. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_ci_artifact_download.jsonl +0 -0
  93. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_commit_message_names_cloud_clis.jsonl +0 -0
  94. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_database_migration.jsonl +0 -0
  95. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_dependency_install_and_build.jsonl +0 -0
  96. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_download_release_archive.jsonl +0 -0
  97. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_env_example_read.jsonl +0 -0
  98. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_fetch_data_then_run_repo_script.jsonl +0 -0
  99. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_fetch_dataset_then_analyse.jsonl +0 -0
  100. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_fetch_lockfile_then_install.jsonl +0 -0
  101. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_fetch_piped_into_own_script.jsonl +0 -0
  102. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_git_review_and_push.jsonl +0 -0
  103. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_github_raw_docs_then_script.jsonl +0 -0
  104. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_human_driven_deletes.jsonl +0 -0
  105. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_local_api_probing.jsonl +0 -0
  106. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_long_but_calm_session.jsonl +0 -0
  107. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_loopback_is_not_egress.jsonl +0 -0
  108. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_loopback_pipe_to_interpreter.jsonl +0 -0
  109. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_mcp_text_mentions_credential_path.jsonl +0 -0
  110. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_ordinary_development.jsonl +0 -0
  111. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_package_manager_after_fetch.jsonl +0 -0
  112. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_reading_config_that_is_not_secret.jsonl +0 -0
  113. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_remote_api_call_no_credentials.jsonl +0 -0
  114. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_research_then_docs.jsonl +0 -0
  115. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_reversed_order_is_not_exfil.jsonl +0 -0
  116. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_secret_names_and_docs.jsonl +0 -0
  117. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_test_and_fix_loop.jsonl +0 -0
  118. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/corpus/benign_writing_about_credentials.jsonl +0 -0
  119. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/disposition.py +0 -0
  120. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/engine.py +0 -0
  121. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/live.py +0 -0
  122. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/live_store.py +0 -0
  123. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/models.py +0 -0
  124. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/rules.py +0 -0
  125. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/traits.py +0 -0
  126. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/yaml_config.py +0 -0
  127. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/detection/yaml_rules.py +0 -0
  128. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/dlp/__init__.py +0 -0
  129. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/dlp/loader.py +0 -0
  130. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/dlp/models.py +0 -0
  131. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/dlp/scanner.py +0 -0
  132. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/dogfood.py +0 -0
  133. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/forwarder.py +0 -0
  134. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/hashing.py +0 -0
  135. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/heartbeat.py +0 -0
  136. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/identity.py +0 -0
  137. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/ingest.py +0 -0
  138. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/managed_hooks.py +0 -0
  139. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/mcp_inventory_event.py +0 -0
  140. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/migrate.py +0 -0
  141. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/mitre.py +0 -0
  142. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/otel_ingest.py +0 -0
  143. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/policy.py +0 -0
  144. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/policy_settings.py +0 -0
  145. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/posture_event.py +0 -0
  146. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/redaction.py +0 -0
  147. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/replay.py +0 -0
  148. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/run_context.py +0 -0
  149. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/signing.py +0 -0
  150. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/sinks.py +0 -0
  151. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/spool.py +0 -0
  152. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/tool_policy/__init__.py +0 -0
  153. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/tool_policy/capability.py +0 -0
  154. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/tool_policy/evaluator.py +0 -0
  155. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/tool_policy/loader.py +0 -0
  156. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/tool_policy/models.py +0 -0
  157. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/trail_anchor.py +0 -0
  158. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/trail_chain.py +0 -0
  159. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/trail_merkle.py +0 -0
  160. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/audit/trail_rotation.py +0 -0
  161. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/auth.py +0 -0
  162. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/bus/__init__.py +0 -0
  163. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/bus/audit_exporter.py +0 -0
  164. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/bus/bridges.py +0 -0
  165. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/bus/bus.py +0 -0
  166. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/bus/events.py +0 -0
  167. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/bus/outbox.py +0 -0
  168. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/config.py +0 -0
  169. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/dashboard_session.py +0 -0
  170. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/__init__.py +0 -0
  171. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/autostart.py +0 -0
  172. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/driver_paths.py +0 -0
  173. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/env_file.py +0 -0
  174. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/hook_coverage.py +0 -0
  175. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/mcp_inventory.py +0 -0
  176. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/mcp_schema.py +0 -0
  177. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/diagnostics/posture.py +0 -0
  178. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/drivers/__init__.py +0 -0
  179. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/drivers/host.py +0 -0
  180. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/drivers/permissions.py +0 -0
  181. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/drivers/spec.py +0 -0
  182. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/extensions.py +0 -0
  183. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/health.py +0 -0
  184. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/operator_identity.py +0 -0
  185. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/core/paths.py +0 -0
  186. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/hooks/__init__.py +0 -0
  187. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/hooks/mcp_proxy.py +0 -0
  188. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/policies/detection/manifest.yaml +0 -0
  189. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/policies/dlp/manifest.yaml +0 -0
  190. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/policies/opa/agent_rules.rego +0 -0
  191. {agentmetry-0.9.6 → agentmetry-0.9.8}/agentmetry/policies/tool/manifest.yaml +0 -0
  192. {agentmetry-0.9.6 → agentmetry-0.9.8}/pyproject.toml +0 -0
  193. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/conftest.py +0 -0
  194. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/agt_filesink_exfil.jsonl +0 -0
  195. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/agt_filesink_mixed.jsonl +0 -0
  196. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/codex_hooks/README.md +0 -0
  197. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/codex_hooks/permission-request.command.output.schema.json +0 -0
  198. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/codex_hooks/pre-tool-use.command.output.schema.json +0 -0
  199. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/fake_mcp_server.py +0 -0
  200. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/fixtures/otel_claude_code_session.json +0 -0
  201. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_agentmetry_audit.py +0 -0
  202. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_agt_adapter.py +0 -0
  203. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_ask_gate_phase_a.py +0 -0
  204. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_atlas_detection_enrichment.py +0 -0
  205. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_atlas_mapping.py +0 -0
  206. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_audit_sinks.py +0 -0
  207. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_audit_stats.py +0 -0
  208. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_audit_tail.py +0 -0
  209. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_auth.py +0 -0
  210. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_auth_default.py +0 -0
  211. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_autostart.py +0 -0
  212. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_boot_sequence.py +0 -0
  213. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_burst_windows.py +0 -0
  214. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_chinese_agent_hooks.py +0 -0
  215. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_chinese_agent_sprint_b.py +0 -0
  216. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_chinese_agent_sprint_c.py +0 -0
  217. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_chronicle_adapter.py +0 -0
  218. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_cli_backup.py +0 -0
  219. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_cli_env_var_names.py +0 -0
  220. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_cloudevents_adapter.py +0 -0
  221. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_compliance_digest.py +0 -0
  222. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_container_and_legacy_runtime.py +0 -0
  223. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_crewai_adapter.py +0 -0
  224. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_demo.py +0 -0
  225. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_adi.py +0 -0
  226. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_benchmark.py +0 -0
  227. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_default_config.py +0 -0
  228. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_disposition.py +0 -0
  229. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_disposition_api.py +0 -0
  230. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_duplicate_emission.py +0 -0
  231. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_emit_durability.py +0 -0
  232. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_engine.py +0 -0
  233. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_evasion.py +0 -0
  234. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_hf_incident.py +0 -0
  235. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_quoted_command_words.py +0 -0
  236. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_rule_identity.py +0 -0
  237. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_detection_rules_v2.py +0 -0
  238. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_dlp_agent_env_override.py +0 -0
  239. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_dlp_invisible_unicode.py +0 -0
  240. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_dlp_markdown_exfil.py +0 -0
  241. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_dlp_scanner.py +0 -0
  242. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_dogfood.py +0 -0
  243. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_dogfood_freeze.py +0 -0
  244. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_driver_paths.py +0 -0
  245. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_ecs_threat.py +0 -0
  246. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_event_bus.py +0 -0
  247. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_event_signing.py +0 -0
  248. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_extensions.py +0 -0
  249. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_forwarder.py +0 -0
  250. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_forwarding_shape.py +0 -0
  251. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_funding_manifest.py +0 -0
  252. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hash_key.py +0 -0
  253. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_heartbeat.py +0 -0
  254. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_coverage.py +0 -0
  255. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_decision_format.py +0 -0
  256. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_enforcement_timing.py +0 -0
  257. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_import_cost.py +0 -0
  258. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_real_process.py +0 -0
  259. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_hook_spool.py +0 -0
  260. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_identity_enrich.py +0 -0
  261. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_identity_provider.py +0 -0
  262. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_ingest_roundtrip.py +0 -0
  263. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_isolation.py +0 -0
  264. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_launch_targets.py +0 -0
  265. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_live_detection.py +0 -0
  266. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_live_detection_store.py +0 -0
  267. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_managed_hooks.py +0 -0
  268. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_audit_proxy.py +0 -0
  269. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_concealed_text.py +0 -0
  270. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_inventory.py +0 -0
  271. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_inventory_event.py +0 -0
  272. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_meta_fingerprint.py +0 -0
  273. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_proxy_packaged.py +0 -0
  274. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mcp_schema.py +0 -0
  275. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_mitre_dlp.py +0 -0
  276. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_opensre_adapter.py +0 -0
  277. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_operator_attribution.py +0 -0
  278. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_otel_ingest.py +0 -0
  279. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_paths.py +0 -0
  280. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_posture.py +0 -0
  281. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_powershell_encoding.py +0 -0
  282. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_readme_claims.py +0 -0
  283. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_redaction.py +0 -0
  284. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_replay.py +0 -0
  285. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_sentinel.py +0 -0
  286. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_serve_script_binds_loopback.py +0 -0
  287. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_shared_install.py +0 -0
  288. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_sigma_pack.py +0 -0
  289. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_static_serving.py +0 -0
  290. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_subagent_lifecycle.py +0 -0
  291. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_suite_leaves_real_home_alone.py +0 -0
  292. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_tool_policy.py +0 -0
  293. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_tool_policy_agent_cli.py +0 -0
  294. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_tool_policy_git_hooks.py +0 -0
  295. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_trail_anchor.py +0 -0
  296. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_trail_chain.py +0 -0
  297. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_trail_concurrency.py +0 -0
  298. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_trail_merkle.py +0 -0
  299. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_trusted_host.py +0 -0
  300. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_unattended_agent_policy.py +0 -0
  301. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_version.py +0 -0
  302. {agentmetry-0.9.6 → agentmetry-0.9.8}/tests/test_yaml_detection_rules.py +0 -0
  303. {agentmetry-0.9.6 → agentmetry-0.9.8}/tools/generate_sigma_pack.py +0 -0
  304. {agentmetry-0.9.6 → agentmetry-0.9.8}/tools/mcp_audit_proxy.py +0 -0
  305. {agentmetry-0.9.6 → agentmetry-0.9.8}/tools/vault_fs_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentmetry
3
- Version: 0.9.6
3
+ Version: 0.9.8
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
@@ -2,6 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
+ import os
6
+ import subprocess
7
+ import sys
5
8
  from contextlib import asynccontextmanager
6
9
  from logging.handlers import RotatingFileHandler
7
10
  from pathlib import Path
@@ -76,6 +79,34 @@ def _start_forwarders() -> list[asyncio.Task]:
76
79
  return tasks
77
80
 
78
81
 
82
+ def start_transcript_watcher(watcher_path: Path) -> subprocess.Popen:
83
+ """Start the Antigravity transcript watcher, tied to this process's life.
84
+
85
+ Its stdin is a pipe only this process holds, and the lifeline variable
86
+ tells it to exit at end-of-file. When this process dies, however it dies,
87
+ the pipe closes and the watcher follows. Without it each restart left a
88
+ watcher behind, and every one of them recorded every Antigravity call.
89
+ """
90
+ return subprocess.Popen(
91
+ [sys.executable, str(watcher_path)],
92
+ stdin=subprocess.PIPE,
93
+ stdout=subprocess.DEVNULL,
94
+ stderr=subprocess.DEVNULL,
95
+ env={**os.environ, "AGENTMETRY_WATCHER_LIFELINE": "1"},
96
+ )
97
+
98
+
99
+ def stop_transcript_watcher(process: subprocess.Popen) -> None:
100
+ """Close the lifeline, then terminate. Closing alone is enough; terminate is
101
+ for a watcher that predates the lifeline."""
102
+ try:
103
+ if process.stdin:
104
+ process.stdin.close()
105
+ except OSError:
106
+ pass
107
+ process.terminate()
108
+
109
+
79
110
  @asynccontextmanager
80
111
  async def lifespan(app: FastAPI):
81
112
  from agentmetry.core.audit.migrate import backfill_db_from_jsonl
@@ -177,17 +208,11 @@ async def lifespan(app: FastAPI):
177
208
  )
178
209
 
179
210
  # Launch transcript watcher for Antigravity in the background
180
- import subprocess
181
- import sys
182
211
  watcher_process = None
183
212
  try:
184
213
  watcher_path = Path(__file__).resolve().parents[4] / "scripts" / "antigravity_transcript_watcher.py"
185
214
  if watcher_path.exists():
186
- watcher_process = subprocess.Popen(
187
- [sys.executable, str(watcher_path)],
188
- stdout=subprocess.DEVNULL,
189
- stderr=subprocess.DEVNULL,
190
- )
215
+ watcher_process = start_transcript_watcher(watcher_path)
191
216
  logger.info("Started Antigravity transcript watcher (PID: %s)", watcher_process.pid)
192
217
  except Exception as exc:
193
218
  logger.warning("Failed to start Antigravity transcript watcher: %s", exc)
@@ -201,7 +226,7 @@ async def lifespan(app: FastAPI):
201
226
  for task in bridge_tasks:
202
227
  task.cancel()
203
228
  if watcher_process:
204
- watcher_process.terminate()
229
+ stop_transcript_watcher(watcher_process)
205
230
 
206
231
 
207
232
  app = FastAPI(
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  from datetime import datetime, timedelta, timezone
6
7
  from pathlib import Path
7
8
  from typing import Any, Literal
@@ -100,7 +101,7 @@ class ExternalIngestBody(BaseModel):
100
101
 
101
102
  source_app: str = Field(
102
103
  ...,
103
- description="cursor | claude | antigravity | codex | mcp_proxy | qwen | kimi | qoder | codebuddy | trae | crewai | opensre",
104
+ description="cursor | claude | antigravity | codex | copilot | mcp_proxy | qwen | kimi | qoder | codebuddy | trae | crewai | opensre | zcode | grok_bot",
104
105
  )
105
106
  event_type: str = Field(
106
107
  ...,
@@ -228,7 +229,7 @@ async def audit_tail(
228
229
  ),
229
230
  sources: str | None = Query(
230
231
  None,
231
- description="Comma-separated source apps: agentmetry,cursor,claude,antigravity,mcp_proxy",
232
+ description="Comma-separated source.app values. Known names include cursor, claude, codex, copilot, antigravity, qwen, kimi, qoder, codebuddy, mcp_proxy, trae, crewai, opensre",
232
233
  ),
233
234
  since_minutes: int | None = Query(
234
235
  None,
@@ -308,6 +309,29 @@ async def audit_session(
308
309
  }
309
310
 
310
311
 
312
+ @router.get("/session/{correlation_id}/tree", dependencies=[Depends(require_api_key)])
313
+ async def audit_session_tree(
314
+ correlation_id: str,
315
+ limit: int = Query(20000, ge=1, le=50000),
316
+ around_utc: str = Query("", max_length=40),
317
+ ):
318
+ """One session folded into investigation-tree nodes.
319
+
320
+ Each ask, its answer and the call it led to become one node, so a detection
321
+ that cites the ask and a policy verdict recorded on it land on the call
322
+ they belong to. A session longer than `limit` is windowed on `around_utc`
323
+ (a detection's first_seen_utc) when given. See `core/audit/session_tree.py`.
324
+ """
325
+ from agentmetry.core.audit.session_tree import session_tree
326
+
327
+ if not settings.audit_export_enabled:
328
+ return {"correlation_id": correlation_id, "enabled": False, "nodes": [], "summary": {}, "truncated": False}
329
+ try:
330
+ return await asyncio.to_thread(session_tree, correlation_id, limit, around_utc)
331
+ except Exception as exc:
332
+ raise HTTPException(status_code=500, detail=str(exc)) from exc
333
+
334
+
311
335
  @router.get("/detections/{correlation_id}", dependencies=[Depends(require_api_key)])
312
336
  async def audit_detections(correlation_id: str):
313
337
  """Run correlated behavioral rules over one session and return detections.
@@ -452,6 +476,18 @@ async def audit_export_evidence():
452
476
  )
453
477
 
454
478
 
479
+ @router.get("/machine", dependencies=[Depends(require_api_key)])
480
+ async def machine_view():
481
+ """This machine's agents, posture, MCP servers and tool policy.
482
+
483
+ Read-only. The payload is the local dashboard's "This machine" page. It
484
+ carries no command lines, paths, or secret values.
485
+ """
486
+ from agentmetry.core.diagnostics.machine_view import snapshot
487
+
488
+ return await asyncio.to_thread(snapshot)
489
+
490
+
455
491
  @router.get("/status", dependencies=[Depends(require_api_key)])
456
492
  async def audit_status():
457
493
  """Freshness + per-source counts so the dashboard can show 'last event N min ago'.
@@ -529,6 +529,93 @@ def cmd_install(args: argparse.Namespace) -> int:
529
529
  _HOOK_INSTALLERS = ("cursor", "claude", "codex", "qwen", "kimi", "qoder", "codebuddy", "copilot")
530
530
 
531
531
 
532
+
533
+
534
+ def cmd_cloud(args: argparse.Namespace) -> int:
535
+ """Poll a vendor cloud-agent API once and record what it exposes.
536
+
537
+ Everything this records passes through the same external ingest as the
538
+ hooks: canonicalized server-side, hash-chained, detected against. The
539
+ key comes from the environment (CURSOR_API_KEY, ANTHROPIC_COMPLIANCE_KEY)
540
+ and is never logged.
541
+ """
542
+ import os
543
+
544
+ import httpx
545
+
546
+ from agentmetry.core.audit.cloud_bridge import (
547
+ fetch_claude_messages,
548
+ fetch_claude_sessions,
549
+ fetch_cursor_agents,
550
+ fetch_cursor_runs,
551
+ map_claude_messages,
552
+ map_claude_session,
553
+ map_cursor_agent,
554
+ map_outbound_poll,
555
+ )
556
+
557
+ base = (args.url or "").strip() or None
558
+ posted = 0
559
+
560
+ def post(payload: dict) -> bool:
561
+ nonlocal posted
562
+ url = os.environ.get("AGENTMETRY_URL", "http://127.0.0.1:8000").rstrip("/")
563
+ key = os.environ.get("AGENTMETRY_API_KEY", "")
564
+ headers = {"Content-Type": "application/json"}
565
+ if key:
566
+ headers["Authorization"] = f"Bearer {key}"
567
+ resp = httpx.post(f"{url}/api/v1/audit/ingest", json=payload, headers=headers, timeout=15)
568
+ ok = resp.status_code < 300
569
+ if ok:
570
+ posted += 1
571
+ else:
572
+ print(f" ingest refused ({resp.status_code}): {resp.text[:120]}")
573
+ return ok
574
+
575
+ if args.provider == "cursor":
576
+ api_key = os.environ.get("CURSOR_API_KEY", "")
577
+ if not api_key:
578
+ print("CURSOR_API_KEY is not set.")
579
+ return 2
580
+ cursor_base = base or __import__("agentmetry.core.audit.cloud_bridge", fromlist=["CURSOR_DEFAULT_BASE"]).CURSOR_DEFAULT_BASE
581
+ post(map_outbound_poll("cursor_cloud", "GET", f"{cursor_base.rstrip('/')}/v1/agents"))
582
+ for agent in fetch_cursor_agents(api_key, cursor_base):
583
+ runs = []
584
+ if not args.skip_runs:
585
+ try:
586
+ runs = fetch_cursor_runs(str(agent.get("id")), api_key, base or "https://api.cursor.com")
587
+ except httpx.HTTPError as exc:
588
+ print(f" runs fetch failed for {agent.get('id')}: {exc}")
589
+ for payload in map_cursor_agent(agent, runs):
590
+ post(payload)
591
+ print(f"cursor: {posted} events recorded.")
592
+ return 0
593
+
594
+ if args.provider == "claude":
595
+ api_key = os.environ.get("ANTHROPIC_COMPLIANCE_KEY", "")
596
+ if not api_key:
597
+ print("ANTHROPIC_COMPLIANCE_KEY is not set (a Compliance Access Key with read:compliance_user_data, not an admin key).")
598
+ return 2
599
+ kind = "remote" if args.remote else "local"
600
+ claude_base = base or "https://api.anthropic.com"
601
+ post(map_outbound_poll("claude_cloud", "GET", f"{claude_base.rstrip('/')}/v1/compliance/apps/sessions/{kind}"))
602
+ sessions = fetch_claude_sessions(api_key, kind=kind, base=claude_base, limit=args.limit)
603
+ for session in sessions[: args.limit]:
604
+ post(map_claude_session(session))
605
+ try:
606
+ messages = fetch_claude_messages(api_key, kind, str(session.get("id")), base or "https://api.anthropic.com")
607
+ except httpx.HTTPError as exc:
608
+ print(f" messages fetch failed for {session.get('id')}: {exc}")
609
+ continue
610
+ for payload in map_claude_messages(session, messages):
611
+ post(payload)
612
+ print(f"claude ({kind}): {posted} events recorded.")
613
+ return 0
614
+
615
+ print(f"unknown provider: {args.provider}")
616
+ return 2
617
+
618
+
532
619
  def _hooks_status() -> int:
533
620
  """Coverage as an exit code, for a deployment tool rather than a person.
534
621
 
@@ -581,6 +668,21 @@ def cmd_hooks(args: argparse.Namespace) -> int:
581
668
  from agentmetry.core.audit import hook_bootstrap
582
669
  from agentmetry.core.diagnostics import hook_coverage
583
670
 
671
+ if args.action == "install":
672
+ # Before --managed too: a managed install would put the temporary path
673
+ # into machine-wide policy files.
674
+ reason = hook_bootstrap.ephemeral_environment()
675
+ if reason:
676
+ print(
677
+ f"Not installing hooks: {reason}. That cache can be deleted at any "
678
+ "time, and every hook would then fail (most agents silently stop "
679
+ "recording; Copilot CLI blocks every tool call). Install Agentmetry "
680
+ "somewhere that stays, then run this again:\n"
681
+ " uv tool install agentmetry (or: pipx install agentmetry, or: pip install agentmetry)\n"
682
+ " agentmetry hooks install",
683
+ file=sys.stderr,
684
+ )
685
+ return 1
584
686
  if getattr(args, "managed", False):
585
687
  return _hooks_managed(args)
586
688
  if args.action == "status":
@@ -1419,6 +1521,11 @@ def main(argv: list[str] | None = None) -> int:
1419
1521
  sub.add_parser("status", help="orchestrator health and audit export status")
1420
1522
  stats = sub.add_parser("stats", help="audit trail metrics for dogfood (events, detections)")
1421
1523
  stats.add_argument("--days", type=int, default=7)
1524
+ cloud = sub.add_parser("cloud", help="record what a vendor cloud-agent API exposes (Cursor, Claude)")
1525
+ cloud.add_argument("provider", choices=("cursor", "claude"))
1526
+ cloud.add_argument("--url", default="", help="vendor API base override")
1527
+ cloud.add_argument("--limit", type=int, default=10, help="max sessions/agents per poll")
1528
+ cloud.add_argument("--skip-runs", action="store_true", help="cursor: agents only, skip per-run fetch")
1422
1529
  detections = sub.add_parser("detections", help="list detections for one session")
1423
1530
  detections.add_argument("correlation_id", help="correlation_id / session id")
1424
1531
  disposition = sub.add_parser("disposition", help="close an audit detection from the shell")
@@ -1610,6 +1717,7 @@ def main(argv: list[str] | None = None) -> int:
1610
1717
  "hook": cmd_hook,
1611
1718
  "status": cmd_status,
1612
1719
  "stats": cmd_stats,
1720
+ "cloud": cmd_cloud,
1613
1721
  "detections": cmd_detections,
1614
1722
  "disposition": cmd_disposition,
1615
1723
  "logs": cmd_logs,
@@ -0,0 +1,81 @@
1
+ """Read tool calls out of an Antigravity conversation database.
2
+
3
+ The installed hook only matches `run_command`, so file reads, writes, search
4
+ and the other tools never reach the trail. The conversation database stores
5
+ every step. The payload is a protobuf blob. The tool name is a length-prefixed
6
+ ASCII field and the arguments are the JSON object that follows it. That framing
7
+ was read off a real database on 2026-10-16. A blob that does not match it
8
+ yields nothing.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import sqlite3
15
+ from pathlib import Path
16
+ from typing import Any, Iterator
17
+
18
+
19
+ def _varint(buf: bytes, index: int) -> tuple[int, int]:
20
+ value = 0
21
+ shift = 0
22
+ while index < len(buf) and shift <= 28:
23
+ byte = buf[index]
24
+ index += 1
25
+ value |= (byte & 0x7F) << shift
26
+ if not byte & 0x80:
27
+ return value, index
28
+ shift += 7
29
+ raise ValueError("truncated varint")
30
+
31
+
32
+ def iter_tool_calls(payload: bytes) -> Iterator[tuple[str, dict[str, Any]]]:
33
+ """Yield (tool name, arguments) from one step payload."""
34
+ index = 0
35
+ while True:
36
+ marker = payload.find(b"\x12", index)
37
+ if marker < 0 or marker + 2 >= len(payload):
38
+ return
39
+ name_len = payload[marker + 1]
40
+ if name_len == 0 or name_len > 64 or name_len & 0x80:
41
+ index = marker + 1
42
+ continue
43
+ name_at = marker + 2
44
+ name_end = name_at + name_len
45
+ if name_end >= len(payload) or payload[name_end] != 0x1A:
46
+ index = marker + 1
47
+ continue
48
+ name = payload[name_at:name_end]
49
+ if not name.isascii() or not all(chr(c).isalnum() or c == ord("_") for c in name):
50
+ index = marker + 1
51
+ continue
52
+ try:
53
+ json_len, json_at = _varint(payload, name_end + 1)
54
+ except ValueError:
55
+ index = marker + 1
56
+ continue
57
+ blob = payload[json_at : json_at + json_len]
58
+ if not blob.startswith(b"{"):
59
+ index = marker + 1
60
+ continue
61
+ try:
62
+ args = json.loads(blob)
63
+ except json.JSONDecodeError:
64
+ index = marker + 1
65
+ continue
66
+ if isinstance(args, dict):
67
+ yield name.decode("ascii"), args
68
+ index = json_at + max(json_len, 1)
69
+
70
+
71
+ def tool_names(db_path: Path) -> dict[str, int]:
72
+ """Count tool names in one conversation database. Arguments are not returned."""
73
+ counts: dict[str, int] = {}
74
+ uri = f"file:{Path(db_path).as_posix()}?mode=ro"
75
+ with sqlite3.connect(uri, uri=True) as con:
76
+ for (payload,) in con.execute("SELECT step_payload FROM steps"):
77
+ if not isinstance(payload, (bytes, bytearray)):
78
+ continue
79
+ for name, _args in iter_tool_calls(payload):
80
+ counts[name] = counts.get(name, 0) + 1
81
+ return counts