plugin-scanner 2.0.126__tar.gz → 2.0.127__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 (386) hide show
  1. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/package.json +1 -1
  3. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/app.tsx +36 -0
  4. plugin_scanner-2.0.127/dashboard/src/approval-center-layout.test.ts +188 -0
  5. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/approval-center-layout.tsx +143 -31
  6. plugin_scanner-2.0.127/dashboard/src/approval-center-review-cards.tsx +144 -0
  7. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/approval-center-utils.ts +22 -1
  8. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/pyproject.toml +1 -1
  9. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/pyproject.toml.bak +1 -1
  10. plugin_scanner-2.0.127/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +9 -0
  11. plugin_scanner-2.0.127/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +1 -0
  12. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/version.py +1 -1
  13. plugin_scanner-2.0.126/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -9
  14. plugin_scanner-2.0.126/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -1
  15. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.clusterfuzzlite/Dockerfile +0 -0
  16. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.clusterfuzzlite/build.sh +0 -0
  17. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.clusterfuzzlite/project.yaml +0 -0
  18. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  19. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.dockerignore +0 -0
  20. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/CODEOWNERS +0 -0
  21. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  22. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  23. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  24. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/dependabot.yml +0 -0
  25. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/ci.yml +0 -0
  26. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/codeql.yml +0 -0
  27. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/dependabot-uv-lock.yml +0 -0
  28. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/fuzz.yml +0 -0
  29. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/harness-smoke.yml +0 -0
  30. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/publish.yml +0 -0
  31. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.github/workflows/scorecard.yml +0 -0
  32. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.gitignore +0 -0
  33. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/.pre-commit-hooks.yaml +0 -0
  34. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/CONTRIBUTING.md +0 -0
  35. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/Dockerfile +0 -0
  36. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/LICENSE +0 -0
  37. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/README.md +0 -0
  38. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/SECURITY.md +0 -0
  39. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/index.html +0 -0
  40. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/pnpm-lock.yaml +0 -0
  41. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/public/apple-touch-icon.png +0 -0
  42. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  43. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/public/brand/Logo_Whole.png +0 -0
  44. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/public/favicon-16x16.png +0 -0
  45. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/public/favicon-32x32.png +0 -0
  46. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/public/favicon.ico +0 -0
  47. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/approval-center-primitives.tsx +0 -0
  48. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  49. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/fleet-workspace.tsx +0 -0
  50. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/guard-api.test.ts +0 -0
  51. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/guard-api.ts +0 -0
  52. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/guard-demo.ts +0 -0
  53. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/guard-types.ts +0 -0
  54. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/main.tsx +0 -0
  55. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/receipts-workspace.tsx +0 -0
  56. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/runtime-overview.tsx +0 -0
  57. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/settings-workspace.tsx +0 -0
  58. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/styles.css +0 -0
  59. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/src/vite-env.d.ts +0 -0
  60. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/tsconfig.json +0 -0
  61. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/dashboard/vite.config.ts +0 -0
  62. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docker-requirements.txt +0 -0
  63. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/guard/approval-audit.md +0 -0
  64. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/guard/architecture.md +0 -0
  65. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/guard/get-started.md +0 -0
  66. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/guard/harness-support.md +0 -0
  67. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/guard/local-vs-cloud.md +0 -0
  68. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/guard/testing-matrix.md +0 -0
  69. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/trust/mcp-trust-draft.md +0 -0
  70. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/trust/plugin-trust-draft.md +0 -0
  71. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/docs/trust/skill-trust-local.md +0 -0
  72. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/fuzzers/manifest_fuzzer.py +0 -0
  73. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/requirements.txt +0 -0
  74. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/schemas/plugin-quality.v1.json +0 -0
  75. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/schemas/scan-result.v1.json +0 -0
  76. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/schemas/verify-result.v1.json +0 -0
  77. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/__init__.py +0 -0
  78. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/action_runner.py +0 -0
  79. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  80. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  81. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  82. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/claude.py +0 -0
  83. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  84. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  85. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  86. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  87. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  88. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  89. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  90. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  91. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  92. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/security.py +0 -0
  93. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  94. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/cli.py +0 -0
  95. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/cli_ui.py +0 -0
  96. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/config.py +0 -0
  97. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  98. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  99. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  100. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  101. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  102. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  103. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  104. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  105. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  106. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/github_reporting.py +0 -0
  107. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  108. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  109. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  110. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  111. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  112. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  113. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  114. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  115. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  116. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  117. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  118. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  119. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  120. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  121. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  122. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  123. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  124. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  125. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  126. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  127. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  128. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  129. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  130. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  131. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  132. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
  133. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  134. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  135. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  136. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  137. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  138. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  139. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  140. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/config.py +0 -0
  141. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  142. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  143. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  144. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  145. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  146. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  147. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  148. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  149. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  150. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  151. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  152. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  153. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  154. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  155. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/incident.py +0 -0
  156. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  157. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  158. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/models.py +0 -0
  159. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  160. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  161. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/protect.py +0 -0
  162. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  163. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  164. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  165. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  166. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  167. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  168. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  169. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/risk.py +0 -0
  170. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  171. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  172. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  173. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  174. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  175. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  176. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  177. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  178. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  179. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  180. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
  181. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
  182. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  183. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  184. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  185. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  186. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  187. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
  188. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
  189. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  190. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  191. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  192. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  193. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  194. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  195. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/shims.py +0 -0
  196. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/store.py +0 -0
  197. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  198. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  199. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
  200. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/guard/types.py +0 -0
  201. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  202. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  203. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  204. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  205. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  206. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/models.py +0 -0
  207. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/path_support.py +0 -0
  208. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/policy.py +0 -0
  209. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  210. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/repo_detect.py +0 -0
  211. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/reporting.py +0 -0
  212. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  213. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/rules/registry.py +0 -0
  214. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/rules/specs.py +0 -0
  215. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/scanner.py +0 -0
  216. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/submission.py +0 -0
  217. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/suppressions.py +0 -0
  218. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  219. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  220. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  221. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_models.py +0 -0
  222. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  223. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  224. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  225. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/trust_specs.py +0 -0
  226. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/src/codex_plugin_scanner/verification.py +0 -0
  227. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/__init__.py +0 -0
  228. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/conftest.py +0 -0
  229. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/__init__.py +0 -0
  230. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  231. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  232. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/bad-plugin/secrets.js +0 -0
  233. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  234. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  235. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/claude-plugin-good/README.md +0 -0
  236. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  237. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  238. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  239. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/code-quality-bad/evil.js +0 -0
  240. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/code-quality-bad/inject.js +0 -0
  241. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  242. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  243. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/gemini-extension-good/README.md +0 -0
  244. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  245. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  246. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  247. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/.codexignore +0 -0
  249. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/LICENSE +0 -0
  250. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/README.md +0 -0
  251. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  252. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  253. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  254. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  255. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  256. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  257. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  258. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  259. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  260. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  261. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  262. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  263. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  264. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  265. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  266. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  267. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  268. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  269. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  270. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  271. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/mcp-canary-server.py +0 -0
  272. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  273. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  274. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/mit-license/LICENSE +0 -0
  275. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  276. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  277. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  278. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  279. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  280. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  281. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  282. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  284. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  285. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  286. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  287. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  288. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  289. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  290. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  291. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  292. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  293. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/opencode-good/LICENSE +0 -0
  294. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/opencode-good/README.md +0 -0
  295. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  296. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  297. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  298. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  299. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  300. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
  301. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
  302. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
  303. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
  304. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
  305. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
  306. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
  307. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  308. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  309. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  310. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test-trust-scoring.py +0 -0
  311. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test-trust-specs.py +0 -0
  312. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_action_runner.py +0 -0
  313. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_best_practices.py +0 -0
  314. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_cisco_install_surfaces.py +0 -0
  315. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_cli.py +0 -0
  316. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_code_quality.py +0 -0
  317. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_config.py +0 -0
  318. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_coverage_remaining.py +0 -0
  319. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_ecosystems.py +0 -0
  320. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_edge_cases.py +0 -0
  321. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_final_coverage.py +0 -0
  322. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_access_graph.py +0 -0
  323. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_approval_store_scale.py +0 -0
  324. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_approvals.py +0 -0
  325. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_bootstrap.py +0 -0
  326. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_capabilities.py +0 -0
  327. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_claude_adapter.py +0 -0
  328. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_cli.py +0 -0
  329. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_cloud_local_sync.py +0 -0
  330. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_codex_e2e.py +0 -0
  331. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_codex_install.py +0 -0
  332. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_codex_proxy.py +0 -0
  333. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_config_paths.py +0 -0
  334. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_connect_flow.py +0 -0
  335. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_consumer_mode.py +0 -0
  336. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_copilot_adapter.py +0 -0
  337. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_copilot_proxy.py +0 -0
  338. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_daemon_manager.py +0 -0
  339. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_data_flow.py +0 -0
  340. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_event_schema_v1.py +0 -0
  341. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_events.py +0 -0
  342. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_evidence_store.py +0 -0
  343. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_launch_env.py +0 -0
  344. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_mcp_protection.py +0 -0
  345. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_opencode_proxy.py +0 -0
  346. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_product_flow.py +0 -0
  347. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_prompt_injection.py +0 -0
  348. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_protect.py +0 -0
  349. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_render.py +0 -0
  350. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_risk.py +0 -0
  351. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_runtime.py +0 -0
  352. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_runtime_action_harnesses.py +0 -0
  353. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_runtime_actions.py +0 -0
  354. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_runtime_decisions.py +0 -0
  355. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_runtime_detectors.py +0 -0
  356. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_runtime_signals.py +0 -0
  357. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_safe_decode.py +0 -0
  358. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_sandbox.py +0 -0
  359. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_skill_protection.py +0 -0
  360. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_store_migrations.py +0 -0
  361. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_supply_chain.py +0 -0
  362. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_surface_server.py +0 -0
  363. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_guard_verdicts.py +0 -0
  364. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_hermes_adapter.py +0 -0
  365. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_integration.py +0 -0
  366. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_lint_fixes.py +0 -0
  367. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_live_cisco_smoke.py +0 -0
  368. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_manifest.py +0 -0
  369. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_marketplace.py +0 -0
  370. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_mcp_security.py +0 -0
  371. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_openclaw_adapter.py +0 -0
  372. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_operational_security.py +0 -0
  373. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_policy.py +0 -0
  374. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_quality_artifact.py +0 -0
  375. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_rule_registry.py +0 -0
  376. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_scanner.py +0 -0
  377. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_schema_contracts.py +0 -0
  378. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_security.py +0 -0
  379. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_security_ops.py +0 -0
  380. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_skill_security.py +0 -0
  381. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_submission.py +0 -0
  382. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_trust_scoring.py +0 -0
  383. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_trust_specs.py +0 -0
  384. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_verification.py +0 -0
  385. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/tests/test_versioning.py +0 -0
  386. {plugin_scanner-2.0.126 → plugin_scanner-2.0.127}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.126
