plugin-scanner 2.0.64__tar.gz → 2.0.66__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 (320) hide show
  1. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/claude_code.py +1 -3
  5. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/commands.py +99 -3
  6. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/connect_flow.py +10 -10
  7. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/render.py +18 -0
  8. plugin_scanner-2.0.66/src/codex_plugin_scanner/guard/edge_events.py +88 -0
  9. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/__init__.py +2 -0
  10. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/runner.py +98 -0
  11. plugin_scanner-2.0.66/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +74 -0
  12. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store.py +122 -0
  13. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/version.py +1 -1
  14. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_approvals.py +86 -0
  15. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_claude_adapter.py +5 -15
  16. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_cli.py +36 -24
  17. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_connect_flow.py +9 -9
  18. plugin_scanner-2.0.66/tests/test_guard_event_schema_v1.py +260 -0
  19. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_runtime.py +72 -21
  20. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_surface_server.py +1 -9
  21. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.clusterfuzzlite/Dockerfile +0 -0
  22. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.clusterfuzzlite/build.sh +0 -0
  23. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.clusterfuzzlite/project.yaml +0 -0
  24. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  25. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.dockerignore +0 -0
  26. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/CODEOWNERS +0 -0
  27. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  28. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  29. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  30. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/dependabot.yml +0 -0
  31. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/ci.yml +0 -0
  32. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/codeql.yml +0 -0
  33. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/dependabot-uv-lock.yml +0 -0
  34. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/fuzz.yml +0 -0
  35. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/harness-smoke.yml +0 -0
  36. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/publish.yml +0 -0
  37. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.github/workflows/scorecard.yml +0 -0
  38. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.gitignore +0 -0
  39. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/.pre-commit-hooks.yaml +0 -0
  40. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/CONTRIBUTING.md +0 -0
  41. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/Dockerfile +0 -0
  42. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/LICENSE +0 -0
  43. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/README.md +0 -0
  44. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/SECURITY.md +0 -0
  45. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/index.html +0 -0
  46. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/package.json +0 -0
  47. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/pnpm-lock.yaml +0 -0
  48. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/public/brand/Logo_Whole.png +0 -0
  49. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/app.tsx +0 -0
  50. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/approval-center-layout.tsx +0 -0
  51. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/approval-center-primitives.tsx +0 -0
  52. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/approval-center-utils.ts +0 -0
  53. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/fleet-workspace.tsx +0 -0
  54. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/guard-api.ts +0 -0
  55. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/guard-demo.ts +0 -0
  56. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/guard-types.ts +0 -0
  57. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/main.tsx +0 -0
  58. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/receipts-workspace.tsx +0 -0
  59. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/runtime-overview.tsx +0 -0
  60. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/styles.css +0 -0
  61. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/src/vite-env.d.ts +0 -0
  62. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/tsconfig.json +0 -0
  63. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/dashboard/vite.config.ts +0 -0
  64. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docker-requirements.txt +0 -0
  65. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/guard/approval-audit.md +0 -0
  66. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/guard/architecture.md +0 -0
  67. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/guard/get-started.md +0 -0
  68. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/guard/harness-support.md +0 -0
  69. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/guard/local-vs-cloud.md +0 -0
  70. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/guard/testing-matrix.md +0 -0
  71. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/trust/mcp-trust-draft.md +0 -0
  72. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/trust/plugin-trust-draft.md +0 -0
  73. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/docs/trust/skill-trust-local.md +0 -0
  74. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/fuzzers/manifest_fuzzer.py +0 -0
  75. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/requirements.txt +0 -0
  76. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/schemas/plugin-quality.v1.json +0 -0
  77. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/schemas/scan-result.v1.json +0 -0
  78. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/schemas/verify-result.v1.json +0 -0
  79. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/__init__.py +0 -0
  80. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/action_runner.py +0 -0
  81. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  82. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  83. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  84. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/claude.py +0 -0
  85. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  86. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  87. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  88. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  89. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  90. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  91. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  92. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  93. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  94. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/security.py +0 -0
  95. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  96. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/cli.py +0 -0
  97. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/cli_ui.py +0 -0
  98. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/config.py +0 -0
  99. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  100. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  101. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  102. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  103. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  104. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  105. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  106. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  107. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  108. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/github_reporting.py +0 -0
  109. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  110. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  111. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  112. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  113. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  114. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  115. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  116. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  117. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  118. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  119. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  120. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  121. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  122. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  123. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  124. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  125. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  126. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  127. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  128. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  129. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  130. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  131. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  132. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/config.py +0 -0
  133. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  134. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  135. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  136. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  137. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  138. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  139. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  140. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  141. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  142. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  143. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/incident.py +0 -0
  144. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  145. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  146. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/models.py +0 -0
  147. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  148. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  149. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/protect.py +0 -0
  150. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  151. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  152. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  153. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  154. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  155. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  156. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/risk.py +0 -0
  157. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  158. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  159. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  160. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  161. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  162. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/shims.py +0 -0
  163. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  164. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  165. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/types.py +0 -0
  166. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  167. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  168. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  169. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  170. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  171. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/models.py +0 -0
  172. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/path_support.py +0 -0
  173. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/policy.py +0 -0
  174. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  175. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/repo_detect.py +0 -0
  176. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/reporting.py +0 -0
  177. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  178. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/registry.py +0 -0
  179. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/specs.py +0 -0
  180. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/scanner.py +0 -0
  181. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/submission.py +0 -0
  182. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/suppressions.py +0 -0
  183. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  184. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  185. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  186. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_models.py +0 -0
  187. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  188. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  189. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  190. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_specs.py +0 -0
  191. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/verification.py +0 -0
  192. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/__init__.py +0 -0
  193. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/conftest.py +0 -0
  194. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/__init__.py +0 -0
  195. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  196. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  197. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/secrets.js +0 -0
  198. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  199. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  200. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/README.md +0 -0
  201. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  202. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  203. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  204. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/code-quality-bad/evil.js +0 -0
  205. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/code-quality-bad/inject.js +0 -0
  206. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  207. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  208. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/README.md +0 -0
  209. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  210. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  211. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  212. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  213. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/.codexignore +0 -0
  214. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/LICENSE +0 -0
  215. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/README.md +0 -0
  216. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  217. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  218. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  219. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  220. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  221. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  222. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  223. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  224. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  225. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  226. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  227. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  228. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  229. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  230. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  231. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  232. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  233. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  234. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  235. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  236. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/mcp-canary-server.py +0 -0
  237. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  238. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  239. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/mit-license/LICENSE +0 -0
  240. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  241. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  242. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  243. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  244. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  245. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  246. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  247. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  249. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  250. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  251. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  252. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  253. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  254. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  255. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  256. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  257. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  258. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/LICENSE +0 -0
  259. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/README.md +0 -0
  260. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  261. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  262. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  263. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  264. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  265. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  266. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  267. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  268. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test-trust-scoring.py +0 -0
  269. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test-trust-specs.py +0 -0
  270. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_action_runner.py +0 -0
  271. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_best_practices.py +0 -0
  272. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_cisco_install_surfaces.py +0 -0
  273. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_cli.py +0 -0
  274. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_code_quality.py +0 -0
  275. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_config.py +0 -0
  276. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_coverage_remaining.py +0 -0
  277. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_ecosystems.py +0 -0
  278. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_edge_cases.py +0 -0
  279. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_final_coverage.py +0 -0
  280. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_bootstrap.py +0 -0
  281. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_capabilities.py +0 -0
  282. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_codex_e2e.py +0 -0
  283. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_codex_install.py +0 -0
  284. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_codex_proxy.py +0 -0
  285. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_config_paths.py +0 -0
  286. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_consumer_mode.py +0 -0
  287. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_copilot_adapter.py +0 -0
  288. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_copilot_proxy.py +0 -0
  289. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_daemon_manager.py +0 -0
  290. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_events.py +0 -0
  291. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_launch_env.py +0 -0
  292. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_opencode_proxy.py +0 -0
  293. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_product_flow.py +0 -0
  294. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_protect.py +0 -0
  295. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_render.py +0 -0
  296. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_risk.py +0 -0
  297. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_store_migrations.py +0 -0
  298. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_guard_verdicts.py +0 -0
  299. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_hermes_adapter.py +0 -0
  300. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_integration.py +0 -0
  301. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_lint_fixes.py +0 -0
  302. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_live_cisco_smoke.py +0 -0
  303. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_manifest.py +0 -0
  304. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_marketplace.py +0 -0
  305. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_mcp_security.py +0 -0
  306. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_operational_security.py +0 -0
  307. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_policy.py +0 -0
  308. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_quality_artifact.py +0 -0
  309. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_rule_registry.py +0 -0
  310. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_scanner.py +0 -0
  311. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_schema_contracts.py +0 -0
  312. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_security.py +0 -0
  313. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_security_ops.py +0 -0
  314. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_skill_security.py +0 -0
  315. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_submission.py +0 -0
  316. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_trust_scoring.py +0 -0
  317. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_trust_specs.py +0 -0
  318. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_verification.py +0 -0
  319. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/tests/test_versioning.py +0 -0
  320. {plugin_scanner-2.0.64 → plugin_scanner-2.0.66}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.64
