controlzero 1.8.2__tar.gz → 1.9.0__tar.gz

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