controlzero 1.9.6__tar.gz → 1.9.7__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.9.6 → controlzero-1.9.7}/CHANGELOG.md +22 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/PKG-INFO +1 -1
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/__init__.py +1 -1
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/audit_remote.py +104 -4
- {controlzero-1.9.6 → controlzero-1.9.7}/pyproject.toml +1 -1
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_sink_wiring.py +127 -3
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hosted_policy_e2e.py +7 -5
- {controlzero-1.9.6 → controlzero-1.9.7}/.gitignore +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/Dockerfile.test +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/LICENSE +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/README.md +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/action_aliases.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/action_validator.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/bundle.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/credential_hook.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/credential_scanner.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/credentials_data/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/dlp_scanner.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/enforcer.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/hook_extractors.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/tool_extractors.json +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/types.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/audit_local.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/canonical.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/__main__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/_secrets.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/console.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/debug_bundle.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/doctor.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/antigravity.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/base.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/claude_code.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/codex_cli.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/gemini_cli.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/kiro.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/hosts/unknown.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/kiro_adapter.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/main.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/migrate.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/spool_cmd.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/telemetry_consent.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/antigravity/hooks.json +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/antigravity.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/autogen.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/claude-code.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/codex-cli.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/cost-cap.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/crewai.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/cursor.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/gemini-cli.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/generic.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/kiro.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/langchain.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/mcp.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/rag.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/client.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/device.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/enrollment.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/error_codes.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/error_codes.yaml +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/errors.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hitl/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hitl/grant_protocol.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hitl/mock.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hitl/pending_approval.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hitl/secret_leak_guard.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hitl/status.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hooks/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hooks/tool_output_handler.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/hosted_policy.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/anthropic.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/autogen.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/braintrust.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/crewai/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/crewai/agent.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/crewai/crew.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/crewai/task.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/crewai/tool.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/google.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/google_adk/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/google_adk/agent.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/google_adk/tool.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/agent.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/callbacks.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/chain.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/graph.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/modern.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langchain/tool.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/langfuse.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/litellm.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/openai.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/pydantic_ai.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/integrations/vercel_ai.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/layout_migration.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/policy_loader.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_compress.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_constants.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_crc32c.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_crypto.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_frame.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_keyring.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_metrics.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_spool.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_state.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/_uploader.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/spool/cz-audit-v1.dict +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/tamper.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/controlzero/tracecontext.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/examples/hello_world.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/conftest.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/integrations/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/integrations/test_google.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/parity/action_aliases.json +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/__init__.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/conftest.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_cli.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_concurrency.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_conformance.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_core.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_crash.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_diskfull.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_durable_default_tamper.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_keychain_dek.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_transcript_localack.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/spool/test_spool_uploader.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_action_aliases.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_action_canonicalization.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_action_validator_t86.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_agent_name_env.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_antigravity_adapter.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_antigravity_ga_blockers_1248.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_antigravity_hook_check.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_antigravity_install.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_api_key_mask.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_audit_remote.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_audit_remote_sdk_version.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_audit_sink_isolation.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_bundle_parser.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_bundle_translate.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_canonical_phase1a.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_carve_out.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_debug_bundle.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_extractor_integration.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_hook.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_hosted_refresh.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_init.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_init_templates.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_tail.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_test.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_cli_validate.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_coding_agent_hooks.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_conditions.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_conformance.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_console.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_credential_hook.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_default_action.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_device.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_dlp_scanner.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_doctor.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_engine_version_consistency.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_enrollment.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_env_dump_438.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_epic_1247_bryan_acceptance.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_error_codes.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_errors_e_codes.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_fail_closed_eval.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_glob_matching.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_5d_email_install.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_cli_flag.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_exceptions.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_get_secret_hitl.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_mock_backend.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_pending_approval.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_request_approval.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_secret_leak_guard.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_6a_wait.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_conformance.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_phase2b_protocol.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_reason_codes.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hitl_validator_keys.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hook_extractors.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hosted_local_audit_1247.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hosts_adapter.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hybrid_mode_strict.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_hybrid_mode_warn.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_install_hook_command.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_install_hooks.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_kiro_adapter.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_kiro_cli_e2e.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_kiro_hook_templates.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_kiro_install.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_layout_migration_t101.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_layout_parity_t102.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_local_mode_dict.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_local_mode_file_json.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_local_mode_file_yaml.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_log_fallback_stderr.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_log_options_ignored_hosted.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_log_rotation.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_migrate.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_min_sdk_version_gate.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_multi_client_per_project_175.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_no_policy_no_key.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_observe_mode_1247.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_package_rename_shim.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_policy_engine_version_phase1b.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_policy_freshness.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_policy_settings.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_policy_source_audit.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_quarantine.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_reason_code.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_refresh.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_secrets.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_sql_semantic_class.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_synthetic_policy_id_t79.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_t103_precedence.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_t104_cache_gc.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_t108_local_override_audit.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_t96_single_audit_log.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_t99_install_prefetch_bundle.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_tamper.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_tamper_behavior.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_tamper_hook.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_telemetry_consent.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_tracecontext.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tests/test_unsafe_int_boundary.py +0 -0
- {controlzero-1.9.6 → controlzero-1.9.7}/tools/cz-kiro-adapter +0 -0
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.7 -- 2026-06-16 (hosted audit delivery for short-lived processes, gh#1292)
|
|
4
|
+
|
|
5
|
+
Fixes a P0 found via a customer report: hosted-mode audit rows were written to
|
|
6
|
+
the local `~/.controlzero/audit.log` but never reached the dashboard for
|
|
7
|
+
short-lived processes (PreToolUse hooks, CLI one-shots).
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Remote audit never delivered for short-lived processes (gh#1292).** Since
|
|
12
|
+
1.9.2 the hosted (`BearerAuditSink`) and enrolled (`RemoteAuditSink`) sinks
|
|
13
|
+
default to the durable encrypted spool, where `log()` WALs the row and an
|
|
14
|
+
opportunistic `daemon=True` thread drains it. A short-lived process exits
|
|
15
|
+
before that thread can finish its HTTPS POST, and `close()` was a fsync-only
|
|
16
|
+
boundary, so the row stayed on local disk and never shipped. The hosted sink
|
|
17
|
+
now hands the drain to a **detached `controlzero spool flush` child** that
|
|
18
|
+
outlives the process (no latency on the agent hot path; the keystore and
|
|
19
|
+
network work happen in the child, never on the close path). The api key
|
|
20
|
+
travels via the child's environment, never on its argv. The enrolled sink
|
|
21
|
+
(opt-in, off the hosted hot path) does a bounded, fail-open in-process drain
|
|
22
|
+
on close instead. Both paths are fail-open: any error leaves the durable WAL
|
|
23
|
+
intact for a later drain. The durable WAL guarantee is unchanged.
|
|
24
|
+
|
|
3
25
|
## 1.9.6 -- 2026-06-16 (Antigravity GA-blocker hardening, gh#1248 / epic gh#925)
|
|
4
26
|
|
|
5
27
|
Closes 3 of the prod-readiness GA blockers for the Antigravity (`agy`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: controlzero
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.7
|
|
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
|
|
@@ -24,6 +24,7 @@ import json
|
|
|
24
24
|
import logging
|
|
25
25
|
import os
|
|
26
26
|
import platform
|
|
27
|
+
import sys
|
|
27
28
|
import threading
|
|
28
29
|
import uuid
|
|
29
30
|
from datetime import datetime, timezone
|
|
@@ -110,6 +111,15 @@ _SPOOL_DEFAULT_DIR = "~/.controlzero/spool"
|
|
|
110
111
|
# processes.
|
|
111
112
|
_SPOOL_HOOK_BUDGET_S = 2.0
|
|
112
113
|
_SPOOL_TIMER_BUDGET_S = 25.0
|
|
114
|
+
# close() handoff budgets (#1292). A short-lived process (PreToolUse hook /
|
|
115
|
+
# CLI one-shot) exits before the daemon drain ships its spooled rows, and
|
|
116
|
+
# close() was a fsync-only boundary -- so the rows never reached the backend.
|
|
117
|
+
# The hosted (Bearer) sink now hands the drain to a DETACHED uploader child
|
|
118
|
+
# that outlives this process (no latency on the agent hot path, keystore +
|
|
119
|
+
# network work happen off the close path). The enrolled sink -- opt-in, off
|
|
120
|
+
# the hosted hot path -- does a bounded, fail-open in-process drain instead.
|
|
121
|
+
_DETACHED_DRAIN_BUDGET_S = 30.0
|
|
122
|
+
_CLOSE_DRAIN_BUDGET_S = 2.0
|
|
113
123
|
|
|
114
124
|
|
|
115
125
|
def _build_wire_entry(
|
|
@@ -284,6 +294,11 @@ class _SpoolWiringMixin:
|
|
|
284
294
|
self._drain_inflight = False
|
|
285
295
|
self._drain_again = False
|
|
286
296
|
self._drain_auth_blocked = False
|
|
297
|
+
# #1292: whether this process WAL-logged at least one entry (gates the
|
|
298
|
+
# close-time handoff so a pure-read process never spawns a drainer) and
|
|
299
|
+
# whether the detached drainer was already spawned (coalesce to one).
|
|
300
|
+
self._spool_logged = False
|
|
301
|
+
self._detached_drain_spawned = False
|
|
287
302
|
self._spool = None
|
|
288
303
|
self._spool_mode = "off"
|
|
289
304
|
# Fast path: flag off/unset, NO hosted default, AND no spool
|
|
@@ -365,6 +380,10 @@ class _SpoolWiringMixin:
|
|
|
365
380
|
except Exception as exc: # noqa: BLE001
|
|
366
381
|
logger.warning("controlzero: spool append failed (%s)", exc)
|
|
367
382
|
return False
|
|
383
|
+
# This process has durably WAL-logged at least one entry; close() may
|
|
384
|
+
# need to hand its drain to a detached uploader so a short-lived
|
|
385
|
+
# process does not strand it (#1292).
|
|
386
|
+
self._spool_logged = True
|
|
368
387
|
if self._spool_mode == _SPOOL_MODE_DURABLE:
|
|
369
388
|
self._drain_async(budget_s=_SPOOL_HOOK_BUDGET_S)
|
|
370
389
|
return True
|
|
@@ -377,6 +396,73 @@ class _SpoolWiringMixin:
|
|
|
377
396
|
def _spool_drain_orphans(self) -> bool:
|
|
378
397
|
return False
|
|
379
398
|
|
|
399
|
+
def _spawn_detached_drain(self) -> None:
|
|
400
|
+
"""Hand the spool drain to a DETACHED child that outlives this
|
|
401
|
+
process, then return immediately (#1292).
|
|
402
|
+
|
|
403
|
+
A short-lived hosted process (PreToolUse hook / CLI one-shot) exits
|
|
404
|
+
before the in-process daemon drain can finish its HTTPS POST, so
|
|
405
|
+
durably-spooled rows never reach the backend -- close() was a
|
|
406
|
+
fsync-only boundary that stranded them. Spawning ``controlzero spool
|
|
407
|
+
flush`` in a new session lets the upload complete after this process
|
|
408
|
+
is gone: no latency on the agent hot path, and the keystore + network
|
|
409
|
+
work happen in the child, never on this close path (the founder
|
|
410
|
+
non-blocking-hook and no-keystore-on-close constraints).
|
|
411
|
+
|
|
412
|
+
Only the Bearer (hosted api-key) sink can be drained this way --
|
|
413
|
+
``cz spool flush`` authenticates with the api key, which it reads from
|
|
414
|
+
the environment (never argv, so the key never lands in ``ps``). The
|
|
415
|
+
enrolled sink has no such entrypoint and uses an in-process close
|
|
416
|
+
drain instead.
|
|
417
|
+
|
|
418
|
+
Fail-open: any error leaves the durable WAL intact for a later drain
|
|
419
|
+
(next long-lived process, the timer, or an explicit ``cz spool
|
|
420
|
+
flush``)."""
|
|
421
|
+
if getattr(self, "_detached_drain_spawned", False):
|
|
422
|
+
return
|
|
423
|
+
if self._spool is None or not getattr(self, "_spool_logged", False):
|
|
424
|
+
return
|
|
425
|
+
api_key = getattr(self, "_api_key", None)
|
|
426
|
+
if not api_key:
|
|
427
|
+
return
|
|
428
|
+
try:
|
|
429
|
+
import subprocess
|
|
430
|
+
|
|
431
|
+
env = dict(os.environ)
|
|
432
|
+
env["CONTROLZERO_API_KEY"] = api_key
|
|
433
|
+
api_url = getattr(self, "_api_url", None)
|
|
434
|
+
if api_url:
|
|
435
|
+
env["CONTROLZERO_API_URL"] = api_url
|
|
436
|
+
popen_kwargs = dict(
|
|
437
|
+
stdin=subprocess.DEVNULL,
|
|
438
|
+
stdout=subprocess.DEVNULL,
|
|
439
|
+
stderr=subprocess.DEVNULL,
|
|
440
|
+
env=env,
|
|
441
|
+
close_fds=True,
|
|
442
|
+
)
|
|
443
|
+
if os.name == "nt": # detach on Windows
|
|
444
|
+
popen_kwargs["creationflags"] = (
|
|
445
|
+
getattr(subprocess, "DETACHED_PROCESS", 0)
|
|
446
|
+
| getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
|
447
|
+
)
|
|
448
|
+
else: # POSIX: escape the process group so it outlives the parent
|
|
449
|
+
popen_kwargs["start_new_session"] = True
|
|
450
|
+
subprocess.Popen( # noqa: S603 -- fixed argv, no shell, no user input
|
|
451
|
+
[
|
|
452
|
+
sys.executable, "-m", "controlzero.cli.main",
|
|
453
|
+
"spool", "flush",
|
|
454
|
+
"--timeout", str(_DETACHED_DRAIN_BUDGET_S),
|
|
455
|
+
],
|
|
456
|
+
**popen_kwargs,
|
|
457
|
+
)
|
|
458
|
+
self._detached_drain_spawned = True
|
|
459
|
+
except Exception as exc: # noqa: BLE001 -- never block/crash the host
|
|
460
|
+
logger.warning(
|
|
461
|
+
"controlzero: detached spool drain spawn failed (%s); audit "
|
|
462
|
+
"rows remain durable in the spool for a later drain",
|
|
463
|
+
exc,
|
|
464
|
+
)
|
|
465
|
+
|
|
380
466
|
def _drain_async(self, budget_s: float = _SPOOL_HOOK_BUDGET_S) -> None:
|
|
381
467
|
"""One opportunistic non-blocking drain in a daemon thread.
|
|
382
468
|
Coalesced: at most one drain in flight per sink instance; an
|
|
@@ -506,8 +592,17 @@ class RemoteAuditSink(_SpoolWiringMixin):
|
|
|
506
592
|
self._closed = True
|
|
507
593
|
self._cancel_flush_timer()
|
|
508
594
|
if self._spool_wal:
|
|
509
|
-
#
|
|
510
|
-
#
|
|
595
|
+
# DURABLE mode (enrolled, opt-in, off the hosted hot path) ships
|
|
596
|
+
# synchronously with a bounded, fail-open budget before the final
|
|
597
|
+
# fsync so a short-lived process does not strand its rows (#1292).
|
|
598
|
+
# spool_only is replay-only and must NEVER live-send on close.
|
|
599
|
+
if (self._spool_mode == _SPOOL_MODE_DURABLE
|
|
600
|
+
and getattr(self, "_spool_logged", False)):
|
|
601
|
+
try:
|
|
602
|
+
self._drain_once(blocking=True,
|
|
603
|
+
budget_s=_CLOSE_DRAIN_BUDGET_S)
|
|
604
|
+
except Exception: # noqa: BLE001 -- fail-open; WAL preserved
|
|
605
|
+
pass
|
|
511
606
|
self._spool_close()
|
|
512
607
|
return
|
|
513
608
|
# Synchronous flush -- we are shutting down
|
|
@@ -748,8 +843,13 @@ class BearerAuditSink(_SpoolWiringMixin):
|
|
|
748
843
|
self._closed = True
|
|
749
844
|
self._cancel_flush_timer()
|
|
750
845
|
if self._spool_wal:
|
|
751
|
-
#
|
|
752
|
-
#
|
|
846
|
+
# DURABLE mode hands the drain to a DETACHED child that outlives
|
|
847
|
+
# this (possibly short-lived) process so its rows are delivered;
|
|
848
|
+
# close() makes NO in-process network call -- the child does the
|
|
849
|
+
# keystore + network work off this hot path (#1292). spool_only is
|
|
850
|
+
# replay-only and must NEVER live-send on close -- only fsync.
|
|
851
|
+
if self._spool_mode == _SPOOL_MODE_DURABLE:
|
|
852
|
+
self._spawn_detached_drain()
|
|
753
853
|
self._spool_close()
|
|
754
854
|
return
|
|
755
855
|
self._flush_sync()
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "controlzero"
|
|
7
|
-
version = "1.9.
|
|
7
|
+
version = "1.9.7"
|
|
8
8
|
description = "AI agent governance: policies, audit, and observability for tool calls. Works locally with no signup."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "Apache-2.0"}
|
|
@@ -17,6 +17,8 @@ from __future__ import annotations
|
|
|
17
17
|
|
|
18
18
|
import collections
|
|
19
19
|
import json
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
20
22
|
import threading
|
|
21
23
|
import time
|
|
22
24
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
@@ -211,6 +213,44 @@ def test_enrolled_sink_still_off_by_default(server, spool_env, monkeypatch):
|
|
|
211
213
|
sink.close()
|
|
212
214
|
|
|
213
215
|
|
|
216
|
+
def test_enrolled_durable_close_drains_in_process(server, spool_env,
|
|
217
|
+
monkeypatch):
|
|
218
|
+
# #1292: the enrolled sink (opt-in, off the hosted hook hot path) cannot
|
|
219
|
+
# use `cz spool flush` (no api key), so on close it does a bounded,
|
|
220
|
+
# fail-open IN-PROCESS drain (blocking=True) rather than stranding a
|
|
221
|
+
# short-lived process's rows. Reverting close() to a bare fsync makes this
|
|
222
|
+
# test fail (no blocking drain observed).
|
|
223
|
+
monkeypatch.setenv("CONTROLZERO_SPOOL", "durable")
|
|
224
|
+
|
|
225
|
+
class FakeState:
|
|
226
|
+
machine_id = "11111111-2222-4333-8444-555555555555"
|
|
227
|
+
|
|
228
|
+
import controlzero.enrollment as enrollment
|
|
229
|
+
monkeypatch.setattr(enrollment, "load_state", lambda: FakeState())
|
|
230
|
+
monkeypatch.setattr(
|
|
231
|
+
enrollment, "sign_request",
|
|
232
|
+
lambda *_a, **_k: {"X-CZ-Machine-ID": FakeState.machine_id,
|
|
233
|
+
"X-CZ-Signature": "sig"})
|
|
234
|
+
sink = RemoteAuditSink(
|
|
235
|
+
api_url=server.url, machine_token="", org_id="org-1",
|
|
236
|
+
machine_id=FakeState.machine_id)
|
|
237
|
+
assert sink._spool is not None
|
|
238
|
+
|
|
239
|
+
blocking_calls = []
|
|
240
|
+
real_drain_once = sink._drain_once
|
|
241
|
+
|
|
242
|
+
def recording_drain_once(*a, **kw):
|
|
243
|
+
blocking_calls.append(bool(kw.get("blocking", False)))
|
|
244
|
+
return real_drain_once(*a, **kw)
|
|
245
|
+
|
|
246
|
+
monkeypatch.setattr(sink, "_drain_once", recording_drain_once)
|
|
247
|
+
sink.log(_entry(1))
|
|
248
|
+
sink.close()
|
|
249
|
+
# close() performed a blocking in-process drain (the daemon drain uses
|
|
250
|
+
# blocking=False, so a True here can only come from close()).
|
|
251
|
+
assert any(blocking_calls)
|
|
252
|
+
|
|
253
|
+
|
|
214
254
|
def test_flag_off_401_still_clears_buffer_legacy(server, spool_env,
|
|
215
255
|
monkeypatch):
|
|
216
256
|
"""The legacy (non-spool) 401 contract is unchanged: sink disables
|
|
@@ -304,7 +344,12 @@ def test_durable_wal_survives_network_down(server, spool_env, monkeypatch):
|
|
|
304
344
|
sink.close()
|
|
305
345
|
|
|
306
346
|
|
|
307
|
-
def
|
|
347
|
+
def test_durable_close_spawns_detached_drain(server, spool_env, monkeypatch):
|
|
348
|
+
# #1292: a short-lived hosted process must not strand its spooled rows.
|
|
349
|
+
# close() makes NO in-process network call (the agent hot path stays
|
|
350
|
+
# fast), but it MUST hand the drain to a DETACHED `controlzero spool
|
|
351
|
+
# flush` child that outlives this process so the rows are delivered.
|
|
352
|
+
# Reverting close() to a bare fsync makes this test fail (spawned == 0).
|
|
308
353
|
monkeypatch.setenv("CONTROLZERO_SPOOL", "durable")
|
|
309
354
|
sink = BearerAuditSink(api_url=server.url, api_key=API_KEY)
|
|
310
355
|
import httpx
|
|
@@ -316,8 +361,18 @@ def test_durable_close_is_not_a_network_flush(server, spool_env, monkeypatch):
|
|
|
316
361
|
raise httpx.ConnectError("down")
|
|
317
362
|
|
|
318
363
|
monkeypatch.setattr(httpx, "post", counting_post)
|
|
364
|
+
|
|
365
|
+
spawned = []
|
|
366
|
+
|
|
367
|
+
class _FakePopen:
|
|
368
|
+
def __init__(self, argv, **kwargs):
|
|
369
|
+
spawned.append((argv, kwargs))
|
|
370
|
+
|
|
371
|
+
import subprocess
|
|
372
|
+
monkeypatch.setattr(subprocess, "Popen", _FakePopen)
|
|
373
|
+
|
|
319
374
|
sink.log(_entry(1))
|
|
320
|
-
# let the opportunistic drain
|
|
375
|
+
# let the opportunistic daemon drain settle (it fails against the dead net)
|
|
321
376
|
deadline = time.monotonic() + 5
|
|
322
377
|
while time.monotonic() < deadline:
|
|
323
378
|
with sink._drain_state_lock:
|
|
@@ -327,9 +382,78 @@ def test_durable_close_is_not_a_network_flush(server, spool_env, monkeypatch):
|
|
|
327
382
|
n_before = len(posted)
|
|
328
383
|
t0 = time.monotonic()
|
|
329
384
|
sink.close()
|
|
330
|
-
# close() returned promptly and made NO network attempt of its own
|
|
385
|
+
# close() returned promptly and made NO network attempt of its own ...
|
|
331
386
|
assert time.monotonic() - t0 < 1.0
|
|
332
387
|
assert len(posted) == n_before
|
|
388
|
+
# ... but it DID hand the drain to a detached `controlzero spool flush`.
|
|
389
|
+
assert len(spawned) == 1
|
|
390
|
+
argv, kwargs = spawned[0]
|
|
391
|
+
assert argv[0] == sys.executable
|
|
392
|
+
assert "spool" in argv and "flush" in argv
|
|
393
|
+
# the api key travels via env, NEVER on argv (no leak in `ps`).
|
|
394
|
+
assert API_KEY not in argv
|
|
395
|
+
assert kwargs["env"]["CONTROLZERO_API_KEY"] == API_KEY
|
|
396
|
+
# detached so it outlives this (possibly short-lived) process.
|
|
397
|
+
if os.name == "nt":
|
|
398
|
+
assert kwargs.get("creationflags", 0) != 0
|
|
399
|
+
else:
|
|
400
|
+
assert kwargs.get("start_new_session") is True
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def test_durable_close_spawn_failure_is_fail_open(server, spool_env,
|
|
404
|
+
monkeypatch):
|
|
405
|
+
# If spawning the detached drainer fails, close() must NOT raise -- the
|
|
406
|
+
# rows stay durable in the WAL for a later drain.
|
|
407
|
+
monkeypatch.setenv("CONTROLZERO_SPOOL", "durable")
|
|
408
|
+
sink = BearerAuditSink(api_url=server.url, api_key=API_KEY)
|
|
409
|
+
|
|
410
|
+
import subprocess
|
|
411
|
+
|
|
412
|
+
def boom(*a, **kw):
|
|
413
|
+
raise OSError("cannot fork")
|
|
414
|
+
|
|
415
|
+
monkeypatch.setattr(subprocess, "Popen", boom)
|
|
416
|
+
sink.log(_entry(1))
|
|
417
|
+
sink.close() # must not raise
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def test_durable_close_no_spawn_when_nothing_logged(server, spool_env,
|
|
421
|
+
monkeypatch):
|
|
422
|
+
# A pure-read process that never WAL-logged must not spawn a drainer.
|
|
423
|
+
monkeypatch.setenv("CONTROLZERO_SPOOL", "durable")
|
|
424
|
+
sink = BearerAuditSink(api_url=server.url, api_key=API_KEY)
|
|
425
|
+
|
|
426
|
+
spawned = []
|
|
427
|
+
|
|
428
|
+
class _FakePopen:
|
|
429
|
+
def __init__(self, argv, **kwargs):
|
|
430
|
+
spawned.append(argv)
|
|
431
|
+
|
|
432
|
+
import subprocess
|
|
433
|
+
monkeypatch.setattr(subprocess, "Popen", _FakePopen)
|
|
434
|
+
sink.close()
|
|
435
|
+
assert spawned == []
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def test_spool_only_close_does_not_spawn_drain(server, spool_env, monkeypatch):
|
|
439
|
+
# spool_only is replay-only ("never live-send"): close() must NOT spawn a
|
|
440
|
+
# detached uploader even after logging -- only durable mode does (#1292).
|
|
441
|
+
# The close-drain is gated on _spool_mode == durable, NOT on _spool_wal
|
|
442
|
+
# (which is also true for spool_only); reverting that gate fails this test.
|
|
443
|
+
monkeypatch.setenv("CONTROLZERO_SPOOL", "spool_only")
|
|
444
|
+
sink = BearerAuditSink(api_url=server.url, api_key=API_KEY)
|
|
445
|
+
|
|
446
|
+
spawned = []
|
|
447
|
+
|
|
448
|
+
class _FakePopen:
|
|
449
|
+
def __init__(self, argv, **kwargs):
|
|
450
|
+
spawned.append(argv)
|
|
451
|
+
|
|
452
|
+
import subprocess
|
|
453
|
+
monkeypatch.setattr(subprocess, "Popen", _FakePopen)
|
|
454
|
+
sink.log(_entry(1))
|
|
455
|
+
sink.close()
|
|
456
|
+
assert spawned == []
|
|
333
457
|
|
|
334
458
|
|
|
335
459
|
def test_durable_401_preserves_spool_and_keeps_logging(server, spool_env,
|
|
@@ -184,11 +184,13 @@ def test_hosted_mode_audit_pushed(mock_backend):
|
|
|
184
184
|
client = Client(api_key="cz_live_e2e_test")
|
|
185
185
|
client.guard("web_search", args={})
|
|
186
186
|
client.guard("send_email", args={})
|
|
187
|
-
# Hosted mode
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
187
|
+
# Hosted mode defaults to the DURABLE encrypted spool (gh#1247): each
|
|
188
|
+
# decision is WAL'd to encrypted disk and delivered by the opportunistic
|
|
189
|
+
# async drain. close() makes NO in-process network call; instead it hands
|
|
190
|
+
# the drain to a DETACHED `controlzero spool flush` child so a short-lived
|
|
191
|
+
# process does not strand its rows (gh#1292). Delivery is guaranteed (WAL)
|
|
192
|
+
# but asynchronous via the daemon drain and/or the detached child; poll for
|
|
193
|
+
# it instead of assuming a synchronous close.
|
|
192
194
|
client.close()
|
|
193
195
|
|
|
194
196
|
deadline = time.monotonic() + 8.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{controlzero-1.9.6 → controlzero-1.9.7}/controlzero/_internal/credentials_data/built_in.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook
RENAMED
|
File without changes
|
{controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook
RENAMED
|
File without changes
|
{controlzero-1.9.6 → controlzero-1.9.7}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|