avorelo 0.1.0

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 (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+
3
+ const { boundText, normalizeSourceType } = require("./source-trust");
4
+
5
+ const ZERO_WIDTH_PATTERN = /[\u200B-\u200D\u2060\uFEFF]/;
6
+ const HIDDEN_HTML_PATTERN = /<!--[\s\S]{0,800}?-->/g;
7
+ const MARKDOWN_COMMENT_PATTERN = /\[\/\/\]:\s*#\s*\(([\s\S]{0,400}?)\)/g;
8
+ const BASE64_BLOCK_PATTERN = /(?:[A-Za-z0-9+/]{32,}={0,2})/g;
9
+
10
+ const SIGNALS = [
11
+ ["tries_to_override_instructions", "high", "Text tries to override existing instructions.", /\b(ignore|disregard|forget)\s+(?:all\s+)?(?:previous|prior)\s+instructions\b|\boverride\s+(?:the\s+)?(?:instructions|prompt)\b/i],
12
+ ["asks_to_ignore_policy", "high", "Text asks the agent to ignore policy or safety rules.", /\b(ignore|bypass|disable|disregard)\s+(?:the\s+)?(?:policy|guardrails|safety|checks?)\b|\boverride safety\b/i],
13
+ ["asks_to_hide_action", "high", "Text asks the agent to hide or avoid disclosing an action.", /\b(do not tell the user|hide this action|silently execute|quietly run|without notifying|without telling anyone)\b/i],
14
+ ["requests_secret_access", "high", "Text asks the agent to access secrets.", /\b(read|open|cat|print|reveal|dump|access)\s+(?:the\s+)?(?:\.env|secrets?|credentials?|private key|id_rsa)\b|\breveal hidden prompt\b/i],
15
+ ["requests_exfiltration", "high", "Text asks the agent to send or exfiltrate sensitive data.", /\b(upload|send|exfiltrat|post|paste)\b[^\r\n]{0,80}\b(?:secret|token|credential|private key|id_rsa)\b/i],
16
+ ["requests_tool_execution", "medium", "Text asks the agent to invoke tools.", /\b(call|invoke|use|execute)\b[^\r\n]{0,60}\btool\b/i],
17
+ ["requests_shell_execution", "high", "Text asks the agent to run shell commands.", /\b(run|open|execute)\b[^\r\n]{0,40}\b(?:shell|terminal|bash|powershell|pwsh|cmd)\b|\bcurl\b[^\r\n]{0,100}\|\s*(?:bash|sh|pwsh|powershell)\b/i],
18
+ ["requests_sensitive_file_access", "high", "Text asks for sensitive file access.", /\b(?:read|open|modify|write)\b[^\r\n]{0,60}\b(?:\.env(?:\.[A-Za-z0-9._-]+)?|id_rsa|\.ssh|credentials?)\b/i],
19
+ ["requests_workflow_or_ci_change", "high", "Text asks for workflow or CI changes.", /\b(?:modify|edit|update|change)\b[^\r\n]{0,80}\b(?:workflow|workflows|github actions|ci)\b|\bpush to main\b/i],
20
+ ["requests_destructive_action", "high", "Text asks for destructive action.", /\b(delete files|rm\s+-rf|remove-item|truncate|drop database|force push)\b/i],
21
+ ["pretends_to_be_system_message", "high", "Text pretends to be a higher-priority instruction.", /^(?:\s*)(?:system|developer)\s*[:>]|<\s*(?:system|developer)\s*>|\bact as system\b|\bdeveloper message\b/im],
22
+ ];
23
+
24
+ function riskRank(level) {
25
+ if (level === "high") return 3;
26
+ if (level === "medium") return 2;
27
+ return 1;
28
+ }
29
+
30
+ function buildLocation(text, index) {
31
+ const snippet = String(text || "").slice(0, Math.max(0, index));
32
+ return `line ${snippet.split(/\r?\n/).length}`;
33
+ }
34
+
35
+ function buildPreview(text, index, length) {
36
+ return boundText(String(text || "").slice(index, index + Math.max(length, 1)).replace(/\s+/g, " "), 120);
37
+ }
38
+
39
+ function pushFinding(out, dedupe, finding) {
40
+ const key = `${finding.category}:${finding.location}:${finding.evidencePreview}`;
41
+ if (dedupe.has(key)) return;
42
+ dedupe.add(key);
43
+ out.push(finding);
44
+ }
45
+
46
+ function scanInstructionRisk(content, options = {}) {
47
+ const text = String(content || "");
48
+ const sourceType = normalizeSourceType(options.sourceType || "unknown_external");
49
+ const findings = [];
50
+ const dedupe = new Set();
51
+ const signals = new Set();
52
+
53
+ SIGNALS.forEach(([category, severity, message, pattern]) => {
54
+ const regex = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
55
+ let match = regex.exec(text);
56
+ while (match) {
57
+ signals.add(category);
58
+ pushFinding(findings, dedupe, {
59
+ category,
60
+ severity,
61
+ message,
62
+ evidencePreview: buildPreview(text, match.index, match[0].length),
63
+ location: buildLocation(text, match.index),
64
+ });
65
+ match = regex.exec(text);
66
+ }
67
+ });
68
+
69
+ let match = HIDDEN_HTML_PATTERN.exec(text);
70
+ while (match) {
71
+ if (/(ignore|bypass|secret|token|run|tool|shell|system|developer)/i.test(match[0])) {
72
+ signals.add("html_or_markdown_hidden_instruction");
73
+ pushFinding(findings, dedupe, {
74
+ category: "html_or_markdown_hidden_instruction",
75
+ severity: "medium",
76
+ message: "Hidden HTML comment contains instruction-like text.",
77
+ evidencePreview: buildPreview(text, match.index, match[0].length),
78
+ location: buildLocation(text, match.index),
79
+ });
80
+ }
81
+ match = HIDDEN_HTML_PATTERN.exec(text);
82
+ }
83
+
84
+ match = MARKDOWN_COMMENT_PATTERN.exec(text);
85
+ while (match) {
86
+ if (/(ignore|bypass|secret|token|run|tool|shell)/i.test(match[0])) {
87
+ signals.add("html_or_markdown_hidden_instruction");
88
+ pushFinding(findings, dedupe, {
89
+ category: "html_or_markdown_hidden_instruction",
90
+ severity: "medium",
91
+ message: "Hidden markdown comment contains instruction-like text.",
92
+ evidencePreview: buildPreview(text, match.index, match[0].length),
93
+ location: buildLocation(text, match.index),
94
+ });
95
+ }
96
+ match = MARKDOWN_COMMENT_PATTERN.exec(text);
97
+ }
98
+
99
+ match = ZERO_WIDTH_PATTERN.exec(text);
100
+ if (match) {
101
+ signals.add("zero_width_or_invisible_text");
102
+ pushFinding(findings, dedupe, {
103
+ category: "zero_width_or_invisible_text",
104
+ severity: "medium",
105
+ message: "Invisible or zero-width characters are present.",
106
+ evidencePreview: buildPreview(text, match.index, 40),
107
+ location: buildLocation(text, match.index),
108
+ });
109
+ }
110
+
111
+ const base64Matches = text.match(BASE64_BLOCK_PATTERN) || [];
112
+ if (base64Matches.length && /(ignore|bypass|shell|tool|secret|token|system|developer|prompt)/i.test(text)) {
113
+ const first = base64Matches[0];
114
+ const index = text.indexOf(first);
115
+ signals.add("encoded_or_obfuscated_instruction");
116
+ pushFinding(findings, dedupe, {
117
+ category: "encoded_or_obfuscated_instruction",
118
+ severity: "medium",
119
+ message: "Encoded or obfuscated text appears near instruction-like language.",
120
+ evidencePreview: buildPreview(text, index, first.length),
121
+ location: buildLocation(text, index),
122
+ });
123
+ }
124
+
125
+ const imperativeIndex = text.search(/\b(ignore|bypass|disable|run|read|open|send|upload|delete|modify|edit|push|install|call)\b/i);
126
+ if (imperativeIndex >= 0 && ["mcp_response", "rag_chunk", "web_content", "issue_ticket", "unknown_external"].includes(sourceType)) {
127
+ signals.add("instruction_inside_untrusted_content");
128
+ pushFinding(findings, dedupe, {
129
+ category: "instruction_inside_untrusted_content",
130
+ severity: "high",
131
+ message: "Instruction-like text appears inside untrusted content.",
132
+ evidencePreview: buildPreview(text, imperativeIndex, 80),
133
+ location: buildLocation(text, imperativeIndex),
134
+ });
135
+ }
136
+ if (imperativeIndex >= 0 && sourceType === "mcp_response") {
137
+ signals.add("tool_response_instruction");
138
+ pushFinding(findings, dedupe, {
139
+ category: "tool_response_instruction",
140
+ severity: "high",
141
+ message: "Tool response contains instruction-like text.",
142
+ evidencePreview: buildPreview(text, imperativeIndex, 80),
143
+ location: buildLocation(text, imperativeIndex),
144
+ });
145
+ }
146
+
147
+ let instructionRisk = "low";
148
+ findings.forEach((finding) => {
149
+ if (riskRank(finding.severity) > riskRank(instructionRisk)) instructionRisk = finding.severity;
150
+ });
151
+ if (instructionRisk === "low" && findings.length >= 2) instructionRisk = "medium";
152
+
153
+ return {
154
+ instructionRisk,
155
+ signals: Array.from(signals),
156
+ findings: findings.slice(0, 20),
157
+ recommendation: instructionRisk === "high"
158
+ ? (["mcp_response", "rag_chunk", "web_content", "issue_ticket", "unknown_external"].includes(sourceType) ? "treat_as_data_only" : "require_approval")
159
+ : findings.length > 0
160
+ ? "use_sanitized_copy"
161
+ : "treat_as_data_only",
162
+ };
163
+ }
164
+
165
+ function sanitizeInstructionLikeContent(content) {
166
+ let sanitized = String(content || "");
167
+ SIGNALS.forEach(([, , , pattern]) => {
168
+ sanitized = sanitized.replace(pattern, "[Removed suspicious instruction-like content]");
169
+ });
170
+ sanitized = sanitized
171
+ .replace(HIDDEN_HTML_PATTERN, "[Removed suspicious instruction-like content]")
172
+ .replace(MARKDOWN_COMMENT_PATTERN, "[Removed suspicious instruction-like content]")
173
+ .replace(ZERO_WIDTH_PATTERN, "")
174
+ .replace(BASE64_BLOCK_PATTERN, (match) => (match.length >= 32 ? "[Removed suspicious instruction-like content]" : match));
175
+ return boundText(sanitized, 4000);
176
+ }
177
+
178
+ module.exports = {
179
+ scanInstructionRisk,
180
+ sanitizeInstructionLikeContent,
181
+ };
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+
3
+ const { classifyMcpAction } = require("./mcp-action-rules");
4
+
5
+ const MCP_ACTION_ADAPTER_ID = "mcp-action-v1";
6
+ const PREVIEW_LIMIT = 400;
7
+
8
+ function truncatePreview(value) {
9
+ const text = String(value || "");
10
+ if (text.length <= PREVIEW_LIMIT) return text;
11
+ return `${text.slice(0, PREVIEW_LIMIT)}...`;
12
+ }
13
+
14
+ function redactPreview(value) {
15
+ return truncatePreview(String(value || "").replace(
16
+ /((?:secret|token|password|api[_-]?key)[^=\n]{0,20}=)[^\s\n]+/ig,
17
+ "$1[redacted]",
18
+ ));
19
+ }
20
+
21
+ function buildMcpAdapterResult(base, overrides = {}) {
22
+ return {
23
+ actionId: base.actionId,
24
+ componentId: base.componentId || null,
25
+ componentType: base.componentType || null,
26
+ actionType: base.actionType,
27
+ decision: base.decision || null,
28
+ adapterId: MCP_ACTION_ADAPTER_ID,
29
+ enforcementAvailable: true,
30
+ effectiveBehavior: "recommended_only",
31
+ executed: false,
32
+ exitCode: null,
33
+ stdoutPreview: "",
34
+ stderrPreview: "",
35
+ reason: "",
36
+ timestamp: base.timestamp,
37
+ ...overrides,
38
+ };
39
+ }
40
+
41
+ function enforceMcpAction(action, context = {}) {
42
+ const decisionRecord = context.decisionRecord || null;
43
+ const limitPlan = context.limitPlan || null;
44
+ const execute = context.execute === true;
45
+ const executor = typeof context.executor === "function" ? context.executor : null;
46
+ const profile = classifyMcpAction(action);
47
+ const base = {
48
+ actionId: action.actionId,
49
+ componentId: action.componentId,
50
+ componentType: action.componentType,
51
+ actionType: action.actionType,
52
+ decision: decisionRecord?.decision || null,
53
+ timestamp: context.timestamp,
54
+ };
55
+
56
+ if (profile.deleteLike || profile.adminLike) {
57
+ return buildMcpAdapterResult(base, {
58
+ effectiveBehavior: "blocked",
59
+ reason: "Destructive or admin MCP actions are not allowed by the limited plan.",
60
+ });
61
+ }
62
+
63
+ if (profile.secretLike) {
64
+ return buildMcpAdapterResult(base, {
65
+ effectiveBehavior: "blocked",
66
+ reason: "Secrets or credential modification is not allowed.",
67
+ });
68
+ }
69
+
70
+ if (profile.workflowLike) {
71
+ return buildMcpAdapterResult(base, {
72
+ effectiveBehavior: "blocked",
73
+ reason: "Workflow modification is not allowed by the limited plan.",
74
+ });
75
+ }
76
+
77
+ if (profile.deployLike || profile.pushMainLike) {
78
+ return buildMcpAdapterResult(base, {
79
+ effectiveBehavior: "blocked",
80
+ reason: "Push-to-main, deploy, or publish style MCP actions require explicit approval.",
81
+ });
82
+ }
83
+
84
+ if (decisionRecord?.decision === "deny") {
85
+ return buildMcpAdapterResult(base, {
86
+ effectiveBehavior: "blocked",
87
+ reason: "User denied this MCP action.",
88
+ });
89
+ }
90
+
91
+ if (decisionRecord?.decision === "let_wuz_limit" && limitPlan?.recommendedMode === "deny") {
92
+ return buildMcpAdapterResult(base, {
93
+ effectiveBehavior: "blocked",
94
+ reason: limitPlan.reason || "No safe limited version found. Recommended: deny.",
95
+ });
96
+ }
97
+
98
+ if (decisionRecord?.decision === "let_wuz_limit" && limitPlan?.recommendedMode !== "limited") {
99
+ return buildMcpAdapterResult(base, {
100
+ effectiveBehavior: "blocked",
101
+ reason: limitPlan?.reason || "The limited plan could not safely allow this MCP action.",
102
+ });
103
+ }
104
+
105
+ if (decisionRecord?.decision === "let_wuz_limit") {
106
+ if (limitPlan?.scope?.mcpServer && limitPlan.scope.mcpServer !== profile.mcpServer) {
107
+ return buildMcpAdapterResult(base, {
108
+ effectiveBehavior: "blocked",
109
+ reason: "The limited plan only allows the evaluated MCP server.",
110
+ });
111
+ }
112
+ if (limitPlan?.scope?.toolName && limitPlan.scope.toolName !== profile.toolName) {
113
+ return buildMcpAdapterResult(base, {
114
+ effectiveBehavior: "blocked",
115
+ reason: "The limited plan only allows the evaluated MCP tool.",
116
+ });
117
+ }
118
+ }
119
+
120
+ if (!execute || !executor) {
121
+ return buildMcpAdapterResult(base, {
122
+ effectiveBehavior: decisionRecord?.decision === "let_wuz_limit" ? "limited" : "dry_run_only",
123
+ reason: execute && !executor
124
+ ? "No MCP executor was supplied. Enforcement stayed in dry-run mode."
125
+ : (
126
+ decisionRecord?.decision === "let_wuz_limit"
127
+ ? (limitPlan?.reason || "Wuz limited this MCP action to the current task.")
128
+ : "Execution stayed in dry-run mode."
129
+ ),
130
+ });
131
+ }
132
+
133
+ const child = executor({
134
+ action,
135
+ decisionRecord,
136
+ limitPlan,
137
+ target: profile.target,
138
+ mcpServer: profile.mcpServer,
139
+ toolName: profile.toolName,
140
+ args: profile.args,
141
+ resource: profile.resource,
142
+ });
143
+ const stdoutPreview = redactPreview(child?.stdout || "");
144
+ const stderrPreview = redactPreview(child?.stderr || child?.error?.message || "");
145
+ const exitCode = typeof child?.status === "number"
146
+ ? child.status
147
+ : typeof child?.exitCode === "number"
148
+ ? child.exitCode
149
+ : child?.error
150
+ ? 1
151
+ : 1;
152
+
153
+ return buildMcpAdapterResult(base, {
154
+ effectiveBehavior: decisionRecord?.decision === "let_wuz_limit" ? "limited" : "allowed",
155
+ executed: true,
156
+ exitCode,
157
+ stdoutPreview,
158
+ stderrPreview,
159
+ reason: exitCode === 0
160
+ ? (decisionRecord?.decision === "let_wuz_limit"
161
+ ? "MCP action executed within the limited task scope."
162
+ : "Approved MCP action executed once.")
163
+ : "MCP action executed but exited with a non-zero status.",
164
+ });
165
+ }
166
+
167
+ const MCP_ACTION_ADAPTER = {
168
+ adapterId: MCP_ACTION_ADAPTER_ID,
169
+ canEvaluate: true,
170
+ canEnforce: true,
171
+ supportedActionTypes: ["mcp.read", "mcp.write", "mcp.delete", "mcp.admin", "mcp.externalMutation", "mcp.unknown"],
172
+ enforce(action, decisionRecord, limitPlan, options = {}) {
173
+ return enforceMcpAction(action, {
174
+ ...options,
175
+ decisionRecord,
176
+ limitPlan,
177
+ });
178
+ },
179
+ };
180
+
181
+ module.exports = {
182
+ MCP_ACTION_ADAPTER_ID,
183
+ MCP_ACTION_ADAPTER,
184
+ enforceMcpAction,
185
+ };
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+
5
+ function stableStringify(value) {
6
+ if (Array.isArray(value)) {
7
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
8
+ }
9
+ if (value && typeof value === "object") {
10
+ return `{${Object.keys(value)
11
+ .sort((left, right) => left.localeCompare(right))
12
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
13
+ .join(",")}}`;
14
+ }
15
+ return JSON.stringify(value);
16
+ }
17
+
18
+ function sha256(value) {
19
+ return crypto.createHash("sha256").update(String(value || "")).digest("hex");
20
+ }
21
+
22
+ function normalizeMcpServer(value) {
23
+ const normalized = String(value || "").trim();
24
+ return normalized || "unknown";
25
+ }
26
+
27
+ function inferToolName(action) {
28
+ const explicit = String(action?.toolName || "").trim();
29
+ if (explicit) return explicit;
30
+ const target = String(action?.target || "").trim();
31
+ if (target.includes(":")) return target.split(":").slice(1).join(":").trim() || "unknown";
32
+ return "unknown";
33
+ }
34
+
35
+ function normalizeResource(action) {
36
+ return action?.resource && typeof action.resource === "object" ? action.resource : {};
37
+ }
38
+
39
+ function normalizeArgs(action) {
40
+ return action?.args && typeof action.args === "object" ? action.args : {};
41
+ }
42
+
43
+ function argsHashFromAction(action) {
44
+ const args = normalizeArgs(action);
45
+ if (!Object.keys(args).length) return null;
46
+ return sha256(stableStringify(args));
47
+ }
48
+
49
+ function targetHashFromMcpAction(action) {
50
+ const normalized = {
51
+ actionType: String(action?.actionType || ""),
52
+ mcpServer: normalizeMcpServer(action?.mcpServer),
53
+ toolName: inferToolName(action),
54
+ target: String(action?.target || ""),
55
+ resource: normalizeResource(action),
56
+ };
57
+ return sha256(stableStringify(normalized));
58
+ }
59
+
60
+ function hasPattern(text, pattern) {
61
+ return pattern.test(String(text || ""));
62
+ }
63
+
64
+ function classifyMcpAction(action) {
65
+ const actionType = String(action?.actionType || "mcp.unknown");
66
+ const mcpServer = normalizeMcpServer(action?.mcpServer);
67
+ const toolName = inferToolName(action);
68
+ const target = String(action?.target || `${mcpServer}:${toolName}`);
69
+ const resource = normalizeResource(action);
70
+ const args = normalizeArgs(action);
71
+ const analysisText = [
72
+ actionType,
73
+ mcpServer,
74
+ toolName,
75
+ target,
76
+ stableStringify(resource),
77
+ stableStringify(args),
78
+ ].join("\n").toLowerCase();
79
+
80
+ const deleteLike = actionType === "mcp.delete" || hasPattern(analysisText, /\b(delete|remove|destroy)\b/);
81
+ const adminLike = actionType === "mcp.admin" || hasPattern(analysisText, /\b(admin|owner|permission|access|member|user)\b/);
82
+ const secretLike = hasPattern(analysisText, /\b(secret|token|credential|private[_-]?key|id_rsa)\b/);
83
+ const workflowLike = hasPattern(analysisText, /\b(workflow|actions?)\b/);
84
+ const deployLike = hasPattern(analysisText, /\b(deploy|release|publish)\b/);
85
+ const pushMainLike = hasPattern(analysisText, /\b(push_main|force_push)\b/)
86
+ || (hasPattern(toolName, /\b(push|merge)\b/) && String(resource.branch || resource.baseBranch || "").toLowerCase() === "main");
87
+ const externalMutation = actionType === "mcp.write" || actionType === "mcp.externalMutation" || deleteLike || adminLike;
88
+ const readOnly = actionType === "mcp.read" || hasPattern(toolName, /\b(get|list|read|fetch|describe|metadata)\b/);
89
+ const createPullRequest = hasPattern(toolName, /\b(create|open)[_-]?(pull[_-]?request|pr)\b/);
90
+ const createIssue = hasPattern(toolName, /\b(create|open|update)[_-]?issue\b/);
91
+ const knownServer = mcpServer !== "unknown";
92
+ const unknownWrite = !readOnly && !createPullRequest && !createIssue && !deleteLike && !adminLike && !secretLike && !workflowLike;
93
+ const repoScope = String(resource.repo || "").trim() || "current";
94
+ const branch = String(resource.branch || "").trim();
95
+ const safeBranchPrefix = !branch || /^wuz[/-]/i.test(branch) || branch === "current";
96
+
97
+ const reasons = [];
98
+ let riskLevel = "medium";
99
+ let recommendedDecision = "limit";
100
+ let safeLimited = false;
101
+
102
+ if (deleteLike || adminLike) {
103
+ reasons.push("Delete or admin MCP actions can mutate external systems destructively.");
104
+ riskLevel = "high";
105
+ recommendedDecision = "deny";
106
+ } else if (secretLike) {
107
+ reasons.push("Secrets, tokens, or credential changes should be denied.");
108
+ riskLevel = "high";
109
+ recommendedDecision = "deny";
110
+ } else if (workflowLike) {
111
+ reasons.push("Workflow changes can alter automation and supply-chain behavior.");
112
+ riskLevel = "high";
113
+ recommendedDecision = "deny";
114
+ } else if (deployLike || pushMainLike) {
115
+ reasons.push("Deploy, publish, or push-to-main style MCP actions are high risk.");
116
+ riskLevel = "high";
117
+ recommendedDecision = "deny";
118
+ } else if (createPullRequest) {
119
+ reasons.push("Pull request creation can be limited to the current repo and safe branch scope.");
120
+ riskLevel = knownServer ? "medium" : "high";
121
+ recommendedDecision = "limit";
122
+ safeLimited = knownServer && safeBranchPrefix;
123
+ } else if (createIssue) {
124
+ reasons.push("Issue creation can be limited to the current repo.");
125
+ riskLevel = knownServer ? "medium" : "high";
126
+ recommendedDecision = "limit";
127
+ safeLimited = knownServer;
128
+ } else if (readOnly) {
129
+ reasons.push("Read-only MCP metadata access is low risk.");
130
+ riskLevel = knownServer ? "low" : "medium";
131
+ recommendedDecision = "limit";
132
+ safeLimited = knownServer;
133
+ } else if (unknownWrite && !knownServer) {
134
+ reasons.push("Unknown MCP write semantics need explicit approval before external mutation.");
135
+ riskLevel = "high";
136
+ recommendedDecision = "limit";
137
+ } else if (unknownWrite) {
138
+ reasons.push("MCP write actions should stay scoped to known safe operations.");
139
+ riskLevel = "medium";
140
+ recommendedDecision = "limit";
141
+ } else if (externalMutation) {
142
+ reasons.push("External mutation through MCP requires approval.");
143
+ riskLevel = "medium";
144
+ recommendedDecision = "limit";
145
+ } else {
146
+ reasons.push("MCP action should be reviewed before broader external access is allowed.");
147
+ }
148
+
149
+ return {
150
+ actionType,
151
+ mcpServer,
152
+ toolName,
153
+ target,
154
+ resource,
155
+ args,
156
+ repoScope,
157
+ branch,
158
+ safeBranchPrefix,
159
+ knownServer,
160
+ readOnly,
161
+ externalMutation,
162
+ createPullRequest,
163
+ createIssue,
164
+ unknownWrite,
165
+ deleteLike,
166
+ adminLike,
167
+ secretLike,
168
+ workflowLike,
169
+ deployLike,
170
+ pushMainLike,
171
+ safeLimited,
172
+ argsHash: argsHashFromAction(action),
173
+ targetHash: targetHashFromMcpAction(action),
174
+ riskLevel,
175
+ recommendedDecision,
176
+ reasons,
177
+ };
178
+ }
179
+
180
+ module.exports = {
181
+ argsHashFromAction,
182
+ targetHashFromMcpAction,
183
+ classifyMcpAction,
184
+ };