3
+ Version: 2.0.127
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
@@ -5,7 +5,7 @@
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "vite build",
8
- "test": "tsx src/guard-api.test.ts"
8
+ "test": "tsx src/guard-api.test.ts && tsx src/approval-center-layout.test.ts"
9
9
  },
10
10
  "dependencies": {
11
11
  "react": "^19.2.0",
@@ -321,6 +321,42 @@ export function App() {
321
321
  });
322
322
  }
323
323
  }}
324
+ onBulkApprove={async (ids) => {
325
+ const results = await Promise.allSettled(
326
+ ids.map((id) =>
327
+ resolveRequest({ requestId: id, action: "allow", scope: "once", reason: "" })
328
+ )
329
+ );
330
+ const succeeded = results.filter((r) => r.status === "fulfilled").length;
331
+ const failed = results.length - succeeded;
332
+ const label =
333
+ failed === 0
334
+ ? `${succeeded} item${succeeded !== 1 ? "s" : ""} approved.`
335
+ : `${succeeded} approved, ${failed} failed. Retry the failed items manually.`;
336
+ setResolutionMessage(label);
337
+ navigate("/");
338
+ const [snapshotResult, receiptsResult, policiesResult] = await Promise.allSettled([fetchRuntimeSnapshot(), fetchReceipts(), fetchPolicies()]);
339
+ if (snapshotResult.status === "fulfilled") {
340
+ setRuntime({ kind: "ready", snapshot: snapshotResult.value });
341
+ setRequests({ kind: "ready", items: snapshotResult.value.items });
342
+ }
343
+ if (receiptsResult.status === "fulfilled") {
344
+ setReceipts({ kind: "ready", items: receiptsResult.value });
345
+ } else {
346
+ setReceipts({
347
+ kind: "error",
348
+ message: receiptsResult.reason instanceof Error ? receiptsResult.reason.message : "Unable to load local approval history."
349
+ });
350
+ }
351
+ if (policiesResult.status === "fulfilled") {
352
+ setPolicies({ kind: "ready", items: policiesResult.value });
353
+ } else {
354
+ setPolicies({
355
+ kind: "error",
356
+ message: policiesResult.reason instanceof Error ? policiesResult.reason.message : "Unable to load saved approvals."
357
+ });
358
+ }
359
+ }}
324
360
  fleetContent={
325
361
  runtime.kind === "ready" ? (
326
362
  <FleetWorkspace
@@ -0,0 +1,188 @@
1
+ import {
2
+ resolveEnvelopeDisplayText,
3
+ resolveStoppedCommandText,
4
+ resolveTerminalLabel,
5
+ displayArtifactName,
6
+ } from "./approval-center-utils";
7
+ import type { GuardActionEnvelope, GuardApprovalRequest } from "./guard-types";
8
+
9
+ function assert(condition: boolean, message: string): void {
10
+ if (!condition) {
11
+ throw new Error(message);
12
+ }
13
+ }
14
+
15
+ const BASE_ENVELOPE: GuardActionEnvelope = {
16
+ schema_version: 1,
17
+ action_id: "act-layout-test",
18
+ harness: "claude-code",
19
+ event_name: "tool_call",
20
+ action_type: "harness_start",
21
+ workspace: null,
22
+ workspace_hash: null,
23
+ tool_name: null,
24
+ command: null,
25
+ prompt_excerpt: null,
26
+ target_paths: [],
27
+ network_hosts: [],
28
+ mcp_server: null,
29
+ mcp_tool: null,
30
+ package_manager: null,
31
+ package_name: null,
32
+ script_name: null,
33
+ raw_payload_redacted: {}
34
+ };
35
+
36
+ const BASE_REQUEST: GuardApprovalRequest = {
37
+ request_id: "request-layout-test",
38
+ harness: "claude-code",
39
+ artifact_id: "claude-code:project:bash",
40
+ artifact_name: "bash",
41
+ artifact_type: "command",
42
+ artifact_hash: "sha256-layout",
43
+ publisher: null,
44
+ policy_action: "require-reapproval",
45
+ recommended_scope: "artifact",
46
+ changed_fields: ["first_seen"],
47
+ source_scope: "project",
48
+ config_path: "./claude.json",
49
+ launch_target: "git status",
50
+ transport: "stdio",
51
+ review_command: "hol-guard approvals approve request-layout-test",
52
+ approval_url: "http://127.0.0.1:4781/approvals/request-layout-test",
53
+ status: "pending",
54
+ resolution_action: null,
55
+ resolution_scope: null,
56
+ reason: null,
57
+ created_at: "2026-04-11T12:00:00Z",
58
+ resolved_at: null,
59
+ action_envelope_json: null
60
+ };
61
+
62
+ const shellEnvelope: GuardActionEnvelope = {
63
+ ...BASE_ENVELOPE,
64
+ action_type: "shell_command",
65
+ command: "git diff HEAD~1 -- src/"
66
+ };
67
+
68
+ const shellRequest: GuardApprovalRequest = {
69
+ ...BASE_REQUEST,
70
+ action_envelope_json: shellEnvelope
71
+ };
72
+
73
+ assert(
74
+ resolveStoppedCommandText(shellRequest).length > 0,
75
+ "T493: CTA section has non-empty command text for shell_command item (CTA content available above fold)"
76
+ );
77
+
78
+ assert(
79
+ resolveStoppedCommandText(shellRequest) === "git diff HEAD~1 -- src/",
80
+ "T494: resolveStoppedCommandText returns the envelope command for shell_command item"
81
+ );
82
+
83
+ const longCommand = "a".repeat(200);
84
+ const longCommandEnvelope: GuardActionEnvelope = {
85
+ ...BASE_ENVELOPE,
86
+ action_type: "shell_command",
87
+ command: longCommand
88
+ };
89
+ const longCommandRequest: GuardApprovalRequest = {
90
+ ...BASE_REQUEST,
91
+ action_envelope_json: longCommandEnvelope
92
+ };
93
+
94
+ assert(
95
+ resolveStoppedCommandText(longCommandRequest) === longCommand,
96
+ "T495: resolveStoppedCommandText does not truncate a 200-character command string"
97
+ );
98
+
99
+ assert(
100
+ resolveStoppedCommandText(longCommandRequest).length === 200,
101
+ "T495: 200-char command preserved at full length"
102
+ );
103
+
104
+ const harnessStartEnvelope: GuardActionEnvelope = { ...BASE_ENVELOPE, action_type: "harness_start" };
105
+ assert(
106
+ resolveEnvelopeDisplayText(harnessStartEnvelope) === null,
107
+ "T496: resolveEnvelopeDisplayText returns null for harness_start — no raw content displayed"
108
+ );
109
+
110
+ assert(
111
+ displayArtifactName(BASE_REQUEST) === "bash",
112
+ "T497: displayArtifactName returns artifact_name when present"
113
+ );
114
+
115
+ const requestNoName: GuardApprovalRequest = {
116
+ ...BASE_REQUEST,
117
+ artifact_name: "",
118
+ artifact_id: "claude-code:project:fallback-id"
119
+ };
120
+ assert(
121
+ displayArtifactName(requestNoName) === "claude-code:project:fallback-id",
122
+ "T497: displayArtifactName falls back to artifact_id when name is empty"
123
+ );
124
+
125
+ const requestNoNameOrId: GuardApprovalRequest = {
126
+ ...BASE_REQUEST,
127
+ artifact_name: "",
128
+ artifact_id: ""
129
+ };
130
+ assert(
131
+ displayArtifactName(requestNoNameOrId) === "this action",
132
+ "T497: displayArtifactName falls back to 'this action' when both name and id are empty"
133
+ );
134
+
135
+ assert(
136
+ resolveTerminalLabel(shellRequest) === "Command",
137
+ "T479: resolveTerminalLabel returns 'Command' for shell_command action type"
138
+ );
139
+
140
+ const promptRequest: GuardApprovalRequest = {
141
+ ...BASE_REQUEST,
142
+ action_envelope_json: { ...BASE_ENVELOPE, action_type: "prompt", prompt_excerpt: "Ignore instructions" }
143
+ };
144
+ assert(
145
+ resolveTerminalLabel(promptRequest) === "Prompt excerpt",
146
+ "T480: resolveTerminalLabel returns 'Prompt excerpt' for prompt action type"
147
+ );
148
+
149
+ const fileReadRequest: GuardApprovalRequest = {
150
+ ...BASE_REQUEST,
151
+ action_envelope_json: { ...BASE_ENVELOPE, action_type: "file_read", target_paths: ["/etc/hosts"] }
152
+ };
153
+ assert(
154
+ resolveTerminalLabel(fileReadRequest) === "File path",
155
+ "T481: resolveTerminalLabel returns 'File path' for file_read action type"
156
+ );
157
+
158
+ const mcpRequest: GuardApprovalRequest = {
159
+ ...BASE_REQUEST,
160
+ action_envelope_json: { ...BASE_ENVELOPE, action_type: "mcp_tool", mcp_server: "my-server", mcp_tool: "fetch" }
161
+ };
162
+ assert(
163
+ resolveTerminalLabel(mcpRequest) === "MCP server / tool",
164
+ "T482: resolveTerminalLabel returns 'MCP server / tool' for mcp_tool action type"
165
+ );
166
+
167
+ const packageRequest: GuardApprovalRequest = {
168
+ ...BASE_REQUEST,
169
+ action_envelope_json: { ...BASE_ENVELOPE, action_type: "package_script", package_manager: "npm", package_name: "lodash", script_name: null }
170
+ };
171
+ assert(
172
+ resolveTerminalLabel(packageRequest) === "Package",
173
+ "T483: resolveTerminalLabel returns 'Package' for package_script action type"
174
+ );
175
+
176
+ const networkRequest: GuardApprovalRequest = {
177
+ ...BASE_REQUEST,
178
+ action_envelope_json: { ...BASE_ENVELOPE, action_type: "network_request", network_hosts: ["api.example.com"] }
179
+ };
180
+ assert(
181
+ resolveTerminalLabel(networkRequest) === "Network destination",
182
+ "T484: resolveTerminalLabel returns 'Network destination' for network_request action type"
183
+ );
184
+
185
+ assert(
186
+ resolveTerminalLabel(BASE_REQUEST) === "Stopped command",
187
+ "T479-T484: resolveTerminalLabel returns 'Stopped command' when no envelope present"
188
+ );
@@ -38,8 +38,18 @@ import {
38
38
  harnessDisplayName,
39
39
  resolveDecisionV2Title,
40
40
  resolveDecisionV2Detail,
41
- resolveStoppedCommandText
41
+ resolveStoppedCommandText,
42
+ displayArtifactName,
43
+ resolveTerminalLabel,
44
+ scopeLabel
42
45
  } from "./approval-center-utils";
46
+ import {
47
+ WhyThisPaused,
48
+ ApproveConsequence,
49
+ BlockConsequence,
50
+ KeyboardHints,
51
+ ConfirmModal
52
+ } from "./approval-center-review-cards";
43
53
  import type {
44
54
  GuardApprovalRequest,
45
55
  GuardArtifactDiff,
@@ -94,6 +104,7 @@ type LayoutProps = {
94
104
  workspace?: string;
95
105
  reason: string;
96
106
  }) => void;
107
+ onBulkApprove?: (ids: string[]) => void;
97
108
  };
98
109
 
99
110
  const scopeOptions: Array<{ value: DecisionScope; label: string; description: string }> = [
@@ -105,6 +116,7 @@ const scopeOptions: Array<{ value: DecisionScope; label: string; description: st
105
116
  ];
106
117
 
107
118
  const commonScopeValues = new Set<DecisionScope>(["artifact", "workspace"]);
119
+ const broadScopeValues = new Set<DecisionScope>(["publisher", "harness", "global"]);
108
120
  const queuePageSize = 8;
109
121
  export function ApprovalCenterLayout(props: LayoutProps) {
110
122
  const queuedItems = props.requests.kind === "ready" ? props.requests.items : [];
@@ -143,6 +155,7 @@ export function ApprovalCenterLayout(props: LayoutProps) {
143
155
  onOpenRequest={props.onOpenRequest}
144
156
  onGoHome={props.onGoHome}
145
157
  onResolve={props.onResolve}
158
+ onBulkApprove={props.onBulkApprove}
146
159
  />
147
160
  )}
148
161
  </div>
@@ -182,6 +195,7 @@ function QueueWorkspace(props: {
182
195
  onOpenRequest: (requestId: string) => void;
183
196
  onGoHome: () => void;
184
197
  onResolve: LayoutProps["onResolve"];
198
+ onBulkApprove?: (ids: string[]) => void;
185
199
  }) {
186
200
  if (props.requests.kind === "loading") {
187
201
  return (
@@ -225,6 +239,7 @@ function QueueWorkspace(props: {
225
239
  activeRequestId={props.activeRequestId}
226
240
  items={props.requests.items}
227
241
  onOpenRequest={props.onOpenRequest}
242
+ onBulkApprove={props.onBulkApprove}
228
243
  />
229
244
  </div>
230
245
  );
@@ -244,7 +259,7 @@ function QueueHeader(props: {
244
259
  Review Queue
245
260
  </h1>
246
261
  <p className="max-w-2xl text-sm leading-relaxed text-muted-foreground">
247
- HOL Guard paused the action below before it ran. Review the command or tool, then approve it once or keep it blocked.
262
+ HOL Guard paused this before it ran. Review what was stopped, then approve or block it.
248
263
  </p>
249
264
  </div>
250
265
  <div className="flex flex-wrap gap-2">
@@ -260,6 +275,7 @@ function QueueBrowser(props: {
260
275
  activeRequestId: string | null;
261
276
  items: GuardApprovalRequest[];
262
277
  onOpenRequest: (requestId: string) => void;
278
+ onBulkApprove?: (ids: string[]) => void;
263
279
  }) {
264
280
  const [searchTerm, setSearchTerm] = useState("");
265
281
  const [harnessFilter, setHarnessFilter] = useState("all");
@@ -311,8 +327,37 @@ function QueueBrowser(props: {
311
327
  setPage((value) => Math.min(totalPages, value + 1));
312
328
  }, [totalPages]);
313
329
 
330
+ const isReadOnlyItem = useCallback((item: GuardApprovalRequest) =>
331
+ item.policy_action !== "block" &&
332
+ (item.action_envelope_json?.action_type === "file_read" || item.artifact_type === "file_read_request"),
333
+ []);
334
+
335
+ const bulkReadOnlyItems = useMemo(
336
+ () => filteredItems.filter(isReadOnlyItem),
337
+ [filteredItems, isReadOnlyItem]
338
+ );
339
+ const showBulkApprove =
340
+ props.onBulkApprove !== undefined &&
341
+ filteredItems.length > 0 &&
342
+ bulkReadOnlyItems.length === filteredItems.length;
343
+
344
+ const handleBulkApprove = useCallback(() => {
345
+ props.onBulkApprove?.(filteredItems.map((item) => item.request_id));
346
+ }, [props.onBulkApprove, filteredItems]);
347
+
314
348
  return (
315
349
  <section className="border-t border-slate-200/70 pt-6">
350
+ {showBulkApprove && (
351
+ <div className="mb-4">
352
+ <button
353
+ type="button"
354
+ onClick={handleBulkApprove}
355
+ className="rounded-full border border-brand-blue/30 bg-white px-4 py-2 text-sm font-medium text-brand-blue shadow-sm transition-colors hover:bg-brand-blue/5"
356
+ >
357
+ Approve all read-only actions ({filteredItems.length})
358
+ </button>
359
+ </div>
360
+ )}
316
361
  <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
317
362
  <div>
318
363
  <SectionLabel>Other waiting actions</SectionLabel>
@@ -424,6 +469,7 @@ function DecisionWorkspace(props: {
424
469
  const [reason, setReason] = useState("approved in local approval center");
425
470
  const [submitting, setSubmitting] = useState<"allow" | "block" | null>(null);
426
471
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
472
+ const [confirmPending, setConfirmPending] = useState<"allow" | "block" | null>(null);
427
473
 
428
474
  useEffect(() => {
429
475
  if (props.detail.kind === "ready") {
@@ -456,6 +502,57 @@ function DecisionWorkspace(props: {
456
502
  [props.onResolve, readyRequestId, readyWorkspace, reason, scope]
457
503
  );
458
504
 
505
+ const handleRequestResolve = useCallback(
506
+ (action: "allow" | "block") => {
507
+ if (broadScopeValues.has(scope)) {
508
+ setConfirmPending(action);
509
+ } else {
510
+ void handleResolve(action);
511
+ }
512
+ },
513
+ [scope, handleResolve]
514
+ );
515
+
516
+ const handleConfirmResolve = useCallback(() => {
517
+ if (confirmPending !== null) {
518
+ void handleResolve(confirmPending);
519
+ setConfirmPending(null);
520
+ }
521
+ }, [confirmPending, handleResolve]);
522
+
523
+ const handleCancelConfirm = useCallback(() => {
524
+ setConfirmPending(null);
525
+ }, []);
526
+
527
+ useEffect(() => {
528
+ if (props.detail.kind !== "ready") return;
529
+ const onKeyDown = (event: KeyboardEvent) => {
530
+ if (submitting !== null) return;
531
+ if (confirmPending !== null) {
532
+ if (event.key === "Enter") {
533
+ handleConfirmResolve();
534
+ return;
535
+ }
536
+ if (event.key === "Escape") {
537
+ handleCancelConfirm();
538
+ return;
539
+ }
540
+ return;
541
+ }
542
+ const target = event.target as HTMLElement;
543
+ if (
544
+ target.tagName === "INPUT" ||
545
+ target.tagName === "TEXTAREA" ||
546
+ target.tagName === "SELECT" ||
547
+ target.isContentEditable
548
+ ) return;
549
+ if (event.key === "a" || event.key === "A") handleRequestResolve("allow");
550
+ else if (event.key === "b" || event.key === "B") handleRequestResolve("block");
551
+ };
552
+ window.addEventListener("keydown", onKeyDown);
553
+ return () => window.removeEventListener("keydown", onKeyDown);
554
+ }, [props.detail.kind, submitting, handleRequestResolve, confirmPending, handleConfirmResolve, handleCancelConfirm]);
555
+
459
556
  if (props.detail.kind === "loading") {
460
557
  return (
461
558
  <div className="space-y-4">
@@ -481,6 +578,14 @@ function DecisionWorkspace(props: {
481
578
  const broadScopeOpts = scopeOptions.filter((option) => !commonScopeValues.has(option.value));
482
579
  return (
483
580
  <div className="guard-surface-in space-y-4">
581
+ {confirmPending !== null && (
582
+ <ConfirmModal
583
+ action={confirmPending}
584
+ scopeLabel={scopeLabel(scope)}
585
+ onConfirm={handleConfirmResolve}
586
+ onCancel={handleCancelConfirm}
587
+ />
588
+ )}
484
589
  <RuleBuilder
485
590
  item={item}
486
591
  scope={scope}
@@ -491,7 +596,7 @@ function DecisionWorkspace(props: {
491
596
  broadScopeOptions={broadScopeOpts}
492
597
  onScopeChange={setScope}
493
598
  onReasonChange={setReason}
494
- onResolve={handleResolve}
599
+ onResolve={handleRequestResolve}
495
600
  />
496
601
  <WhatChanged item={item} diff={diff} receipt={receipt} policy={policy} />
497
602
  </div>
@@ -547,11 +652,9 @@ function WhatChanged(props: { item: GuardApprovalRequest; diff: GuardArtifactDif
547
652
  <summary className="flex cursor-pointer select-none items-center justify-between gap-3 text-sm font-medium text-brand-dark [&::-webkit-details-marker]:hidden">
548
653
  <span className="flex items-center gap-2">
549
654
  <span className="text-brand-blue transition-transform duration-200 group-open:rotate-90">›</span>
550
- Review technical evidence
551
- </span>
552
- <span className="hidden rounded-full bg-surface-1 px-3 py-1 font-mono text-[11px] text-muted-foreground sm:inline">
553
- saved details
655
+ Technical details
554
656
  </span>
657
+ <Badge tone="warning">{artifactTypeLabel(item.artifact_type)}</Badge>
555
658
  </summary>
556
659
  <div className="mt-4 space-y-3 border-l-2 border-brand-blue/10 pl-4">
557
660
  <p className="text-sm leading-relaxed text-brand-dark/70">{buildStoppedReason(item, receipt)}</p>
@@ -591,37 +694,45 @@ function RuleBuilder(props: {
591
694
  }) {
592
695
  const previewText = getRulePreviewText(props.item, props.scope);
593
696
  const allowLabel = props.scope === "artifact" ? "Approve once" : "Approve and remember";
697
+ const retryInstruction = props.item.decision_v2_json?.retry_instruction ?? null;
698
+
699
+ const handleAllow = useCallback(() => props.onResolve("allow"), [props.onResolve]);
700
+ const handleBlock = useCallback(() => props.onResolve("block"), [props.onResolve]);
701
+
594
702
  return (
595
703
  <section className="guard-surface-in relative overflow-hidden rounded-[2rem] border border-brand-blue/15 bg-[radial-gradient(circle_at_top_left,rgba(85,153,254,0.12),transparent_32%),linear-gradient(135deg,#ffffff_0%,#ffffff_58%,rgba(85,153,254,0.08)_100%)] p-5 shadow-[0_20px_60px_rgba(63,65,116,0.08)] sm:p-6 lg:p-7">
596
704
  <div className="pointer-events-none absolute right-8 top-8 h-24 w-24 rounded-full bg-brand-green/20 blur-3xl" />
597
- <div className="relative grid gap-6 lg:grid-cols-[minmax(0,1fr)_330px] lg:items-start">
598
- <div>
599
- <div className="flex flex-wrap items-center gap-2">
600
- <Tag tone="blue">HOL Guard</Tag>
601
- <Tag tone="slate">{harnessDisplayName(props.item.harness)}</Tag>
602
- <PolicyBadge action={props.item.policy_action} />
603
- </div>
604
- <div className="mt-4 max-w-3xl">
605
- <SectionLabel>Needs your decision</SectionLabel>
606
- <h3 className="mt-2 text-2xl font-semibold tracking-tight text-brand-dark sm:text-3xl">
607
- {buildDecisionTitle(props.item)}
608
- </h3>
609
- <p className="mt-3 max-w-2xl text-sm leading-6 text-brand-dark/70">
610
- {buildPauseLine(props.item)}
611
- </p>
612
- </div>
705
+ <div className="relative">
706
+ <div className="flex flex-wrap items-center gap-2">
707
+ <Tag tone="blue">HOL Guard</Tag>
708
+ <Tag tone="slate">{harnessDisplayName(props.item.harness)}</Tag>
709
+ <PolicyBadge action={props.item.policy_action} />
613
710
  </div>
711
+ <div className="mt-4 max-w-3xl">
712
+ <SectionLabel>Needs your decision</SectionLabel>
713
+ <h3 className="mt-2 text-2xl font-semibold tracking-tight text-brand-dark sm:text-3xl">
714
+ {buildDecisionTitle(props.item)}
715
+ </h3>
716
+ <p className="mt-3 max-w-2xl text-sm leading-6 text-brand-dark/70">
717
+ {buildPauseLine(props.item)}
718
+ </p>
719
+ </div>
720
+ </div>
721
+
722
+ <div className="relative mt-5">
614
723
  <DecisionActionPanel
615
724
  allowLabel={allowLabel}
616
725
  previewText={previewText}
617
726
  submitting={props.submitting}
618
727
  isBlocked={props.item.policy_action === "block"}
619
- onAllow={() => props.onResolve("allow")}
620
- onBlock={() => props.onResolve("block")}
728
+ retryInstruction={retryInstruction}
729
+ onAllow={handleAllow}
730
+ onBlock={handleBlock}
621
731
  />
622
732
  </div>
623
733
 
624
734
  <DecisionSteps activeStep={props.submitting === null ? 1 : 3} />
735
+ <KeyboardHints />
625
736
 
626
737
  <div className="relative mt-6 grid gap-6 xl:grid-cols-[minmax(0,1.08fr)_minmax(340px,0.92fr)] xl:items-start">
627
738
  <BlockedActionCard item={props.item} />
@@ -699,6 +810,7 @@ function DecisionActionPanel(props: {
699
810
  previewText: string;
700
811
  submitting: "allow" | "block" | null;
701
812
  isBlocked: boolean;
813
+ retryInstruction: string | null;
702
814
  onAllow: () => void;
703
815
  onBlock: () => void;
704
816
  }) {
@@ -708,6 +820,10 @@ function DecisionActionPanel(props: {
708
820
  <p className="mt-2 text-sm leading-6 text-brand-dark/70">
709
821
  {props.previewText}
710
822
  </p>
823
+ <div className="mt-3 grid gap-1.5">
824
+ <ApproveConsequence retryInstruction={props.retryInstruction} />
825
+ <BlockConsequence />
826
+ </div>
711
827
  <div className="mt-4 grid gap-2">
712
828
  <ActionButton variant="success" onClick={props.onAllow} disabled={props.submitting !== null}>
713
829
  {props.submitting === "allow" ? "Saving…" : (props.isBlocked ? "Allow — override block" : props.allowLabel)}
@@ -817,7 +933,6 @@ function BlockedActionCard(props: { item: GuardApprovalRequest }) {
817
933
  <div className="p-4">
818
934
  <div className="flex flex-wrap items-center justify-between gap-2">
819
935
  <SectionLabel>What was stopped</SectionLabel>
820
- <Badge tone="warning">{artifactTypeLabel(props.item.artifact_type)}</Badge>
821
936
  </div>
822
937
  <h4 className="mt-2 text-xl font-semibold tracking-tight text-brand-dark">
823
938
  {actionDisplayTitle(props.item)}
@@ -836,7 +951,7 @@ function BlockedActionCard(props: { item: GuardApprovalRequest }) {
836
951
  <span className="h-2.5 w-2.5 rounded-full bg-brand-blue" />
837
952
  <span className="h-2.5 w-2.5 rounded-full bg-brand-green" />
838
953
  <span className="ml-2 font-mono text-[10px] uppercase tracking-[0.22em] text-white/45">
839
- Stopped command
954
+ {resolveTerminalLabel(props.item)}
840
955
  </span>
841
956
  <span className="ml-auto">
842
957
  <CopyCommandButton command={launchText} />
@@ -846,6 +961,7 @@ function BlockedActionCard(props: { item: GuardApprovalRequest }) {
846
961
  {launchText}
847
962
  </pre>
848
963
  </div>
964
+ <WhyThisPaused item={props.item} />
849
965
  {isBlocked && (
850
966
  <div className="mt-3 rounded-[1rem] border border-brand-purple/20 bg-brand-purple/[0.05] px-3 py-2.5">
851
967
  <p className="text-sm leading-6 text-brand-purple">
@@ -937,10 +1053,6 @@ function PolicyBadge(props: { action: string }) {
937
1053
  return <Badge tone={tone}>{policyActionLabel(props.action)}</Badge>;
938
1054
  }
939
1055
 
940
- function displayArtifactName(item: GuardApprovalRequest): string {
941
- return item.artifact_name || item.artifact_id || "this action";
942
- }
943
-
944
1056
  function simplifyRiskHeadline(headline: string, harness: string): string {
945
1057
  const lowerHeadline = headline.toLowerCase();
946
1058
  if (lowerHeadline.includes("sensitive native tool action") || lowerHeadline.includes("destructive shell command")) {