controlzero 1.8.2__tar.gz → 1.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. {controlzero-1.8.2 → controlzero-1.9.1}/CHANGELOG.md +81 -0
  2. {controlzero-1.8.2 → controlzero-1.9.1}/PKG-INFO +2 -1
  3. {controlzero-1.8.2 → controlzero-1.9.1}/README.md +1 -0
  4. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/__init__.py +1 -1
  5. controlzero-1.9.1/controlzero/cli/hosts/antigravity.py +161 -0
  6. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/base.py +9 -0
  7. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/claude_code.py +14 -0
  8. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/main.py +420 -5
  9. controlzero-1.9.1/controlzero/cli/templates/antigravity/hooks.json +28 -0
  10. controlzero-1.9.1/controlzero/cli/templates/antigravity.yaml +93 -0
  11. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/client.py +108 -24
  12. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/device.py +24 -3
  13. {controlzero-1.8.2 → controlzero-1.9.1}/pyproject.toml +8 -1
  14. controlzero-1.9.1/tests/test_antigravity_adapter.py +275 -0
  15. controlzero-1.9.1/tests/test_antigravity_hook_check.py +160 -0
  16. controlzero-1.9.1/tests/test_antigravity_install.py +383 -0
  17. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_device.py +54 -1
  18. controlzero-1.9.1/tests/test_hosted_local_audit_1247.py +217 -0
  19. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hosts_adapter.py +6 -3
  20. controlzero-1.9.1/tests/test_log_options_ignored_hosted.py +50 -0
  21. controlzero-1.8.2/controlzero/cli/hosts/antigravity.py +0 -76
  22. controlzero-1.8.2/tests/test_log_options_ignored_hosted.py +0 -35
  23. {controlzero-1.8.2 → controlzero-1.9.1}/.gitignore +0 -0
  24. {controlzero-1.8.2 → controlzero-1.9.1}/Dockerfile.test +0 -0
  25. {controlzero-1.8.2 → controlzero-1.9.1}/LICENSE +0 -0
  26. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/__init__.py +0 -0
  27. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/action_aliases.py +0 -0
  28. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/action_validator.py +0 -0
  29. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/bundle.py +0 -0
  30. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/credential_hook.py +0 -0
  31. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/credential_scanner.py +0 -0
  32. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/credentials_data/__init__.py +0 -0
  33. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
  34. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/dlp_scanner.py +0 -0
  35. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/enforcer.py +0 -0
  36. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/hook_extractors.py +0 -0
  37. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/tool_extractors.json +0 -0
  38. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/_internal/types.py +0 -0
  39. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/audit_local.py +0 -0
  40. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/audit_remote.py +0 -0
  41. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/canonical.py +0 -0
  42. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/__init__.py +0 -0
  43. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/_secrets.py +0 -0
  44. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/console.py +0 -0
  45. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/debug_bundle.py +0 -0
  46. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/doctor.py +0 -0
  47. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/__init__.py +0 -0
  48. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/codex_cli.py +0 -0
  49. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/gemini_cli.py +0 -0
  50. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/kiro.py +0 -0
  51. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/hosts/unknown.py +0 -0
  52. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/kiro_adapter.py +0 -0
  53. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/migrate.py +0 -0
  54. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/spool_cmd.py +0 -0
  55. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/telemetry_consent.py +0 -0
  56. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/autogen.yaml +0 -0
  57. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/claude-code.yaml +0 -0
  58. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/codex-cli.yaml +0 -0
  59. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/cost-cap.yaml +0 -0
  60. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/crewai.yaml +0 -0
  61. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/cursor.yaml +0 -0
  62. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/gemini-cli.yaml +0 -0
  63. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/generic.yaml +0 -0
  64. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
  65. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -0
  66. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -0
  67. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/kiro/kiro.yaml +0 -0
  68. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/langchain.yaml +0 -0
  69. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/mcp.yaml +0 -0
  70. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/cli/templates/rag.yaml +0 -0
  71. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/enrollment.py +0 -0
  72. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/error_codes.py +0 -0
  73. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/error_codes.yaml +0 -0
  74. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/errors.py +0 -0
  75. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hitl/__init__.py +0 -0
  76. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hitl/grant_protocol.py +0 -0
  77. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hitl/mock.py +0 -0
  78. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hitl/pending_approval.py +0 -0
  79. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hitl/secret_leak_guard.py +0 -0
  80. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hitl/status.py +0 -0
  81. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hooks/__init__.py +0 -0
  82. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hooks/tool_output_handler.py +0 -0
  83. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/hosted_policy.py +0 -0
  84. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/__init__.py +0 -0
  85. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/anthropic.py +0 -0
  86. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/autogen.py +0 -0
  87. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/braintrust.py +0 -0
  88. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/crewai/__init__.py +0 -0
  89. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/crewai/agent.py +0 -0
  90. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/crewai/crew.py +0 -0
  91. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/crewai/task.py +0 -0
  92. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/crewai/tool.py +0 -0
  93. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/google.py +0 -0
  94. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/google_adk/__init__.py +0 -0
  95. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/google_adk/agent.py +0 -0
  96. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/google_adk/tool.py +0 -0
  97. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/__init__.py +0 -0
  98. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/agent.py +0 -0
  99. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/callbacks.py +0 -0
  100. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/chain.py +0 -0
  101. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/graph.py +0 -0
  102. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/modern.py +0 -0
  103. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langchain/tool.py +0 -0
  104. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/langfuse.py +0 -0
  105. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/litellm.py +0 -0
  106. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/openai.py +0 -0
  107. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/pydantic_ai.py +0 -0
  108. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/integrations/vercel_ai.py +0 -0
  109. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/layout_migration.py +0 -0
  110. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/policy_loader.py +0 -0
  111. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/__init__.py +0 -0
  112. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_compress.py +0 -0
  113. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_constants.py +0 -0
  114. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_crc32c.py +0 -0
  115. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_crypto.py +0 -0
  116. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_frame.py +0 -0
  117. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_metrics.py +0 -0
  118. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_spool.py +0 -0
  119. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_state.py +0 -0
  120. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/_uploader.py +0 -0
  121. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/spool/cz-audit-v1.dict +0 -0
  122. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/tamper.py +0 -0
  123. {controlzero-1.8.2 → controlzero-1.9.1}/controlzero/tracecontext.py +0 -0
  124. {controlzero-1.8.2 → controlzero-1.9.1}/examples/hello_world.py +0 -0
  125. {controlzero-1.8.2 → controlzero-1.9.1}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
  126. {controlzero-1.8.2 → controlzero-1.9.1}/tests/conftest.py +0 -0
  127. {controlzero-1.8.2 → controlzero-1.9.1}/tests/integrations/__init__.py +0 -0
  128. {controlzero-1.8.2 → controlzero-1.9.1}/tests/integrations/test_google.py +0 -0
  129. {controlzero-1.8.2 → controlzero-1.9.1}/tests/parity/action_aliases.json +0 -0
  130. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/__init__.py +0 -0
  131. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/conftest.py +0 -0
  132. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_cli.py +0 -0
  133. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_concurrency.py +0 -0
  134. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_conformance.py +0 -0
  135. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_core.py +0 -0
  136. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_crash.py +0 -0
  137. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_diskfull.py +0 -0
  138. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_sink_wiring.py +0 -0
  139. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_transcript_localack.py +0 -0
  140. {controlzero-1.8.2 → controlzero-1.9.1}/tests/spool/test_spool_uploader.py +0 -0
  141. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_action_aliases.py +0 -0
  142. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_action_canonicalization.py +0 -0
  143. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_action_validator_t86.py +0 -0
  144. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_agent_name_env.py +0 -0
  145. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_api_key_mask.py +0 -0
  146. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_audit_remote.py +0 -0
  147. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_audit_remote_sdk_version.py +0 -0
  148. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_audit_sink_isolation.py +0 -0
  149. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_bundle_parser.py +0 -0
  150. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_bundle_translate.py +0 -0
  151. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_canonical_phase1a.py +0 -0
  152. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_carve_out.py +0 -0
  153. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_debug_bundle.py +0 -0
  154. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_extractor_integration.py +0 -0
  155. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_hook.py +0 -0
  156. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_hosted_refresh.py +0 -0
  157. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_init.py +0 -0
  158. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_init_templates.py +0 -0
  159. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_tail.py +0 -0
  160. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_test.py +0 -0
  161. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_cli_validate.py +0 -0
  162. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_coding_agent_hooks.py +0 -0
  163. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_conditions.py +0 -0
  164. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_conformance.py +0 -0
  165. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_console.py +0 -0
  166. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_credential_hook.py +0 -0
  167. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_default_action.py +0 -0
  168. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_dlp_scanner.py +0 -0
  169. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_doctor.py +0 -0
  170. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_engine_version_consistency.py +0 -0
  171. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_enrollment.py +0 -0
  172. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_env_dump_438.py +0 -0
  173. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_error_codes.py +0 -0
  174. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_errors_e_codes.py +0 -0
  175. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_fail_closed_eval.py +0 -0
  176. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_glob_matching.py +0 -0
  177. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_5d_email_install.py +0 -0
  178. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_cli_flag.py +0 -0
  179. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_exceptions.py +0 -0
  180. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_get_secret_hitl.py +0 -0
  181. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_mock_backend.py +0 -0
  182. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_pending_approval.py +0 -0
  183. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_request_approval.py +0 -0
  184. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_secret_leak_guard.py +0 -0
  185. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_6a_wait.py +0 -0
  186. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_conformance.py +0 -0
  187. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_phase2b_protocol.py +0 -0
  188. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_reason_codes.py +0 -0
  189. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hitl_validator_keys.py +0 -0
  190. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hook_extractors.py +0 -0
  191. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hosted_policy_e2e.py +0 -0
  192. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hybrid_mode_strict.py +0 -0
  193. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_hybrid_mode_warn.py +0 -0
  194. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_install_hook_command.py +0 -0
  195. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_install_hooks.py +0 -0
  196. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_kiro_adapter.py +0 -0
  197. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_kiro_hook_templates.py +0 -0
  198. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_kiro_install.py +0 -0
  199. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_layout_migration_t101.py +0 -0
  200. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_layout_parity_t102.py +0 -0
  201. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_local_mode_dict.py +0 -0
  202. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_local_mode_file_json.py +0 -0
  203. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_local_mode_file_yaml.py +0 -0
  204. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_log_fallback_stderr.py +0 -0
  205. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_log_rotation.py +0 -0
  206. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_migrate.py +0 -0
  207. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_min_sdk_version_gate.py +0 -0
  208. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_multi_client_per_project_175.py +0 -0
  209. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_no_policy_no_key.py +0 -0
  210. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_package_rename_shim.py +0 -0
  211. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_policy_engine_version_phase1b.py +0 -0
  212. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_policy_freshness.py +0 -0
  213. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_policy_settings.py +0 -0
  214. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_policy_source_audit.py +0 -0
  215. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_quarantine.py +0 -0
  216. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_reason_code.py +0 -0
  217. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_refresh.py +0 -0
  218. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_secrets.py +0 -0
  219. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_sql_semantic_class.py +0 -0
  220. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_synthetic_policy_id_t79.py +0 -0
  221. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_t103_precedence.py +0 -0
  222. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_t104_cache_gc.py +0 -0
  223. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_t108_local_override_audit.py +0 -0
  224. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_t96_single_audit_log.py +0 -0
  225. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_t99_install_prefetch_bundle.py +0 -0
  226. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_tamper.py +0 -0
  227. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_tamper_behavior.py +0 -0
  228. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_tamper_hook.py +0 -0
  229. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_telemetry_consent.py +0 -0
  230. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_tracecontext.py +0 -0
  231. {controlzero-1.8.2 → controlzero-1.9.1}/tests/test_unsafe_int_boundary.py +0 -0
  232. {controlzero-1.8.2 → controlzero-1.9.1}/tools/cz-kiro-adapter +0 -0
