controlzero 1.11.4__tar.gz → 1.11.5__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 (268) hide show
  1. {controlzero-1.11.4 → controlzero-1.11.5}/CHANGELOG.md +22 -1
  2. {controlzero-1.11.4 → controlzero-1.11.5}/PKG-INFO +1 -1
  3. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/__init__.py +1 -1
  4. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/doctor.py +3 -1
  5. controlzero-1.11.5/controlzero/cli/hook_entry.py +118 -0
  6. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/hosts/__init__.py +18 -1
  7. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/hosts/antigravity.py +92 -4
  8. controlzero-1.11.5/controlzero/cli/hosts/base.py +532 -0
  9. controlzero-1.11.5/controlzero/cli/hosts/claude_code.py +191 -0
  10. controlzero-1.11.5/controlzero/cli/hosts/codex_cli.py +101 -0
  11. controlzero-1.11.5/controlzero/cli/hosts/cursor.py +322 -0
  12. controlzero-1.11.5/controlzero/cli/hosts/cursor_cli.py +223 -0
  13. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/hosts/gemini_cli.py +27 -7
  14. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/hosts/kiro.py +53 -3
  15. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/hosts/unknown.py +18 -2
  16. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/main.py +439 -46
  17. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/error_codes.yaml +22 -3
  18. {controlzero-1.11.4 → controlzero-1.11.5}/pyproject.toml +8 -2
  19. controlzero-1.11.5/tests/_fixtures/antigravity-envelope-expected.json +146 -0
  20. controlzero-1.11.5/tests/test_adapter_interface.py +384 -0
  21. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_antigravity_adapter.py +70 -11
  22. controlzero-1.11.5/tests/test_antigravity_envelope_parity.py +96 -0
  23. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_antigravity_ga_blockers_1248.py +3 -3
  24. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_antigravity_install.py +48 -0
  25. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_hook.py +15 -3
  26. controlzero-1.11.5/tests/test_cli_hook_entry.py +81 -0
  27. controlzero-1.11.5/tests/test_cursor_adapter.py +362 -0
  28. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_device.py +25 -38
  29. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hosts_adapter.py +207 -33
  30. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_install_hooks.py +24 -11
  31. controlzero-1.11.5/tests/test_policy_load_failclosed_f3.py +279 -0
  32. controlzero-1.11.4/controlzero/cli/hosts/base.py +0 -171
  33. controlzero-1.11.4/controlzero/cli/hosts/claude_code.py +0 -135
  34. controlzero-1.11.4/controlzero/cli/hosts/codex_cli.py +0 -54
  35. {controlzero-1.11.4 → controlzero-1.11.5}/.gitignore +0 -0
  36. {controlzero-1.11.4 → controlzero-1.11.5}/Dockerfile.test +0 -0
  37. {controlzero-1.11.4 → controlzero-1.11.5}/LICENSE +0 -0
  38. {controlzero-1.11.4 → controlzero-1.11.5}/README.md +0 -0
  39. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/__main__.py +0 -0
  40. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/__init__.py +0 -0
  41. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/action_aliases.py +0 -0
  42. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/action_validator.py +0 -0
  43. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/bundle.py +0 -0
  44. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/credential_hook.py +0 -0
  45. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/credential_scanner.py +0 -0
  46. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/credentials_data/__init__.py +0 -0
  47. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
  48. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/dlp_scanner.py +0 -0
  49. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/enforcer.py +0 -0
  50. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/hook_extractors.py +0 -0
  51. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/tool_extractors.json +0 -0
  52. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/_internal/types.py +0 -0
  53. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/audit_local.py +0 -0
  54. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/audit_remote.py +0 -0
  55. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/canonical.py +0 -0
  56. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/__init__.py +0 -0
  57. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/__main__.py +0 -0
  58. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/_secrets.py +0 -0
  59. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/console.py +0 -0
  60. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/debug_bundle.py +0 -0
  61. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/exec_cmd.py +0 -0
  62. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/kiro_adapter.py +0 -0
  63. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/migrate.py +0 -0
  64. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/spool_cmd.py +0 -0
  65. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/telemetry_consent.py +0 -0
  66. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/antigravity/hooks.json +0 -0
  67. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/antigravity.yaml +0 -0
  68. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/autogen.yaml +0 -0
  69. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/claude-code.yaml +0 -0
  70. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/codex-cli.yaml +0 -0
  71. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/cost-cap.yaml +0 -0
  72. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/crewai.yaml +0 -0
  73. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/cursor.yaml +0 -0
  74. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/gemini-cli.yaml +0 -0
  75. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/generic.yaml +0 -0
  76. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
  77. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -0
  78. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -0
  79. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/kiro/kiro.yaml +0 -0
  80. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/langchain.yaml +0 -0
  81. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/mcp.yaml +0 -0
  82. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/templates/rag.yaml +0 -0
  83. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/cli/update_cmd.py +0 -0
  84. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/client.py +0 -0
  85. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/device.py +0 -0
  86. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/enrollment.py +0 -0
  87. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/error_codes.py +0 -0
  88. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/errors.py +0 -0
  89. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hitl/__init__.py +0 -0
  90. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hitl/grant_protocol.py +0 -0
  91. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hitl/mock.py +0 -0
  92. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hitl/pending_approval.py +0 -0
  93. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hitl/secret_leak_guard.py +0 -0
  94. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hitl/status.py +0 -0
  95. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hooks/__init__.py +0 -0
  96. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hooks/tool_output_handler.py +0 -0
  97. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/hosted_policy.py +0 -0
  98. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/__init__.py +0 -0
  99. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/anthropic.py +0 -0
  100. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/autogen.py +0 -0
  101. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/braintrust.py +0 -0
  102. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/crewai/__init__.py +0 -0
  103. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/crewai/agent.py +0 -0
  104. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/crewai/crew.py +0 -0
  105. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/crewai/task.py +0 -0
  106. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/crewai/tool.py +0 -0
  107. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/google.py +0 -0
  108. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/google_adk/__init__.py +0 -0
  109. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/google_adk/agent.py +0 -0
  110. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/google_adk/tool.py +0 -0
  111. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/__init__.py +0 -0
  112. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/agent.py +0 -0
  113. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/callbacks.py +0 -0
  114. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/chain.py +0 -0
  115. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/graph.py +0 -0
  116. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/modern.py +0 -0
  117. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langchain/tool.py +0 -0
  118. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/langfuse.py +0 -0
  119. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/litellm.py +0 -0
  120. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/openai.py +0 -0
  121. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/pydantic_ai.py +0 -0
  122. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/integrations/vercel_ai.py +0 -0
  123. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/layout_migration.py +0 -0
  124. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/policy_loader.py +0 -0
  125. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/secrets/__init__.py +0 -0
  126. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/secrets/reference.py +0 -0
  127. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/secrets/resolver.py +0 -0
  128. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/secrets/secretstr.py +0 -0
  129. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/__init__.py +0 -0
  130. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_compress.py +0 -0
  131. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_constants.py +0 -0
  132. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_crc32c.py +0 -0
  133. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_crypto.py +0 -0
  134. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_frame.py +0 -0
  135. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_keyring.py +0 -0
  136. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_metrics.py +0 -0
  137. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_spool.py +0 -0
  138. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_state.py +0 -0
  139. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/_uploader.py +0 -0
  140. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/spool/cz-audit-v1.dict +0 -0
  141. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/tamper.py +0 -0
  142. {controlzero-1.11.4 → controlzero-1.11.5}/controlzero/tracecontext.py +0 -0
  143. {controlzero-1.11.4 → controlzero-1.11.5}/examples/hello_world.py +0 -0
  144. {controlzero-1.11.4 → controlzero-1.11.5}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
  145. {controlzero-1.11.4 → controlzero-1.11.5}/tests/conftest.py +0 -0
  146. {controlzero-1.11.4 → controlzero-1.11.5}/tests/integrations/__init__.py +0 -0
  147. {controlzero-1.11.4 → controlzero-1.11.5}/tests/integrations/test_google.py +0 -0
  148. {controlzero-1.11.4 → controlzero-1.11.5}/tests/parity/action_aliases.json +0 -0
  149. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/__init__.py +0 -0
  150. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/conftest.py +0 -0
  151. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_cli.py +0 -0
  152. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_concurrency.py +0 -0
  153. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_conformance.py +0 -0
  154. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_core.py +0 -0
  155. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_crash.py +0 -0
  156. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_diskfull.py +0 -0
  157. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_durable_default_tamper.py +0 -0
  158. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_keychain_dek.py +0 -0
  159. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_sink_wiring.py +0 -0
  160. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_transcript_localack.py +0 -0
  161. {controlzero-1.11.4 → controlzero-1.11.5}/tests/spool/test_spool_uploader.py +0 -0
  162. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_action_aliases.py +0 -0
  163. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_action_canonicalization.py +0 -0
  164. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_action_validator_t86.py +0 -0
  165. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_agent_name_env.py +0 -0
  166. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_antigravity_hook_check.py +0 -0
  167. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_antigravity_posttooluse_observe_58.py +0 -0
  168. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_antigravity_tool_vocab_1303.py +0 -0
  169. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_api_key_mask.py +0 -0
  170. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_audit_remote.py +0 -0
  171. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_audit_remote_sdk_version.py +0 -0
  172. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_audit_sink_isolation.py +0 -0
  173. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_bundle_cache_freshness_1303.py +0 -0
  174. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_bundle_parser.py +0 -0
  175. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_bundle_translate.py +0 -0
  176. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_canonical_phase1a.py +0 -0
  177. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_carve_out.py +0 -0
  178. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_debug_bundle.py +0 -0
  179. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_extractor_integration.py +0 -0
  180. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_hosted_refresh.py +0 -0
  181. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_init.py +0 -0
  182. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_init_templates.py +0 -0
  183. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_tail.py +0 -0
  184. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_test.py +0 -0
  185. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_cli_validate.py +0 -0
  186. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_coding_agent_hooks.py +0 -0
  187. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_conditions.py +0 -0
  188. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_config_format_parity_1303.py +0 -0
  189. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_conformance.py +0 -0
  190. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_console.py +0 -0
  191. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_credential_hook.py +0 -0
  192. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_default_action.py +0 -0
  193. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_dlp_scanner.py +0 -0
  194. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_doctor.py +0 -0
  195. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_engine_version_consistency.py +0 -0
  196. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_enrollment.py +0 -0
  197. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_env_dump_438.py +0 -0
  198. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_epic_1247_bryan_acceptance.py +0 -0
  199. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_error_codes.py +0 -0
  200. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_errors_e_codes.py +0 -0
  201. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_fail_closed_eval.py +0 -0
  202. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_failopen_1303.py +0 -0
  203. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_glob_matching.py +0 -0
  204. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_5d_email_install.py +0 -0
  205. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_cli_flag.py +0 -0
  206. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_exceptions.py +0 -0
  207. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_get_secret_hitl.py +0 -0
  208. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_mock_backend.py +0 -0
  209. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_pending_approval.py +0 -0
  210. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_request_approval.py +0 -0
  211. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_secret_leak_guard.py +0 -0
  212. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_6a_wait.py +0 -0
  213. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_conformance.py +0 -0
  214. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_phase2b_protocol.py +0 -0
  215. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_reason_codes.py +0 -0
  216. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hitl_validator_keys.py +0 -0
  217. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hook_extractors.py +0 -0
  218. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hosted_local_audit_1247.py +0 -0
  219. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hosted_local_shadow_warn_1265.py +0 -0
  220. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hosted_policy_e2e.py +0 -0
  221. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hybrid_mode_strict.py +0 -0
  222. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_hybrid_mode_warn.py +0 -0
  223. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_install_hook_command.py +0 -0
  224. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_kiro_adapter.py +0 -0
  225. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_kiro_cli_e2e.py +0 -0
  226. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_kiro_cli_hook_pin_1265.py +0 -0
  227. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_kiro_hook_templates.py +0 -0
  228. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_kiro_install.py +0 -0
  229. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_layout_migration_t101.py +0 -0
  230. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_layout_parity_t102.py +0 -0
  231. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_local_mode_dict.py +0 -0
  232. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_local_mode_file_json.py +0 -0
  233. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_local_mode_file_yaml.py +0 -0
  234. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_log_fallback_stderr.py +0 -0
  235. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_log_options_ignored_hosted.py +0 -0
  236. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_log_rotation.py +0 -0
  237. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_migrate.py +0 -0
  238. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_min_sdk_version_gate.py +0 -0
  239. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_multi_client_per_project_175.py +0 -0
  240. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_no_policy_no_key.py +0 -0
  241. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_observe_mode_1247.py +0 -0
  242. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_package_rename_shim.py +0 -0
  243. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_part3_active_policy_count_1303.py +0 -0
  244. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_policy_engine_version_phase1b.py +0 -0
  245. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_policy_freshness.py +0 -0
  246. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_policy_settings.py +0 -0
  247. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_policy_source_audit.py +0 -0
  248. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_quarantine.py +0 -0
  249. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_reason_code.py +0 -0
  250. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_refresh.py +0 -0
  251. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_secrets.py +0 -0
  252. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_secrets_reference_resolver.py +0 -0
  253. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_sql_semantic_class.py +0 -0
  254. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_synthetic_policy_id_t79.py +0 -0
  255. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_t103_precedence.py +0 -0
  256. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_t104_cache_gc.py +0 -0
  257. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_t108_local_override_audit.py +0 -0
  258. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_t96_single_audit_log.py +0 -0
  259. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_t99_install_prefetch_bundle.py +0 -0
  260. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_tamper.py +0 -0
  261. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_tamper_behavior.py +0 -0
  262. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_tamper_hook.py +0 -0
  263. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_telemetry_consent.py +0 -0
  264. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_tracecontext.py +0 -0
  265. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_unsafe_int_boundary.py +0 -0
  266. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_update_cmd.py +0 -0
  267. {controlzero-1.11.4 → controlzero-1.11.5}/tests/test_upgrade_nudge.py +0 -0
  268. {controlzero-1.11.4 → controlzero-1.11.5}/tools/cz-kiro-adapter +0 -0
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.11.5 -- 2026-06-19 (cross-surface CONTROLZERO_CLIENT foreign-pin fail-open)
4
+
5
+ Closes a routing fail-open where an explicit `CONTROLZERO_CLIENT` pin -- set by
6
+ `controlzero install <host>` so a kiro/codex/antigravity/cursor hook routes to
7
+ the right adapter -- was NOT authoritative. The `ClaudeCodeAdapter` is first in
8
+ the registry and falls through to ambient Anthropic env hints (`CLAUDECODE` /
9
+ `CLAUDE_CODE` / ...), so a correctly installed foreign-host hook that ALSO
10
+ carried an ambient `CLAUDECODE` (e.g. launched from a Claude Code-spawned shell)
11
+ was hijacked to the Claude adapter, which renders the wrong wire shape and reads
12
+ as allow on a destructive tool call.
13
+
14
+ ### Fixed
15
+
16
+ - **Foreign `CONTROLZERO_CLIENT` pin now defers (cross-surface fail-open).**
17
+ Every host adapter's `claim()` defers when `CONTROLZERO_CLIENT` names a
18
+ DIFFERENT client, via the shared `HostAdapter._pinned_elsewhere` helper, so an
19
+ installer-pinned hook can never be hijacked by an ambient `CLAUDECODE`;
20
+ `UnknownHostAdapter` stays the always-claim backstop. A 7-surface guard matrix
21
+ (`tests/test_hosts_adapter.py`) pins it fail-if-reverted. Mirrors the Node SDK
22
+ fix. gh#81.
23
+
3
24
  ## 1.11.4 -- 2026-06-17 (close the count==0 degraded fail-open + stale-empty-cache replay + cached-bundle integrity, gh#1303)
