controlzero 1.7.0__tar.gz → 1.8.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.0/.gitignore +255 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/CHANGELOG.md +41 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/PKG-INFO +15 -8
- {controlzero-1.7.0 → controlzero-1.8.0}/README.md +14 -7
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/__init__.py +21 -1
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/bundle.py +15 -1
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/enforcer.py +11 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/audit_remote.py +310 -3
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/hosts/__init__.py +8 -0
- controlzero-1.8.0/controlzero/cli/hosts/antigravity.py +76 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/hosts/base.py +22 -0
- controlzero-1.8.0/controlzero/cli/hosts/kiro.py +108 -0
- controlzero-1.8.0/controlzero/cli/kiro_adapter.py +129 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/main.py +496 -17
- controlzero-1.8.0/controlzero/cli/spool_cmd.py +270 -0
- controlzero-1.8.0/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +15 -0
- controlzero-1.8.0/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +15 -0
- controlzero-1.8.0/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +14 -0
- controlzero-1.8.0/controlzero/cli/templates/kiro/kiro.yaml +84 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/client.py +201 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/device.py +30 -7
- controlzero-1.8.0/controlzero/error_codes.py +157 -0
- controlzero-1.8.0/controlzero/error_codes.yaml +520 -0
- controlzero-1.8.0/controlzero/hitl/grant_protocol.py +368 -0
- controlzero-1.8.0/controlzero/spool/__init__.py +99 -0
- controlzero-1.8.0/controlzero/spool/_compress.py +89 -0
- controlzero-1.8.0/controlzero/spool/_constants.py +65 -0
- controlzero-1.8.0/controlzero/spool/_crc32c.py +36 -0
- controlzero-1.8.0/controlzero/spool/_crypto.py +100 -0
- controlzero-1.8.0/controlzero/spool/_frame.py +329 -0
- controlzero-1.8.0/controlzero/spool/_metrics.py +74 -0
- controlzero-1.8.0/controlzero/spool/_spool.py +1126 -0
- controlzero-1.8.0/controlzero/spool/_state.py +154 -0
- controlzero-1.8.0/controlzero/spool/_uploader.py +724 -0
- controlzero-1.8.0/controlzero/spool/cz-audit-v1.dict +0 -0
- controlzero-1.8.0/controlzero/tracecontext.py +130 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/pyproject.toml +23 -1
- controlzero-1.8.0/tests/spool/__init__.py +0 -0
- controlzero-1.8.0/tests/spool/conftest.py +208 -0
- controlzero-1.8.0/tests/spool/test_spool_cli.py +168 -0
- controlzero-1.8.0/tests/spool/test_spool_concurrency.py +110 -0
- controlzero-1.8.0/tests/spool/test_spool_conformance.py +316 -0
- controlzero-1.8.0/tests/spool/test_spool_core.py +415 -0
- controlzero-1.8.0/tests/spool/test_spool_crash.py +106 -0
- controlzero-1.8.0/tests/spool/test_spool_diskfull.py +77 -0
- controlzero-1.8.0/tests/spool/test_spool_sink_wiring.py +447 -0
- controlzero-1.8.0/tests/spool/test_spool_transcript_localack.py +102 -0
- controlzero-1.8.0/tests/spool/test_spool_uploader.py +569 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_dlp_scanner.py +154 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_error_codes.py +58 -0
- controlzero-1.8.0/tests/test_hitl_phase2b_protocol.py +568 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hosted_policy_e2e.py +152 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hosts_adapter.py +177 -0
- controlzero-1.8.0/tests/test_kiro_adapter.py +166 -0
- controlzero-1.8.0/tests/test_kiro_install.py +75 -0
- controlzero-1.8.0/tests/test_tracecontext.py +89 -0
- controlzero-1.8.0/tools/cz-kiro-adapter +31 -0
- controlzero-1.7.0/controlzero/error_codes.py +0 -627
- {controlzero-1.7.0 → controlzero-1.8.0}/Dockerfile.test +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/LICENSE +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/action_aliases.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/action_validator.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/credential_hook.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/credential_scanner.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/credentials_data/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/dlp_scanner.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/hook_extractors.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/tool_extractors.json +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/_internal/types.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/audit_local.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/canonical.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/_secrets.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/console.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/debug_bundle.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/doctor.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/hosts/claude_code.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/hosts/codex_cli.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/hosts/gemini_cli.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/hosts/unknown.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/migrate.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/telemetry_consent.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/autogen.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/claude-code.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/codex-cli.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/cost-cap.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/crewai.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/cursor.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/gemini-cli.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/generic.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/langchain.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/mcp.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/cli/templates/rag.yaml +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/enrollment.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/errors.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hitl/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hitl/mock.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hitl/pending_approval.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hitl/secret_leak_guard.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hitl/status.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hooks/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hooks/tool_output_handler.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/hosted_policy.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/anthropic.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/autogen.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/braintrust.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/crewai/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/crewai/agent.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/crewai/crew.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/crewai/task.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/crewai/tool.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/google.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/google_adk/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/google_adk/agent.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/google_adk/tool.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/agent.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/callbacks.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/chain.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/graph.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/modern.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langchain/tool.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/langfuse.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/litellm.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/openai.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/pydantic_ai.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/integrations/vercel_ai.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/layout_migration.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/policy_loader.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/controlzero/tamper.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/examples/hello_world.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/conftest.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/integrations/__init__.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/integrations/test_google.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/parity/action_aliases.json +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_action_aliases.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_action_canonicalization.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_action_validator_t86.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_agent_name_env.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_api_key_mask.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_audit_remote.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_audit_remote_sdk_version.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_audit_sink_isolation.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_bundle_parser.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_bundle_translate.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_canonical_phase1a.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_carve_out.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_debug_bundle.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_extractor_integration.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_hook.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_hosted_refresh.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_init.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_init_templates.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_tail.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_test.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_cli_validate.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_coding_agent_hooks.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_conditions.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_conformance.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_console.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_credential_hook.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_default_action.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_device.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_doctor.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_engine_version_consistency.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_enrollment.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_env_dump_438.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_errors_e_codes.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_fail_closed_eval.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_glob_matching.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_5d_email_install.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_cli_flag.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_exceptions.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_get_secret_hitl.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_mock_backend.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_pending_approval.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_request_approval.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_secret_leak_guard.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_6a_wait.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_conformance.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_reason_codes.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hitl_validator_keys.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hook_extractors.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hybrid_mode_strict.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_hybrid_mode_warn.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_install_hook_command.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_install_hooks.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_layout_migration_t101.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_layout_parity_t102.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_local_mode_dict.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_local_mode_file_json.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_local_mode_file_yaml.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_log_fallback_stderr.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_log_options_ignored_hosted.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_log_rotation.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_migrate.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_min_sdk_version_gate.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_multi_client_per_project_175.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_no_policy_no_key.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_package_rename_shim.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_policy_engine_version_phase1b.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_policy_freshness.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_policy_settings.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_policy_source_audit.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_quarantine.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_reason_code.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_refresh.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_secrets.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_sql_semantic_class.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_synthetic_policy_id_t79.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_t103_precedence.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_t104_cache_gc.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_t108_local_override_audit.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_t96_single_audit_log.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_t99_install_prefetch_bundle.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_tamper.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_tamper_behavior.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_tamper_hook.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_telemetry_consent.py +0 -0
- {controlzero-1.7.0 → controlzero-1.8.0}/tests/test_unsafe_int_boundary.py +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Worktrees
|
|
2
|
+
.worktrees/
|
|
3
|
+
|
|
4
|
+
# Preflight state (drift-confirmed marker etc.). Local-only, never committed.
|
|
5
|
+
.preflight-state/
|
|
6
|
+
|
|
7
|
+
# Transient QA / design captures at repo root.
|
|
8
|
+
# Long-term reference screenshots live under docs-site/static/ or in Obsidian.
|
|
9
|
+
/*.png
|
|
10
|
+
/dashboard-*.md
|
|
11
|
+
/uat-*.png
|
|
12
|
+
/audit-*.png
|
|
13
|
+
/billing-*.png
|
|
14
|
+
/sso-*.png
|
|
15
|
+
/notifications-*.png
|
|
16
|
+
/settings-*.png
|
|
17
|
+
|
|
18
|
+
# Dependencies
|
|
19
|
+
node_modules/
|
|
20
|
+
vendor/
|
|
21
|
+
|
|
22
|
+
# Build outputs
|
|
23
|
+
dist/
|
|
24
|
+
build/
|
|
25
|
+
.next/
|
|
26
|
+
out/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
# Go binaries
|
|
29
|
+
apps/control-zero-platform/backend/server
|
|
30
|
+
|
|
31
|
+
# Test & Coverage
|
|
32
|
+
coverage/
|
|
33
|
+
htmlcov/
|
|
34
|
+
.coverage
|
|
35
|
+
*.lcov
|
|
36
|
+
.pytest_cache/
|
|
37
|
+
.vitest/
|
|
38
|
+
test-results/
|
|
39
|
+
playwright-report/
|
|
40
|
+
.playwright/
|
|
41
|
+
__pycache__/
|
|
42
|
+
*.pyc
|
|
43
|
+
*.pyo
|
|
44
|
+
|
|
45
|
+
# Environment and secrets (deny-all, allow explicitly)
|
|
46
|
+
.env*
|
|
47
|
+
!.envrc
|
|
48
|
+
production-secrets*
|
|
49
|
+
vault-backup.key
|
|
50
|
+
*.pem
|
|
51
|
+
*.key
|
|
52
|
+
*.p12
|
|
53
|
+
*.pfx
|
|
54
|
+
id_rsa
|
|
55
|
+
id_ed25519
|
|
56
|
+
*.keystore
|
|
57
|
+
# Firebase credentials and config (contains API keys / service account)
|
|
58
|
+
firebase.js
|
|
59
|
+
firebase.json
|
|
60
|
+
firebase-service-account*.json
|
|
61
|
+
*-firebase-adminsdk-*.json
|
|
62
|
+
firebase-adminsdk-*.json
|
|
63
|
+
serviceAccountKey.json
|
|
64
|
+
cz-service-account.json
|
|
65
|
+
firebase-credentials.json/
|
|
66
|
+
google-services.json
|
|
67
|
+
GoogleService-Info.plist
|
|
68
|
+
!*.pub
|
|
69
|
+
!.env.example
|
|
70
|
+
!.env.local.example
|
|
71
|
+
!.env.production.template
|
|
72
|
+
!.env.production.example
|
|
73
|
+
!.env.test
|
|
74
|
+
|
|
75
|
+
# Explicitly block files that contain or may contain real credentials (override allowlist above)
|
|
76
|
+
.env.dev
|
|
77
|
+
.env.production
|
|
78
|
+
.env.production.complete
|
|
79
|
+
.env.production.local
|
|
80
|
+
.env.local
|
|
81
|
+
.env.*.local
|
|
82
|
+
*.env.real
|
|
83
|
+
*.env.secret
|
|
84
|
+
.env.vercel
|
|
85
|
+
|
|
86
|
+
# IDE & Editors
|
|
87
|
+
.idea/
|
|
88
|
+
.vscode/
|
|
89
|
+
*.swp
|
|
90
|
+
*.swo
|
|
91
|
+
*.sublime-workspace
|
|
92
|
+
*.sublime-project
|
|
93
|
+
|
|
94
|
+
# OS files
|
|
95
|
+
.DS_Store
|
|
96
|
+
Thumbs.db
|
|
97
|
+
*.log
|
|
98
|
+
|
|
99
|
+
# Go
|
|
100
|
+
*.exe
|
|
101
|
+
*.exe~
|
|
102
|
+
*.dll
|
|
103
|
+
*.so
|
|
104
|
+
*.dylib
|
|
105
|
+
|
|
106
|
+
# Docker
|
|
107
|
+
docker-compose.override.yml
|
|
108
|
+
.docker/
|
|
109
|
+
|
|
110
|
+
# Production logs and certificates
|
|
111
|
+
logs/
|
|
112
|
+
letsencrypt/
|
|
113
|
+
*.log
|
|
114
|
+
|
|
115
|
+
# DLP test reports (generated, not committed)
|
|
116
|
+
reports/
|
|
117
|
+
# ...but committed long-form markdown reports under docs/reports/ are kept
|
|
118
|
+
!docs/reports/
|
|
119
|
+
!docs/reports/**/*.md
|
|
120
|
+
|
|
121
|
+
# MkDocs
|
|
122
|
+
site/
|
|
123
|
+
|
|
124
|
+
# Turbo (if adopted)
|
|
125
|
+
.turbo/
|
|
126
|
+
|
|
127
|
+
# Changeset
|
|
128
|
+
.changeset/*.md
|
|
129
|
+
!.changeset/config.json
|
|
130
|
+
!.changeset/README.md
|
|
131
|
+
|
|
132
|
+
# Vercel
|
|
133
|
+
.vercel/
|
|
134
|
+
|
|
135
|
+
# Rust
|
|
136
|
+
target/
|
|
137
|
+
Cargo.lock
|
|
138
|
+
*.rlib
|
|
139
|
+
|
|
140
|
+
# Python virtual environments
|
|
141
|
+
venv/
|
|
142
|
+
.venv/
|
|
143
|
+
*.egg-info/
|
|
144
|
+
|
|
145
|
+
# Gateway
|
|
146
|
+
apps/control-zero-gateway/.venv/
|
|
147
|
+
apps/control-zero-gateway/__pycache__/
|
|
148
|
+
|
|
149
|
+
# Selenium test outputs
|
|
150
|
+
tests/selenium/screenshots/
|
|
151
|
+
tests/selenium/report.html
|
|
152
|
+
|
|
153
|
+
# Miscellaneous
|
|
154
|
+
*.bak
|
|
155
|
+
*.tmp
|
|
156
|
+
*.temp
|
|
157
|
+
.cache/
|
|
158
|
+
.vercel
|
|
159
|
+
# Internal documentation (sensitive -- do not track)
|
|
160
|
+
docs/internal/
|
|
161
|
+
|
|
162
|
+
# Playwright MCP
|
|
163
|
+
.playwright-mcp/
|
|
164
|
+
|
|
165
|
+
# Session artifacts (prevent future accumulation)
|
|
166
|
+
*_SUMMARY.md
|
|
167
|
+
*_STATUS.md
|
|
168
|
+
*_COMPLETE.md
|
|
169
|
+
|
|
170
|
+
# E2E test screenshots
|
|
171
|
+
e2e-*.png
|
|
172
|
+
cz-*.png
|
|
173
|
+
.superpowers/
|
|
174
|
+
|
|
175
|
+
# AI tooling directories
|
|
176
|
+
.gemini/
|
|
177
|
+
.claude/launch.json
|
|
178
|
+
|
|
179
|
+
# Docusaurus build cache (should not be tracked)
|
|
180
|
+
docs-site/.docusaurus/
|
|
181
|
+
|
|
182
|
+
# Test/governance tester scripts (generated during testing sessions)
|
|
183
|
+
*_tester.py
|
|
184
|
+
verify_and_screenshot.js
|
|
185
|
+
|
|
186
|
+
# HTML reports generated during testing sessions
|
|
187
|
+
*_AUDIT.html
|
|
188
|
+
*_REPORT.html
|
|
189
|
+
*_ANALYSIS.html
|
|
190
|
+
e2e-report.html
|
|
191
|
+
sit-uat-report.html
|
|
192
|
+
UAT_COMPREHENSIVE_REPORT.html
|
|
193
|
+
uat-screenshots/
|
|
194
|
+
|
|
195
|
+
# Offensive summary reports
|
|
196
|
+
SUMMARY_OFFENSIVE_*.md
|
|
197
|
+
.gstack/
|
|
198
|
+
|
|
199
|
+
# Deployment docs (contain server-specific credentials)
|
|
200
|
+
docs/deployment/
|
|
201
|
+
docs/SSH_RECOVERY_GUIDE.md
|
|
202
|
+
docs/HETZNER_OS_INSTALLATION.md
|
|
203
|
+
docs/VERCEL_SETUP_WEBAPPS.md
|
|
204
|
+
scripts/fix-ssh-config.sh
|
|
205
|
+
scripts/hetzner-post-install.sh
|
|
206
|
+
docs/knowledge-base/
|
|
207
|
+
docs/superpowers/
|
|
208
|
+
# Air-gap build artifacts (generated tarballs and package directories)
|
|
209
|
+
cz-air-gap-*/
|
|
210
|
+
cz-air-gap-*.tar.gz
|
|
211
|
+
cz-support-*.tar.gz
|
|
212
|
+
|
|
213
|
+
# SSL Proxy -- customer CA certs and generated certs (never track secrets)
|
|
214
|
+
services/ssl-proxy/certs/
|
|
215
|
+
|
|
216
|
+
# UAT screenshot artifacts
|
|
217
|
+
uat-*.png
|
|
218
|
+
|
|
219
|
+
# Stray HTML reports in docs/. These are large exported artifacts
|
|
220
|
+
# (SIT/UAT reports, factsheet, launch readiness) that accumulate
|
|
221
|
+
# untracked and risk being swept up by `git add .`. The
|
|
222
|
+
# sit-uat-report-2026-03-22.html in particular is 128MB and would
|
|
223
|
+
# explode the repo. Real docs go under docs/knowledge-base/ or
|
|
224
|
+
# docs/designs/ instead.
|
|
225
|
+
docs/sit-uat-report-*.html
|
|
226
|
+
docs/launch-readiness-report-*.html
|
|
227
|
+
docs/control-zero-factsheet.html
|
|
228
|
+
docs/control-zero-review.html
|
|
229
|
+
|
|
230
|
+
# Reference screenshots (keep local, not in repo)
|
|
231
|
+
agentdefenders-full.png
|
|
232
|
+
cz-revamp-live.png
|
|
233
|
+
|
|
234
|
+
# Agent worktrees (created by Claude Code during parallel work)
|
|
235
|
+
.claude/worktrees/
|
|
236
|
+
|
|
237
|
+
# direnv + sops secrets. Encrypted files under secrets/ are safe to
|
|
238
|
+
# commit; anything matching *.plain or *.dec is a decryption artifact
|
|
239
|
+
# and must NEVER land in git.
|
|
240
|
+
.envrc.local
|
|
241
|
+
.direnv/
|
|
242
|
+
secrets/*.plain
|
|
243
|
+
secrets/*.dec
|
|
244
|
+
.claude/scheduled_tasks.lock
|
|
245
|
+
|
|
246
|
+
# Local documentation, patent assets, and legal research (sensitive, never commit)
|
|
247
|
+
doc_local/
|
|
248
|
+
|
|
249
|
+
# Go build/module caches (generated by `go build`, `go test`, etc.)
|
|
250
|
+
**/.gocache/
|
|
251
|
+
**/.gomodcache/
|
|
252
|
+
/node_modules
|
|
253
|
+
|
|
254
|
+
# Antigravity (agy) CLI local workspace
|
|
255
|
+
.antigravitycli/
|
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.0 -- 2026-06-15 (Kiro, epic gh#877)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Kiro (AWS agentic IDE) governance.** Control Zero now governs Kiro the
|
|
8
|
+
same way it governs Claude Code / Codex / Gemini: tool calls route through
|
|
9
|
+
the shared `controlzero hook-check` -> policy engine -> audit path, with
|
|
10
|
+
project scope honored and audit rows attributed to `agent="kiro"`
|
|
11
|
+
(`canonical_source` `kiro_cli` / `kiro_ide`).
|
|
12
|
+
|
|
13
|
+
- **Kiro CLI (`kiro-cli chat`): BETA -- full fidelity.** The CLI passes the
|
|
14
|
+
event JSON on stdin in snake_case, byte-compatible with what hook-check
|
|
15
|
+
already consumes, so `preToolUse` / `postToolUse` / `userPromptSubmit`
|
|
16
|
+
governance (including argument-level deny rules) works directly. The new
|
|
17
|
+
`KiroCLIAdapter` claims these by their camelCase event names, which are
|
|
18
|
+
byte-disjoint from Claude Code's PascalCase events.
|
|
19
|
+
|
|
20
|
+
- **Kiro IDE: limited (see below).** The IDE delivers its hook payload via
|
|
21
|
+
the `USER_PROMPT` env var, not stdin. The packaged `cz-kiro-adapter`
|
|
22
|
+
console script normalises that to the stdin shape and pipes it to
|
|
23
|
+
hook-check. **The Prompt Submit and File Save hooks are active** (they
|
|
24
|
+
carry data). **The Pre Tool Use hook ships DISABLED** because upstream
|
|
25
|
+
Kiro (#7375/#7408/#7500, IDE v0.11) currently delivers an empty payload
|
|
26
|
+
(`USER_PROMPT={}`) to IDE `preToolUse` run-command hooks -- with no tool
|
|
27
|
+
context, an enabled hook would fail closed and block every tool call.
|
|
28
|
+
Re-enable it once upstream ships tool context (or set
|
|
29
|
+
`CZ_KIRO_UNKNOWN_PAYLOAD=open` to trade pre-tool policy for availability).
|
|
30
|
+
|
|
31
|
+
- **`controlzero kiro init`** one-command installer (`--surface cli|ide|both`,
|
|
32
|
+
idempotent, `--dry-run`, `--uninstall`). Writes the Kiro hook configs from
|
|
33
|
+
committed templates, installs the `~/.controlzero/policy.yaml` Kiro
|
|
34
|
+
template, and registers the Kiro agent fingerprint.
|
|
35
|
+
|
|
36
|
+
- **Fail-safe defaults.** The IDE shim never crashes the IDE: an empty /
|
|
37
|
+
unparseable payload is handled per `CZ_KIRO_UNKNOWN_PAYLOAD`
|
|
38
|
+
(default `closed` = block, governance-safe), and a missing `controlzero`
|
|
39
|
+
CLI allows rather than bricking the editor.
|
|
40
|
+
|
|
41
|
+
See `docs/integrations/kiro.md` for the covered-vs-bounded matrix and
|
|
42
|
+
`docs/integrations/kiro-hook-payloads.md` for the per-event payload shapes.
|
|
43
|
+
|
|
3
44
|
## 1.7.0 -- 2026-05-19 (T86, gh#391)
|
|
4
45
|
|
|
5
46
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: controlzero
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.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
|
|
@@ -52,7 +52,7 @@ Provides-Extra: openai
|
|
|
52
52
|
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
53
53
|
Description-Content-Type: text/markdown
|
|
54
54
|
|
|
55
|
-
#
|
|
55
|
+
# controlzero
|
|
56
56
|
|
|
57
57
|
AI agent governance for Python. Policies, audit, and observability for tool calls.
|
|
58
58
|
Works locally with no signup.
|
|
@@ -116,12 +116,19 @@ The generated `controlzero.yaml` is the tutorial. It ships with annotated
|
|
|
116
116
|
rules covering the common patterns: allow lists, deny lists, wildcards, and
|
|
117
117
|
the catch-all.
|
|
118
118
|
|
|
119
|
-
Templates available:
|
|
119
|
+
Templates available (`controlzero init -t <name>`):
|
|
120
120
|
|
|
121
|
-
- `
|
|
122
|
-
- `
|
|
123
|
-
- `
|
|
124
|
-
- `
|
|
121
|
+
- `generic` — Hello World template (default)
|
|
122
|
+
- `rag` — RAG agent template (block exfiltration)
|
|
123
|
+
- `mcp` — MCP server template
|
|
124
|
+
- `cost-cap` — model allow-listing and cost guards
|
|
125
|
+
- `claude-code` — Claude Code hook starter
|
|
126
|
+
- `langchain` — LangChain tool guardrails
|
|
127
|
+
- `crewai` — CrewAI starter policy
|
|
128
|
+
- `cursor` — Cursor / editor hook starter
|
|
129
|
+
- `autogen` — AutoGen starter policy
|
|
130
|
+
- `codex-cli` — Codex CLI hook starter
|
|
131
|
+
- `gemini-cli` — Gemini CLI hook starter
|
|
125
132
|
|
|
126
133
|
## Loading a policy
|
|
127
134
|
|
|
@@ -368,7 +375,7 @@ async def async_poll(request_id: str) -> dict:
|
|
|
368
375
|
resolved = await pending.wait_async(async_poll)
|
|
369
376
|
```
|
|
370
377
|
|
|
371
|
-
|
|
378
|
+
1.7.0 ships `client.get_secret_poll_fn()` as a built-in poller helper for the secret-read approval path. The general `request_approval()` flow still takes an explicit `poll_fn` so callers can plug their own HTTP stack.
|
|
372
379
|
|
|
373
380
|
### Mock backend for tests
|
|
374
381
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# controlzero
|
|
2
2
|
|
|
3
3
|
AI agent governance for Python. Policies, audit, and observability for tool calls.
|
|
4
4
|
Works locally with no signup.
|
|
@@ -62,12 +62,19 @@ The generated `controlzero.yaml` is the tutorial. It ships with annotated
|
|
|
62
62
|
rules covering the common patterns: allow lists, deny lists, wildcards, and
|
|
63
63
|
the catch-all.
|
|
64
64
|
|
|
65
|
-
Templates available:
|
|
65
|
+
Templates available (`controlzero init -t <name>`):
|
|
66
66
|
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
67
|
+
- `generic` — Hello World template (default)
|
|
68
|
+
- `rag` — RAG agent template (block exfiltration)
|
|
69
|
+
- `mcp` — MCP server template
|
|
70
|
+
- `cost-cap` — model allow-listing and cost guards
|
|
71
|
+
- `claude-code` — Claude Code hook starter
|
|
72
|
+
- `langchain` — LangChain tool guardrails
|
|
73
|
+
- `crewai` — CrewAI starter policy
|
|
74
|
+
- `cursor` — Cursor / editor hook starter
|
|
75
|
+
- `autogen` — AutoGen starter policy
|
|
76
|
+
- `codex-cli` — Codex CLI hook starter
|
|
77
|
+
- `gemini-cli` — Gemini CLI hook starter
|
|
71
78
|
|
|
72
79
|
## Loading a policy
|
|
73
80
|
|
|
@@ -314,7 +321,7 @@ async def async_poll(request_id: str) -> dict:
|
|
|
314
321
|
resolved = await pending.wait_async(async_poll)
|
|
315
322
|
```
|
|
316
323
|
|
|
317
|
-
|
|
324
|
+
1.7.0 ships `client.get_secret_poll_fn()` as a built-in poller helper for the secret-read approval path. The general `request_approval()` flow still takes an explicit `poll_fn` so callers can plug their own HTTP stack.
|
|
318
325
|
|
|
319
326
|
### Mock backend for tests
|
|
320
327
|
|
|
@@ -27,13 +27,33 @@ from controlzero.errors import (
|
|
|
27
27
|
PolicyValidationError,
|
|
28
28
|
)
|
|
29
29
|
from controlzero.hitl import PendingApproval
|
|
30
|
+
from controlzero.hitl.grant_protocol import (
|
|
31
|
+
REASON_HITL_BACKEND_UNREACHABLE,
|
|
32
|
+
REASON_HITL_GRANT_APPROVED,
|
|
33
|
+
REASON_HITL_GRANT_CANCELED,
|
|
34
|
+
REASON_HITL_GRANT_DENIED,
|
|
35
|
+
REASON_HITL_GRANT_EXPIRED,
|
|
36
|
+
REASON_HITL_GRANT_REVOKED,
|
|
37
|
+
REASON_HITL_GRANT_TIMEOUT,
|
|
38
|
+
REASON_HITL_RETRY_GUARD_FAILED,
|
|
39
|
+
REASON_HITL_RETRY_LOOP,
|
|
40
|
+
)
|
|
30
41
|
from controlzero.policy_loader import load_policy
|
|
31
42
|
|
|
32
|
-
__version__ = "1.
|
|
43
|
+
__version__ = "1.8.0"
|
|
33
44
|
|
|
34
45
|
__all__ = [
|
|
35
46
|
"Client",
|
|
36
47
|
"PendingApproval",
|
|
48
|
+
"REASON_HITL_BACKEND_UNREACHABLE",
|
|
49
|
+
"REASON_HITL_GRANT_APPROVED",
|
|
50
|
+
"REASON_HITL_GRANT_CANCELED",
|
|
51
|
+
"REASON_HITL_GRANT_DENIED",
|
|
52
|
+
"REASON_HITL_GRANT_EXPIRED",
|
|
53
|
+
"REASON_HITL_GRANT_REVOKED",
|
|
54
|
+
"REASON_HITL_GRANT_TIMEOUT",
|
|
55
|
+
"REASON_HITL_RETRY_GUARD_FAILED",
|
|
56
|
+
"REASON_HITL_RETRY_LOOP",
|
|
37
57
|
"PolicyDecision",
|
|
38
58
|
"PolicyDeniedError",
|
|
39
59
|
"PolicyLoadError",
|
|
@@ -570,7 +570,7 @@ def translate_to_local_policy(payload: dict) -> dict:
|
|
|
570
570
|
"reason_code": "NO_ACTIVE_POLICIES",
|
|
571
571
|
})
|
|
572
572
|
|
|
573
|
-
|
|
573
|
+
out = {
|
|
574
574
|
"version": "1",
|
|
575
575
|
"rules": flat,
|
|
576
576
|
"settings": {
|
|
@@ -580,6 +580,20 @@ def translate_to_local_policy(payload: dict) -> dict:
|
|
|
580
580
|
},
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
+
# #906 / #907 Phase 2 seam (hosted path): carry the bundle-level
|
|
584
|
+
# ``dlp_rules`` array through to the translated local-policy dict under
|
|
585
|
+
# the SAME key the scanner reads (load_dlp_rules_from_policy reads
|
|
586
|
+
# ``dlp_rules``). Without this, a bundle-projected custom DLP rule reaches
|
|
587
|
+
# the hosted ``Client(api_key=...)`` path (via _get_raw_policy_data ->
|
|
588
|
+
# load_dlp_rules_from_policy) as nothing, so only the CLI /api/policy path
|
|
589
|
+
# would enforce custom rules. Only emit the key when present so the
|
|
590
|
+
# translated shape stays byte-identical for bundles without dlp_rules.
|
|
591
|
+
bundle_dlp_rules = payload.get("dlp_rules")
|
|
592
|
+
if isinstance(bundle_dlp_rules, list) and bundle_dlp_rules:
|
|
593
|
+
out["dlp_rules"] = bundle_dlp_rules
|
|
594
|
+
|
|
595
|
+
return out
|
|
596
|
+
|
|
583
597
|
|
|
584
598
|
def make_bundle_missing_policy(
|
|
585
599
|
default_on_missing: Optional[str] = None,
|
|
@@ -206,6 +206,17 @@ class PolicyDecision:
|
|
|
206
206
|
# makes spoof attempts and selector misconfigurations
|
|
207
207
|
# indistinguishable in the dashboard.
|
|
208
208
|
gate_matched: str = "none"
|
|
209
|
+
# HITL Phase 2b grants protocol (gh#820 SDK follow-up). The
|
|
210
|
+
# compiled policy engine sets requires_approval=True when the
|
|
211
|
+
# matched function appears in the require_approval set. The SDK
|
|
212
|
+
# then drives the grants flow (POST /api/grants + poll). Both
|
|
213
|
+
# fields default to the non-HITL shape so any decision built
|
|
214
|
+
# without them behaves exactly as before (backward compatible).
|
|
215
|
+
requires_approval: bool = False
|
|
216
|
+
# Canonical action label shipped to the grants endpoint when
|
|
217
|
+
# requires_approval is True. None falls back to policy_id at
|
|
218
|
+
# request time so the approver always sees a label.
|
|
219
|
+
approval_action: Optional[str] = None
|
|
209
220
|
|
|
210
221
|
@property
|
|
211
222
|
def decision(self) -> str:
|