controlzero 1.8.2__tar.gz → 1.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {controlzero-1.8.2 → controlzero-1.9.0}/CHANGELOG.md +52 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/PKG-INFO +2 -1
- {controlzero-1.8.2 → controlzero-1.9.0}/README.md +1 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/__init__.py +1 -1
- controlzero-1.9.0/controlzero/cli/hosts/antigravity.py +161 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/base.py +9 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/claude_code.py +14 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/main.py +420 -5
- controlzero-1.9.0/controlzero/cli/templates/antigravity/hooks.json +28 -0
- controlzero-1.9.0/controlzero/cli/templates/antigravity.yaml +93 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/device.py +24 -3
- {controlzero-1.8.2 → controlzero-1.9.0}/pyproject.toml +8 -1
- controlzero-1.9.0/tests/test_antigravity_adapter.py +275 -0
- controlzero-1.9.0/tests/test_antigravity_hook_check.py +160 -0
- controlzero-1.9.0/tests/test_antigravity_install.py +383 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_device.py +54 -1
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hosts_adapter.py +6 -3
- controlzero-1.8.2/controlzero/cli/hosts/antigravity.py +0 -76
- {controlzero-1.8.2 → controlzero-1.9.0}/.gitignore +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/Dockerfile.test +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/LICENSE +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/action_aliases.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/action_validator.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/bundle.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/credential_hook.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/credential_scanner.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/credentials_data/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/dlp_scanner.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/enforcer.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/hook_extractors.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/tool_extractors.json +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/_internal/types.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/audit_local.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/audit_remote.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/canonical.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/_secrets.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/console.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/debug_bundle.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/doctor.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/codex_cli.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/gemini_cli.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/kiro.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/hosts/unknown.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/kiro_adapter.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/migrate.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/spool_cmd.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/telemetry_consent.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/autogen.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/claude-code.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/codex-cli.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/cost-cap.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/crewai.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/cursor.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/gemini-cli.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/generic.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/kiro/kiro.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/langchain.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/mcp.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/cli/templates/rag.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/client.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/enrollment.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/error_codes.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/error_codes.yaml +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/errors.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hitl/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hitl/grant_protocol.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hitl/mock.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hitl/pending_approval.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hitl/secret_leak_guard.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hitl/status.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hooks/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hooks/tool_output_handler.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/hosted_policy.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/anthropic.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/autogen.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/braintrust.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/crewai/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/crewai/agent.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/crewai/crew.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/crewai/task.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/crewai/tool.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/google.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/google_adk/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/google_adk/agent.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/google_adk/tool.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/agent.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/callbacks.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/chain.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/graph.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/modern.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langchain/tool.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/langfuse.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/litellm.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/openai.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/pydantic_ai.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/integrations/vercel_ai.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/layout_migration.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/policy_loader.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_compress.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_constants.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_crc32c.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_crypto.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_frame.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_metrics.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_spool.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_state.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/_uploader.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/spool/cz-audit-v1.dict +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/tamper.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/controlzero/tracecontext.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/examples/hello_world.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/conftest.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/integrations/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/integrations/test_google.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/parity/action_aliases.json +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/__init__.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/conftest.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_cli.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_concurrency.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_conformance.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_core.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_crash.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_diskfull.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_sink_wiring.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_transcript_localack.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/spool/test_spool_uploader.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_action_aliases.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_action_canonicalization.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_action_validator_t86.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_agent_name_env.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_api_key_mask.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_audit_remote.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_audit_remote_sdk_version.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_audit_sink_isolation.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_bundle_parser.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_bundle_translate.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_canonical_phase1a.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_carve_out.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_debug_bundle.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_extractor_integration.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_hook.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_hosted_refresh.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_init.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_init_templates.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_tail.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_test.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_cli_validate.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_coding_agent_hooks.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_conditions.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_conformance.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_console.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_credential_hook.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_default_action.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_dlp_scanner.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_doctor.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_engine_version_consistency.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_enrollment.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_env_dump_438.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_error_codes.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_errors_e_codes.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_fail_closed_eval.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_glob_matching.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_5d_email_install.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_cli_flag.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_exceptions.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_get_secret_hitl.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_mock_backend.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_pending_approval.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_request_approval.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_secret_leak_guard.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_6a_wait.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_conformance.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_phase2b_protocol.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_reason_codes.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hitl_validator_keys.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hook_extractors.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hosted_policy_e2e.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hybrid_mode_strict.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_hybrid_mode_warn.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_install_hook_command.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_install_hooks.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_kiro_adapter.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_kiro_hook_templates.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_kiro_install.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_layout_migration_t101.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_layout_parity_t102.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_local_mode_dict.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_local_mode_file_json.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_local_mode_file_yaml.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_log_fallback_stderr.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_log_options_ignored_hosted.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_log_rotation.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_migrate.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_min_sdk_version_gate.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_multi_client_per_project_175.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_no_policy_no_key.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_package_rename_shim.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_policy_engine_version_phase1b.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_policy_freshness.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_policy_settings.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_policy_source_audit.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_quarantine.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_reason_code.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_refresh.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_secrets.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_sql_semantic_class.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_synthetic_policy_id_t79.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_t103_precedence.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_t104_cache_gc.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_t108_local_override_audit.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_t96_single_audit_log.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_t99_install_prefetch_bundle.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_tamper.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_tamper_behavior.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_tamper_hook.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_telemetry_consent.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_tracecontext.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tests/test_unsafe_int_boundary.py +0 -0
- {controlzero-1.8.2 → controlzero-1.9.0}/tools/cz-kiro-adapter +0 -0
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.0 -- 2026-06-15 (Antigravity install CLI, epic gh#925)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Google Antigravity (`agy`) governance -- BETA.** The `controlzero install
|
|
8
|
+
antigravity` / `uninstall antigravity` commands complete the Antigravity
|
|
9
|
+
integration on top of the host adapter that shipped in 1.8.0 (gh#927). Both
|
|
10
|
+
Antigravity surfaces (the IDE and the `agy` CLI) deliver the hook payload as
|
|
11
|
+
JSON on stdin, so they route directly through the shared
|
|
12
|
+
`controlzero hook-check` -> policy engine -> audit path with no shim, and
|
|
13
|
+
audit rows are attributed to `agent="antigravity"`.
|
|
14
|
+
|
|
15
|
+
- **Real EXTERNAL hooks.json contract.** Wires the `PreToolUse` (deciding
|
|
16
|
+
gate) + `PostToolUse` (observe) file-based events -- stdin JSON in, stdout
|
|
17
|
+
JSON out (camelCase), NOT exit-code based -- which is the contract the agy
|
|
18
|
+
IDE/CLI actually executes. (The in-process Python-SDK hook *class* names
|
|
19
|
+
`PreToolCallDecideHook` / `PostToolCallHook` are a different API and would
|
|
20
|
+
never fire from hooks.json.) The adapter ALWAYS emits an explicit
|
|
21
|
+
`{"decision": ...}`: an empty `{}` is read by agy as `invalid_args` and
|
|
22
|
+
DENIES the tool (cmux#5358), so silence-that-prints-`{}` is impossible.
|
|
23
|
+
|
|
24
|
+
- **`controlzero install antigravity`** (gh#928). Merges a `PreToolUse` +
|
|
25
|
+
`PostToolUse` block (broad `*` matcher) into the Antigravity `hooks.json`.
|
|
26
|
+
Writes the engine-EXECUTED path `~/.gemini/config/hooks.json` by default
|
|
27
|
+
and, for the user scope, also mirrors to the `agy` TUI `/hooks` display
|
|
28
|
+
path `~/.gemini/antigravity-cli/hooks.json` (antigravity-cli#49) so the
|
|
29
|
+
hook both fires AND displays; `<cwd>/.agents/hooks.json` with `--project`,
|
|
30
|
+
or a custom path with `--settings`. Idempotent (re-installs collapse to
|
|
31
|
+
one entry; byte-identical state), preserves pre-existing third-party hooks
|
|
32
|
+
(incl. ones sharing a block) and unrelated keys, and writes the
|
|
33
|
+
`~/.controlzero/policy.yaml` Antigravity template. `uninstall antigravity`
|
|
34
|
+
removes only the Control Zero entries (cleaning both paths) and preserves
|
|
35
|
+
your policy + audit log. Supports `--force` / `--merge` and `--api-key` /
|
|
36
|
+
`--email` for dashboard sync.
|
|
37
|
+
|
|
38
|
+
- **Human-in-the-loop on the wire.** A Control Zero approval gate (a deny
|
|
39
|
+
flagged `requires_approval`) maps to Antigravity's `ask` decision by
|
|
40
|
+
default. A plain `ask` can be auto-satisfied by a cached "Always Allow",
|
|
41
|
+
so set `CZ_ANTIGRAVITY_HITL_DECISION=force_ask` (a guaranteed prompt, once
|
|
42
|
+
verified on your pinned agy build) or `=deny` for a hard block. An
|
|
43
|
+
unrecognized value safely falls back to `ask`.
|
|
44
|
+
|
|
45
|
+
- **Agent fingerprint.** `detect_client_name()` now recognises Antigravity
|
|
46
|
+
via the `ANTIGRAVITY_` / `AGY_` env prefixes (checked BEFORE the Gemini CLI
|
|
47
|
+
branch, since Antigravity shares the `~/.gemini/` config lineage and may
|
|
48
|
+
also carry `GEMINI_*` vars). A stray `GEMINI_API_KEY` is never mislabeled
|
|
49
|
+
as Antigravity.
|
|
50
|
+
|
|
51
|
+
- **Docs.** New `docs/integrations/antigravity.md` install guide alongside
|
|
52
|
+
the existing `antigravity-hook-payloads.md` reference; `antigravity` added
|
|
53
|
+
to the SDK README template list, tagged **BETA**.
|
|
54
|
+
|
|
3
55
|
## 1.8.2 -- 2026-06-15 (Kiro IDE hook contract hotfix, epic gh#877)
|
|
4
56
|
|
|
5
57
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: controlzero
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.0
|
|
4
4
|
Summary: AI agent governance: policies, audit, and observability for tool calls. Works locally with no signup.
|
|
5
5
|
Project-URL: Homepage, https://controlzero.ai
|
|
6
6
|
Project-URL: Documentation, https://docs.controlzero.ai
|
|
@@ -130,6 +130,7 @@ Templates available (`controlzero init -t <name>`):
|
|
|
130
130
|
- `codex-cli` — Codex CLI hook starter
|
|
131
131
|
- `gemini-cli` — Gemini CLI hook starter
|
|
132
132
|
- `kiro` — Kiro (AWS IDE) hook starter (Beta)
|
|
133
|
+
- `antigravity` — Google Antigravity (IDE + `agy` CLI) hook starter (Beta)
|
|
133
134
|
|
|
134
135
|
## Loading a policy
|
|
135
136
|
|
|
@@ -76,6 +76,7 @@ Templates available (`controlzero init -t <name>`):
|
|
|
76
76
|
- `codex-cli` — Codex CLI hook starter
|
|
77
77
|
- `gemini-cli` — Gemini CLI hook starter
|
|
78
78
|
- `kiro` — Kiro (AWS IDE) hook starter (Beta)
|
|
79
|
+
- `antigravity` — Google Antigravity (IDE + `agy` CLI) hook starter (Beta)
|
|
79
80
|
|
|
80
81
|
## Loading a policy
|
|
81
82
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Google Antigravity (agy) external file-based hook adapter.
|
|
2
|
+
|
|
3
|
+
Antigravity's EXTERNAL hook contract (the one wired through ``hooks.json``
|
|
4
|
+
read by the IDE and the ``agy`` CLI) is distinct from the in-process
|
|
5
|
+
Python-SDK hook *classes* (``PreToolCallDecideHook`` / ``PostToolCallHook``,
|
|
6
|
+
registered via ``HookRunner``). Control Zero hooks via the FILE contract, so
|
|
7
|
+
this adapter speaks that protocol -- not the SDK class names.
|
|
8
|
+
|
|
9
|
+
External contract (verified against antigravity.google/docs/hooks and four
|
|
10
|
+
real integrators -- antigravity-cli#49, cmux#5358, munder-difflin#52/#54,
|
|
11
|
+
danicat.dev):
|
|
12
|
+
|
|
13
|
+
* Events: ``PreToolUse`` (the deciding gate -- CAN deny), ``PostToolUse``
|
|
14
|
+
(observe only), plus ``PreInvocation`` / ``PostInvocation`` / ``Stop``
|
|
15
|
+
(model-call lifecycle; ``PreInvocation`` cannot deny). Control Zero's
|
|
16
|
+
governance gate is ``PreToolUse``; the post scan is ``PostToolUse``.
|
|
17
|
+
There is NO prompt-submit / UserPromptSubmit event.
|
|
18
|
+
* Delivery: stdin = JSON in, stdout = JSON out, camelCase. NOT exit-code
|
|
19
|
+
based. (A crashing / non-zero / timed-out hook is read as a deny --
|
|
20
|
+
fail-closed -- so the renderer must never raise.)
|
|
21
|
+
* PreToolUse INPUT: ``{toolCall:{name,args}, stepIdx, conversationId,
|
|
22
|
+
workspacePaths[], transcriptPath}`` (``artifactDirectoryPath`` may also
|
|
23
|
+
appear). ``normalize_payload`` flattens ``toolCall`` -> ``tool_name`` /
|
|
24
|
+
``tool_input`` so the shared extractor + globs work unchanged.
|
|
25
|
+
* PreToolUse OUTPUT: ``{"decision": "allow"|"deny"|"ask", "reason": "..."}``.
|
|
26
|
+
CRITICAL FAIL-CLOSED LANDMINE (cmux#5358): an empty / decision-less object
|
|
27
|
+
``{}`` on stdout is read by agy as ``invalid_args`` and DENIES the tool.
|
|
28
|
+
We therefore ALWAYS emit an explicit ``{"decision": ...}`` -- the allow
|
|
29
|
+
path is ``{"decision":"allow"}``, never silence-that-still-prints-{}.
|
|
30
|
+
|
|
31
|
+
HITL mapping: a Control Zero approval gate maps to ``ask`` by default. A
|
|
32
|
+
plain ``ask`` CAN be auto-satisfied by a cached "Always Allow" (so it is not
|
|
33
|
+
a guaranteed prompt); some agy builds also accept ``force_ask`` (always
|
|
34
|
+
prompts). Because ``force_ask`` is not confirmed across all builds and an
|
|
35
|
+
unrecognized decision risks the same ``invalid_args`` -> deny trap, the HITL
|
|
36
|
+
decision token is configurable via ``CZ_ANTIGRAVITY_HITL_DECISION`` (default
|
|
37
|
+
``ask``; set to ``force_ask`` once verified against your pinned agy, or
|
|
38
|
+
``deny`` for a hard block-with-reason).
|
|
39
|
+
|
|
40
|
+
Subagent tool calls fire the parent ``PreToolUse`` hook, so one adapter
|
|
41
|
+
governs parent + subagents.
|
|
42
|
+
|
|
43
|
+
Reference (pinned): see docs/integrations/antigravity-hook-payloads.md
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import os
|
|
49
|
+
from typing import Mapping
|
|
50
|
+
|
|
51
|
+
from controlzero.cli.hosts.base import CZDecision, HostAdapter
|
|
52
|
+
|
|
53
|
+
# File-based EVENT names (NOT the in-process SDK class names). PreToolUse is
|
|
54
|
+
# the deciding gate; PostToolUse is observe-only.
|
|
55
|
+
_ANTIGRAVITY_HOOK_EVENTS = frozenset(
|
|
56
|
+
{"PreToolUse", "PostToolUse", "PreInvocation", "PostInvocation", "Stop"}
|
|
57
|
+
)
|
|
58
|
+
# Events SAFE to claim by name alone. PreToolUse / PostToolUse / Stop collide
|
|
59
|
+
# with Claude Code's PascalCase event names, so a payload carrying ONLY one of
|
|
60
|
+
# those (no toolCall envelope, no env marker) is ambiguous and must fall to
|
|
61
|
+
# Claude Code (which sits ahead in the registry). PreInvocation / PostInvocation
|
|
62
|
+
# are Antigravity-distinct -- no other host emits them -- so they are safe
|
|
63
|
+
# disambiguators. Antigravity's unambiguous signal is the ``toolCall`` envelope,
|
|
64
|
+
# which the claim below keys on first.
|
|
65
|
+
_ANTIGRAVITY_CLAIM_EVENTS = frozenset({"PreInvocation", "PostInvocation"})
|
|
66
|
+
_CLIENT_ALIASES = frozenset({"antigravity", "agy", "antigravity-cli"})
|
|
67
|
+
_ENV_PREFIXES = ("ANTIGRAVITY_", "AGY_")
|
|
68
|
+
|
|
69
|
+
# Source-confirmed decision tokens for PreToolUse output.
|
|
70
|
+
_VALID_DECISIONS = frozenset({"allow", "deny", "ask", "force_ask"})
|
|
71
|
+
_DEFAULT_HITL_DECISION = "ask"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _resolve_hitl_decision(env: Mapping[str, str] | None = None) -> str:
|
|
75
|
+
"""Resolve the decision token used for a HITL approval gate.
|
|
76
|
+
|
|
77
|
+
Default ``ask`` (source-confirmed valid on every agy build). Operators
|
|
78
|
+
can opt into ``force_ask`` (guaranteed prompt) or ``deny`` once verified
|
|
79
|
+
against their pinned agy via ``CZ_ANTIGRAVITY_HITL_DECISION``. An
|
|
80
|
+
unrecognized value falls back to ``ask`` so we never emit a token that
|
|
81
|
+
could trip the ``invalid_args`` -> deny trap.
|
|
82
|
+
"""
|
|
83
|
+
src = env if env is not None else os.environ
|
|
84
|
+
raw = (src.get("CZ_ANTIGRAVITY_HITL_DECISION") or "").strip().lower()
|
|
85
|
+
return raw if raw in _VALID_DECISIONS else _DEFAULT_HITL_DECISION
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AntigravityAdapter(HostAdapter):
|
|
89
|
+
name = "antigravity"
|
|
90
|
+
canonical_source = "antigravity"
|
|
91
|
+
# Antigravity decides from the stdout JSON, NOT the exit code. A non-zero
|
|
92
|
+
# exit is read as a fail-closed deny that would override an ``ask`` /
|
|
93
|
+
# ``allow`` verdict (turning a HITL prompt into a hard block), so the
|
|
94
|
+
# hook-check command must exit 0 for this host and let the JSON decide.
|
|
95
|
+
decision_via_exit_code = False
|
|
96
|
+
|
|
97
|
+
def claim(self, payload: dict, env: Mapping[str, str]) -> bool:
|
|
98
|
+
if (env.get("CONTROLZERO_CLIENT") or "").strip().lower() in _CLIENT_ALIASES:
|
|
99
|
+
return True
|
|
100
|
+
for key in env:
|
|
101
|
+
if key.upper().startswith(_ENV_PREFIXES):
|
|
102
|
+
return True
|
|
103
|
+
if isinstance(payload, dict):
|
|
104
|
+
# Unambiguous Antigravity signal: the toolCall envelope.
|
|
105
|
+
if "toolCall" in payload:
|
|
106
|
+
return True
|
|
107
|
+
# Only the Antigravity-distinct lifecycle events are safe to claim
|
|
108
|
+
# by name; PreToolUse / PostToolUse / Stop collide with Claude Code
|
|
109
|
+
# and are disambiguated by toolCall / env instead.
|
|
110
|
+
if payload.get("hook_event_name") in _ANTIGRAVITY_CLAIM_EVENTS:
|
|
111
|
+
return True
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
def normalize_payload(self, payload: dict) -> dict:
|
|
115
|
+
if not isinstance(payload, dict):
|
|
116
|
+
return payload
|
|
117
|
+
tool_call = payload.get("toolCall")
|
|
118
|
+
if not isinstance(tool_call, dict):
|
|
119
|
+
return payload
|
|
120
|
+
normalized = dict(payload) # preserves conversationId / workspacePaths
|
|
121
|
+
name = tool_call.get("name")
|
|
122
|
+
args = tool_call.get("args")
|
|
123
|
+
if name is not None and not normalized.get("tool_name"):
|
|
124
|
+
normalized["tool_name"] = name
|
|
125
|
+
if args is not None and not normalized.get("tool_input"):
|
|
126
|
+
normalized["tool_input"] = args if isinstance(args, dict) else {}
|
|
127
|
+
return normalized
|
|
128
|
+
|
|
129
|
+
def render(self, decision: CZDecision) -> dict:
|
|
130
|
+
"""Render to Antigravity's PreToolUse stdout-JSON contract.
|
|
131
|
+
|
|
132
|
+
ALWAYS emits an explicit ``decision`` key. An empty / decision-less
|
|
133
|
+
object would be read by agy as ``invalid_args`` and DENY the tool
|
|
134
|
+
(cmux#5358), so silence-that-still-prints-{} is never produced here.
|
|
135
|
+
camelCase per the external contract; Control Zero diagnostic
|
|
136
|
+
metadata is namespaced under ``controlzero`` so it cannot collide
|
|
137
|
+
with agy's reserved output keys.
|
|
138
|
+
"""
|
|
139
|
+
if decision.is_hitl:
|
|
140
|
+
verdict = _resolve_hitl_decision()
|
|
141
|
+
elif decision.is_deny:
|
|
142
|
+
verdict = "deny"
|
|
143
|
+
else:
|
|
144
|
+
verdict = "allow"
|
|
145
|
+
|
|
146
|
+
envelope: dict = {
|
|
147
|
+
"decision": verdict,
|
|
148
|
+
"reason": decision.reason,
|
|
149
|
+
# Diagnostic metadata, namespaced so it never shadows a reserved
|
|
150
|
+
# agy key (decision / reason / permissionOverrides).
|
|
151
|
+
"controlzero": {
|
|
152
|
+
"reasonCode": decision.reason_code,
|
|
153
|
+
"tool": decision.tool,
|
|
154
|
+
"extractedMethod": decision.method,
|
|
155
|
+
"action": decision.extracted_action,
|
|
156
|
+
"actionSemanticClass": decision.semantic_class,
|
|
157
|
+
"tamperDetected": decision.tamper_detected,
|
|
158
|
+
"auditChainBroken": decision.audit_chain_broken,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
return envelope
|
|
@@ -127,6 +127,15 @@ class HostAdapter:
|
|
|
127
127
|
#: Default is ``"unknown"`` -- subclasses MUST override.
|
|
128
128
|
canonical_source: str = "unknown"
|
|
129
129
|
|
|
130
|
+
#: Whether the host signals a deny via the PROCESS EXIT CODE
|
|
131
|
+
#: (non-zero = block, the Claude Code / Gemini / Codex / Kiro
|
|
132
|
+
#: convention) or purely via the stdout JSON ``decision`` field.
|
|
133
|
+
#: Antigravity is stdout-JSON based: a non-zero exit is read as a
|
|
134
|
+
#: fail-closed deny that would OVERRIDE an ``ask``/``allow`` JSON
|
|
135
|
+
#: verdict, so its hook must exit 0 and let the JSON decide. The
|
|
136
|
+
#: hook-check command consults this to pick the deny/HITL exit code.
|
|
137
|
+
decision_via_exit_code: bool = True
|
|
138
|
+
|
|
130
139
|
# -- detection --------------------------------------------------
|
|
131
140
|
|
|
132
141
|
def claim(self, payload: dict, env: Mapping[str, str]) -> bool:
|
|
@@ -61,6 +61,20 @@ class ClaudeCodeAdapter(HostAdapter):
|
|
|
61
61
|
):
|
|
62
62
|
return True
|
|
63
63
|
|
|
64
|
+
# Disambiguation: Google Antigravity's external hooks use the SAME
|
|
65
|
+
# PascalCase event names (PreToolUse / PostToolUse / Stop) but pack
|
|
66
|
+
# the call under a top-level ``toolCall`` envelope, which Claude Code
|
|
67
|
+
# never sends. If ``toolCall`` is present this is an Antigravity
|
|
68
|
+
# payload -- do NOT claim it (let the AntigravityAdapter claim it).
|
|
69
|
+
# This runs BEFORE the Anthropic env-hint check: an Antigravity hook
|
|
70
|
+
# subprocess can inherit CLAUDECODE / CLAUDE_CODE from a Claude-spawned
|
|
71
|
+
# parent shell, and claiming on that stale env hint would emit Claude's
|
|
72
|
+
# approve/block tokens -- invalid for Antigravity, read as a deny.
|
|
73
|
+
# Only the explicit CONTROLZERO_CLIENT=claude_code override above
|
|
74
|
+
# outranks the toolCall signal.
|
|
75
|
+
if isinstance(payload, dict) and "toolCall" in payload:
|
|
76
|
+
return False
|
|
77
|
+
|
|
64
78
|
# Env-var hint: any of the Anthropic-family vars present.
|
|
65
79
|
for hint in self._ENV_HINTS:
|
|
66
80
|
if env.get(hint):
|