4
25
 
5
26
  Closes a degraded-bundle fail-open in the count-provenance discriminator plus
@@ -715,7 +736,7 @@ secret-value-leak guard that gates every outbound HITL payload.
715
736
  POSTs a HITL approval request, builds the body from the
716
737
  `PolicyDecision`, threads in `X-CZ-Requestor-Email` from
717
738
  `~/.controlzero/config.yaml`, sends a per-call `Idempotency-Key`, and
718
- returns a `PendingApproval`. Maps backend error codes E1305-E1308 onto
739
+ returns a `PendingApproval`. Maps backend error codes E1705-E1708 onto
719
740
  the matching SDK exceptions. (#577)
720
741
 
721
742
  ### Conformance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: controlzero
3
- Version: 1.11.4
3
+ Version: 1.11.5
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
@@ -41,7 +41,7 @@ from controlzero.hitl.grant_protocol import (
41
41
  from controlzero.policy_loader import load_policy
42
42
  from controlzero.secrets import SecretRef, SecretStr, secret_ref
43
43
 
44
- __version__ = "1.11.4"
44
+ __version__ = "1.11.5"
45
45
 
46
46
  __all__ = [
47
47
  "Client",
@@ -353,7 +353,9 @@ def _antigravity_dry_run_roundtrips() -> bool:
353
353
  adapter = select_adapter({}, {"CONTROLZERO_CLIENT": "antigravity"})
354
354
  if getattr(adapter, "name", "") != "antigravity":
355
355
  return False
356
- out = adapter.render(
356
+ # render_envelope is the per-host wire dict; render() folds it into a
357
+ # RenderResult (exit code + stdout). The dry-run inspects the envelope.
358
+ out = adapter.render_envelope(
357
359
  CZDecision(effect="allow", reason="doctor dry-run", reason_code="RULE_MATCH")
358
360
  )
359
361
  return isinstance(out, dict) and out.get("decision") == "allow"
@@ -0,0 +1,118 @@
1
+ """Crash-proof console-script entry point for the ``controlzero`` CLI (#1189).
2
+
3
+ The ``controlzero`` console script declared in ``pyproject.toml`` MUST NOT be
4
+ pointed directly at ``controlzero.cli.main:cli``. The pip-generated wrapper
5
+ imports the target at module load with no guard, so a top-level import crash in
6
+ ``controlzero/cli/main.py`` (a missing transitive dependency, a packaging
7
+ defect, a syntax error in any eagerly-imported submodule) raises before
8
+ ``cli()`` runs. The wrapper then exits nonzero with a traceback, and a host that
9
+ treats nonzero-with-no-decision as "proceed" (Claude Code's PreToolUse falls
10
+ back to allow on a hook it cannot evaluate) silently lets the tool call
11
+ through. That is a fail-OPEN on the governance hot path -- the exact defect
12
+ QA #1189 flagged.
13
+
14
+ This shim makes the entry point crash-proof:
15
+
16
+ * The heavy import of ``controlzero.cli.main`` happens inside a try/except
17
+ with a ``sys.path`` source-checkout fallback, mirroring
18
+ ``tools/cz-kiro-adapter`` so a dev checkout that has not been pip-installed
19
+ still resolves the package.
20
+ * If the import or evaluation of the CLI cannot complete AT ALL and this is a
21
+ hook invocation (``hook-check`` on argv -- the governance hot path), the
22
+ shim fails SAFE: it writes a hard, human-readable error to stderr and exits
23
+ with the host's DENY/block code (Claude Code PreToolUse = exit 2), never a
24
+ silent allow.
25
+ * For a non-hook invocation (interactive ``controlzero ...`` usage) a broken
26
+ install should surface its traceback so the user can fix it -- there is no
27
+ governance decision at stake -- so the shim re-raises in that case.
28
+
29
+ What this shim does NOT change: the runtime fail-OPEN contract that lives
30
+ inside ``hook_check`` (E1501 bad-JSON, E1502 timeout, empty stdin, missing
31
+ tool_name) is intentional and is preserved. Those paths are reached only after
32
+ the CLI imports successfully and represent a *decision* to pass through, not an
33
+ import crash. This shim only converts the previously-silent import-crash
34
+ fall-through into an explicit fail-safe deny.
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ import os
40
+ import sys
41
+
42
+ # Host block/deny exit codes for the hook hot path. Claude Code's PreToolUse
43
+ # protocol treats exit 2 as "block"; Kiro reuses the same convention. We do not
44
+ # have the resolved host adapter available before the import succeeds, so we use
45
+ # the lowest-common-denominator block code (2), which every supported host
46
+ # interprets as deny rather than allow.
47
+ _HOOK_DENY_EXIT_CODE = 2
48
+
49
+ _HOOK_SUBCOMMANDS = frozenset({"hook-check"})
50
+
51
+
52
+ def _is_hook_invocation(argv: list[str]) -> bool:
53
+ """True when the process was launched on the governance hot path.
54
+
55
+ The ``hook-check`` subcommand is what hosts (Claude Code, Kiro, Codex,
56
+ Gemini, Antigravity) wire into their PreToolUse hooks. Detecting it on argv
57
+ lets the shim choose fail-SAFE (deny) on an import crash for hook calls
58
+ while leaving interactive CLI usage to surface its traceback normally.
59
+ """
60
+ return any(arg in _HOOK_SUBCOMMANDS for arg in argv[1:])
61
+
62
+
63
+ def cli() -> None:
64
+ """Crash-proof wrapper around ``controlzero.cli.main:cli``.
65
+
66
+ Defers the heavy import so an import-time crash cannot escape as a bare
67
+ nonzero traceback that a host would mistake for "allow".
68
+ """
69
+ try:
70
+ from controlzero.cli.main import cli as _real_cli
71
+ except ModuleNotFoundError:
72
+ # Possibly a source checkout where the package is not pip-installed.
73
+ # Add the SDK root to sys.path and retry once, mirroring
74
+ # tools/cz-kiro-adapter. controlzero/cli/hook_entry.py -> SDK root is
75
+ # three levels up (controlzero/cli/ -> controlzero/ -> SDK root).
76
+ _sdk_root = os.path.dirname(
77
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
78
+ )
79
+ if _sdk_root not in sys.path:
80
+ sys.path.insert(0, _sdk_root)
81
+ try:
82
+ from controlzero.cli.main import cli as _real_cli
83
+ except BaseException: # noqa: BLE001 -- crash-proof boundary
84
+ _fail_safe_or_reraise()
85
+ return
86
+ except BaseException: # noqa: BLE001 -- crash-proof boundary
87
+ # Any other top-level import failure (ImportError on a transitive dep,
88
+ # a syntax error in an eagerly-imported submodule, etc).
89
+ _fail_safe_or_reraise()
90
+ return
91
+
92
+ _real_cli()
93
+
94
+
95
+ def _fail_safe_or_reraise() -> None:
96
+ """On import failure: fail-safe DENY for hook calls, re-raise otherwise.
97
+
98
+ Reads the live exception from ``sys.exc_info`` so it can re-raise the
99
+ original traceback for interactive usage.
100
+ """
101
+ if _is_hook_invocation(sys.argv):
102
+ exc = sys.exc_info()[1]
103
+ # Hard, loud error to stderr -- never a silent allow. The host sees a
104
+ # nonzero block exit code and denies the tool call.
105
+ sys.stderr.write(
106
+ "[Control Zero] hook entry point could not load the policy engine "
107
+ f"({type(exc).__name__}: {exc}); DENYING this tool call (fail-safe). "
108
+ "Reinstall the controlzero package to restore policy enforcement.\n"
109
+ )
110
+ sys.stderr.flush()
111
+ sys.exit(_HOOK_DENY_EXIT_CODE)
112
+ # Not a hook invocation: surface the real traceback so the user can fix
113
+ # their install. No governance decision is at stake here.
114
+ raise
115
+
116
+
117
+ if __name__ == "__main__":
118
+ cli()
@@ -55,9 +55,18 @@ known host treats no-decision as "proceed") and the universal
55
55
  from __future__ import annotations
56
56
 
57
57
  from controlzero.cli.hosts.antigravity import AntigravityAdapter
58
- from controlzero.cli.hosts.base import CZDecision, HostAdapter
58
+ from controlzero.cli.hosts.base import (
59
+ AdapterError,
60
+ CoverageDecl,
61
+ CZDecision,
62
+ HostAdapter,
63
+ RenderResult,
64
+ ToolCall,
65
+ )
59
66
  from controlzero.cli.hosts.claude_code import ClaudeCodeAdapter
60
67
  from controlzero.cli.hosts.codex_cli import CodexCLIAdapter
68
+ from controlzero.cli.hosts.cursor import CursorIDEAdapter
69
+ from controlzero.cli.hosts.cursor_cli import CursorCLIAdapter
61
70
  from controlzero.cli.hosts.gemini_cli import GeminiCLIAdapter
62
71
  from controlzero.cli.hosts.kiro import KiroCLIAdapter, KiroIDEAdapter
63
72
  from controlzero.cli.hosts.unknown import UnknownHostAdapter
@@ -77,8 +86,10 @@ REGISTRY: list[HostAdapter] = [
77
86
  CodexCLIAdapter(),
78
87
  GeminiCLIAdapter(),
79
88
  AntigravityAdapter(), # distinct toolCall / PreToolCall signature
89
+ CursorCLIAdapter(), # beforeShellExecution + top-level `command`, no tool_name (before KiroCLI)
80
90
  KiroCLIAdapter(), # camelCase hook_event_name (disjoint from Claude PascalCase)
81
91
  KiroIDEAdapter(), # CZ_KIRO_SURFACE=ide / TERM_PROGRAM=kiro (shim-set)
92
+ CursorIDEAdapter(), # cursor_version / generation_id payload marker or CURSOR_* env (after KiroIDE)
82
93
  UnknownHostAdapter(), # always claims; must stay at the tail
83
94
  ]
84
95
 
@@ -105,9 +116,15 @@ def select_adapter(payload: dict, env) -> HostAdapter:
105
116
 
106
117
  __all__ = [
107
118
  "CZDecision",
119
+ "ToolCall",
120
+ "RenderResult",
121
+ "CoverageDecl",
122
+ "AdapterError",
108
123
  "HostAdapter",
109
124
  "ClaudeCodeAdapter",
110
125
  "CodexCLIAdapter",
126
+ "CursorCLIAdapter",
127
+ "CursorIDEAdapter",
111
128
  "GeminiCLIAdapter",
112
129
  "AntigravityAdapter",
113
130
  "KiroCLIAdapter",
@@ -55,7 +55,7 @@ from __future__ import annotations
55
55
  import os
56
56
  from typing import Mapping
57
57
 
58
- from controlzero.cli.hosts.base import CZDecision, HostAdapter
58
+ from controlzero.cli.hosts.base import CoverageDecl, CZDecision, HostAdapter
59
59
 
60
60
  # File-based EVENT names (NOT the in-process SDK class names). PreToolUse is
61
61
  # the deciding gate; PostToolUse is observe-only.
@@ -70,9 +70,62 @@ _ANTIGRAVITY_HOOK_EVENTS = frozenset(
70
70
  # disambiguators. Antigravity's unambiguous signal is the ``toolCall`` envelope,
71
71
  # which the claim below keys on first.
72
72
  _ANTIGRAVITY_CLAIM_EVENTS = frozenset({"PreInvocation", "PostInvocation"})
73
- _CLIENT_ALIASES = frozenset({"antigravity", "agy", "antigravity-cli"})
73
+ # CONTROLZERO_CLIENT values the adapter CLAIMS as Antigravity. Includes the
74
+ # surface-split forms (#58) so an install pinning CONTROLZERO_CLIENT=
75
+ # antigravity_ide / antigravity_cli is still claimed.
76
+ _CLIENT_ALIASES = frozenset(
77
+ {"antigravity", "agy", "antigravity-cli", "antigravity_ide", "antigravity_cli"}
78
+ )
74
79
  _ENV_PREFIXES = ("ANTIGRAVITY_", "AGY_")
75
80
 
81
+ # Surface split (#58): one adapter governs BOTH the Antigravity IDE and the
82
+ # ``agy`` CLI -- they share the SAME external PreToolUse stdout-decision hook
83
+ # contract, the same engine-executed ``~/.gemini/config/hooks.json``, and the
84
+ # same sanitized hook environment (only a GEMINI_PROJECT_DIR-style whitelist is
85
+ # forwarded; arbitrary user env vars do NOT reach the hook subshell -- verified
86
+ # cmux#4768). Enforcement is therefore IDENTICAL on both; the founder wants the
87
+ # AUDIT/coverage SOURCE label to distinguish them.
88
+ #
89
+ # There is NO runtime signal that tells the IDE apart from the CLI at hook time
90
+ # (Google's own docs + blog confirm a shared harness + synced settings; the
91
+ # stdin payload carries no surface marker, and the env is sanitized). So the
92
+ # split is INSTALL-TIME: each installer writes the surface into the hook command
93
+ # as ``CONTROLZERO_CLIENT=antigravity_ide`` / ``=antigravity_cli`` (an env var
94
+ # set INSIDE the executed command survives agy's env sanitization because the
95
+ # shell running the command sets it -- it is not inherited from the parent).
96
+ #
97
+ # Back-compat (#58): a bare ``antigravity`` (old SDKs / env-only detection)
98
+ # stays ``antigravity`` -- its own canonical value, NOT remapped -- so existing
99
+ # audit rows and pre-split emitters are never downgraded or rewritten.
100
+ _SOURCE_ANTIGRAVITY = "antigravity"
101
+ _SOURCE_ANTIGRAVITY_IDE = "antigravity_ide"
102
+ _SOURCE_ANTIGRAVITY_CLI = "antigravity_cli"
103
+ # CONTROLZERO_CLIENT values that resolve to the CLI surface. The CLI install
104
+ # pins ``antigravity_cli``; the legacy agy aliases also resolve to CLI since
105
+ # ``agy`` IS the CLI.
106
+ _CLI_SURFACE_CLIENTS = frozenset({"antigravity_cli", "agy", "antigravity-cli"})
107
+
108
+
109
+ def _resolve_antigravity_source(env: Mapping[str, str] | None = None) -> str:
110
+ """Resolve the canonical audit SOURCE for the Antigravity surface (#58).
111
+
112
+ Install-time split: the installer pins ``CONTROLZERO_CLIENT`` in the hook
113
+ command so the runtime knows which surface fired:
114
+
115
+ * ``antigravity_ide`` -> ``antigravity_ide`` (the IDE installer).
116
+ * ``antigravity_cli`` / ``agy`` / ``antigravity-cli`` -> ``antigravity_cli``
117
+ (the agy CLI installer; the legacy agy aliases ARE the CLI).
118
+ * anything else (bare ``antigravity``, unset, env-only detection) ->
119
+ ``antigravity`` -- the back-compat value, never remapped.
120
+ """
121
+ src = env if env is not None else os.environ
122
+ raw = (src.get("CONTROLZERO_CLIENT") or "").strip().lower()
123
+ if raw == _SOURCE_ANTIGRAVITY_IDE:
124
+ return _SOURCE_ANTIGRAVITY_IDE
125
+ if raw in _CLI_SURFACE_CLIENTS:
126
+ return _SOURCE_ANTIGRAVITY_CLI
127
+ return _SOURCE_ANTIGRAVITY
128
+
76
129
  # Source-confirmed decision tokens for PreToolUse output.
77
130
  _VALID_DECISIONS = frozenset({"allow", "deny", "ask", "force_ask"})
78
131
 
@@ -130,16 +183,51 @@ def _resolve_hitl_decision(env: Mapping[str, str] | None = None) -> str:
130
183
 
131
184
  class AntigravityAdapter(HostAdapter):
132
185
  name = "antigravity"
133
- canonical_source = "antigravity"
134
186
  # Antigravity decides from the stdout JSON, NOT the exit code. A non-zero
135
187
  # exit is read as a fail-closed deny that would override an ``ask`` /
136
188
  # ``allow`` verdict (turning a HITL prompt into a hard block), so the
137
189
  # hook-check command must exit 0 for this host and let the JSON decide.
138
190
  decision_via_exit_code = False
139
191
 
192
+ # Coverage-as-data (#202). Antigravity's PreToolUse gate sees the tool
193
+ # arguments and decides via the stdout JSON (block_mechanism is
194
+ # stdout_decision, NOT exit code). CRITICAL: agy reads an empty /
195
+ # decision-less stdout as invalid_args -> DENY, so empty_stdout_is="deny"
196
+ # and the render envelope ALWAYS carries an explicit decision. PostToolUse /
197
+ # PostInvocation / Stop are observe only.
198
+ coverage = CoverageDecl(
199
+ tier="enforced",
200
+ block_mechanism="stdout_decision",
201
+ deciding_events=("PreToolUse",),
202
+ observe_events=("PostToolUse", "PostInvocation", "Stop"),
203
+ delivers_tool_args=True,
204
+ empty_stdout_is="deny",
205
+ covered_tool_families=("*",),
206
+ min_core_version="1.9.0",
207
+ adapter_contract_version="1",
208
+ )
209
+
210
+ @property
211
+ def canonical_source(self) -> str: # type: ignore[override]
212
+ """Audit SOURCE label, split IDE vs CLI at install time (#58).
213
+
214
+ Resolved from ``CONTROLZERO_CLIENT`` so the single adapter governs both
215
+ surfaces (shared hook contract -> identical enforcement) while the audit
216
+ row distinguishes ``antigravity_ide`` from ``antigravity_cli``. A bare
217
+ ``antigravity`` (old SDKs / env-only detection) stays ``antigravity``
218
+ for back-compat. ``name`` stays ``"antigravity"`` so the fail-closed
219
+ guards that key on the adapter NAME are unaffected by the source split.
220
+ """
221
+ return _resolve_antigravity_source()
222
+
140
223
  def claim(self, payload: dict, env: Mapping[str, str]) -> bool:
141
224
  if (env.get("CONTROLZERO_CLIENT") or "").strip().lower() in _CLIENT_ALIASES:
142
225
  return True
226
+ # Cross-surface routing fail-open backstop: a foreign pin defers so the
227
+ # pinned host's adapter (not this one) claims the call -- even when an
228
+ # ambient ANTIGRAVITY_/AGY_ env var is also present.
229
+ if self._pinned_elsewhere(env, _CLIENT_ALIASES):
230
+ return False
143
231
  for key in env:
144
232
  if key.upper().startswith(_ENV_PREFIXES):
145
233
  return True
@@ -169,7 +257,7 @@ class AntigravityAdapter(HostAdapter):
169
257
  normalized["tool_input"] = args if isinstance(args, dict) else {}
170
258
  return normalized
171
259
 
172
- def render(self, decision: CZDecision) -> dict:
260
+ def render_envelope(self, decision: CZDecision) -> dict:
173
261
  """Render to Antigravity's PreToolUse stdout-JSON contract.
174
262
 
175
263
  ALWAYS emits an explicit ``decision`` key. An empty / decision-less