controlzero 1.13.10__tar.gz → 1.13.12__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 (342) hide show
  1. {controlzero-1.13.10 → controlzero-1.13.12}/.gitignore +14 -1
  2. {controlzero-1.13.10 → controlzero-1.13.12}/CHANGELOG.md +400 -6
  3. {controlzero-1.13.10 → controlzero-1.13.12}/PKG-INFO +1 -1
  4. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/__init__.py +3 -2
  5. controlzero-1.13.12/controlzero/_internal/action_subject.py +474 -0
  6. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/action_validator.py +8 -0
  7. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/dlp_output_observer.py +7 -1
  8. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/dlp_scanner.py +148 -2
  9. controlzero-1.13.12/controlzero/_internal/enforcement_outcome.py +646 -0
  10. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/enforcer.py +836 -54
  11. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/hook_extractors.py +29 -0
  12. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/audit_remote.py +26 -0
  13. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/debug_bundle.py +26 -1
  14. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/doctor.py +84 -23
  15. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/enforcement.py +156 -11
  16. controlzero-1.13.12/controlzero/cli/enforcement_verdict.py +1316 -0
  17. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/antigravity.py +142 -36
  18. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/base.py +151 -4
  19. controlzero-1.13.12/controlzero/cli/hosts/claims.py +566 -0
  20. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/claude_code.py +29 -1
  21. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/codex_cli.py +14 -1
  22. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/cursor_cli.py +27 -3
  23. controlzero-1.13.12/controlzero/cli/hosts/kiro.py +799 -0
  24. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/main.py +1176 -191
  25. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/setup_wizard.py +55 -9
  26. controlzero-1.13.12/controlzero/cli/templates/kiro/kiro.yaml +176 -0
  27. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/client.py +800 -8
  28. controlzero-1.13.12/controlzero/hook_integrity.py +1347 -0
  29. controlzero-1.13.12/controlzero/host_invocation.py +723 -0
  30. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/_export_dlp.py +7 -1
  31. controlzero-1.13.12/controlzero/integrations/_mask_apply.py +146 -0
  32. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/anthropic.py +5 -0
  33. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/autogen.py +15 -4
  34. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/agent.py +5 -0
  35. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/crew.py +15 -0
  36. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/task.py +10 -0
  37. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/tool.py +43 -4
  38. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google.py +5 -0
  39. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google_adk/agent.py +5 -0
  40. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google_adk/tool.py +34 -2
  41. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/agent.py +20 -0
  42. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/chain.py +25 -0
  43. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/graph.py +30 -0
  44. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/tool.py +41 -2
  45. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/litellm.py +5 -0
  46. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/openai.py +10 -0
  47. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/pydantic_ai.py +17 -2
  48. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/vercel_ai.py +5 -0
  49. {controlzero-1.13.10 → controlzero-1.13.12}/pyproject.toml +34 -1
  50. controlzero-1.13.10/Dockerfile.test +0 -24
  51. controlzero-1.13.10/controlzero/cli/hosts/kiro.py +0 -364
  52. controlzero-1.13.10/controlzero/cli/templates/kiro/kiro.yaml +0 -102
  53. controlzero-1.13.10/controlzero/hook_integrity.py +0 -680
  54. controlzero-1.13.10/examples/hello_world.py +0 -20
  55. controlzero-1.13.10/ruff.toml +0 -40
  56. controlzero-1.13.10/tests/_fixtures/antigravity-envelope-expected.json +0 -146
  57. controlzero-1.13.10/tests/_fixtures/jcs_args_hash_vectors.json +0 -111
  58. controlzero-1.13.10/tests/conftest.py +0 -68
  59. controlzero-1.13.10/tests/fixtures/adapter-contracts/antigravity.json +0 -21
  60. controlzero-1.13.10/tests/fixtures/adapter-contracts/claude_code.json +0 -44
  61. controlzero-1.13.10/tests/fixtures/adapter-contracts/codex_cli.json +0 -24
  62. controlzero-1.13.10/tests/fixtures/adapter-contracts/cursor.json +0 -31
  63. controlzero-1.13.10/tests/fixtures/adapter-contracts/gemini_cli.json +0 -36
  64. controlzero-1.13.10/tests/fixtures/adapter-contracts/kiro.json +0 -39
  65. controlzero-1.13.10/tests/integrations/__init__.py +0 -0
  66. controlzero-1.13.10/tests/integrations/test_google.py +0 -96
  67. controlzero-1.13.10/tests/parity/action_aliases.json +0 -55
  68. controlzero-1.13.10/tests/spool/__init__.py +0 -0
  69. controlzero-1.13.10/tests/spool/conftest.py +0 -208
  70. controlzero-1.13.10/tests/spool/test_spool_cli.py +0 -168
  71. controlzero-1.13.10/tests/spool/test_spool_concurrency.py +0 -110
  72. controlzero-1.13.10/tests/spool/test_spool_conformance.py +0 -316
  73. controlzero-1.13.10/tests/spool/test_spool_core.py +0 -415
  74. controlzero-1.13.10/tests/spool/test_spool_crash.py +0 -106
  75. controlzero-1.13.10/tests/spool/test_spool_diskfull.py +0 -77
  76. controlzero-1.13.10/tests/spool/test_spool_durable_default_tamper.py +0 -353
  77. controlzero-1.13.10/tests/spool/test_spool_keychain_dek.py +0 -444
  78. controlzero-1.13.10/tests/spool/test_spool_sink_wiring.py +0 -671
  79. controlzero-1.13.10/tests/spool/test_spool_transcript_localack.py +0 -102
  80. controlzero-1.13.10/tests/spool/test_spool_uploader.py +0 -569
  81. controlzero-1.13.10/tests/test_action_aliases.py +0 -224
  82. controlzero-1.13.10/tests/test_action_canonicalization.py +0 -36
  83. controlzero-1.13.10/tests/test_action_validator_t86.py +0 -129
  84. controlzero-1.13.10/tests/test_adapter_contracts.py +0 -215
  85. controlzero-1.13.10/tests/test_adapter_interface.py +0 -402
  86. controlzero-1.13.10/tests/test_agent_name_env.py +0 -83
  87. controlzero-1.13.10/tests/test_agy_docs_no_exit2_1373.py +0 -131
  88. controlzero-1.13.10/tests/test_antigravity_adapter.py +0 -531
  89. controlzero-1.13.10/tests/test_antigravity_envelope_parity.py +0 -108
  90. controlzero-1.13.10/tests/test_antigravity_ga_blockers_1248.py +0 -565
  91. controlzero-1.13.10/tests/test_antigravity_hook_check.py +0 -164
  92. controlzero-1.13.10/tests/test_antigravity_install.py +0 -431
  93. controlzero-1.13.10/tests/test_antigravity_posttooluse_observe_58.py +0 -295
  94. controlzero-1.13.10/tests/test_antigravity_tool_vocab_1303.py +0 -322
  95. controlzero-1.13.10/tests/test_api_key_mask.py +0 -59
  96. controlzero-1.13.10/tests/test_api_url_config.py +0 -72
  97. controlzero-1.13.10/tests/test_audit_project_id_attribution_pr2.py +0 -304
  98. controlzero-1.13.10/tests/test_audit_remote.py +0 -715
  99. controlzero-1.13.10/tests/test_audit_remote_io_capture.py +0 -141
  100. controlzero-1.13.10/tests/test_audit_remote_sdk_version.py +0 -144
  101. controlzero-1.13.10/tests/test_audit_sink_isolation.py +0 -67
  102. controlzero-1.13.10/tests/test_bundle_cache_freshness_1303.py +0 -302
  103. controlzero-1.13.10/tests/test_bundle_parser.py +0 -238
  104. controlzero-1.13.10/tests/test_bundle_translate.py +0 -263
  105. controlzero-1.13.10/tests/test_canonical_phase1a.py +0 -230
  106. controlzero-1.13.10/tests/test_capability_matrix_doc.py +0 -175
  107. controlzero-1.13.10/tests/test_cli_carve_out.py +0 -513
  108. controlzero-1.13.10/tests/test_cli_coverage.py +0 -62
  109. controlzero-1.13.10/tests/test_cli_debug_bundle.py +0 -390
  110. controlzero-1.13.10/tests/test_cli_extractor_integration.py +0 -527
  111. controlzero-1.13.10/tests/test_cli_hook.py +0 -521
  112. controlzero-1.13.10/tests/test_cli_hook_entry.py +0 -189
  113. controlzero-1.13.10/tests/test_cli_hosted_refresh.py +0 -103
  114. controlzero-1.13.10/tests/test_cli_init.py +0 -43
  115. controlzero-1.13.10/tests/test_cli_init_templates.py +0 -24
  116. controlzero-1.13.10/tests/test_cli_tail.py +0 -33
  117. controlzero-1.13.10/tests/test_cli_test.py +0 -59
  118. controlzero-1.13.10/tests/test_cli_validate.py +0 -37
  119. controlzero-1.13.10/tests/test_coding_agent_hooks.py +0 -894
  120. controlzero-1.13.10/tests/test_condition_operators_1495.py +0 -191
  121. controlzero-1.13.10/tests/test_conditions.py +0 -200
  122. controlzero-1.13.10/tests/test_config_format_parity_1303.py +0 -145
  123. controlzero-1.13.10/tests/test_conformance.py +0 -335
  124. controlzero-1.13.10/tests/test_console.py +0 -87
  125. controlzero-1.13.10/tests/test_credential_hook.py +0 -737
  126. controlzero-1.13.10/tests/test_cross_adapter_shell_enforcement.py +0 -334
  127. controlzero-1.13.10/tests/test_cursor_adapter.py +0 -373
  128. controlzero-1.13.10/tests/test_dashboard_shell_template_1904.py +0 -746
  129. controlzero-1.13.10/tests/test_default_action.py +0 -505
  130. controlzero-1.13.10/tests/test_deny_rule_reconciliation_1011.py +0 -247
  131. controlzero-1.13.10/tests/test_deny_wins_default_catchall_1620.py +0 -130
  132. controlzero-1.13.10/tests/test_device.py +0 -357
  133. controlzero-1.13.10/tests/test_dlp_agent_remediation_1938.py +0 -366
  134. controlzero-1.13.10/tests/test_dlp_audit_hashing_1881.py +0 -88
  135. controlzero-1.13.10/tests/test_dlp_egress_conformance_matrix.py +0 -1091
  136. controlzero-1.13.10/tests/test_dlp_mask.py +0 -519
  137. controlzero-1.13.10/tests/test_dlp_mask_hosts_1938.py +0 -164
  138. controlzero-1.13.10/tests/test_dlp_origin_contract.py +0 -620
  139. controlzero-1.13.10/tests/test_dlp_path5_audit_egress_1967.py +0 -174
  140. controlzero-1.13.10/tests/test_dlp_paths_8_9_observe_1967.py +0 -170
  141. controlzero-1.13.10/tests/test_dlp_scan_coverage_1927_1931.py +0 -634
  142. controlzero-1.13.10/tests/test_dlp_scanner.py +0 -1218
  143. controlzero-1.13.10/tests/test_doctor.py +0 -150
  144. controlzero-1.13.10/tests/test_engine_version_consistency.py +0 -82
  145. controlzero-1.13.10/tests/test_enrollment.py +0 -820
  146. controlzero-1.13.10/tests/test_env_dump_438.py +0 -148
  147. controlzero-1.13.10/tests/test_epic_1247_bryan_acceptance.py +0 -531
  148. controlzero-1.13.10/tests/test_error_codes.py +0 -122
  149. controlzero-1.13.10/tests/test_errors_e_codes.py +0 -301
  150. controlzero-1.13.10/tests/test_fail_closed_eval.py +0 -19
  151. controlzero-1.13.10/tests/test_failopen_1303.py +0 -96
  152. controlzero-1.13.10/tests/test_feature_parity_maturity.py +0 -187
  153. controlzero-1.13.10/tests/test_git_target_1734.py +0 -242
  154. controlzero-1.13.10/tests/test_glob_matching.py +0 -287
  155. controlzero-1.13.10/tests/test_hitl_5d_email_install.py +0 -137
  156. controlzero-1.13.10/tests/test_hitl_6a_cli_flag.py +0 -306
  157. controlzero-1.13.10/tests/test_hitl_6a_exceptions.py +0 -166
  158. controlzero-1.13.10/tests/test_hitl_6a_get_secret_hitl.py +0 -672
  159. controlzero-1.13.10/tests/test_hitl_6a_mock_backend.py +0 -342
  160. controlzero-1.13.10/tests/test_hitl_6a_pending_approval.py +0 -362
  161. controlzero-1.13.10/tests/test_hitl_6a_request_approval.py +0 -729
  162. controlzero-1.13.10/tests/test_hitl_6a_secret_leak_guard.py +0 -334
  163. controlzero-1.13.10/tests/test_hitl_6a_wait.py +0 -673
  164. controlzero-1.13.10/tests/test_hitl_conformance.py +0 -286
  165. controlzero-1.13.10/tests/test_hitl_phase2b_protocol.py +0 -643
  166. controlzero-1.13.10/tests/test_hitl_reason_codes.py +0 -78
  167. controlzero-1.13.10/tests/test_hitl_validator_keys.py +0 -271
  168. controlzero-1.13.10/tests/test_hook_command_pinning_1705.py +0 -325
  169. controlzero-1.13.10/tests/test_hook_extractors.py +0 -566
  170. controlzero-1.13.10/tests/test_hook_integrity.py +0 -521
  171. controlzero-1.13.10/tests/test_hook_mask_failclosed.py +0 -95
  172. controlzero-1.13.10/tests/test_hook_warn_surfaces_reason_1903.py +0 -479
  173. controlzero-1.13.10/tests/test_hosted_local_audit_1247.py +0 -219
  174. controlzero-1.13.10/tests/test_hosted_local_shadow_warn_1265.py +0 -265
  175. controlzero-1.13.10/tests/test_hosted_policy_e2e.py +0 -383
  176. controlzero-1.13.10/tests/test_hosts_adapter.py +0 -686
  177. controlzero-1.13.10/tests/test_hybrid_mode_strict.py +0 -90
  178. controlzero-1.13.10/tests/test_hybrid_mode_warn.py +0 -28
  179. controlzero-1.13.10/tests/test_install_api_url.py +0 -69
  180. controlzero-1.13.10/tests/test_install_hook_command.py +0 -145
  181. controlzero-1.13.10/tests/test_install_hooks.py +0 -1244
  182. controlzero-1.13.10/tests/test_integration_export_dlp_1967.py +0 -294
  183. controlzero-1.13.10/tests/test_io_capture_producer.py +0 -172
  184. controlzero-1.13.10/tests/test_kiro_adapter.py +0 -1422
  185. controlzero-1.13.10/tests/test_kiro_agent_harness_routing.py +0 -196
  186. controlzero-1.13.10/tests/test_kiro_cli_e2e.py +0 -353
  187. controlzero-1.13.10/tests/test_kiro_cli_hook_pin_1265.py +0 -133
  188. controlzero-1.13.10/tests/test_kiro_cli_v210_hooks.py +0 -120
  189. controlzero-1.13.10/tests/test_kiro_hook_templates.py +0 -324
  190. controlzero-1.13.10/tests/test_kiro_ide_v2_hooks_1944.py +0 -480
  191. controlzero-1.13.10/tests/test_kiro_install.py +0 -463
  192. controlzero-1.13.10/tests/test_kiro_install_standardisation.py +0 -218
  193. controlzero-1.13.10/tests/test_kiro_version_schemes.py +0 -123
  194. controlzero-1.13.10/tests/test_layout_migration_t101.py +0 -158
  195. controlzero-1.13.10/tests/test_layout_parity_t102.py +0 -229
  196. controlzero-1.13.10/tests/test_local_mode_dict.py +0 -98
  197. controlzero-1.13.10/tests/test_local_mode_file_json.py +0 -33
  198. controlzero-1.13.10/tests/test_local_mode_file_yaml.py +0 -62
  199. controlzero-1.13.10/tests/test_localized_reason.py +0 -303
  200. controlzero-1.13.10/tests/test_log_fallback_stderr.py +0 -33
  201. controlzero-1.13.10/tests/test_log_options_ignored_hosted.py +0 -50
  202. controlzero-1.13.10/tests/test_log_rotation.py +0 -66
  203. controlzero-1.13.10/tests/test_migrate.py +0 -128
  204. controlzero-1.13.10/tests/test_min_sdk_version_gate.py +0 -206
  205. controlzero-1.13.10/tests/test_multi_client_per_project_175.py +0 -810
  206. controlzero-1.13.10/tests/test_no_policy_no_key.py +0 -28
  207. controlzero-1.13.10/tests/test_observe_mode_1247.py +0 -362
  208. controlzero-1.13.10/tests/test_package_rename_shim.py +0 -41
  209. controlzero-1.13.10/tests/test_part3_active_policy_count_1303.py +0 -142
  210. controlzero-1.13.10/tests/test_per_client_isolation_1011.py +0 -250
  211. controlzero-1.13.10/tests/test_policy_engine_version_phase1b.py +0 -73
  212. controlzero-1.13.10/tests/test_policy_freshness.py +0 -274
  213. controlzero-1.13.10/tests/test_policy_load_failclosed_f3.py +0 -278
  214. controlzero-1.13.10/tests/test_policy_settings.py +0 -84
  215. controlzero-1.13.10/tests/test_policy_source_audit.py +0 -149
  216. controlzero-1.13.10/tests/test_pr4_install_messaging.py +0 -380
  217. controlzero-1.13.10/tests/test_pr4_output.py +0 -144
  218. controlzero-1.13.10/tests/test_pr4_status.py +0 -160
  219. controlzero-1.13.10/tests/test_probe_audit_isolation.py +0 -177
  220. controlzero-1.13.10/tests/test_prompt_submit_dlp_1966.py +0 -421
  221. controlzero-1.13.10/tests/test_quarantine.py +0 -191
  222. controlzero-1.13.10/tests/test_reason_code.py +0 -392
  223. controlzero-1.13.10/tests/test_reason_code_hitl_grant_1596.py +0 -34
  224. controlzero-1.13.10/tests/test_refresh.py +0 -567
  225. controlzero-1.13.10/tests/test_secrets.py +0 -225
  226. controlzero-1.13.10/tests/test_secrets_reference_resolver.py +0 -191
  227. controlzero-1.13.10/tests/test_setup_wizard.py +0 -504
  228. controlzero-1.13.10/tests/test_shell_semantic_class.py +0 -243
  229. controlzero-1.13.10/tests/test_source_labels.py +0 -95
  230. controlzero-1.13.10/tests/test_sql_semantic_class.py +0 -212
  231. controlzero-1.13.10/tests/test_synthetic_policy_id_t79.py +0 -324
  232. controlzero-1.13.10/tests/test_t103_precedence.py +0 -164
  233. controlzero-1.13.10/tests/test_t104_cache_gc.py +0 -174
  234. controlzero-1.13.10/tests/test_t108_local_override_audit.py +0 -161
  235. controlzero-1.13.10/tests/test_t96_single_audit_log.py +0 -125
  236. controlzero-1.13.10/tests/test_t99_install_prefetch_bundle.py +0 -182
  237. controlzero-1.13.10/tests/test_tamper.py +0 -203
  238. controlzero-1.13.10/tests/test_tamper_behavior.py +0 -163
  239. controlzero-1.13.10/tests/test_tamper_hook.py +0 -307
  240. controlzero-1.13.10/tests/test_telemetry_consent.py +0 -90
  241. controlzero-1.13.10/tests/test_tls_ca_bundle.py +0 -112
  242. controlzero-1.13.10/tests/test_tracecontext.py +0 -89
  243. controlzero-1.13.10/tests/test_unsafe_int_boundary.py +0 -66
  244. controlzero-1.13.10/tests/test_update_cmd.py +0 -77
  245. controlzero-1.13.10/tests/test_upgrade_nudge.py +0 -54
  246. controlzero-1.13.10/tests/test_version_aware_fail_closed.py +0 -212
  247. controlzero-1.13.10/tools/cz-kiro-adapter +0 -31
  248. {controlzero-1.13.10 → controlzero-1.13.12}/LICENSE +0 -0
  249. {controlzero-1.13.10 → controlzero-1.13.12}/README.md +0 -0
  250. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/__main__.py +0 -0
  251. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/__init__.py +0 -0
  252. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/_messages.py +0 -0
  253. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/action_aliases.py +0 -0
  254. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/bundle.py +0 -0
  255. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/condition_ops.py +0 -0
  256. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credential_hook.py +0 -0
  257. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credential_scanner.py +0 -0
  258. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credentials_data/__init__.py +0 -0
  259. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
  260. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/dlp_remediation.py +0 -0
  261. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/io_capture_producer.py +0 -0
  262. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/source_labels.py +0 -0
  263. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/tool_extractors.json +0 -0
  264. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/types.py +0 -0
  265. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/audit_local.py +0 -0
  266. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/canonical.py +0 -0
  267. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/__init__.py +0 -0
  268. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/__main__.py +0 -0
  269. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/_secrets.py +0 -0
  270. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/agent_config.py +0 -0
  271. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/console.py +0 -0
  272. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/exec_cmd.py +0 -0
  273. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hook_entry.py +0 -0
  274. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/__init__.py +0 -0
  275. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/cursor.py +0 -0
  276. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/gemini_cli.py +0 -0
  277. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/unknown.py +0 -0
  278. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/kiro_adapter.py +0 -0
  279. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/maturity.py +0 -0
  280. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/migrate.py +0 -0
  281. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/output.py +0 -0
  282. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/spool_cmd.py +0 -0
  283. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/telemetry_consent.py +0 -0
  284. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/antigravity/hooks.json +0 -0
  285. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/antigravity.yaml +0 -0
  286. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/autogen.yaml +0 -0
  287. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/claude-code.yaml +0 -0
  288. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/codex-cli.yaml +0 -0
  289. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/cost-cap.yaml +0 -0
  290. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/crewai.yaml +0 -0
  291. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/cursor.yaml +0 -0
  292. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/gemini-cli.yaml +0 -0
  293. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/generic.yaml +0 -0
  294. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
  295. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -0
  296. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -0
  297. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/langchain.yaml +0 -0
  298. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/mcp.yaml +0 -0
  299. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/rag.yaml +0 -0
  300. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/update_cmd.py +0 -0
  301. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/device.py +0 -0
  302. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/enrollment.py +0 -0
  303. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/error_codes.py +0 -0
  304. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/error_codes.yaml +0 -0
  305. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/errors.py +0 -0
  306. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/__init__.py +0 -0
  307. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/grant_protocol.py +0 -0
  308. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/mock.py +0 -0
  309. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/pending_approval.py +0 -0
  310. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/secret_leak_guard.py +0 -0
  311. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/status.py +0 -0
  312. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hooks/__init__.py +0 -0
  313. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hooks/tool_output_handler.py +0 -0
  314. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hosted_policy.py +0 -0
  315. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/__init__.py +0 -0
  316. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/braintrust.py +0 -0
  317. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/__init__.py +0 -0
  318. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google_adk/__init__.py +0 -0
  319. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/__init__.py +0 -0
  320. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/callbacks.py +0 -0
  321. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/modern.py +0 -0
  322. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langfuse.py +0 -0
  323. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/layout_migration.py +0 -0
  324. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/policy_loader.py +0 -0
  325. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/__init__.py +0 -0
  326. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/reference.py +0 -0
  327. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/resolver.py +0 -0
  328. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/secretstr.py +0 -0
  329. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/__init__.py +0 -0
  330. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_compress.py +0 -0
  331. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_constants.py +0 -0
  332. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_crc32c.py +0 -0
  333. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_crypto.py +0 -0
  334. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_frame.py +0 -0
  335. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_keyring.py +0 -0
  336. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_metrics.py +0 -0
  337. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_spool.py +0 -0
  338. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_state.py +0 -0
  339. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_uploader.py +0 -0
  340. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/cz-audit-v1.dict +0 -0
  341. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/tamper.py +0 -0
  342. {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/tracecontext.py +0 -0
@@ -141,8 +141,21 @@ site/
141
141
 
142
142
  # Rust
143
143
  target/
144
- Cargo.lock
145
144
  *.rlib
145
+ # Cargo.lock is COMMITTED, deliberately. Cargo's guidance is to commit the
146
+ # lockfile for binaries and applications and omit it only for libraries
147
+ # published to a registry. Nothing here is published to crates.io: every
148
+ # package in this repo is either a cdylib/staticlib compiled into the
149
+ # Python/Node SDKs across the FFI boundary (crates/*) or a shipped binary
150
+ # (services/shadow-ai-scout). Both are applications for lockfile purposes.
151
+ #
152
+ # It was ignored until 2026-08-14, which meant every CI run re-resolved
153
+ # dependencies from scratch. A third party publishing a new version could,
154
+ # and did, break main with no commit to this repo: icu_* 2.3.0 shipped with
155
+ # MSRV 1.88 and `cargo clippy` on our pinned 1.86.0 started failing on a
156
+ # tree nobody had touched. It also meant the artifact that was tested was
157
+ # not provably the artifact that shipped, so a dependency audit of one said
158
+ # nothing about the other.
146
159
 
147
160
  # Python virtual environments
148
161
  venv/
@@ -1,5 +1,390 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.13.12 (2026-08-16): your audit trail now states whether the HOST enforced, and deny rules that never fired now fire
6
+
7
+ Two things in this release change what a customer can conclude from what we
8
+ ship. Read them before the rest.
9
+
10
+ **1. The audit row used to claim a block the host never performs (#2113, #2114,
11
+ #2236).** The row is the artefact an auditor reads to decide whether a customer
12
+ was protected. Until this release, every decision row that REACHED the host-event
13
+ branch came out `enforcement_outcome: enforced`,
14
+ `enforcement_reason: host_enforced`, `enforced: True` -- and on the observe-only
15
+ surfaces that was false.
16
+
17
+ Stated precisely, because the scope matters: this was never all rows. The
18
+ pre-fix `resolve_enforcement` returned earlier and correctly for a non-blocking
19
+ effect (`not_applicable / effect_not_blocking`), a post-execution event
20
+ (`not_enforced / post_execution_event`) and an unknown tier
21
+ (`indeterminate / unknown_tier`), none of which depend on the declaration. The
22
+ rows that were wrong are the ones those guards let through: a blocking effect,
23
+ on a known tier, at a pre-execution event -- which is exactly the population an
24
+ auditor cares about, because it is the set where a block was supposedly imposed.
25
+
26
+ #2203 taught `resolve_enforcement` to consult each adapter's own
27
+ `CoverageDecl`, then wired it into two of its three consumers: the resolver and
28
+ the deny message. The third -- the audit row -- was missed. `client.py` had been
29
+ reading `raw_event` / `deciding_events` / `observe_events` out of
30
+ `context["cz_enforcement_ctx"]` since #2203, but nothing ever put them there.
31
+ Every row was therefore resolved against an empty
32
+ `EventDeclaration(raw="", deciding=(), observe=())`, `declares_anything` was
33
+ False on every row ever written, so the two declaration-dependent branches --
34
+ `not_enforced / event_declared_observe` and
35
+ `indeterminate / event_not_declared_deciding` -- could not fire at all, and any
36
+ row reaching them fell through to the affirmative default instead.
37
+
38
+ Driven on pristine `origin/main`, `kiro_cli` + `userPromptSubmit`:
39
+
40
+ enforcement_outcome : enforced
41
+ enforcement_reason : host_enforced
42
+ enforced : True
43
+
44
+ `KiroCLIAdapter` declares that exact event OBSERVE-ONLY, and kiro-cli 2.16.2 was
45
+ driven answering the prompt after BOTH an exit-2 hook and a structured stdout
46
+ deny. The same row now reads:
47
+
48
+ enforcement_outcome : not_enforced
49
+ enforcement_reason : event_declared_observe_only
50
+ enforced : False
51
+
52
+ **What this means for you.** Rows already in your audit store that say
53
+ `enforced / host_enforced` for a prompt-submission event on an observe-only host
54
+ are not evidence the call was blocked. Nothing about the host's behaviour
55
+ changed in this release -- kiro-cli did not start or stop enforcing anything.
56
+ What changed is that the record now distinguishes "the host imposed this" from
57
+ "we asked and cannot establish that it was imposed". If you query or alert on
58
+ `enforced`, expect observe-only surfaces to move from True to False, and treat
59
+ that as the field becoming correct rather than as a regression.
60
+
61
+ **2. A deny rule naming a host tool name was inert on the shipped hook path
62
+ (#2245).** `deny: Read` was accepted by `validate`, previewed as DENY by
63
+ `test`, and silently ALLOWED by the real `controlzero hook-check`. Three
64
+ surfaces told the customer they were protected while the enforcement path
65
+ ignored the rule, and the audit row for the allowed call was byte-identical to
66
+ one written with no rule authored at all. Measured across the shipped security
67
+ template -- 11 template tool names -- the base enforced 4 of 11 bare names; this
68
+ release enforces 11 of 11, and `controlzero test` now agrees with the hook
69
+ because both build the subject through the hook's own factory.
70
+
71
+ ### Changed: what an audit row asserts about enforcement (#2113, #2114, #2236)
72
+
73
+ - **Enforcement is now bound to the host's own event declaration.** The
74
+ `cz_enforcement_ctx` keys the audit path has always read are now actually
75
+ populated, so the per-host branch can fire.
76
+
77
+ - **An absent enforcement-context key is silence, not an empty declaration
78
+ (#2218).** "This host declared nothing" and "this host declared an explicitly
79
+ empty deciding set" are different facts and are now encoded in the TYPE rather
80
+ than in a flag, so the two cannot collapse into one another.
81
+
82
+ - **`render()` now receives the host event.** It previously did not, so the WIRE
83
+ posture was resolved against an empty `EventDeclaration` while the audit row
84
+ used the real one. A single `hook-check` run could put
85
+ `indeterminate / event_not_declared_deciding` on the wire and write
86
+ `enforced / host_enforced` to the row -- two answers to "was the customer
87
+ protected?" out of one process, on 7 of the 9 shipped adapters. The method's
88
+ own docstring said this could not happen.
89
+
90
+ - **`CursorCLIAdapter.render` no longer drops the posture.** Its override could
91
+ not accept the event pair and rebuilt its `RenderResult` without
92
+ `enforcement`, silently downgrading the ask-path posture to NOT_FINALIZED. A
93
+ control applied at one point on one path, hidden in a subclass override.
94
+
95
+ - **A contradictory declaration is now a named posture, INDETERMINATE.** An
96
+ adapter declaring one event both deciding and observe was previously resolved
97
+ by EVALUATION ORDER -- `is_declared_observe()` is tested first, so the observe
98
+ branch won by position over a contradiction nobody was told about. A
99
+ contradictory declaration is not evidence the host let the call through, and
100
+ `_enforcement_notice` now carries a specific line for it that says it is our
101
+ defect to fix, instead of inheriting the generic "could not confirm" that
102
+ reads identically to the host merely being unobservable. No shipped adapter is
103
+ currently self-contradictory, and that is asserted in both directions.
104
+
105
+ ### Fixed
106
+
107
+ - **A deny rule naming a HOST tool name was inert on the hook path (#2245).**
108
+ The originally-filed hypothesis -- a colon-less subject -- is REFUTED and
109
+ pinned as such: every matcher formats `tool:method` unconditionally, and
110
+ `Read:*` already matched the empty-method subject `Read:`. The real mechanism
111
+ is an alias-resolution asymmetry: the hook resolved the HOST name to its
112
+ canonical family name (`Read` -> `file_read`) and discarded the host spelling
113
+ before the gate ran, while the rule kept the host spelling. `Bash` survived
114
+ only because it is its own canonical name. The gate is now asked about every
115
+ name the call actually carried -- the host spelling AND its canonical family
116
+ name -- and NEVER about sibling aliases, because the alias map is many-to-one
117
+ and canonicalising the RULE would make `deny: Edit` also block `Write`. Both
118
+ directions are pinned. The widening ships behind a phase-1 monotone-closing
119
+ gate: closing flips ship, opening flips are suppressed and counted as
120
+ `match_semantics=new_suppressed`.
121
+
122
+ - **The #2245 phase gate could not see the context axis, and a deny became an
123
+ allow (#2320).** #2245 injects `subject_origin` into the decision context.
124
+ Rule conditions merge the whole context, so a rule can match on a key that did
125
+ not exist before -- and `_choose_match`'s monotone-closing gate enumerated the
126
+ ACTION axis only, so it could not observe a flip caused that way. Driven on
127
+ `main`:
128
+
129
+ base c99dba92e rc=2 DENY policy_id=old-deny (no match_semantics)
130
+ main 6dd972815 rc=0 ALLOW policy_id=origin-allow match_semantics=unchanged
131
+ fixed rc=2 DENY policy_id=old-deny match_semantics=new_suppressed
132
+
133
+ The audit stamped `unchanged` on the exact call whose effect flipped. That is
134
+ worse than an absent field: the record actively asserted the false thing. The
135
+ fix declares, per `SubjectOrigin`, which context keys pre-existed
136
+ (`PRE_WIDENING_CONTEXT_KEYS`, sourced from the pre-fix tree) and evaluates the
137
+ rule walk against both contexts. It is a positive set with import-time
138
+ exhaustiveness, so a new origin is unshippable until declared. `unchanged` is
139
+ now true BY CONSTRUCTION rather than by assumption, and `match_semantics`
140
+ gains `indeterminate` for "the pre-widening verdict could not be established".
141
+ The 11 x 3 action matrix is unchanged at 33/33, so #2245 loses nothing.
142
+
143
+ - **Kiro sub-agent SPAWNS were not gated at `preToolUse` (#2331).**
144
+ `_KIRO_V3_PRE_TOOL_MATCHERS` wired `execute_bash` / `fs_write` / `fs_read` /
145
+ `use_aws` only, so an operator who wrote `deny: use_subagent` had a rule that
146
+ could not fire. Driven on kiro-cli 2.18.1: with the old matcher list
147
+ `use_subagent` reached `preToolUse` ZERO times and the sub-agent completed;
148
+ with the spawn matchers added it reaches `preToolUse` and exit 2 blocks it
149
+ ("PreToolHook blocked the tool execution").
150
+
151
+ **This is admission control in SCOPE, not enforcement in strength**, and the
152
+ limit was observed rather than theorised: in the same run the model responded
153
+ to the blocked spawn by doing the identical work through its own
154
+ `execute_bash`. The Kiro coverage tier stays `partial`.
155
+
156
+ **`use_subagent` is the only spawn tool established as real on kiro-cli
157
+ 2.18.1.** The matcher tuple carries five names. Do not read that as five
158
+ gateable spawn surfaces:
159
+
160
+ - `delegate` -- INERT. Experimental and disabled; the binary says so verbatim
161
+ ("Delegate tool is experimental and not enabled. Use /experiment to enable
162
+ it").
163
+ - `agent_crew` -- INERT. Present in the binary's alias table but not exposed.
164
+ - `spawn_session` -- INERT, and structurally so: it is not a tool name at all
165
+ but a `command` enum value inside the `session_management` tool, which makes
166
+ it wrong as a MATCHER rather than merely unused.
167
+ - `session_management` -- **status NOT ESTABLISHED.** The driven enumeration
168
+ accounted for the three names above; it recorded nothing either way about
169
+ this one, and the shipped Kiro template still carries commented guidance
170
+ naming it. It is neither claimed as coverage nor asserted to be inert here,
171
+ because neither has been driven. Treat it as unproven until it is.
172
+
173
+ Driven: the agent's own tool enumeration returned 13 tools and none of the
174
+ three named INERT above, and declaring all four explicitly surfaced none of
175
+ them. They are carried so the matcher is already in place if a future kiro-cli
176
+ exposes them. Today only `use_subagent` is shown to gate anything, and this
177
+ release claims nothing more.
178
+
179
+ - **`controlzero verify --prove` certified enforcement from an exit code we
180
+ produce (#2305).** Driven on a box where all seven host binaries were ABSENT,
181
+ with the hook repointed at a script whose entire body is
182
+ `cat > /dev/null; exit 2`, it reported `[GREEN] claude_code -- effective gate
183
+ holds + real deny blocked` and `status: ENFORCING`, exit 0. The same shim
184
+ exiting 0 produced RED, which is what establishes that no host was in the loop
185
+ at any point. Three states that must never collapse -- ENFORCING /
186
+ NOT_ENFORCING / UNVERIFIABLE -- were two, and the collapse always resolved
187
+ optimistically. `ENFORCING` is now reachable from ONE point in one resolver,
188
+ and the gate there is deliberately NOT "some host evidence exists" -- that
189
+ weaker gate shipped in the first draft of this module and was refuted, because
190
+ "a config file has a foreign key" unlocked the same sentence as an executed
191
+ binary. Evidence licenses NAMED claims, and an affirmative requires the
192
+ specific `HOST_PRESENT` claim, plus `effective_state_ok` and `probe_blocked`
193
+ both true -- so the verdict object cannot be constructed while asserting
194
+ something it does not hold. `HostEvidence` is produced by collectors that read
195
+ an artifact the HOST emitted, and evidence collected from one adapter cannot
196
+ certify another. Stated exactly rather than absolutely: this closes the
197
+ accidental paths, not a determined one -- the private sentinel can be imported
198
+ deliberately, so this is a chokepoint, not a proof of unforgeability. Also
199
+ fixed in the same pass: probe
200
+ errors collapsed into "did not block"; `doctor` rendered a bare all-clear
201
+ having examined nothing and swallowed reader exceptions; antigravity presence
202
+ tested a path the installer never writes; and install self-checks claimed a
203
+ host blocked something.
204
+
205
+ - **Cross-SDK prompt vocabulary is now pinned (#2331).** Every test asserted one
206
+ SDK against literals it restated itself, so a rename on either side left both
207
+ suites green -- the #2071 shape that let #2108 be closed while still live in
208
+ Python. The Node suite now EXTRACTS the vocabulary from Python source at run
209
+ time and drives Node against it, asserting an exact-set IFF over a bounded
210
+ pool rather than one-way containment, with a match-count and uniqueness
211
+ assertion on every extraction so a moved symbol fails loudly instead of
212
+ passing vacuously.
213
+
214
+ ### Changed (AUDIT SCHEMA BREAK: the per-finding `masked` boolean is gone)
215
+
216
+ - **A DLP finding's `masked: True` is replaced by `mask_state` (#2096).**
217
+ `masked` was stamped from the RULE'S ACTION (`m.action == "mask"`), which is
218
+ what the rule says to DO -- never evidence that anything was done. A call
219
+ DENIED on `mask_unsupported_on_host` redacted nothing and never ran, yet
220
+ produced a findings array byte-identical to one for a call that really was
221
+ redacted. `args_hash` is taken over the ORIGINAL arguments on every path, so
222
+ nothing else in the row discriminated either. Established by driving
223
+ `Client.guard` against pristine `origin/main`: four deny scenarios and one
224
+ allow scenario all emitted `'masked': True`.
225
+
226
+ A boolean cannot carry this truth, so it is now a closed vocabulary, matching
227
+ the Node SDK exactly and asserted against the shared cross-SDK manifest
228
+ `tests/parity/dlp_control_outcomes.json`:
229
+
230
+ | `mask_state` | meaning |
231
+ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
232
+ | `applied` | the caller acknowledged consuming the redacted args via `decision.apply_masked_args()`. The ONLY value that asserts that acknowledgement. |
233
+ | `declared_not_confirmed` | the SDK redacted into `decision.masked_args` and the caller declared `supports_arg_rewrite=True`; whether it forwards them is not observable. |
234
+ | `not_applied` | a mask rule matched and NOTHING was redacted (the call was denied instead). |
235
+ | `not_applicable` | the rule's action is `detect`/`block`, or this is a post-execution output observation or a third-party export scan. |
236
+
237
+ Treat any unrecognised value as NOT `applied`.
238
+
239
+ **What `applied` does NOT mean.** It is one link short of "the tool received
240
+ redacted bytes", deliberately. `apply_masked_args()` emits the record and then
241
+ returns the args, so a caller can acknowledge and still forward the originals.
242
+ No value in this vocabulary asserts delivery to the tool, because the SDK
243
+ cannot observe what the caller does next.
244
+
245
+ - **`DLPScanner.get_findings_for_audit(matches)` now REQUIRES a second
246
+ argument**, `mask_state`, and rejects both `"applied"` and any value outside
247
+ the vocabulary with a `TypeError` before a single finding is built. The
248
+ posture for an unknown value is REJECT, not downgrade and not pass-through.
249
+
250
+ - **Every decision audit row now carries `dlp_mask_state`**, including rows with
251
+ no DLP findings at all (`not_applicable`). Absence is not a state: "no mask
252
+ was in play" and "a mask fired and was not honoured" are separate, queryable
253
+ facts.
254
+
255
+ - **`decision.masked_args` is deep-cloned and frozen** before `guard()` returns.
256
+ An in-place mutation through the ordinary mapping and sequence operations
257
+ raises the new, exported `MaskedArgsFrozenError` (a `TypeError` subclass) at
258
+ the line attempting it, rather than succeeding and leaving the
259
+ `dlp_mask_applied` record describing bytes the caller never forwarded. Take
260
+ `dict(decision.masked_args)` for a mutable copy. Chosen over a hash comparison
261
+ deliberately: a comparison detects the tamper only if someone remembers to
262
+ call it.
263
+
264
+ **The guarantee is BOUNDED, and the bound is stated rather than implied.**
265
+ Python has no true deep freeze. The protection covers the JSON-shaped tree,
266
+ which is the only shape a redaction is produced in; it is not a claim that
267
+ every in-place mutation is impossible. Specifically: a `set` / `frozenset`
268
+ leaf and a custom mutable object leaf are returned BY IDENTITY, so mutating
269
+ one the caller still holds does change the tree the acknowledgement returns.
270
+ Both are left alone deliberately -- neither is JSON-representable, so neither
271
+ can survive the RFC 8785 canonicaliser or any host envelope, meaning a mask
272
+ decision cannot legitimately carry one.
273
+
274
+ ### Added
275
+
276
+ - **`decision.apply_masked_args()`** -- the single producer of
277
+ `mask_state: "applied"`. Present only on a decision that carries a redaction;
278
+ `None` everywhere else, so "there is nothing to acknowledge" and "the caller
279
+ declined to acknowledge" stay distinguishable. Calling it emits a
280
+ `dlp_mask_applied` audit event carrying `args_hash` + `masked_args_hash` (the
281
+ pair an auditor joins on to see that the bytes actually changed) and returns
282
+ the frozen redacted arguments.
283
+
284
+ - **`controlzero.MaskedArgsFrozenError`** is exported from the package root.
285
+
286
+ ### Known limitations, stated plainly
287
+
288
+ - The Python SDK's DLP decision fields -- `dlp_findings`, `dlp_status`,
289
+ `dlp_masked` and now `dlp_mask_state` -- are written to the LOCAL audit log
290
+ only. `audit_remote._build_wire_entry` is an explicit allowlist and forwards
291
+ none of them, so none of this is visible in a hosted audit trail today. That
292
+ is pre-existing behaviour and unchanged here; #2096 corrects what the record
293
+ SAYS, not where it is shipped.
294
+
295
+ - The Go SDK's `GetFindingsForAudit` gained the same closed vocabulary, but it
296
+ still has NO first-party caller: the Go `Guard` attaches no findings to any
297
+ decision or audit row, so no Go audit row carries a `mask_state`. That is a
298
+ declared gap, pinned by a test that fails the day anyone wires it up. The Go
299
+ SDK also left the build on 2026-08-14 (#2291) and is not published from this
300
+ release.
301
+
302
+ - **Kiro sub-agent coverage is CONFIG-SCOPED, not absent, and one path stays
303
+ ungated.** It would be wrong in both directions to say sub-agents were
304
+ previously ungoverned. Driven on kiro-cli 2.18.1, 19 legs, each with a nonce
305
+ side-effect proving the call actually ran: a sub-agent that runs an agent
306
+ config carrying our hooks DOES fire `preToolUse` and `postToolUse` inside the
307
+ sub-agent (leg G), and a child hook exiting 2 blocks the sub-agent's own tool
308
+ call (leg J). Hooks resolve from the SUB-AGENT'S OWN config and are not
309
+ inherited from the parent.
310
+
311
+ The genuine gap is leg K: a sub-agent spawned WITHOUT an explicit `agent_name`
312
+ runs an implicit config that carries no hooks, and setting
313
+ `chat.defaultAgent` to a hooked agent does not help -- leg K establishes that
314
+ the implicit agent does not resolve to it, so the path cannot be reached by
315
+ writing hooks anywhere on disk. That is kiro **#7755**, which is OPEN with no
316
+ maintainer response and no assignee. Do not cite #7671 for this: it is CLOSED
317
+ AS COMPLETED, was filed against 0.11.130, and the behaviour it describes
318
+ ("hooks will not trigger in subagents") is contradicted by the 2.18.1 binary.
319
+ The vendor documentation still carries that sentence; for a claim about what
320
+ ships today, the binary outranks the doc.
321
+
322
+ Gating the SPAWN is what makes `deny: use_subagent` reachable, and it narrows
323
+ the ungoverned surface rather than eliminating it.
324
+
325
+ - **Scope of every driven Kiro claim in this release: kiro-cli 2.18.1 on
326
+ macOS**, `chat --no-interactive`, `chat.enableSubagent=true`. It is NOT a
327
+ claim about the whole `>=2.1.0,<3.0.0` range the adapter declares.
328
+
329
+ - **Kiro matches by ALIAS FAMILY, not literal matcher name.** Observed on
330
+ 2.18.1 in two independent pairs: an `execute_bash` matcher fired on
331
+ `tool_name` `shell`, and `fs_write` fired on `write`. Each matcher must stay
332
+ a single tool-name token -- a regex-style `shell|execute_bash` alternation
333
+ matches a tool LITERALLY named that, which is to say nothing at all, and
334
+ would silently disable enforcement.
335
+
336
+ ## 1.13.11 (2026-08-10): kiro_cli prompts were never actually scanned
337
+
338
+ 1.13.10 said "Prompt submission is now a scanned surface on the hosts that
339
+ expose one". For `kiro_cli` that was not true, and this release makes it true.
340
+
341
+ ### Fixed
342
+
343
+ - **`kiro_cli` returned an affirmative ALLOW on a prompt it never read (#2108).**
344
+ A `userPromptSubmit` payload carries no `tool_name`, so it took the
345
+ missing-tool_name branch: under `--strict` (what the installer writes) it was
346
+ REFUSED without being scanned, and without `--strict` it was ALLOWED without
347
+ being scanned. Driven against the real kiro-cli 2.16.2 payload, all three of
348
+ a secret-bearing prompt, a clean prompt and an empty prompt produced
349
+ BYTE-IDENTICAL output -- the prompt text was not an input to the decision at
350
+ all -- and NO audit row was written, so the call was unattributable.
351
+ The prompt is now lifted into the `prompt` pseudo-tool and rides the existing
352
+ DLP path. The three cases now differ, and every one is audited.
353
+
354
+ - **An ambient `CLAUDECODE` environment variable stole the routing.** A
355
+ camelCase kiro payload in a shell where `CLAUDECODE=1` was claimed by the
356
+ Claude Code adapter, and the secret never reached the scanner. Anyone running
357
+ kiro-cli inside a Claude Code session had no prompt DLP at all.
358
+
359
+ - **Padding a prompt past the 1 MiB scan cap bypassed DLP entirely.** The
360
+ bounded UTF-8 prefix is now scanned, and a clean prefix is still a deny with
361
+ `skipped/payload_over_size_cap` -- never reported as `clean`.
362
+
363
+ - **`--strict` failed open on a non-dict payload.** It now refuses, like the
364
+ other four unusable-payload branches.
365
+
366
+ - **`prompt` was not a canonical tool**, so authoring `deny: prompt:*` -- the
367
+ exact rule this feature exists for -- printed "not a known canonical action".
368
+
369
+ - **kiro-cli v3 PascalCase prompt events** were uncovered; normalisation is now
370
+ shared by both Kiro adapters so the result does not depend on which adapter
371
+ won the routing race.
372
+
373
+ ### Known limitations, stated plainly
374
+
375
+ - **kiro-cli does not enforce this event.** Driven on 2.16.2: a hook exiting 2
376
+ on `userPromptSubmit` is logged as failed and the prompt reaches the model
377
+ anyway; a structured stdout deny is also ignored. Control Zero SCANS and
378
+ RECORDS the prompt on this host. It does not block it. Nothing in this
379
+ release claims otherwise.
380
+
381
+ - **The raw-prompt marker does not reach the hosted dashboard.** It reaches the
382
+ DLP scanner and the LOCAL audit record; surfacing it remotely needs a backend
383
+ field and is not done here.
384
+
385
+ - **The Node SDK's equivalent fix is still unreachable as installed (#2208).**
386
+ This release fixes Python only.
387
+
3
388
  ## 1.13.10 (2026-07-30): DLP now covers prompts and tool output, not just tool arguments
4
389
 
5
390
  Until this release, Control Zero scanned the ARGUMENTS of a tool call and
@@ -20,6 +405,15 @@ routes that looked identical to a working setup. All three are closed here.
20
405
  keeping the secret it had just detected. Now gated on a named set of
21
406
  value-bearing categories, so a category added later cannot reach cleartext.
22
407
 
408
+ - **Control Zero's own audit egress was shipping your raw tool input and
409
+ output (#1967 path 5).** `input_payload` / `output_payload` carried customer
410
+ content verbatim to our backend, and redaction was deferred to the backend --
411
+ i.e. applied after the bytes had already left your machine. They are now
412
+ scanned and redacted BEFORE the wire, every sensitive span rather than only
413
+ `mask`-action rules, and the payload is DROPPED rather than forwarded if the
414
+ scanner cannot run. `io_dlp_scan_status` is always set, so "not scanned" stays
415
+ distinguishable from "scanned and clean".
416
+
23
417
  - **Spooled audit records were replayed without ever being scanned (#1967).**
24
418
  Replay validated JSON shape, tool name and decision -- never payload content
25
419
  -- then transmitted the stored bytes as-is, possibly days later on a
@@ -62,9 +456,9 @@ the loader does not consume.
62
456
 
63
457
  ### Not in this release
64
458
 
65
- Tool output is observed, not blocked. Control Zero's own audit egress (the path
66
- where we ship your data to our backend) is still unscanned; that is tracked and
67
- is the last uncovered egress path.
459
+ Tool output is observed, not blocked. That is the intended behaviour, not a
460
+ limitation awaiting a fix: an agent reading a file is normal work, and blocking
461
+ it would make Control Zero unusable.
68
462
 
69
463
  ## 1.13.9 (2026-07-28): Kiro IDE hooks execute on 1.0.x and stay in the correct project
70
464
 
@@ -549,7 +943,7 @@ coverage declaration intentionally remains observe-only.
549
943
 
550
944
  ### Fixed
551
945
 
552
- - **kiro-cli Windows / non-UTF-8 install crash (Ken Lee report).** `controlzero
946
+ - **kiro-cli Windows / non-UTF-8 install crash (customer report).** `controlzero
553
947
  kiro init` captured kiro-cli's output in text mode using the OS locale codec, so
554
948
  kiro-cli's UTF-8 output crashed the install's subprocess reader thread on
555
949
  non-UTF-8 Windows locales (Korean CP949, Japanese SJIS, Chinese GBK). That crash
@@ -1185,7 +1579,7 @@ changes do not perform.
1185
1579
 
1186
1580
  ## 1.9.3 -- 2026-06-16 (posture release: empty-bundle OBSERVE + self-explaining no-rule-match deny, epic gh#1247)
1187
1581
 
1188
- The consolidated **posture release** for epic gh#1247 (customer Bryan).
1582
+ The consolidated **posture release** for epic gh#1247 (a customer).
1189
1583
  Three customer-visible behaviors land together so the engine's "what
1190
1584
  happens when a tool isn't covered by a rule" story is coherent across
1191
1585
  both SDKs and the backend:
@@ -1200,7 +1594,7 @@ both SDKs and the backend:
1200
1594
 
1201
1595
  - **Hosted no-rule-match deny is now self-explaining (folds gh#1257).** A
1202
1596
  non-empty bundle with `default_action=deny` and zero rule matches (the
1203
- exact shape of Bryan's "Db read only" allow-list, which excludes
1597
+ exact shape of that customer's "Db read only" allow-list, which excludes
1204
1598
  `bash`) used to deny with the bare, bug-looking message
1205
1599
  `No matching policy rule (fail-closed default)` -- no path out. The
1206
1600
  deny reason now NAMES the unmatched action (e.g. `bash:find`) and the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: controlzero
3
- Version: 1.13.10
3
+ Version: 1.13.12
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
@@ -19,7 +19,7 @@ for framework examples (LangChain, LangGraph, OpenAI Agents, Anthropic, CrewAI,
19
19
  AutoGen, Pydantic AI, MCP servers, and more).
20
20
  """
21
21
 
22
- from controlzero.client import Client
22
+ from controlzero.client import Client, MaskedArgsFrozenError
23
23
  from controlzero.errors import (
24
24
  PolicyDecision,
25
25
  PolicyDeniedError,
@@ -41,10 +41,11 @@ 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.13.10"
44
+ __version__ = "1.13.12"
45
45
 
46
46
  __all__ = [
47
47
  "Client",
48
+ "MaskedArgsFrozenError",
48
49
  "PendingApproval",
49
50
  "SecretRef",
50
51
  "SecretStr",