plugin-scanner 2.0.99__tar.gz → 2.0.101__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 (346) hide show
  1. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/approvals.py +8 -0
  5. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/commands.py +14 -0
  6. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/consumer/service.py +7 -1
  7. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/models.py +1 -0
  8. plugin_scanner-2.0.101/src/codex_plugin_scanner/guard/policy/__init__.py +5 -0
  9. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/policy/engine.py +31 -0
  10. plugin_scanner-2.0.101/src/codex_plugin_scanner/guard/runtime/decisions.py +211 -0
  11. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store.py +1 -0
  12. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store_approvals.py +13 -9
  13. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/version.py +1 -1
  14. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_approvals.py +29 -3
  15. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime.py +48 -1
  16. plugin_scanner-2.0.101/tests/test_guard_runtime_decisions.py +129 -0
  17. plugin_scanner-2.0.99/src/codex_plugin_scanner/guard/policy/__init__.py +0 -5
  18. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/Dockerfile +0 -0
  19. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/build.sh +0 -0
  20. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/project.yaml +0 -0
  21. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  22. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.dockerignore +0 -0
  23. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/CODEOWNERS +0 -0
  24. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  25. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  26. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  27. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/dependabot.yml +0 -0
  28. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/ci.yml +0 -0
  29. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/codeql.yml +0 -0
  30. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/dependabot-uv-lock.yml +0 -0
  31. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/fuzz.yml +0 -0
  32. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/harness-smoke.yml +0 -0
  33. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/publish.yml +0 -0
  34. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/scorecard.yml +0 -0
  35. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.gitignore +0 -0
  36. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.pre-commit-hooks.yaml +0 -0
  37. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/CONTRIBUTING.md +0 -0
  38. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/Dockerfile +0 -0
  39. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/LICENSE +0 -0
  40. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/README.md +0 -0
  41. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/SECURITY.md +0 -0
  42. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/index.html +0 -0
  43. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/package.json +0 -0
  44. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/pnpm-lock.yaml +0 -0
  45. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/apple-touch-icon.png +0 -0
  46. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  47. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/brand/Logo_Whole.png +0 -0
  48. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/favicon-16x16.png +0 -0
  49. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/favicon-32x32.png +0 -0
  50. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/favicon.ico +0 -0
  51. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/app.tsx +0 -0
  52. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/approval-center-layout.tsx +0 -0
  53. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/approval-center-primitives.tsx +0 -0
  54. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/approval-center-utils.ts +0 -0
  55. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/fleet-workspace.tsx +0 -0
  56. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-api.test.ts +0 -0
  57. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-api.ts +0 -0
  58. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-demo.ts +0 -0
  59. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-types.ts +0 -0
  60. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/main.tsx +0 -0
  61. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/receipts-workspace.tsx +0 -0
  62. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/runtime-overview.tsx +0 -0
  63. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/settings-workspace.tsx +0 -0
  64. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/styles.css +0 -0
  65. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/vite-env.d.ts +0 -0
  66. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/tsconfig.json +0 -0
  67. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/vite.config.ts +0 -0
  68. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docker-requirements.txt +0 -0
  69. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/approval-audit.md +0 -0
  70. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/architecture.md +0 -0
  71. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/get-started.md +0 -0
  72. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/harness-support.md +0 -0
  73. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/local-vs-cloud.md +0 -0
  74. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/testing-matrix.md +0 -0
  75. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/trust/mcp-trust-draft.md +0 -0
  76. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/trust/plugin-trust-draft.md +0 -0
  77. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/trust/skill-trust-local.md +0 -0
  78. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/fuzzers/manifest_fuzzer.py +0 -0
  79. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/requirements.txt +0 -0
  80. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/schemas/plugin-quality.v1.json +0 -0
  81. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/schemas/scan-result.v1.json +0 -0
  82. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/schemas/verify-result.v1.json +0 -0
  83. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/__init__.py +0 -0
  84. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/action_runner.py +0 -0
  85. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  86. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  87. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  88. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/claude.py +0 -0
  89. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  90. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  91. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  92. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  93. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  94. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  95. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  96. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  97. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  98. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/security.py +0 -0
  99. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  100. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/cli.py +0 -0
  101. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/cli_ui.py +0 -0
  102. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/config.py +0 -0
  103. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  104. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  105. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  106. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  107. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  108. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  109. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  110. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  111. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  112. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/github_reporting.py +0 -0
  113. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  114. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  115. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  116. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  117. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  118. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  119. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  120. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  121. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  122. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  123. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  124. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  125. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  126. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  127. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  128. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  129. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  130. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  131. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  132. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  133. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  134. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  135. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  136. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  137. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  138. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  139. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  140. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  141. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  142. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/config.py +0 -0
  143. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  144. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  145. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  146. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  147. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  148. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  149. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  150. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  151. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  152. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  153. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  154. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  155. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  156. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  157. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  158. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/incident.py +0 -0
  159. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  160. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  161. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/protect.py +0 -0
  162. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  163. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  164. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  165. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  166. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  167. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  168. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  169. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/risk.py +0 -0
  170. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  171. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  172. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  173. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  174. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  175. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  176. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  177. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  178. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  179. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  180. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/shims.py +0 -0
  181. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  182. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/types.py +0 -0
  183. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  184. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  185. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  186. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  187. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  188. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/models.py +0 -0
  189. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/path_support.py +0 -0
  190. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/policy.py +0 -0
  191. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  192. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/repo_detect.py +0 -0
  193. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/reporting.py +0 -0
  194. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  195. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/rules/registry.py +0 -0
  196. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/rules/specs.py +0 -0
  197. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/scanner.py +0 -0
  198. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/submission.py +0 -0
  199. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/suppressions.py +0 -0
  200. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  201. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  202. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  203. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_models.py +0 -0
  204. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  205. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  206. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  207. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_specs.py +0 -0
  208. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/verification.py +0 -0
  209. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/__init__.py +0 -0
  210. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/conftest.py +0 -0
  211. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/__init__.py +0 -0
  212. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  213. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  214. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/bad-plugin/secrets.js +0 -0
  215. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  216. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  217. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/README.md +0 -0
  218. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  219. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  220. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  221. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/code-quality-bad/evil.js +0 -0
  222. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/code-quality-bad/inject.js +0 -0
  223. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  224. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  225. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/README.md +0 -0
  226. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  227. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  228. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  229. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  230. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/.codexignore +0 -0
  231. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/LICENSE +0 -0
  232. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/README.md +0 -0
  233. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  234. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  235. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  236. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  237. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  238. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  239. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  240. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  241. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  242. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  243. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  244. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  245. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  246. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  247. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  249. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  250. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  251. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  252. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  253. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/mcp-canary-server.py +0 -0
  254. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  255. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  256. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/mit-license/LICENSE +0 -0
  257. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  258. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  259. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  260. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  261. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  262. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  263. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  264. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  265. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  266. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  267. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  268. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  269. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  270. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  271. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  272. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  273. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  274. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  275. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/LICENSE +0 -0
  276. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/README.md +0 -0
  277. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  278. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  279. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  280. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  281. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  282. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  284. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  285. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test-trust-scoring.py +0 -0
  286. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test-trust-specs.py +0 -0
  287. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_action_runner.py +0 -0
  288. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_best_practices.py +0 -0
  289. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_cisco_install_surfaces.py +0 -0
  290. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_cli.py +0 -0
  291. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_code_quality.py +0 -0
  292. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_config.py +0 -0
  293. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_coverage_remaining.py +0 -0
  294. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_ecosystems.py +0 -0
  295. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_edge_cases.py +0 -0
  296. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_final_coverage.py +0 -0
  297. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_bootstrap.py +0 -0
  298. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_capabilities.py +0 -0
  299. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_claude_adapter.py +0 -0
  300. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_cli.py +0 -0
  301. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_codex_e2e.py +0 -0
  302. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_codex_install.py +0 -0
  303. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_codex_proxy.py +0 -0
  304. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_config_paths.py +0 -0
  305. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_connect_flow.py +0 -0
  306. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_consumer_mode.py +0 -0
  307. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_copilot_adapter.py +0 -0
  308. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_copilot_proxy.py +0 -0
  309. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_daemon_manager.py +0 -0
  310. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_event_schema_v1.py +0 -0
  311. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_events.py +0 -0
  312. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_launch_env.py +0 -0
  313. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_opencode_proxy.py +0 -0
  314. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_product_flow.py +0 -0
  315. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_protect.py +0 -0
  316. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_render.py +0 -0
  317. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_risk.py +0 -0
  318. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime_action_harnesses.py +0 -0
  319. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime_actions.py +0 -0
  320. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime_signals.py +0 -0
  321. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_store_migrations.py +0 -0
  322. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_surface_server.py +0 -0
  323. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_verdicts.py +0 -0
  324. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_hermes_adapter.py +0 -0
  325. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_integration.py +0 -0
  326. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_lint_fixes.py +0 -0
  327. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_live_cisco_smoke.py +0 -0
  328. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_manifest.py +0 -0
  329. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_marketplace.py +0 -0
  330. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_mcp_security.py +0 -0
  331. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_openclaw_adapter.py +0 -0
  332. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_operational_security.py +0 -0
  333. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_policy.py +0 -0
  334. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_quality_artifact.py +0 -0
  335. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_rule_registry.py +0 -0
  336. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_scanner.py +0 -0
  337. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_schema_contracts.py +0 -0
  338. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_security.py +0 -0
  339. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_security_ops.py +0 -0
  340. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_skill_security.py +0 -0
  341. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_submission.py +0 -0
  342. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_trust_scoring.py +0 -0
  343. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_trust_specs.py +0 -0
  344. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_verification.py +0 -0
  345. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_versioning.py +0 -0
  346. {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.99
3
+ Version: 2.0.101
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.99"
7
+ version = "2.0.101"
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.99"
7
+ version = "2.0.101"
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"
@@ -88,6 +88,7 @@ def queue_blocked_approvals(
88
88
  launch_summary=incident["launch_summary"],
89
89
  risk_headline=incident["risk_headline"],
90
90
  action_envelope_json=_item_action_envelope_json(item),
91
+ decision_v2_json=_item_decision_v2_json(item),
91
92
  )
92
93
  persisted_request_id = store.add_approval_request(request, timestamp)
93
94
  if persisted_request_id != request.request_id:
@@ -342,6 +343,13 @@ def _item_action_envelope_json(item: dict[str, object]) -> dict[str, object] | N
342
343
  return {str(key): item_value for key, item_value in value.items() if isinstance(key, str)}
343
344
 
344
345
 
346
+ def _item_decision_v2_json(item: dict[str, object]) -> dict[str, object] | None:
347
+ value = item.get("decision_v2_json")
348
+ if not isinstance(value, Mapping):
349
+ return None
350
+ return {str(key): item_value for key, item_value in value.items() if isinstance(key, str)}
351
+
352
+
345
353
  def _string_list(value: object) -> list[str]:
346
354
  if not isinstance(value, list):
347
355
  return []
@@ -15,6 +15,7 @@ import sys
15
15
  import urllib.error
16
16
  import urllib.parse
17
17
  import webbrowser
18
+ from collections.abc import Mapping
18
19
  from contextlib import suppress
19
20
  from dataclasses import replace
20
21
  from datetime import datetime, timedelta, timezone
@@ -3130,6 +3131,9 @@ def _native_prompt_context(artifact: GuardArtifact) -> str:
3130
3131
 
3131
3132
 
3132
3133
  def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: dict[str, object]) -> str:
