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.
- {agentmetry-0.8.0 → agentmetry-0.9.1}/PKG-INFO +1 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/routes/audit.py +15 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/cli/__init__.py +60 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/disposition.py +10 -4
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/loader.py +8 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/scanner.py +7 -4
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/evidence_pack.py +2 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/external.py +9 -7
- agentmetry-0.9.1/agentmetry/core/audit/otel_ingest.py +526 -0
- agentmetry-0.9.1/agentmetry/core/audit/policy_settings.py +162 -0
- agentmetry-0.9.1/agentmetry/core/audit/run_context.py +197 -0
- agentmetry-0.9.1/agentmetry/core/audit/tool_policy/capability.py +42 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/tool_policy/evaluator.py +24 -15
- agentmetry-0.9.1/agentmetry/core/audit/tool_policy/loader.py +89 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/tool_policy/models.py +4 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/doctor.py +37 -0
- agentmetry-0.9.1/agentmetry/core/operator_identity.py +80 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/version.py +1 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/hooks/ingest.py +159 -33
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/conftest.py +37 -0
- agentmetry-0.9.1/tests/fixtures/otel_claude_code_session.json +902 -0
- agentmetry-0.9.1/tests/test_ask_gate_phase_a.py +289 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cli_commands.py +6 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_scanner.py +10 -7
- agentmetry-0.9.1/tests/test_hook_decision_format.py +198 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_enforcement_timing.py +7 -2
- agentmetry-0.9.1/tests/test_hook_import_cost.py +179 -0
- agentmetry-0.9.1/tests/test_hook_real_process.py +100 -0
- agentmetry-0.9.1/tests/test_operator_attribution.py +231 -0
- agentmetry-0.9.1/tests/test_otel_ingest.py +354 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_readme_claims.py +22 -3
- agentmetry-0.9.1/tests/test_suite_leaves_real_home_alone.py +40 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_tool_policy_agent_cli.py +7 -1
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_tool_policy_git_hooks.py +7 -1
- agentmetry-0.8.0/agentmetry/core/audit/run_context.py +0 -129
- agentmetry-0.8.0/agentmetry/core/audit/tool_policy/loader.py +0 -44
- {agentmetry-0.8.0 → agentmetry-0.9.1}/.dockerignore +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/.env.agentmetry-demo +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/.env.example +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/.gitignore +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/Dockerfile +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/README.md +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/main.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/routes/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/websocket.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/api/ws_bridge.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/cli/__main__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/agt.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/chronicle.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/cloudevents.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/ecs.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/adapters/splunk.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/alerts.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/atlas.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/canonical.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/compliance_digest.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/benchmark.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_approval_denied_then_executed.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_arbitrary_host_stage_execute.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_autonomous_unapproved_write.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_credential_exfil.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_credential_then_cloud_api.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_destructive_delete_burst.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_discovery_then_collect.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_dotfile_then_git_push.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_encoded_command_download.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_env_credential_exfil.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_hashed_only_no_command.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_interpreter_egress.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_pr_merged_without_review.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_proc_substitution_cradle.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_remote_pipe_to_shell.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_remote_staging_then_execute.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_secret_manager_then_egress.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_session_tool_burst.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_single_command_exfil.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_single_quoted_interpreter_egress.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_ssh_directory_exfil.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_subagent_swarm.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_timestamp_collision.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/attack_untrusted_input_then_action.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_authoring_merge_fixtures.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_autonomous_after_approval.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_build_artifact_cleanup.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_ci_artifact_download.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_commit_message_names_cloud_clis.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_database_migration.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_dependency_install_and_build.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_download_release_archive.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_env_example_read.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_data_then_run_repo_script.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_dataset_then_analyse.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_lockfile_then_install.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_fetch_piped_into_own_script.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_git_review_and_push.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_github_raw_docs_then_script.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_human_driven_deletes.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_local_api_probing.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_long_but_calm_session.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_loopback_is_not_egress.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_loopback_pipe_to_interpreter.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_mcp_text_mentions_credential_path.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_ordinary_development.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_package_manager_after_fetch.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_reading_config_that_is_not_secret.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_remote_api_call_no_credentials.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_research_then_docs.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_reversed_order_is_not_exfil.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_secret_names_and_docs.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_test_and_fix_loop.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/benign_writing_about_credentials.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/corpus/corpus.yaml +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/engine.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/live.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/live_store.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/models.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/rules.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/traits.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/yaml_config.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/detection/yaml_rules.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dlp/models.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/dogfood.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/hashing.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/heartbeat.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/hook_bootstrap.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/identity.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/ingest.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/migrate.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/mitre.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/policy.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/redaction.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/replay.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/sinks.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/spool.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/tool_policy/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_anchor.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_chain.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_db.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/audit/trail_merkle.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/auth.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/audit_exporter.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/bridges.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/bus.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/events.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/bus/outbox.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/config.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/autostart.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/driver_paths.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/env_file.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/hook_coverage.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/mcp_inventory.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/diagnostics/mcp_schema.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/host.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/permissions.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/drivers/spec.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/extensions.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/core/health.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/hooks/__init__.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/detection/manifest.yaml +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/dlp/manifest.yaml +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/opa/agent_rules.rego +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/agentmetry/policies/tool/manifest.yaml +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/pyproject.toml +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/fixtures/agt_filesink_exfil.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/fixtures/agt_filesink_mixed.jsonl +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/fixtures/fake_mcp_server.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_agentmetry_audit.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_agentmetry_ingest_client.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_agt_adapter.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_atlas_detection_enrichment.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_atlas_mapping.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_audit_sinks.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_audit_stats.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_audit_tail.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_auth.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_autostart.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_boot_sequence.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_burst_windows.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chinese_agent_hooks.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chinese_agent_sprint_b.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chinese_agent_sprint_c.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_chronicle_adapter.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cli_backup.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cli_env_var_names.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_cloudevents_adapter.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_compliance_digest.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_crewai_adapter.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_adi.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_benchmark.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_default_config.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_disposition.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_disposition_api.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_duplicate_emission.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_emit_durability.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_engine.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_evasion.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_hf_incident.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_quoted_command_words.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_rule_identity.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_detection_rules_v2.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_agent_env_override.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_invisible_unicode.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dlp_markdown_exfil.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_doctor.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dogfood.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_dogfood_freeze.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_driver_paths.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_ecs_threat.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_event_bus.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_evidence_pack.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_extensions.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_external_ingest.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_forwarding_shape.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_funding_manifest.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_heartbeat.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_bootstrap.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_coverage.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_hook_spool.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_identity_enrich.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_ingest_roundtrip.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_isolation.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_launch_targets.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_live_detection.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_live_detection_store.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_audit_proxy.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_concealed_text.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_inventory.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_meta_fingerprint.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mcp_schema.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_mitre_dlp.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_opensre_adapter.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_powershell_encoding.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_redaction.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_replay.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_sigma_pack.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_static_serving.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_subagent_lifecycle.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_tool_policy.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_anchor.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_chain.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_concurrency.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_trail_merkle.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_unattended_agent_policy.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_version.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tests/test_yaml_detection_rules.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tools/generate_sigma_pack.py +0 -0
- {agentmetry-0.8.0 → agentmetry-0.9.1}/tools/mcp_audit_proxy.py +0 -0
- {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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
447
|
-
"initiator": {"type": "human", "id": decided_by
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
cfg = policy_settings()
|
|
27
|
+
rules = load_dlp_rules(cfg.dlp_rules_path)
|
|
25
28
|
for r in rules:
|
|
26
|
-
if not
|
|
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 =
|
|
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":
|
|
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
|
|
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":
|
|
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(
|