@@ -1,5 +1,86 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.1 -- 2026-06-16 (hosted-mode local audit log P0, epic gh#1247)
4
+
5
+ ### Fixed
6
+
7
+ - **(P0) Hosted mode never wrote the local `~/.controlzero/audit.log`.** After
8
+ `controlzero install claude-code --api-key cz_live_...` (hosted mode), tool
9
+ calls reached the dashboard (remote audit) but the local audit log stayed
10
+ frozen indefinitely -- even though the claude-code template and the install
11
+ success message both promise that every tool call is logged to
12
+ `~/.controlzero/audit.log`. Root cause: `Client.__init__` constructed the
13
+ `LocalAuditLogger` ONLY when no API key was set, so hosted mode left the
14
+ local sink `None` and `_audit_decision()` skipped the local write (allow AND
15
+ deny). The local log is now written in EVERY mode; the remote sink is layered
16
+ on top, not instead. A blocked (deny) call is now locally recorded too -- the
17
+ exact case a customer hit (Claude Code `Bash` calls denied by no-rule-match).
18
+ This also restores `cz debug-bundle` and the tamper hash-chain, which read
19
+ the local log. Regression test: `tests/test_hosted_local_audit_1247.py`.
20
+
21
+ ### Security
22
+
23
+ - **Local audit log redacts PII/financial DLP plaintext.** Now that hosted mode
24
+ writes the local plaintext audit log, a DLP finding's raw `matched_text` (which
25
+ is plaintext for the pii/financial categories; the secret category is already
26
+ SHA-256 hashed) is stripped from the LOCAL row -- it would otherwise expose
27
+ PII/financial data to anyone who can read the file, content hosted mode kept
28
+ server-side only. The finding metadata (rule_id/category/location) is
29
+ preserved so the local log still records THAT a rule fired, and the remote
30
+ sink keeps full fidelity.
31
+
32
+ ## 1.9.0 -- 2026-06-15 (Antigravity install CLI, epic gh#925)
33
+
34
+ ### Added
35
+
36
+ - **Google Antigravity (`agy`) governance -- BETA.** The `controlzero install
37
+ antigravity` / `uninstall antigravity` commands complete the Antigravity
38
+ integration on top of the host adapter that shipped in 1.8.0 (gh#927). Both
39
+ Antigravity surfaces (the IDE and the `agy` CLI) deliver the hook payload as
40
+ JSON on stdin, so they route directly through the shared
41
+ `controlzero hook-check` -> policy engine -> audit path with no shim, and
42
+ audit rows are attributed to `agent="antigravity"`.
43
+
44
+ - **Real EXTERNAL hooks.json contract.** Wires the `PreToolUse` (deciding
45
+ gate) + `PostToolUse` (observe) file-based events -- stdin JSON in, stdout
46
+ JSON out (camelCase), NOT exit-code based -- which is the contract the agy
47
+ IDE/CLI actually executes. (The in-process Python-SDK hook *class* names
48
+ `PreToolCallDecideHook` / `PostToolCallHook` are a different API and would
49
+ never fire from hooks.json.) The adapter ALWAYS emits an explicit
50
+ `{"decision": ...}`: an empty `{}` is read by agy as `invalid_args` and
51
+ DENIES the tool (cmux#5358), so silence-that-prints-`{}` is impossible.
52
+
53
+ - **`controlzero install antigravity`** (gh#928). Merges a `PreToolUse` +
54
+ `PostToolUse` block (broad `*` matcher) into the Antigravity `hooks.json`.
55
+ Writes the engine-EXECUTED path `~/.gemini/config/hooks.json` by default
56
+ and, for the user scope, also mirrors to the `agy` TUI `/hooks` display
57
+ path `~/.gemini/antigravity-cli/hooks.json` (antigravity-cli#49) so the
58
+ hook both fires AND displays; `<cwd>/.agents/hooks.json` with `--project`,
59
+ or a custom path with `--settings`. Idempotent (re-installs collapse to
60
+ one entry; byte-identical state), preserves pre-existing third-party hooks
61
+ (incl. ones sharing a block) and unrelated keys, and writes the
62
+ `~/.controlzero/policy.yaml` Antigravity template. `uninstall antigravity`
63
+ removes only the Control Zero entries (cleaning both paths) and preserves
64
+ your policy + audit log. Supports `--force` / `--merge` and `--api-key` /
65
+ `--email` for dashboard sync.
66
+
67
+ - **Human-in-the-loop on the wire.** A Control Zero approval gate (a deny
68
+ flagged `requires_approval`) maps to Antigravity's `ask` decision by
69
+ default. A plain `ask` can be auto-satisfied by a cached "Always Allow",
70
+ so set `CZ_ANTIGRAVITY_HITL_DECISION=force_ask` (a guaranteed prompt, once
71
+ verified on your pinned agy build) or `=deny` for a hard block. An
72
+ unrecognized value safely falls back to `ask`.
73
+
74
+ - **Agent fingerprint.** `detect_client_name()` now recognises Antigravity
75
+ via the `ANTIGRAVITY_` / `AGY_` env prefixes (checked BEFORE the Gemini CLI
76
+ branch, since Antigravity shares the `~/.gemini/` config lineage and may
77
+ also carry `GEMINI_*` vars). A stray `GEMINI_API_KEY` is never mislabeled
78
+ as Antigravity.
79
+
80
+ - **Docs.** New `docs/integrations/antigravity.md` install guide alongside
81
+ the existing `antigravity-hook-payloads.md` reference; `antigravity` added
82
+ to the SDK README template list, tagged **BETA**.
83
+
3
84
  ## 1.8.2 -- 2026-06-15 (Kiro IDE hook contract hotfix, epic gh#877)
4
85
 
5
86
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: controlzero
3
- Version: 1.8.2
3
+ Version: 1.9.1
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
 
@@ -40,7 +40,7 @@ from controlzero.hitl.grant_protocol import (
40
40
  )
41
41
  from controlzero.policy_loader import load_policy
42
42
 
43
- __version__ = "1.8.2"
43
+ __version__ = "1.9.1"
44
44
 
45
45
  __all__ = [
46
46
  "Client",
@@ -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):