3134
+ decision_message = _decision_v2_harness_message(response_payload)
3135
+ if decision_message is not None:
3136
+ return decision_message
3133
3137
  if artifact.artifact_type == "prompt_request":
3134
3138
  harness = response_payload.get("harness")
3135
3139
  prompt_classes = _prompt_request_classes(artifact)
@@ -3173,6 +3177,16 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
3173
3177
  return "HOL Guard flagged this request for review."
3174
3178
 
3175
3179
 
3180
+ def _decision_v2_harness_message(response_payload: dict[str, object]) -> str | None:
3181
+ decision_v2 = response_payload.get("decision_v2_json")
3182
+ if not isinstance(decision_v2, Mapping):
3183
+ return None
3184
+ message = decision_v2.get("harness_message")
3185
+ if isinstance(message, str) and message.strip():
3186
+ return message.strip()
3187
+ return None
3188
+
3189
+
3176
3190
  def _claude_prompt_additional_context(
3177
3191
  *,
3178
3192
  harness: str,
@@ -15,9 +15,10 @@ from ..capabilities import compute_capability_delta, normalize_artifact_capabili
15
15
  from ..config import GuardConfig
16
16
  from ..incident import build_incident_context
17
17
  from ..models import GuardArtifact, HarnessDetection, PolicyDecision
18
- from ..policy import decide_action
18
+ from ..policy import build_decision_v2, decide_action
19
19
  from ..receipts import build_receipt
20
20
  from ..risk import artifact_risk_signals_typed, artifact_risk_summary, summarize_signals
21
+ from ..runtime.signals import RiskSignalV2
21
22
  from ..schemas import build_consumer_mode_contract
22
23
  from ..store import GuardStore
23
24
  from ..types import (
@@ -384,6 +385,7 @@ def evaluate_detection(
384
385
  current_capabilities = normalize_artifact_capabilities(artifact)
385
386
  capability_delta = compute_capability_delta(previous_capabilities, current_capabilities)
386
387
  structured_signals = artifact_risk_signals_typed(artifact)
388
+ risk_signals_v2 = tuple(RiskSignalV2.from_guard_signal(signal) for signal in structured_signals)
387
389
  history_context = build_history_context(store, detection.harness, artifact.artifact_id, artifact.publisher)
388
390
  provenance_bundle = build_provenance_bundle(store, artifact.publisher)
389
391
  verdict = score_verdict(structured_signals, capability_delta, provenance_bundle, history_context)
@@ -405,6 +407,7 @@ def evaluate_detection(
405
407
  )
406
408
  if _is_blocking_action(policy_action):
407
409
  blocked = True
410
+ decision_v2 = build_decision_v2(policy_action, reason=policy_action, signals=risk_signals_v2)
408
411
  risk_signals = tuple(signal.explanation for signal in structured_signals)
409
412
  risk_summary = artifact_risk_summary(artifact) if structured_signals else summarize_signals(())
410
413
  changed_capabilities = [delta.delta_type for delta in capability_delta] or list(diff["changed_fields"])
@@ -495,6 +498,7 @@ def evaluate_detection(
495
498
  "changed": diff["changed"],
496
499
  "changed_fields": diff["changed_fields"],
497
500
  "policy_action": policy_action,
501
+ "decision_v2_json": decision_v2.to_dict(),
498
502
  "artifact_hash": diff["current_hash"],
499
503
  "risk_signals": list(risk_signals),
500
504
  "risk_summary": risk_summary,
@@ -536,6 +540,7 @@ def evaluate_detection(
536
540
  )
537
541
  if _is_blocking_action(policy_action):
538
542
  blocked = True
543
+ decision_v2 = build_decision_v2(policy_action, reason=policy_action)
539
544
  artifact_name = previous.get("name")
540
545
  source_scope = previous.get("source_scope")
541
546
  config_path = previous.get("config_path")
@@ -605,6 +610,7 @@ def evaluate_detection(
605
610
  "changed": True,
606
611
  "changed_fields": ["removed"],
607
612
  "policy_action": policy_action,
613
+ "decision_v2_json": decision_v2.to_dict(),
608
614
  "artifact_hash": previous_hash,
609
615
  "removed": True,
610
616
  "risk_signals": ["artifact removed from local harness configuration"],
@@ -179,6 +179,7 @@ class GuardApprovalRequest:
179
179
  launch_summary: str | None = None
180
180
  risk_headline: str | None = None
181
181
  action_envelope_json: dict[str, object] | None = None
182
+ decision_v2_json: dict[str, object] | None = None
182
183
 
183
184
  def to_dict(self) -> dict[str, object]:
184
185
  payload = asdict(self)
@@ -0,0 +1,5 @@
1
+ """Guard policy helpers."""
2
+
3
+ from .engine import build_decision_v2, decide_action, decide_action_with_v2
4
+
5
+ __all__ = ["build_decision_v2", "decide_action", "decide_action_with_v2"]
@@ -2,8 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Sequence
6
+
5
7
  from ..config import GuardConfig
6
8
  from ..models import GuardAction
9
+ from ..runtime.decisions import GuardDecisionV2, decision_from_legacy_policy_action
10
+ from ..runtime.signals import RiskSignalV2
7
11
 
8
12
  VALID_GUARD_ACTIONS = {"allow", "warn", "review", "block", "sandbox-required", "require-reapproval"}
9
13
  SAFE_CHANGED_HASH_ACTION: GuardAction = "require-reapproval"
@@ -29,3 +33,30 @@ def decide_action(
29
33
  if config.default_action in VALID_GUARD_ACTIONS:
30
34
  return config.default_action
31
35
  return SAFE_DEFAULT_ACTION
36
+
37
+
38
+ def build_decision_v2(
39
+ policy_action: GuardAction,
40
+ *,
41
+ reason: str,
42
+ signals: Sequence[RiskSignalV2] = (),
43
+ ) -> GuardDecisionV2:
44
+ return decision_from_legacy_policy_action(policy_action, reason=reason, signals=signals)
45
+
46
+
47
+ def decide_action_with_v2(
48
+ configured_action: str | None,
49
+ default_action: str | None,
50
+ config: GuardConfig,
51
+ changed: bool,
52
+ *,
53
+ reason: str,
54
+ signals: Sequence[RiskSignalV2] = (),
55
+ ) -> tuple[GuardAction, GuardDecisionV2]:
56
+ action = decide_action(
57
+ configured_action=configured_action,
58
+ default_action=default_action,
59
+ config=config,
60
+ changed=changed,
61
+ )
62
+ return action, build_decision_v2(action, reason=reason, signals=signals)
@@ -0,0 +1,211 @@
1
+ """Typed runtime decisions for Guard pause and approval UX."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping, Sequence
6
+ from dataclasses import dataclass
7
+ from typing import Literal
8
+
9
+ from codex_plugin_scanner.guard.models import GuardAction
10
+ from codex_plugin_scanner.guard.runtime.signals import (
11
+ RiskConfidenceLabel,
12
+ RiskSignalV2,
13
+ )
14
+
15
+ GuardDecisionAction = Literal["allow", "warn", "ask", "block"]
16
+
17
+ _ACTION_MESSAGES: dict[GuardAction, tuple[GuardDecisionAction, str, str, str]] = {
18
+ "allow": (
19
+ "allow",
20
+ "Allowed by policy",
21
+ "Policy allows this action.",
22
+ "HOL Guard allowed this action because policy already trusts it.",
23
+ ),
24
+ "warn": (
25
+ "warn",
26
+ "Risk signals found",
27
+ "HOL Guard noticed risk signals, but policy allows the harness to continue.",
28
+ "Review the warning if this action was unexpected.",
29
+ ),
30
+ "review": (
31
+ "ask",
32
+ "Approval required",
33
+ "HOL Guard needs your approval before this action can run.",
34
+ "Choose an approval scope, then retry in the harness.",
35
+ ),
36
+ "sandbox-required": (
37
+ "ask",
38
+ "Sandbox review required",
39
+ "HOL Guard wants this action reviewed and run in a sandboxed path.",
40
+ "Run it in a sandbox or choose a scoped approval before retrying.",
41
+ ),
42
+ "require-reapproval": (
43
+ "ask",
44
+ "Fresh approval required",
45
+ "HOL Guard needs a fresh approval because this action changed.",
46
+ "Choose the smallest approval scope that matches your intent, then retry.",
47
+ ),
48
+ "block": (
49
+ "block",
50
+ "Blocked by policy",
51
+ "HOL Guard blocked this action.",
52
+ "Review the details before changing policy or retrying.",
53
+ ),
54
+ }
55
+
56
+
57
+ @dataclass(frozen=True, slots=True)
58
+ class GuardDecisionV2:
59
+ """Product-facing Guard decision with harness and dashboard copy."""
60
+
61
+ action: GuardDecisionAction
62
+ reason: str
63
+ user_title: str
64
+ user_body: str
65
+ harness_message: str
66
+ dashboard_primary_detail: str
67
+ approval_scopes: tuple[str, ...]
68
+ retry_instruction: str | None
69
+ signals: tuple[RiskSignalV2, ...]
70
+ confidence: RiskConfidenceLabel
71
+
72
+ def to_dict(self) -> dict[str, object]:
73
+ return {
74
+ "action": self.action,
75
+ "reason": self.reason,
76
+ "user_title": self.user_title,
77
+ "user_body": self.user_body,
78
+ "harness_message": self.harness_message,
79
+ "dashboard_primary_detail": self.dashboard_primary_detail,
80
+ "approval_scopes": list(self.approval_scopes),
81
+ "retry_instruction": self.retry_instruction,
82
+ "signals": [signal.to_dict() for signal in self.signals],
83
+ "confidence": self.confidence,
84
+ }
85
+
86
+ @classmethod
87
+ def from_dict(cls, payload: Mapping[str, object]) -> GuardDecisionV2:
88
+ return cls(
89
+ action=_parse_action(payload.get("action")),
90
+ reason=_required_string(payload, "reason"),
91
+ user_title=_required_string(payload, "user_title"),
92
+ user_body=_required_string(payload, "user_body"),
93
+ harness_message=_required_string(payload, "harness_message"),
94
+ dashboard_primary_detail=_required_string(payload, "dashboard_primary_detail"),
95
+ approval_scopes=_parse_string_tuple(payload.get("approval_scopes"), "approval_scopes"),
96
+ retry_instruction=_optional_string(payload, "retry_instruction"),
97
+ signals=_parse_signals(payload.get("signals")),
98
+ confidence=_parse_confidence(payload.get("confidence")),
99
+ )
100
+
101
+
102
+ def decision_from_legacy_policy_action(
103
+ policy_action: GuardAction,
104
+ *,
105
+ reason: str,
106
+ signals: Sequence[RiskSignalV2] = (),
107
+ ) -> GuardDecisionV2:
108
+ action, user_title, harness_message, retry_instruction = _ACTION_MESSAGES[policy_action]
109
+ signal_tuple = tuple(signals)
110
+ confidence = _highest_confidence(signal_tuple)
111
+ dashboard_detail = _dashboard_detail_from_signals(signal_tuple, harness_message)
112
+ return GuardDecisionV2(
113
+ action=action,
114
+ reason=reason,
115
+ user_title=user_title,
116
+ user_body=dashboard_detail,
117
+ harness_message=harness_message,
118
+ dashboard_primary_detail=dashboard_detail,
119
+ approval_scopes=_approval_scopes_for_action(action),
120
+ retry_instruction=None if action in {"allow", "warn"} else retry_instruction,
121
+ signals=signal_tuple,
122
+ confidence=confidence,
123
+ )
124
+
125
+
126
+ def _approval_scopes_for_action(action: GuardDecisionAction) -> tuple[str, ...]:
127
+ if action != "ask":
128
+ return ()
129
+ return ("artifact", "workspace", "publisher", "harness")
130
+
131
+
132
+ def _dashboard_detail_from_signals(signals: tuple[RiskSignalV2, ...], fallback: str) -> str:
133
+ if not signals:
134
+ return fallback
135
+ strongest = max(signals, key=lambda item: _confidence_rank(item.confidence))
136
+ return strongest.plain_reason
137
+
138
+
139
+ def _highest_confidence(signals: tuple[RiskSignalV2, ...]) -> RiskConfidenceLabel:
140
+ if not signals:
141
+ return "likely"
142
+ return max((signal.confidence for signal in signals), key=_confidence_rank)
143
+
144
+
145
+ def _confidence_rank(confidence: RiskConfidenceLabel) -> int:
146
+ match confidence:
147
+ case "strong":
148
+ return 3
149
+ case "likely":
150
+ return 2
151
+ case "weak":
152
+ return 1
153
+
154
+
155
+ def _parse_action(value: object) -> GuardDecisionAction:
156
+ match value:
157
+ case "allow":
158
+ return "allow"
159
+ case "warn":
160
+ return "warn"
161
+ case "ask":
162
+ return "ask"
163
+ case "block":
164
+ return "block"
165
+ case _:
166
+ raise ValueError("action must be a known Guard decision action")
167
+
168
+
169
+ def _parse_confidence(value: object) -> RiskConfidenceLabel:
170
+ match value:
171
+ case "weak":
172
+ return "weak"
173
+ case "likely":
174
+ return "likely"
175
+ case "strong":
176
+ return "strong"
177
+ case _:
178
+ raise ValueError("confidence must be a known confidence label")
179
+
180
+
181
+ def _parse_signals(value: object) -> tuple[RiskSignalV2, ...]:
182
+ if not isinstance(value, list):
183
+ raise ValueError("signals must be a list")
184
+ signals: list[RiskSignalV2] = []
185
+ for item in value:
186
+ if not isinstance(item, Mapping):
187
+ raise ValueError(f"signal item must be an object, got {type(item).__name__}")
188
+ signals.append(RiskSignalV2.from_dict(item))
189
+ return tuple(signals)
190
+
191
+
192
+ def _parse_string_tuple(value: object, key: str) -> tuple[str, ...]:
193
+ if not isinstance(value, list) or not all(isinstance(item, str) and item.strip() for item in value):
194
+ raise ValueError(f"{key} must be a list of non-empty strings")
195
+ return tuple(value)
196
+
197
+
198
+ def _required_string(payload: Mapping[str, object], key: str) -> str:
199
+ value = payload.get(key)
200
+ if not isinstance(value, str) or not value.strip():
201
+ raise ValueError(f"{key} must be a non-empty string")
202
+ return value
203
+
204
+
205
+ def _optional_string(payload: Mapping[str, object], key: str) -> str | None:
206
+ value = payload.get(key)
207
+ if value is None:
208
+ return None
209
+ if not isinstance(value, str):
210
+ raise ValueError(f"{key} must be a string or null")
211
+ return value
@@ -667,6 +667,7 @@ class GuardStore:
667
667
  self._ensure_approval_column(connection, "launch_summary", "text")
668
668
  self._ensure_approval_column(connection, "risk_headline", "text")
669
669
  self._ensure_approval_column(connection, "action_envelope_json", "text")
670
+ self._ensure_approval_column(connection, "decision_v2_json", "text")
670
671
  self._ensure_approval_column(connection, "workspace", "text")
671
672
  self._ensure_attachment_column(connection, "lease_id", "text not null default ''")
672
673
  self._ensure_attachment_column(connection, "lease_expires_at", "text")
@@ -34,8 +34,9 @@ def approval_schema_statement() -> str:
34
34
  why_now text,
35
35
  launch_summary text,
36
36
  risk_headline text,
37
- action_envelope_json text,
38
- review_command text not null,
37
+ action_envelope_json text,
38
+ decision_v2_json text,
39
+ review_command text not null,
39
40
  approval_url text not null,
40
41
  status text not null,
41
42
  resolution_action text,
@@ -69,7 +70,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
69
70
  recommended_scope = ?, changed_fields_json = ?, source_scope = ?, config_path = ?, workspace = ?,
70
71
  launch_target = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
71
72
  artifact_label = ?, source_label = ?, trigger_summary = ?, why_now = ?, launch_summary = ?,
72
- risk_headline = ?, action_envelope_json = ?,
73
+ risk_headline = ?, action_envelope_json = ?, decision_v2_json = ?,
73
74
  review_command = ?, approval_url = ?, created_at = ?
74
75
  where request_id = ?
75
76
  """,
@@ -95,6 +96,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
95
96
  request.launch_summary,
96
97
  request.risk_headline,
97
98
  json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
99
+ json.dumps(request.decision_v2_json) if request.decision_v2_json is not None else None,
98
100
  review_command,
99
101
  approval_url,
100
102
  now,
@@ -109,10 +111,10 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
109
111
  recommended_scope, changed_fields_json, source_scope, config_path, workspace,
110
112
  launch_target, transport, risk_summary,
111
113
  risk_signals_json, artifact_label, source_label, trigger_summary, why_now, launch_summary, risk_headline,
112
- action_envelope_json, review_command,
113
- approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
114
- )
115
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
114
+ action_envelope_json, decision_v2_json, review_command,
115
+ approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
116
+ )
117
+ values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
116
118
  """,
117
119
  (
118
120
  request.request_id,
@@ -139,6 +141,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
139
141
  request.launch_summary,
140
142
  request.risk_headline,
141
143
  json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
144
+ json.dumps(request.decision_v2_json) if request.decision_v2_json is not None else None,
142
145
  request.review_command,
143
146
  request.approval_url,
144
147
  "pending",
@@ -186,7 +189,7 @@ def list_approval_requests(
186
189
  select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
187
190
  recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
188
191
  risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
189
- launch_summary, risk_headline, action_envelope_json, review_command,
192
+ launch_summary, risk_headline, action_envelope_json, decision_v2_json, review_command,
190
193
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
191
194
  from approval_requests
192
195
  {where_clause}
@@ -205,7 +208,7 @@ def get_approval_request(connection: sqlite3.Connection, request_id: str) -> dic
205
208
  select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
206
209
  recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
207
210
  risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
208
- launch_summary, risk_headline, action_envelope_json, review_command,
211
+ launch_summary, risk_headline, action_envelope_json, decision_v2_json, review_command,
209
212
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
210
213
  from approval_requests
211
214
  where request_id = ?
@@ -277,6 +280,7 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
277
280
  "launch_summary": row["launch_summary"],
278
281
  "risk_headline": row["risk_headline"],
279
282
  "action_envelope_json": _optional_json_object(row["action_envelope_json"]),
283
+ "decision_v2_json": _optional_json_object(row["decision_v2_json"]),
280
284
  "review_command": str(row["review_command"]),
281
285
  "approval_url": str(row["approval_url"]),
282
286
  "status": str(row["status"]),
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.99"
3
+ __version__ = "2.0.101"
@@ -90,6 +90,18 @@ class TestGuardApprovals:
90
90
  "script_name": None,
91
91
  "raw_payload_redacted": {"tool_name": "Bash"},
92
92
  }
93
+ decision_v2_json = {
94
+ "action": "ask",
95
+ "reason": "require-reapproval",
96
+ "user_title": "Review workspace_skill",
97
+ "user_body": "HOL Guard needs approval before this action continues.",
98
+ "harness_message": "HOL Guard paused this action for approval.",
99
+ "dashboard_primary_detail": "Shell command can read a local secret file.",
100
+ "approval_scopes": ["artifact", "workspace"],
101
+ "retry_instruction": "Approve it in HOL Guard, then retry.",
102
+ "signals": [],
103
+ "confidence": "likely",
104
+ }
93
105
  request = GuardApprovalRequest(
94
106
  request_id="req-123",
95
107
  harness="codex",
@@ -105,6 +117,7 @@ class TestGuardApprovals:
105
117
  review_command="hol-guard approvals approve req-123",
106
118
  approval_url="http://127.0.0.1:4455/approvals/req-123",
107
119
  action_envelope_json=action_envelope_json,
120
+ decision_v2_json=decision_v2_json,
108
121
  )
109
122
 
110
123
  store.add_approval_request(request, "2026-04-11T00:00:00+00:00")
@@ -122,11 +135,13 @@ class TestGuardApprovals:
122
135
  assert pending[0]["approval_url"] == "http://127.0.0.1:4455/approvals/req-123"
123
136
  assert pending[0]["workspace"] == str(workspace_dir)
124
137
  assert pending[0]["action_envelope_json"] == action_envelope_json
138
+ assert pending[0]["decision_v2_json"] == decision_v2_json
125
139
  assert resolved is not None
126
140
  assert resolved["status"] == "resolved"
127
141
  assert resolved["resolution_action"] == "allow"
128
142
  assert resolved["resolution_scope"] == "artifact"
129
143
  assert resolved["action_envelope_json"] == action_envelope_json
144
+ assert resolved["decision_v2_json"] == decision_v2_json
130
145
 
131
146
  def test_guard_store_loads_old_approval_rows_without_action_envelope(self, tmp_path):
132
147
  guard_home = tmp_path / "guard-home"
@@ -225,8 +240,9 @@ class TestGuardApprovals:
225
240
  assert request is not None
226
241
  assert request["request_id"] == "req-old"
227
242
  assert request["action_envelope_json"] is None
243
+ assert request["decision_v2_json"] is None
228
244
 
229
- def test_guard_store_ignores_malformed_action_envelope_json(self, tmp_path):
245
+ def test_guard_store_ignores_malformed_action_and_decision_json(self, tmp_path):
230
246
  store = GuardStore(tmp_path / "guard-home")
231
247
  connection = sqlite3.connect(store.path)
232
248
  try:
@@ -236,10 +252,13 @@ class TestGuardApprovals:
236
252
  request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher,
237
253
  policy_action, recommended_scope, changed_fields_json, source_scope, config_path, workspace,
238
254
  launch_target, transport, risk_summary, risk_signals_json, artifact_label, source_label,
239
- trigger_summary, why_now, launch_summary, risk_headline, action_envelope_json, review_command,
255
+ trigger_summary, why_now, launch_summary, risk_headline, action_envelope_json, decision_v2_json,
256
+ review_command,
240
257
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
241
258
  )
242
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
259
+ values (
260
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
261
+ )
243
262
  """,
244
263
  (
245
264
  "req-bad-envelope",
@@ -266,6 +285,7 @@ class TestGuardApprovals:
266
285
  None,
267
286
  None,
268
287
  "{not-json",
288
+ "{also-not-json",
269
289
  "hol-guard approvals approve req-bad-envelope",
270
290
  "http://127.0.0.1/pending",
271
291
  "pending",
@@ -284,6 +304,7 @@ class TestGuardApprovals:
284
304
 
285
305
  assert request is not None
286
306
  assert request["action_envelope_json"] is None
307
+ assert request["decision_v2_json"] is None
287
308
 
288
309
  def test_guard_surface_daemon_client_recovers_missing_auth_token(self, tmp_path, monkeypatch):
289
310
  guard_home = tmp_path / "guard-home"
@@ -1960,6 +1981,11 @@ class TestGuardApprovals:
1960
1981
  assert output["approval_center_url"].startswith("http://127.0.0.1:")
1961
1982
  assert approvals[0]["harness"] == "codex"
1962
1983
  assert approvals[0]["status"] == "pending"
1984
+ assert approvals[0]["decision_v2_json"]["action"] == "ask"
1985
+ assert (
1986
+ approvals[0]["decision_v2_json"]["harness_message"]
1987
+ == "HOL Guard needs a fresh approval because this action changed."
1988
+ )
1963
1989
 
1964
1990
  def test_guard_approvals_cli_lists_and_resolves_requests(self, tmp_path, capsys):
1965
1991
  home_dir = tmp_path / "home"