3
+ Version: 2.0.66
4
4
  Summary: Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows.
5
5
  Project-URL: Homepage, https://github.com/hashgraph-online/ai-plugin-scanner
6
6
  Project-URL: Repository, https://github.com/hashgraph-online/ai-plugin-scanner
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.64"
7
+ version = "2.0.66"
8
8
  description = "Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hol-guard"
7
- version = "2.0.64"
7
+ version = "2.0.66"
8
8
  description = "Protect local AI harnesses with HOL Guard and run scanner checks for Codex, Claude, Cursor, Gemini, and OpenCode."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -23,7 +23,6 @@ CLAUDE_GUARD_POST_TOOL_MATCHER = f"{CLAUDE_GUARD_TOOL_MATCHER}|AskUserQuestion"
23
23
  CLAUDE_GUARD_NOTIFICATION_MATCHER = "permission_prompt"
24
24
  CLAUDE_GUARD_SESSION_START_MATCHERS = ("startup", "resume", "clear", "compact")
25
25
  CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS = 30
26
- CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS = 20
27
26
  CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS = 10
28
27
  CLAUDE_GUARD_SESSION_START_TIMEOUT_SECONDS = 10
29
28
  CLAUDE_GUARD_STOP_TIMEOUT_SECONDS = 10
@@ -47,7 +46,6 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
47
46
  ("PreToolUse", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
48
47
  ("PermissionRequest", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
49
48
  ("PostToolUse", CLAUDE_GUARD_POST_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
50
- ("UserPromptSubmit", None, CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS),
51
49
  ("Notification", CLAUDE_GUARD_NOTIFICATION_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
52
50
  ("Stop", None, CLAUDE_GUARD_STOP_TIMEOUT_SECONDS),
53
51
  ):
@@ -60,7 +58,7 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
60
58
 
61
59
 
62
60
  def _remove_unsupported_guard_hook_groups(hooks: dict[str, object]) -> None:
63
- for key in ("PermissionDenied",):
61
+ for key in ("PermissionDenied", "UserPromptSubmit"):
64
62
  entries = hooks.get(key)
65
63
  if not isinstance(entries, list):
66
64
  continue
@@ -311,8 +311,15 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
311
311
  policy_parser.add_argument("--json", action="store_true")
312
312
  policy_parser.set_defaults(policy_action=action)
313
313
 
314
- policies_parser = guard_subparsers.add_parser("policies", help="List stored Guard policy decisions")
314
+ policies_parser = guard_subparsers.add_parser("policies", help="List or clear stored Guard policy decisions")
315
+ policies_parser.add_argument("policies_command", nargs="?", choices=("clear",))
315
316
  policies_parser.add_argument("--harness")
317
+ policies_parser.add_argument("--source")
318
+ policies_parser.add_argument(
319
+ "--all",
320
+ action="store_true",
321
+ help="Clear decisions across every harness; cannot be combined with --harness",
322
+ )
316
323
  _add_guard_common_args(policies_parser)
317
324
  policies_parser.add_argument("--json", action="store_true")
318
325
 
@@ -775,6 +782,46 @@ def run_guard_command(
775
782
  return 0
776
783
 
777
784
  if args.guard_command == "policies":
785
+ if getattr(args, "policies_command", None) == "clear":
786
+ harness = getattr(args, "harness", None)
787
+ clear_all = bool(getattr(args, "all", False))
788
+ if clear_all and harness is not None:
789
+ _emit(
790
+ "policies",
791
+ {
792
+ "error": "Choose either --all or --harness <name> when clearing Guard policy decisions.",
793
+ "cleared": 0,
794
+ "harness": harness,
795
+ "source": getattr(args, "source", None),
796
+ },
797
+ getattr(args, "json", False),
798
+ )
799
+ return 2
800
+ if not clear_all and harness is None:
801
+ _emit(
802
+ "policies",
803
+ {
804
+ "error": "Choose --harness <name> or --all when clearing Guard policy decisions.",
805
+ "cleared": 0,
806
+ },
807
+ getattr(args, "json", False),
808
+ )
809
+ return 2
810
+ cleared = store.clear_policy_decisions(
811
+ None if clear_all else harness,
812
+ getattr(args, "source", None),
813
+ )
814
+ _emit(
815
+ "policies",
816
+ {
817
+ "generated_at": _now(),
818
+ "cleared": cleared,
819
+ "harness": None if clear_all else harness,
820
+ "source": getattr(args, "source", None),
821
+ },
822
+ getattr(args, "json", False),
823
+ )
824
+ return 0
778
825
  policy_items = store.list_policy_decisions(getattr(args, "harness", None))
779
826
  items = _filter_policy_items(policy_items, active_only=True)
780
827
  _emit("policies", {"generated_at": _now(), "items": items}, getattr(args, "json", False))
@@ -1233,10 +1280,13 @@ def run_guard_command(
1233
1280
  )
1234
1281
  return 0
1235
1282
  if _canonical_harness_name(args.harness) == "claude-code" and _hook_event_name(payload) == "Stop":
1236
- denied = _persist_claude_pending_permission_denials(store, payload)
1283
+ discarded = _discard_claude_pending_permissions(store, payload)
1237
1284
  store.add_event(
1238
1285
  "claude/turn_stop",
1239
- {"session_id": payload.get("session_id"), "saved_denials": denied},
1286
+ {
1287
+ "session_id": payload.get("session_id"),
1288
+ "discarded_pending_permissions": discarded,
1289
+ },
1240
1290
  _now(),
1241
1291
  )
1242
1292
  return 0
@@ -1371,6 +1421,14 @@ def run_guard_command(
1371
1421
  artifact=runtime_artifact,
1372
1422
  artifact_hash=runtime_artifact_hash,
1373
1423
  )
1424
+ if _should_allow_claude_user_prompt_submit_without_output(
1425
+ args,
1426
+ event_name=event_name,
1427
+ policy_action=policy_action,
1428
+ artifact=runtime_artifact,
1429
+ output_stream=output_stream,
1430
+ ):
1431
+ return 0
1374
1432
  if _should_emit_copilot_hook_response(args):
1375
1433
  _emit_copilot_hook_response(
1376
1434
  policy_action=policy_action,
@@ -1724,6 +1782,23 @@ def _should_emit_prequeue_native_hook_response(
1724
1782
  return output_stream is not None
1725
1783
 
1726
1784
 
1785
+ def _should_allow_claude_user_prompt_submit_without_output(
1786
+ args: argparse.Namespace,
1787
+ *,
1788
+ event_name: str,
1789
+ policy_action: str,
1790
+ artifact: GuardArtifact,
1791
+ output_stream: TextIO | None,
1792
+ ) -> bool:
1793
+ return (
1794
+ _canonical_harness_name(args.harness) == "claude-code"
1795
+ and event_name == "UserPromptSubmit"
1796
+ and policy_action == "require-reapproval"
1797
+ and not _prompt_requires_hard_block(artifact)
1798
+ and (not getattr(args, "json", False) or output_stream is not None)
1799
+ )
1800
+
1801
+
1727
1802
  def _emit_claude_permission_request_passthrough(*, output_stream: TextIO | None = None) -> None:
1728
1803
  if output_stream is not None:
1729
1804
  output_stream.write("")
@@ -2044,6 +2119,27 @@ def _persist_claude_native_permission_for_runtime_artifact(
2044
2119
  return True
2045
2120
 
2046
2121
 
2122
+ def _discard_claude_pending_permissions(store: GuardStore, payload: dict[str, object]) -> int:
2123
+ session_id = _optional_string(payload.get("session_id"))
2124
+ if session_id is None:
2125
+ return 0
2126
+ index_key = _claude_pending_permission_index_key(session_id)
2127
+ try:
2128
+ index_payload = store.get_sync_payload(index_key)
2129
+ except (OSError, sqlite3.Error):
2130
+ return 0
2131
+ if not isinstance(index_payload, list):
2132
+ return 0
2133
+ pending_keys = [str(item) for item in index_payload]
2134
+ if not pending_keys:
2135
+ return 0
2136
+ try:
2137
+ store.delete_sync_payloads([*pending_keys, index_key])
2138
+ except (OSError, sqlite3.Error):
2139
+ return 0
2140
+ return len(pending_keys)
2141
+
2142
+
2047
2143
  def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[str, object]) -> int:
2048
2144
  session_id = _optional_string(payload.get("session_id"))
2049
2145
  if session_id is None:
@@ -107,36 +107,37 @@ def run_guard_connect_command(
107
107
  sync_payload = sync_receipts(store)
108
108
  except GuardSyncNotAvailableError as plan_error:
109
109
  plan_msg = str(plan_error).strip() or "Cloud sync requires a paid Guard plan."
110
+ pending_sync_payload = dict(runtime_sync_summary)
111
+ pending_sync_payload["synced_at"] = None
110
112
  pending_state = _record_connect_result(
111
113
  daemon_client=daemon_client,
112
114
  store=store,
113
115
  request_id=str(connect_request["request_id"]),
114
- status="connected",
115
- milestone="sync_not_available",
116
+ status="retry_required",
117
+ milestone="first_sync_failed",
116
118
  reason=plan_msg,
119
+ sync=pending_sync_payload,
117
120
  )
118
121
  return build_connect_payload(
119
122
  state=pending_state,
120
123
  browser_opened=browser_opened,
121
124
  connect_url=browser_url,
122
125
  sync_url=sync_url,
123
- connected=True,
126
+ connected=False,
127
+ sync=pending_sync_payload,
124
128
  sync_available=False,
125
129
  sync_message=plan_msg,
126
130
  )
127
131
  except (RuntimeError, OSError, urllib.error.URLError, json.JSONDecodeError) as error:
128
132
  sync_message = str(error)
129
- if runtime_sync_error and not _is_paid_plan_sync_error(sync_message):
130
- sync_message = runtime_sync_error
131
- sync_is_plan_limited = _is_paid_plan_sync_error(sync_message)
132
133
  pending_sync_payload = dict(runtime_sync_summary)
133
134
  pending_sync_payload["synced_at"] = None
134
135
  pending_state = _record_connect_result(
135
136
  daemon_client=daemon_client,
136
137
  store=store,
137
138
  request_id=str(connect_request["request_id"]),
138
- status="connected",
139
- milestone="first_sync_pending",
139
+ status="retry_required",
140
+ milestone="first_sync_failed",
140
141
  reason=sync_message,
141
142
  sync=pending_sync_payload,
142
143
  )
@@ -145,10 +146,9 @@ def run_guard_connect_command(
145
146
  browser_opened=browser_opened,
146
147
  connect_url=browser_url,
147
148
  sync_url=sync_url,
148
- connected=True,
149
+ connected=False,
149
150
  sync=pending_sync_payload,
150
151
  sync_message=sync_message,
151
- sync_available=False if sync_is_plan_limited else None,
152
152
  )
153
153
  sync_payload["runtime_session_synced_at"] = runtime_sync_summary["runtime_session_synced_at"]
154
154
  sync_payload["runtime_session_id"] = runtime_sync_summary["runtime_session_id"]
@@ -295,6 +295,24 @@ def _render_inventory(console: Console, payload: dict[str, object]) -> None:
295
295
 
296
296
 
297
297
  def _render_policies(console: Console, payload: dict[str, object]) -> None:
298
+ if "cleared" in payload or "error" in payload:
299
+ error = payload.get("error")
300
+ cleared = int(payload.get("cleared", 0) or 0)
301
+ scope = str(payload.get("harness") or "all harnesses")
302
+ source = payload.get("source")
303
+ body = Table.grid(padding=(0, 1))
304
+ body.add_row("Outcome", str(error) if error else f"cleared {cleared} decision{'s' if cleared != 1 else ''}")
305
+ body.add_row("Harness", scope)
306
+ if source:
307
+ body.add_row("Source", str(source))
308
+ console.print(
309
+ Panel(
310
+ body,
311
+ title="Guard policy clear",
312
+ border_style="red" if error else "green",
313
+ )
314
+ )
315
+ return
298
316
  items = _coerce_dict_list(payload.get("items"))
299
317
  console.print(
300
318
  Panel.fit(
@@ -0,0 +1,88 @@
1
+ """Builders for Guard Cloud v1 events emitted by the local edge runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ from typing import cast
7
+
8
+ from .models import GuardReceipt
9
+ from .schemas.guard_event_v1 import GuardEventType, GuardEventV1
10
+
11
+
12
+ def build_receipt_event(
13
+ receipt: GuardReceipt,
14
+ *,
15
+ device_id: str | None = None,
16
+ workspace_id: str | None = None,
17
+ ) -> GuardEventV1:
18
+ payload: dict[str, object] = {
19
+ "receiptId": receipt.receipt_id,
20
+ "harness": receipt.harness,
21
+ "artifactId": receipt.artifact_id,
22
+ "artifactHash": receipt.artifact_hash,
23
+ "artifactName": receipt.artifact_name,
24
+ "sourceScope": receipt.source_scope,
25
+ "policyDecision": receipt.policy_decision,
26
+ "capabilitiesSummary": receipt.capabilities_summary,
27
+ "changedCapabilities": list(receipt.changed_capabilities),
28
+ "provenanceSummary": receipt.provenance_summary,
29
+ "userOverride": receipt.user_override,
30
+ }
31
+ event_id = f"guard-event-{_fingerprint('receipt.created', receipt.receipt_id)[:32]}"
32
+ return GuardEventV1(
33
+ event_id=event_id,
34
+ idempotency_key=f"receipt.created:{receipt.receipt_id}",
35
+ event_type="receipt.created",
36
+ source="edge",
37
+ occurred_at=receipt.timestamp,
38
+ workspace_id=workspace_id,
39
+ device_id=device_id,
40
+ payload=payload,
41
+ )
42
+
43
+
44
+ def build_approval_event(
45
+ *,
46
+ request_id: str,
47
+ event_type: str,
48
+ occurred_at: str,
49
+ payload: dict[str, object],
50
+ device_id: str | None = None,
51
+ workspace_id: str | None = None,
52
+ ) -> GuardEventV1:
53
+ if event_type not in {"approval.created", "approval.resolved"}:
54
+ raise ValueError("Approval event type must be approval.created or approval.resolved")
55
+ return GuardEventV1(
56
+ event_id=f"guard-event-{_fingerprint(event_type, request_id, occurred_at)[:32]}",
57
+ idempotency_key=f"{event_type}:{request_id}:{occurred_at}",
58
+ event_type=cast(GuardEventType, event_type),
59
+ source="approval-center",
60
+ occurred_at=occurred_at,
61
+ workspace_id=workspace_id,
62
+ device_id=device_id,
63
+ payload=payload,
64
+ )
65
+
66
+
67
+ def build_policy_event(
68
+ *,
69
+ policy_key: str,
70
+ occurred_at: str,
71
+ payload: dict[str, object],
72
+ device_id: str | None = None,
73
+ workspace_id: str | None = None,
74
+ ) -> GuardEventV1:
75
+ return GuardEventV1(
76
+ event_id=f"guard-event-{_fingerprint('policy.changed', policy_key, occurred_at)[:32]}",
77
+ idempotency_key=f"policy.changed:{policy_key}:{occurred_at}",
78
+ event_type="policy.changed",
79
+ source="policy",
80
+ occurred_at=occurred_at,
81
+ workspace_id=workspace_id,
82
+ device_id=device_id,
83
+ payload=payload,
84
+ )
85
+
86
+
87
+ def _fingerprint(*parts: str) -> str:
88
+ return hashlib.sha256(":".join(parts).encode()).hexdigest()
@@ -4,6 +4,7 @@ from .runner import (
4
4
  GuardSyncNotAvailableError,
5
5
  GuardSyncNotConfiguredError,
6
6
  guard_run,
7
+ sync_guard_events,
7
8
  sync_receipts,
8
9
  sync_runtime_session,
9
10
  )
@@ -12,6 +13,7 @@ __all__ = [
12
13
  "GuardSyncNotAvailableError",
13
14
  "GuardSyncNotConfiguredError",
14
15
  "guard_run",
16
+ "sync_guard_events",
15
17
  "sync_receipts",
16
18
  "sync_runtime_session",
17
19
  ]
@@ -480,10 +480,69 @@ def sync_receipts(store: GuardStore) -> dict[str, object]:
480
480
  "inventory": 0,
481
481
  "inventory_tracked": len(inventory),
482
482
  }
483
+ summary["guard_events_v1"] = sync_guard_events(store)
483
484
  store.set_sync_payload("sync_summary", summary, now)
484
485
  return summary
485
486
 
486
487
 
488
+ def sync_guard_events(store: GuardStore) -> dict[str, object]:
489
+ """Push pending GuardEventV1 envelopes to Guard Cloud."""
490
+
491
+ credentials = store.get_sync_credentials()
492
+ if credentials is None:
493
+ raise GuardSyncNotConfiguredError("Guard is not logged in.")
494
+ sync_url = _guard_events_sync_url(str(credentials["sync_url"]))
495
+ total_events = 0
496
+ total_accepted = 0
497
+ synced_at = _now()
498
+ while True:
499
+ pending_events = store.list_guard_events_v1(uploaded=False, limit=200)
500
+ if not pending_events:
501
+ break
502
+ body = json.dumps({"events": [event["payload"] for event in pending_events]}).encode("utf-8")
503
+ request = urllib.request.Request(
504
+ sync_url,
505
+ data=body,
506
+ method="POST",
507
+ headers=_guard_sync_headers(str(credentials["token"])),
508
+ )
509
+ try:
510
+ payload = _urlopen_json_with_timeout_retry(
511
+ request=request,
512
+ timeout_seconds=_SYNC_HTTP_TIMEOUT_SECONDS,
513
+ retry_timeout_seconds=_SYNC_HTTP_RETRY_TIMEOUT_SECONDS,
514
+ )
515
+ except urllib.error.HTTPError as error:
516
+ if error.code == 404:
517
+ summary = {
518
+ "synced_at": synced_at,
519
+ "events": total_events,
520
+ "accepted": total_accepted,
521
+ "sync_skipped": True,
522
+ "sync_reason": "guard_events_endpoint_unavailable",
523
+ }
524
+ store.set_sync_payload("guard_events_v1_summary", summary, synced_at)
525
+ return summary
526
+ if error.code == 403:
527
+ is_plan, message = _check_plan_restriction_403(error)
528
+ if is_plan:
529
+ raise GuardSyncNotAvailableError(message) from error
530
+ raise RuntimeError(message) from error
531
+ raise RuntimeError(_sync_http_error_message(error)) from error
532
+ except OSError as error:
533
+ raise RuntimeError(_sync_url_error_message(error)) from error
534
+ completed_ids = _completed_guard_event_ids(payload)
535
+ synced_at = _sync_timestamp(payload)
536
+ uploaded = store.mark_guard_events_v1_uploaded(completed_ids, synced_at)
537
+ total_events += len(pending_events)
538
+ total_accepted += uploaded
539
+ if uploaded == 0 or len(pending_events) < 200:
540
+ break
541
+ summary = {"synced_at": synced_at, "events": total_events, "accepted": total_accepted}
542
+ store.set_sync_payload("guard_events_v1_summary", summary, synced_at)
543
+ return summary
544
+
545
+
487
546
  def sync_runtime_session(
488
547
  store: GuardStore,
489
548
  *,
@@ -951,6 +1010,45 @@ def _normalized_runtime_sessions_sync_url(sync_url: str) -> str:
951
1010
  )
952
1011
 
953
1012
 
1013
+ def _guard_events_sync_url(sync_url: str) -> str:
1014
+ parsed = urllib.parse.urlsplit(_normalized_receipts_sync_url(sync_url))
1015
+ if parsed.path.rstrip("/").endswith("/api/v1/guard/events"):
1016
+ return urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, parsed.path.rstrip("/"), parsed.query, ""))
1017
+ path = parsed.path.rstrip("/")
1018
+ for suffix in (
1019
+ "/api/guard/receipts/sync",
1020
+ "/guard/receipts/sync",
1021
+ "/registry/api/v1/guard/receipts/sync",
1022
+ ):
1023
+ if path.endswith(suffix):
1024
+ path = path[: -len(suffix)]
1025
+ break
1026
+ return urllib.parse.urlunsplit(
1027
+ (
1028
+ parsed.scheme,
1029
+ parsed.netloc,
1030
+ path.rstrip("/") + "/api/v1/guard/events",
1031
+ parsed.query,
1032
+ "",
1033
+ )
1034
+ )
1035
+
1036
+
1037
+ def _completed_guard_event_ids(payload: dict[str, object]) -> list[str]:
1038
+ statuses = payload.get("statuses")
1039
+ if not isinstance(statuses, list):
1040
+ return []
1041
+ completed: list[str] = []
1042
+ for item in statuses:
1043
+ if not isinstance(item, dict):
1044
+ continue
1045
+ status = str(item.get("status") or "")
1046
+ event_id = item.get("eventId")
1047
+ if status in {"accepted", "duplicate", "rejected"} and isinstance(event_id, str):
1048
+ completed.append(event_id)
1049
+ return completed
1050
+
1051
+
954
1052
  def _cloud_sync_receipts_payload(store: GuardStore, receipts: list[dict[str, object]]) -> list[dict[str, object]]:
955
1053
  device_id, device_name = _guard_device_metadata(store)
956
1054
  return [_cloud_sync_receipt_payload(receipt, device_id=device_id, device_name=device_name) for receipt in receipts]
@@ -0,0 +1,74 @@
1
+ """Guard Cloud event schema shared by the edge runtime and v1 ingest API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Literal, cast
7
+
8
+ GuardEventSource = Literal["edge", "approval-center", "policy", "protect-api"]
9
+ GuardEventType = Literal["receipt.created", "approval.created", "approval.resolved", "policy.changed"]
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class GuardEventV1:
14
+ """Versioned event envelope for replay-safe Guard Cloud sync."""
15
+
16
+ event_id: str
17
+ idempotency_key: str
18
+ event_type: GuardEventType
19
+ source: GuardEventSource
20
+ occurred_at: str
21
+ workspace_id: str | None = None
22
+ device_id: str | None = None
23
+ payload: dict[str, object] = field(default_factory=dict)
24
+ schema_version: str = "guard.event.v1"
25
+
26
+ def to_dict(self) -> dict[str, object]:
27
+ return {
28
+ "schemaVersion": self.schema_version,
29
+ "eventId": self.event_id,
30
+ "idempotencyKey": self.idempotency_key,
31
+ "eventType": self.event_type,
32
+ "source": self.source,
33
+ "occurredAt": self.occurred_at,
34
+ "workspaceId": self.workspace_id,
35
+ "deviceId": self.device_id,
36
+ "payload": self.payload,
37
+ }
38
+
39
+ @classmethod
40
+ def from_dict(cls, payload: dict[str, object]) -> GuardEventV1:
41
+ schema_version = str(payload.get("schemaVersion") or "")
42
+ if schema_version != "guard.event.v1":
43
+ raise ValueError("Guard event schemaVersion must be guard.event.v1")
44
+ event_id = _required_string(payload, "eventId")
45
+ idempotency_key = _required_string(payload, "idempotencyKey")
46
+ event_type = _required_string(payload, "eventType")
47
+ source = _required_string(payload, "source")
48
+ occurred_at = _required_string(payload, "occurredAt")
49
+ event_payload = payload.get("payload")
50
+ if not isinstance(event_payload, dict):
51
+ raise ValueError("Guard event payload must be an object")
52
+ if event_type not in {"receipt.created", "approval.created", "approval.resolved", "policy.changed"}:
53
+ raise ValueError(f"Unsupported Guard event type: {event_type}")
54
+ if source not in {"edge", "approval-center", "policy", "protect-api"}:
55
+ raise ValueError(f"Unsupported Guard event source: {source}")
56
+ workspace_id = payload.get("workspaceId")
57
+ device_id = payload.get("deviceId")
58
+ return cls(
59
+ event_id=event_id,
60
+ idempotency_key=idempotency_key,
61
+ event_type=cast(GuardEventType, event_type),
62
+ source=cast(GuardEventSource, source),
63
+ occurred_at=occurred_at,
64
+ workspace_id=workspace_id if isinstance(workspace_id, str) else None,
65
+ device_id=device_id if isinstance(device_id, str) else None,
66
+ payload={str(key): value for key, value in event_payload.items()},
67
+ )
68
+
69
+
70
+ def _required_string(payload: dict[str, object], key: str) -> str:
71
+ value = payload.get(key)
72
+ if not isinstance(value, str) or not value.strip():
73
+ raise ValueError(f"Guard event {key} must be a non-empty string")
74
+ return value