plugin-scanner 2.0.95__tar.gz → 2.0.97__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 (341) hide show
  1. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/approvals.py +9 -0
  5. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/commands.py +35 -0
  6. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/models.py +2 -0
  7. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/actions.py +215 -14
  8. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/runner.py +63 -0
  9. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store.py +1 -0
  10. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store_approvals.py +35 -19
  11. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/version.py +1 -1
  12. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_approvals.py +181 -0
  13. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_runtime.py +4 -0
  14. plugin_scanner-2.0.97/tests/test_guard_runtime_action_harnesses.py +316 -0
  15. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/Dockerfile +0 -0
  16. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/build.sh +0 -0
  17. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/project.yaml +0 -0
  18. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  19. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.dockerignore +0 -0
  20. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/CODEOWNERS +0 -0
  21. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  22. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  23. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  24. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/dependabot.yml +0 -0
  25. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/ci.yml +0 -0
  26. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/codeql.yml +0 -0
  27. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/dependabot-uv-lock.yml +0 -0
  28. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/fuzz.yml +0 -0
  29. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/harness-smoke.yml +0 -0
  30. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/publish.yml +0 -0
  31. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/scorecard.yml +0 -0
  32. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.gitignore +0 -0
  33. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.pre-commit-hooks.yaml +0 -0
  34. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/CONTRIBUTING.md +0 -0
  35. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/Dockerfile +0 -0
  36. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/LICENSE +0 -0
  37. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/README.md +0 -0
  38. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/SECURITY.md +0 -0
  39. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/index.html +0 -0
  40. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/package.json +0 -0
  41. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/pnpm-lock.yaml +0 -0
  42. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/apple-touch-icon.png +0 -0
  43. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  44. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/brand/Logo_Whole.png +0 -0
  45. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/favicon-16x16.png +0 -0
  46. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/favicon-32x32.png +0 -0
  47. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/favicon.ico +0 -0
  48. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/app.tsx +0 -0
  49. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/approval-center-layout.tsx +0 -0
  50. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/approval-center-primitives.tsx +0 -0
  51. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/approval-center-utils.ts +0 -0
  52. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/fleet-workspace.tsx +0 -0
  53. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-api.test.ts +0 -0
  54. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-api.ts +0 -0
  55. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-demo.ts +0 -0
  56. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-types.ts +0 -0
  57. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/main.tsx +0 -0
  58. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/receipts-workspace.tsx +0 -0
  59. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/runtime-overview.tsx +0 -0
  60. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/settings-workspace.tsx +0 -0
  61. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/styles.css +0 -0
  62. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/vite-env.d.ts +0 -0
  63. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/tsconfig.json +0 -0
  64. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/vite.config.ts +0 -0
  65. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docker-requirements.txt +0 -0
  66. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/approval-audit.md +0 -0
  67. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/architecture.md +0 -0
  68. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/get-started.md +0 -0
  69. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/harness-support.md +0 -0
  70. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/local-vs-cloud.md +0 -0
  71. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/testing-matrix.md +0 -0
  72. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/trust/mcp-trust-draft.md +0 -0
  73. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/trust/plugin-trust-draft.md +0 -0
  74. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/trust/skill-trust-local.md +0 -0
  75. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/fuzzers/manifest_fuzzer.py +0 -0
  76. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/requirements.txt +0 -0
  77. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/schemas/plugin-quality.v1.json +0 -0
  78. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/schemas/scan-result.v1.json +0 -0
  79. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/schemas/verify-result.v1.json +0 -0
  80. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/__init__.py +0 -0
  81. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/action_runner.py +0 -0
  82. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  83. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  84. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  85. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/claude.py +0 -0
  86. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  87. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  88. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  89. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  90. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  91. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  92. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  93. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  94. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  95. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/security.py +0 -0
  96. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  97. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/cli.py +0 -0
  98. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/cli_ui.py +0 -0
  99. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/config.py +0 -0
  100. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  101. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  102. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  103. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  104. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  105. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  106. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  107. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  108. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  109. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/github_reporting.py +0 -0
  110. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  111. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  112. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  113. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  114. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  115. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  116. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  117. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  118. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  119. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  120. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  121. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  122. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  123. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  124. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  125. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  126. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  127. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  128. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  129. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  130. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  131. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  132. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  133. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  134. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  135. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  136. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  137. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  138. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  139. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/config.py +0 -0
  140. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  141. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  142. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  143. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  144. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  145. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  146. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  147. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  148. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  149. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  150. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  151. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  152. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  153. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  154. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  155. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  156. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/incident.py +0 -0
  157. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  158. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  159. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  160. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  161. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/protect.py +0 -0
  162. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  163. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  164. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  165. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  166. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  167. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  168. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  169. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/risk.py +0 -0
  170. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  171. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  172. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  173. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  174. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  175. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  176. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  177. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/shims.py +0 -0
  178. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  179. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/types.py +0 -0
  180. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  181. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  182. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  183. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  184. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  185. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/models.py +0 -0
  186. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/path_support.py +0 -0
  187. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/policy.py +0 -0
  188. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  189. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/repo_detect.py +0 -0
  190. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/reporting.py +0 -0
  191. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  192. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/rules/registry.py +0 -0
  193. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/rules/specs.py +0 -0
  194. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/scanner.py +0 -0
  195. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/submission.py +0 -0
  196. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/suppressions.py +0 -0
  197. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  198. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  199. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  200. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_models.py +0 -0
  201. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  202. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  203. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  204. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_specs.py +0 -0
  205. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/verification.py +0 -0
  206. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/__init__.py +0 -0
  207. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/conftest.py +0 -0
  208. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/__init__.py +0 -0
  209. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  210. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  211. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/bad-plugin/secrets.js +0 -0
  212. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  213. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  214. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/README.md +0 -0
  215. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  216. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  217. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  218. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/code-quality-bad/evil.js +0 -0
  219. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/code-quality-bad/inject.js +0 -0
  220. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  221. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  222. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/README.md +0 -0
  223. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  224. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  225. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  226. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  227. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/.codexignore +0 -0
  228. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/LICENSE +0 -0
  229. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/README.md +0 -0
  230. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  231. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  232. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  233. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  234. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  235. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  236. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  237. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  238. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  239. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  240. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  241. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  242. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  243. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  244. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  245. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  246. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  247. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  248. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  249. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  250. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/mcp-canary-server.py +0 -0
  251. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  252. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  253. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/mit-license/LICENSE +0 -0
  254. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  255. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  256. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  257. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  258. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  259. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  260. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  261. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  262. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  263. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  264. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  265. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  266. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  267. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  268. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  269. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  270. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  271. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  272. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/LICENSE +0 -0
  273. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/README.md +0 -0
  274. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  275. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  276. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  277. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  278. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  279. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  280. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  281. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  282. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test-trust-scoring.py +0 -0
  283. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test-trust-specs.py +0 -0
  284. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_action_runner.py +0 -0
  285. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_best_practices.py +0 -0
  286. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_cisco_install_surfaces.py +0 -0
  287. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_cli.py +0 -0
  288. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_code_quality.py +0 -0
  289. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_config.py +0 -0
  290. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_coverage_remaining.py +0 -0
  291. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_ecosystems.py +0 -0
  292. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_edge_cases.py +0 -0
  293. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_final_coverage.py +0 -0
  294. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_bootstrap.py +0 -0
  295. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_capabilities.py +0 -0
  296. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_claude_adapter.py +0 -0
  297. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_cli.py +0 -0
  298. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_codex_e2e.py +0 -0
  299. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_codex_install.py +0 -0
  300. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_codex_proxy.py +0 -0
  301. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_config_paths.py +0 -0
  302. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_connect_flow.py +0 -0
  303. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_consumer_mode.py +0 -0
  304. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_copilot_adapter.py +0 -0
  305. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_copilot_proxy.py +0 -0
  306. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_daemon_manager.py +0 -0
  307. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_event_schema_v1.py +0 -0
  308. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_events.py +0 -0
  309. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_launch_env.py +0 -0
  310. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_opencode_proxy.py +0 -0
  311. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_product_flow.py +0 -0
  312. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_protect.py +0 -0
  313. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_render.py +0 -0
  314. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_risk.py +0 -0
  315. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_runtime_actions.py +0 -0
  316. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_store_migrations.py +0 -0
  317. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_surface_server.py +0 -0
  318. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_verdicts.py +0 -0
  319. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_hermes_adapter.py +0 -0
  320. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_integration.py +0 -0
  321. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_lint_fixes.py +0 -0
  322. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_live_cisco_smoke.py +0 -0
  323. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_manifest.py +0 -0
  324. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_marketplace.py +0 -0
  325. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_mcp_security.py +0 -0
  326. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_openclaw_adapter.py +0 -0
  327. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_operational_security.py +0 -0
  328. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_policy.py +0 -0
  329. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_quality_artifact.py +0 -0
  330. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_rule_registry.py +0 -0
  331. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_scanner.py +0 -0
  332. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_schema_contracts.py +0 -0
  333. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_security.py +0 -0
  334. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_security_ops.py +0 -0
  335. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_skill_security.py +0 -0
  336. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_submission.py +0 -0
  337. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_trust_scoring.py +0 -0
  338. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_trust_specs.py +0 -0
  339. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_verification.py +0 -0
  340. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_versioning.py +0 -0
  341. {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.95
3
+ Version: 2.0.97
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.95"
7
+ version = "2.0.97"
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.95"
7
+ version = "2.0.97"
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"
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import time
6
6
  import uuid
7
+ from collections.abc import Mapping
7
8
  from dataclasses import replace
8
9
  from datetime import datetime, timezone
9
10
  from pathlib import Path
@@ -86,6 +87,7 @@ def queue_blocked_approvals(
86
87
  why_now=incident["why_now"],
87
88
  launch_summary=incident["launch_summary"],
88
89
  risk_headline=incident["risk_headline"],
90
+ action_envelope_json=_item_action_envelope_json(item),
89
91
  )
90
92
  persisted_request_id = store.add_approval_request(request, timestamp)
91
93
  if persisted_request_id != request.request_id:
@@ -333,6 +335,13 @@ def _item_risk_signals(item: dict[str, object], artifact) -> tuple[str, ...]:
333
335
  return artifact_risk_signals(artifact) if artifact is not None else ()
334
336
 
335
337
 
338
+ def _item_action_envelope_json(item: dict[str, object]) -> dict[str, object] | None:
339
+ value = item.get("action_envelope_json")
340
+ if not isinstance(value, Mapping):
341
+ return None
342
+ return {str(key): item_value for key, item_value in value.items() if isinstance(key, str)}
343
+
344
+
336
345
  def _string_list(value: object) -> list[str]:
337
346
  if not isinstance(value, list):
338
347
  return []
@@ -80,6 +80,7 @@ from ..proxy import (
80
80
  )
81
81
  from ..receipts import build_receipt
82
82
  from ..risk import artifact_risk_signals, artifact_risk_summary
83
+ from ..runtime.actions import GuardActionEnvelope, normalize_harness_payload
83
84
  from ..runtime.runner import (
84
85
  GuardSyncNotConfiguredError,
85
86
  extract_prompt_requests,
@@ -1206,6 +1207,12 @@ def run_guard_command(
1206
1207
  runtime_workspace = current_workspace
1207
1208
  if args.harness == "copilot":
1208
1209
  runtime_workspace = _resolve_copilot_workspace_root(runtime_workspace)
1210
+ action_envelope = _hook_action_envelope(
1211
+ harness=args.harness,
1212
+ payload=payload,
1213
+ home_dir=context.home_dir,
1214
+ workspace=runtime_workspace,
1215
+ )
1209
1216
  copilot_hook_stage = _copilot_hook_stage(payload) if args.harness == "copilot" else None
1210
1217
  copilot_runtime_tool_call = (
1211
1218
  _copilot_runtime_tool_call(
@@ -1309,6 +1316,7 @@ def run_guard_command(
1309
1316
  "launch_target": json.dumps(runtime_arguments, sort_keys=True)
1310
1317
  if runtime_arguments is not None
1311
1318
  else runtime_artifact.command,
1319
+ "action_envelope_json": _action_envelope_json(action_envelope),
1312
1320
  }
1313
1321
  ]
1314
1322
  }
@@ -1667,6 +1675,7 @@ def run_guard_command(
1667
1675
  "source_scope": runtime_artifact.source_scope,
1668
1676
  "config_path": runtime_artifact.config_path,
1669
1677
  "launch_target": _runtime_request_summary(runtime_artifact),
1678
+ "action_envelope_json": _action_envelope_json(action_envelope),
1670
1679
  }
1671
1680
  ]
1672
1681
  }
@@ -3619,6 +3628,32 @@ def _load_hook_payload(event_file: str | None, *, input_text: str | None = None)
3619
3628
  return _normalize_hook_payload(payload) if isinstance(payload, dict) else {}
3620
3629
 
3621
3630
 
3631
+ _ACTION_ENVELOPE_HARNESSES = frozenset({"codex", "claude-code", "opencode", "copilot", "gemini"})
3632
+
3633
+
3634
+ def _hook_action_envelope(
3635
+ *,
3636
+ harness: str,
3637
+ payload: dict[str, object],
3638
+ home_dir: Path,
3639
+ workspace: Path | None,
3640
+ ) -> GuardActionEnvelope | None:
3641
+ canonical_harness = _canonical_harness_name(harness)
3642
+ if canonical_harness not in _ACTION_ENVELOPE_HARNESSES:
3643
+ return None
3644
+ return normalize_harness_payload(
3645
+ canonical_harness,
3646
+ _hook_event_name(payload) or "PreToolUse",
3647
+ payload,
3648
+ workspace=workspace,
3649
+ home_dir=home_dir,
3650
+ )
3651
+
3652
+
3653
+ def _action_envelope_json(envelope: GuardActionEnvelope | None) -> dict[str, object] | None:
3654
+ return envelope.to_dict() if envelope is not None else None
3655
+
3656
+
3622
3657
  def _normalize_hook_payload(payload: dict[str, object]) -> dict[str, object]:
3623
3658
  normalized = dict(payload)
3624
3659
  for source_key, target_key in (
@@ -141,6 +141,7 @@ class GuardReceipt:
141
141
  user_override: str | None = None
142
142
  artifact_name: str | None = None
143
143
  source_scope: str | None = None
144
+ action_envelope_json: dict[str, object] | None = None
144
145
 
145
146
  def to_dict(self) -> dict[str, object]:
146
147
  payload = asdict(self)
@@ -177,6 +178,7 @@ class GuardApprovalRequest:
177
178
  why_now: str | None = None
178
179
  launch_summary: str | None = None
179
180
  risk_headline: str | None = None
181
+ action_envelope_json: dict[str, object] | None = None
180
182
 
181
183
  def to_dict(self) -> dict[str, object]:
182
184
  payload = asdict(self)
@@ -73,9 +73,12 @@ _SENSITIVE_RAW_KEYS = frozenset(
73
73
  )
74
74
  _SENSITIVE_RAW_KEY_ALIASES = frozenset(key.replace("_", "") for key in _SENSITIVE_RAW_KEYS)
75
75
  _HOOK_EVENT_NAME_MAP = {
76
+ "prompt": "UserPromptSubmit",
76
77
  "userpromptsubmit": "UserPromptSubmit",
77
78
  "userpromptsubmitted": "UserPromptSubmit",
79
+ "pretool": "PreToolUse",
78
80
  "pretooluse": "PreToolUse",
81
+ "posttool": "PostToolUse",
79
82
  "posttooluse": "PostToolUse",
80
83
  "permissionrequest": "PermissionRequest",
81
84
  }
@@ -229,11 +232,122 @@ def normalize_codex_hook_payload(
229
232
  ) -> GuardActionEnvelope:
230
233
  """Normalize a Codex hook payload into a typed action envelope."""
231
234
 
235
+ return _normalize_action_payload(
236
+ payload,
237
+ harness="codex",
238
+ default_event_name=None,
239
+ workspace=workspace,
240
+ home_dir=home_dir,
241
+ )
242
+
243
+
244
+ def normalize_claude_hook_payload(
245
+ payload: Mapping[str, object],
246
+ *,
247
+ workspace: Path | str | None = None,
248
+ home_dir: Path | str | None = None,
249
+ ) -> GuardActionEnvelope:
250
+ """Normalize a Claude Code hook payload into a typed action envelope."""
251
+
252
+ return _normalize_action_payload(
253
+ payload,
254
+ harness="claude-code",
255
+ default_event_name=None,
256
+ workspace=workspace,
257
+ home_dir=home_dir,
258
+ )
259
+
260
+
261
+ def normalize_opencode_payload(
262
+ payload: Mapping[str, object],
263
+ *,
264
+ workspace: Path | str | None = None,
265
+ home_dir: Path | str | None = None,
266
+ ) -> GuardActionEnvelope:
267
+ """Normalize an OpenCode runtime payload into a typed action envelope."""
268
+
269
+ return _normalize_action_payload(
270
+ payload,
271
+ harness="opencode",
272
+ default_event_name=None,
273
+ workspace=workspace,
274
+ home_dir=home_dir,
275
+ )
276
+
277
+
278
+ def normalize_copilot_payload(
279
+ payload: Mapping[str, object],
280
+ *,
281
+ workspace: Path | str | None = None,
282
+ home_dir: Path | str | None = None,
283
+ ) -> GuardActionEnvelope:
284
+ """Normalize a Copilot runtime payload into a typed action envelope."""
285
+
286
+ return _normalize_action_payload(
287
+ payload,
288
+ harness="copilot",
289
+ default_event_name=None,
290
+ workspace=workspace,
291
+ home_dir=home_dir,
292
+ )
293
+
294
+
295
+ def normalize_gemini_payload(
296
+ payload: Mapping[str, object],
297
+ *,
298
+ workspace: Path | str | None = None,
299
+ home_dir: Path | str | None = None,
300
+ ) -> GuardActionEnvelope:
301
+ """Normalize a Gemini runtime payload into a typed action envelope."""
302
+
303
+ return _normalize_action_payload(
304
+ payload,
305
+ harness="gemini",
306
+ default_event_name=None,
307
+ workspace=workspace,
308
+ home_dir=home_dir,
309
+ )
310
+
311
+
312
+ def normalize_harness_payload(
313
+ harness: str,
314
+ event_name: str,
315
+ payload: Mapping[str, object],
316
+ *,
317
+ workspace: Path | str | None = None,
318
+ home_dir: Path | str | None = None,
319
+ ) -> GuardActionEnvelope:
320
+ """Normalize any supported Guard harness payload into a typed action envelope."""
321
+
322
+ normalized_harness = harness.strip().lower()
323
+ normalizers = {
324
+ "codex": normalize_codex_hook_payload,
325
+ "claude": normalize_claude_hook_payload,
326
+ "claude-code": normalize_claude_hook_payload,
327
+ "opencode": normalize_opencode_payload,
328
+ "copilot": normalize_copilot_payload,
329
+ "gemini": normalize_gemini_payload,
330
+ }
331
+ normalizer = normalizers.get(normalized_harness)
332
+ if normalizer is None:
333
+ raise ValueError(f"Unsupported Guard harness for action normalization: {harness}")
334
+ normalized_payload = _payload_with_default_event(payload, event_name)
335
+ return normalizer(normalized_payload, workspace=workspace, home_dir=home_dir)
336
+
337
+
338
+ def _normalize_action_payload(
339
+ payload: Mapping[str, object],
340
+ *,
341
+ harness: str,
342
+ default_event_name: str | None,
343
+ workspace: Path | str | None,
344
+ home_dir: Path | str | None,
345
+ ) -> GuardActionEnvelope:
232
346
  normalized_payload = dict(payload)
347
+ if default_event_name is not None:
348
+ normalized_payload = _payload_with_default_event(normalized_payload, default_event_name)
233
349
  event_name = _hook_event_name(normalized_payload)
234
- explicit_tool_name = _string_value(normalized_payload.get("tool_name")) or _string_value(
235
- normalized_payload.get("toolName")
236
- )
350
+ explicit_tool_name = _tool_name_from_payload(normalized_payload)
237
351
  tool_call_name, tool_call_input = _tool_call_from_payload(
238
352
  normalized_payload.get("toolCalls"),
239
353
  expected_tool_name=explicit_tool_name,
@@ -244,10 +358,10 @@ def normalize_codex_hook_payload(
244
358
  tool_input = tool_call_input
245
359
  raw_command = _command_from_payload(tool_input)
246
360
  command = _command_detail(raw_command, home_dir=home_dir)
247
- prompt_text = _prompt_text(normalized_payload.get("prompt"))
361
+ prompt_text = _prompt_text(_prompt_value(normalized_payload))
248
362
  prompt_excerpt = _prompt_excerpt(prompt_text)
249
- mcp_server, mcp_tool = _mcp_parts(tool_name)
250
- action_type = _codex_action_type(
363
+ mcp_server, mcp_tool = _mcp_details(normalized_payload, tool_name)
364
+ action_type = _action_type(
251
365
  event_name=event_name,
252
366
  tool_name=tool_name,
253
367
  command=raw_command,
@@ -266,7 +380,7 @@ def normalize_codex_hook_payload(
266
380
  return GuardActionEnvelope(
267
381
  schema_version=_SCHEMA_VERSION,
268
382
  action_id="",
269
- harness="codex",
383
+ harness=harness,
270
384
  event_name=event_name,
271
385
  action_type=action_type,
272
386
  workspace=workspace_label,
@@ -329,6 +443,26 @@ def _mapping_value(value: object) -> Mapping[str, object]:
329
443
  return {}
330
444
 
331
445
 
446
+ def _payload_with_default_event(payload: Mapping[str, object], event_name: str) -> dict[str, object]:
447
+ normalized_payload = dict(payload)
448
+ if not event_name.strip():
449
+ return normalized_payload
450
+ for key in ("event", "eventName", "hook_event_name", "hookEventName", "hook_name", "hookName"):
451
+ value = normalized_payload.get(key)
452
+ if isinstance(value, str) and value.strip():
453
+ return normalized_payload
454
+ normalized_payload["hook_event_name"] = event_name
455
+ return normalized_payload
456
+
457
+
458
+ def _tool_name_from_payload(payload: Mapping[str, object]) -> str | None:
459
+ for key in ("tool_name", "toolName", "name", "tool"):
460
+ value = _string_value(payload.get(key))
461
+ if value is not None:
462
+ return value
463
+ return None
464
+
465
+
332
466
  def _tool_input_from_payload(payload: Mapping[str, object]) -> Mapping[str, object]:
333
467
  for key in ("tool_input", "toolInput", "toolArgs", "arguments"):
334
468
  parsed = _mapping_from_value(payload.get(key))
@@ -375,7 +509,7 @@ def _mapping_from_value(value: object) -> Mapping[str, object] | None:
375
509
 
376
510
 
377
511
  def _hook_event_name(payload: Mapping[str, object]) -> str:
378
- for key in ("event", "hook_event_name", "hookEventName", "hook_name"):
512
+ for key in ("event", "eventName", "hook_event_name", "hookEventName", "hook_name", "hookName"):
379
513
  value = payload.get(key)
380
514
  if isinstance(value, str) and value.strip():
381
515
  stripped = value.strip()
@@ -383,6 +517,14 @@ def _hook_event_name(payload: Mapping[str, object]) -> str:
383
517
  return "PreToolUse"
384
518
 
385
519
 
520
+ def _prompt_value(payload: Mapping[str, object]) -> object:
521
+ for key in ("prompt", "userPrompt", "user_prompt", "message", "text", "input"):
522
+ value = payload.get(key)
523
+ if isinstance(value, str) and value.strip():
524
+ return value
525
+ return None
526
+
527
+
386
528
  def _command_from_payload(tool_input: Mapping[str, object]) -> str | None:
387
529
  for key in _COMMAND_KEYS:
388
530
  value = tool_input.get(key)
@@ -413,16 +555,70 @@ def _prompt_excerpt(prompt_text: str | None) -> str | None:
413
555
  return prompt_text[:_PROMPT_EXCERPT_LIMIT]
414
556
 
415
557
 
416
- def _mcp_parts(tool_name: str | None) -> tuple[str | None, str | None]:
417
- if tool_name is None or not tool_name.startswith("mcp__"):
558
+ def _mcp_details(payload: Mapping[str, object], tool_name: str | None) -> tuple[str | None, str | None]:
559
+ explicit_server = _string_from_keys(payload, ("mcp_server", "mcpServer", "server", "serverName"))
560
+ explicit_tool = _string_from_keys(payload, ("mcp_tool", "mcpTool"))
561
+ tool_name_value = _string_from_keys(payload, ("tool_name", "toolName"))
562
+ parts_server, parts_tool = _mcp_parts(tool_name, known_servers=_known_mcp_servers(payload))
563
+ server = explicit_server or parts_server
564
+ tool = explicit_tool or parts_tool
565
+ if tool is None and server is not None and tool_name_value is not None:
566
+ tool = tool_name_value
567
+ return server, tool
568
+
569
+
570
+ def _string_from_keys(payload: Mapping[str, object], keys: tuple[str, ...]) -> str | None:
571
+ for key in keys:
572
+ value = _string_value(payload.get(key))
573
+ if value is not None:
574
+ return value
575
+ return None
576
+
577
+
578
+ def _known_mcp_servers(payload: Mapping[str, object]) -> tuple[str, ...]:
579
+ servers: set[str] = set()
580
+ for key in ("mcp_servers", "mcpServers", "servers"):
581
+ value = payload.get(key)
582
+ if isinstance(value, Mapping):
583
+ servers.update(str(server_name).strip() for server_name in value if isinstance(server_name, str))
584
+ elif isinstance(value, list):
585
+ servers.update(item.strip() for item in value if isinstance(item, str) and item.strip())
586
+ return tuple(
587
+ sorted(
588
+ (server for server in servers if server), key=lambda server: len(_mcp_server_token(server)), reverse=True
589
+ )
590
+ )
591
+
592
+
593
+ def _mcp_parts(tool_name: str | None, *, known_servers: tuple[str, ...] = ()) -> tuple[str | None, str | None]:
594
+ if tool_name is None:
595
+ return None, None
596
+ if "/" in tool_name:
597
+ server, tool = tool_name.split("/", 1)
598
+ return (server, tool) if server and tool else (None, None)
599
+ if tool_name.startswith("mcp__"):
600
+ parts = tool_name.split("__", 2)
601
+ if len(parts) == 3 and parts[1] and parts[2]:
602
+ return parts[1], parts[2]
418
603
  return None, None
419
- parts = tool_name.split("__", 2)
420
- if len(parts) != 3 or not parts[1] or not parts[2]:
604
+ if tool_name.startswith("mcp_"):
605
+ suffix = tool_name[len("mcp_") :]
606
+ for server in known_servers:
607
+ server_token = _mcp_server_token(server)
608
+ prefix = f"{server_token}_"
609
+ if suffix.startswith(prefix):
610
+ tool = suffix[len(prefix) :]
611
+ return (server, tool) if tool else (None, None)
421
612
  return None, None
422
- return parts[1], parts[2]
613
+ return None, None
614
+
615
+
616
+ def _mcp_server_token(value: str) -> str:
617
+ token = re.sub(r"[^a-z0-9]+", "_", value.strip().lower())
618
+ return token.strip("_")
423
619
 
424
620
 
425
- def _codex_action_type(
621
+ def _action_type(
426
622
  *,
427
623
  event_name: str,
428
624
  tool_name: str | None,
@@ -573,7 +769,12 @@ def _normalized_command(command: str | None) -> str | None:
573
769
  __all__ = [
574
770
  "GuardActionEnvelope",
575
771
  "GuardActionType",
772
+ "normalize_claude_hook_payload",
576
773
  "normalize_codex_hook_payload",
774
+ "normalize_copilot_payload",
775
+ "normalize_gemini_payload",
776
+ "normalize_harness_payload",
777
+ "normalize_opencode_payload",
577
778
  "redacted_workspace_label",
578
779
  "stable_action_hash",
579
780
  ]
@@ -12,6 +12,7 @@ import urllib.error
12
12
  import urllib.parse
13
13
  import urllib.request
14
14
  from collections.abc import Callable
15
+ from contextlib import suppress
15
16
  from datetime import datetime, timezone
16
17
  from pathlib import Path
17
18
  from typing import Any
@@ -24,6 +25,7 @@ from ..consumer import detect_harness, evaluate_detection
24
25
  from ..models import GuardArtifact, HarnessDetection, PolicyDecision
25
26
  from ..store import GuardStore
26
27
  from ..types import PromptRequest, RemediationAction
28
+ from .actions import GuardActionEnvelope, redacted_workspace_label
27
29
 
28
30
  _APPROVAL_METADATA_KEYS = (
29
31
  "approval_center_url",
@@ -233,12 +235,18 @@ def guard_run(
233
235
  if not evaluation["blocked"]:
234
236
  evaluation = evaluate_detection(detection, store, config, default_action=default_action, persist=True)
235
237
 
238
+ action_envelope = _guard_run_action_envelope(harness, context, passthrough_args)
239
+ if evaluation["blocked"]:
240
+ evaluation = _evaluation_with_action_envelope(evaluation, action_envelope)
241
+
236
242
  if not dry_run and interactive_resolver is not None and evaluation["blocked"]:
237
243
  evaluation = interactive_resolver(detection, evaluation)
238
244
  elif not dry_run and blocked_resolver is not None and evaluation["blocked"]:
239
245
  pending_evaluation = blocked_resolver(detection, evaluation)
240
246
  detection = _detection_with_prompt_artifacts(detect_harness(harness, context), context, passthrough_args)
241
247
  reevaluated = evaluate_detection(detection, store, config, default_action=default_action, persist=True)
248
+ if reevaluated["blocked"]:
249
+ reevaluated = _evaluation_with_action_envelope(reevaluated, action_envelope)
242
250
  for key in _APPROVAL_METADATA_KEYS:
243
251
  if key in pending_evaluation:
244
252
  reevaluated[key] = pending_evaluation[key]
@@ -274,6 +282,61 @@ def guard_run(
274
282
  return evaluation
275
283
 
276
284
 
285
+ def _guard_run_action_envelope(
286
+ harness: str,
287
+ context: HarnessContext,
288
+ passthrough_args: list[str],
289
+ ) -> GuardActionEnvelope:
290
+ workspace = context.workspace_dir
291
+ workspace_hash = None
292
+ if workspace is not None:
293
+ workspace_path = workspace.expanduser()
294
+ with suppress(OSError):
295
+ workspace_path = workspace_path.resolve()
296
+ workspace_hash = hashlib.sha256(str(workspace_path).encode("utf-8")).hexdigest()
297
+ return GuardActionEnvelope(
298
+ schema_version=1,
299
+ action_id="",
300
+ harness=harness,
301
+ event_name="HarnessStart",
302
+ action_type="harness_start",
303
+ workspace=redacted_workspace_label(workspace, home_dir=context.home_dir),
304
+ workspace_hash=workspace_hash,
305
+ tool_name=None,
306
+ command=None,
307
+ prompt_excerpt=None,
308
+ target_paths=(),
309
+ network_hosts=(),
310
+ mcp_server=None,
311
+ mcp_tool=None,
312
+ package_manager=None,
313
+ package_name=None,
314
+ script_name=None,
315
+ raw_payload_redacted={"passthrough_arg_count": len(passthrough_args)},
316
+ )
317
+
318
+
319
+ def _evaluation_with_action_envelope(
320
+ evaluation: dict[str, Any],
321
+ action_envelope: GuardActionEnvelope,
322
+ ) -> dict[str, Any]:
323
+ artifacts = evaluation.get("artifacts")
324
+ if not isinstance(artifacts, list):
325
+ return evaluation
326
+ action_payload = action_envelope.to_dict()
327
+ normalized_artifacts: list[object] = []
328
+ changed = False
329
+ for item in artifacts:
330
+ if isinstance(item, dict) and "action_envelope_json" not in item:
331
+ normalized_artifacts.append({**item, "action_envelope_json": action_payload})
332
+ changed = True
333
+ else:
334
+ normalized_artifacts.append(item)
335
+ if not changed:
336
+ return evaluation
337
+ return {**evaluation, "artifacts": normalized_artifacts}
338
+
339
+
277
340
  def _guard_run_config_paths(
278
341
  *,
279
342
  detection: HarnessDetection,
@@ -666,6 +666,7 @@ class GuardStore:
666
666
  self._ensure_approval_column(connection, "why_now", "text")
667
667
  self._ensure_approval_column(connection, "launch_summary", "text")
668
668
  self._ensure_approval_column(connection, "risk_headline", "text")
669
+ self._ensure_approval_column(connection, "action_envelope_json", "text")
669
670
  self._ensure_approval_column(connection, "workspace", "text")
670
671
  self._ensure_attachment_column(connection, "lease_id", "text not null default ''")
671
672
  self._ensure_attachment_column(connection, "lease_expires_at", "text")
@@ -30,12 +30,13 @@ def approval_schema_statement() -> str:
30
30
  risk_signals_json text not null default '[]',
31
31
  artifact_label text,
32
32
  source_label text,
33
- trigger_summary text,
34
- why_now text,
35
- launch_summary text,
36
- risk_headline text,
37
- review_command text not null,
38
- approval_url text not null,
33
+ trigger_summary text,
34
+ why_now text,
35
+ launch_summary text,
36
+ risk_headline text,
37
+ action_envelope_json text,
38
+ review_command text not null,
39
+ approval_url text not null,
39
40
  status text not null,
40
41
  resolution_action text,
41
42
  resolution_scope text,
@@ -68,7 +69,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
68
69
  recommended_scope = ?, changed_fields_json = ?, source_scope = ?, config_path = ?, workspace = ?,
69
70
  launch_target = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
70
71
  artifact_label = ?, source_label = ?, trigger_summary = ?, why_now = ?, launch_summary = ?,
71
- risk_headline = ?,
72
+ risk_headline = ?, action_envelope_json = ?,
72
73
  review_command = ?, approval_url = ?, created_at = ?
73
74
  where request_id = ?
74
75
  """,
@@ -93,6 +94,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
93
94
  request.why_now,
94
95
  request.launch_summary,
95
96
  request.risk_headline,
97
+ json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
96
98
  review_command,
97
99
  approval_url,
98
100
  now,
@@ -105,12 +107,12 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
105
107
  insert into approval_requests (
106
108
  request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
107
109
  recommended_scope, changed_fields_json, source_scope, config_path, workspace,
108
- launch_target, transport, risk_summary,
109
- risk_signals_json, artifact_label, source_label, trigger_summary, why_now, launch_summary, risk_headline,
110
- review_command,
111
- approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
112
- )
113
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
110
+ launch_target, transport, risk_summary,
111
+ 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
116
  """,
115
117
  (
116
118
  request.request_id,
@@ -136,6 +138,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
136
138
  request.why_now,
137
139
  request.launch_summary,
138
140
  request.risk_headline,
141
+ json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
139
142
  request.review_command,
140
143
  request.approval_url,
141
144
  "pending",
@@ -182,9 +185,9 @@ def list_approval_requests(
182
185
  query = f"""
183
186
  select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
184
187
  recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
185
- risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
186
- launch_summary, risk_headline, review_command,
187
- approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
188
+ risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
189
+ launch_summary, risk_headline, action_envelope_json, review_command,
190
+ approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
188
191
  from approval_requests
189
192
  {where_clause}
190
193
  order by created_at desc
@@ -201,9 +204,9 @@ def get_approval_request(connection: sqlite3.Connection, request_id: str) -> dic
201
204
  """
202
205
  select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
203
206
  recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
204
- risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
205
- launch_summary, risk_headline, review_command,
206
- approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
207
+ risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
208
+ launch_summary, risk_headline, action_envelope_json, review_command,
209
+ approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
207
210
  from approval_requests
208
211
  where request_id = ?
209
212
  """,
@@ -273,6 +276,7 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
273
276
  "why_now": row["why_now"],
274
277
  "launch_summary": row["launch_summary"],
275
278
  "risk_headline": row["risk_headline"],
279
+ "action_envelope_json": _optional_json_object(row["action_envelope_json"]),
276
280
  "review_command": str(row["review_command"]),
277
281
  "approval_url": str(row["approval_url"]),
278
282
  "status": str(row["status"]),
@@ -282,3 +286,15 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
282
286
  "created_at": str(row["created_at"]),
283
287
  "resolved_at": row["resolved_at"],
284
288
  }
289
+
290
+
291
+ def _optional_json_object(value: object) -> dict[str, object] | None:
292
+ if value is None:
293
+ return None
294
+ try:
295
+ parsed = json.loads(str(value))
296
+ except (json.JSONDecodeError, TypeError, ValueError):
297
+ return None
298
+ if isinstance(parsed, dict):
299
+ return {str(key): item for key, item in parsed.items() if isinstance(key, str)}
300
+ return None
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.95"
3
+ __version__ = "2.0.97"