@opacedev/astro-ai-content-checker 0.3.1

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 (185) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/LICENSE +9 -0
  3. package/README.md +236 -0
  4. package/SBOM.cdx.json +172 -0
  5. package/SECURITY.md +9 -0
  6. package/THIRD_PARTY_NOTICES.md +36 -0
  7. package/dist/build-report-html.d.ts +3 -0
  8. package/dist/highlight.js +155 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.js +43 -0
  11. package/dist/options.d.ts +21 -0
  12. package/dist/options.js +6 -0
  13. package/dist/receipt.d.ts +15 -0
  14. package/dist/receipt.js +100 -0
  15. package/dist/report.d.ts +55 -0
  16. package/dist/report.js +12 -0
  17. package/dist/sections.js +181 -0
  18. package/dist/share.d.ts +47 -0
  19. package/dist/share.js +72 -0
  20. package/dist/shared-EX54KI6H.js +1503 -0
  21. package/dist/shared-MMXWTX2U.js +43 -0
  22. package/dist/toolbar.js +13478 -0
  23. package/node_modules/@opacedev/ai-content-checker-browser/LICENSE +21 -0
  24. package/node_modules/@opacedev/ai-content-checker-browser/README.md +145 -0
  25. package/node_modules/@opacedev/ai-content-checker-browser/dist/dom/visible-text.d.ts +14 -0
  26. package/node_modules/@opacedev/ai-content-checker-browser/dist/dom/visible-text.js +47 -0
  27. package/node_modules/@opacedev/ai-content-checker-browser/dist/index.d.ts +3 -0
  28. package/node_modules/@opacedev/ai-content-checker-browser/dist/index.js +99 -0
  29. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/client.d.ts +12 -0
  30. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/client.js +17 -0
  31. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/entry.d.ts +1 -0
  32. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/entry.js +3984 -0
  33. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/protocol.d.ts +30 -0
  34. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/protocol.js +1 -0
  35. package/node_modules/@opacedev/ai-content-checker-browser/package.json +60 -0
  36. package/node_modules/@opacedev/ai-content-checker-contracts/LICENSE +21 -0
  37. package/node_modules/@opacedev/ai-content-checker-contracts/README.md +93 -0
  38. package/node_modules/@opacedev/ai-content-checker-contracts/dist/index.d.ts +58 -0
  39. package/node_modules/@opacedev/ai-content-checker-contracts/dist/index.js +17 -0
  40. package/node_modules/@opacedev/ai-content-checker-contracts/package.json +57 -0
  41. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/analysis-request.schema.d.ts +44 -0
  42. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/analysis-result.schema.d.ts +122 -0
  43. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/candidate.schema.d.ts +81 -0
  44. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/capabilities.schema.d.ts +24 -0
  45. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/checker-result.schema.d.ts +342 -0
  46. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/common.schema.d.ts +10 -0
  47. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/envelope.schema.d.ts +14 -0
  48. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/gate-result.schema.d.ts +19 -0
  49. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/integrity-receipt.schema.d.ts +122 -0
  50. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/job.schema.d.ts +188 -0
  51. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/method-result.schema.d.ts +39 -0
  52. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/pattern-finding.schema.d.ts +26 -0
  53. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/protected-span.schema.d.ts +35 -0
  54. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/rewrite-request.schema.d.ts +65 -0
  55. package/node_modules/@opacedev/ai-content-checker-contracts/src/index.ts +63 -0
  56. package/node_modules/@opacedev/ai-content-checker-contracts/src/semantic-validation.mjs +32 -0
  57. package/node_modules/@opacedev/ai-content-checker-core/LICENSE +21 -0
  58. package/node_modules/@opacedev/ai-content-checker-core/README.md +186 -0
  59. package/node_modules/@opacedev/ai-content-checker-core/dist/bundle.js +4744 -0
  60. package/node_modules/@opacedev/ai-content-checker-core/dist/capabilities.d.ts +16 -0
  61. package/node_modules/@opacedev/ai-content-checker-core/dist/capabilities.js +7 -0
  62. package/node_modules/@opacedev/ai-content-checker-core/dist/diff/diff.d.ts +17 -0
  63. package/node_modules/@opacedev/ai-content-checker-core/dist/diff/diff.js +76 -0
  64. package/node_modules/@opacedev/ai-content-checker-core/dist/fixes/preview.d.ts +27 -0
  65. package/node_modules/@opacedev/ai-content-checker-core/dist/fixes/preview.js +47 -0
  66. package/node_modules/@opacedev/ai-content-checker-core/dist/gates/policy.d.ts +14 -0
  67. package/node_modules/@opacedev/ai-content-checker-core/dist/gates/policy.js +16 -0
  68. package/node_modules/@opacedev/ai-content-checker-core/dist/index.d.ts +15 -0
  69. package/node_modules/@opacedev/ai-content-checker-core/dist/index.js +15 -0
  70. package/node_modules/@opacedev/ai-content-checker-core/dist/inspect.d.ts +8 -0
  71. package/node_modules/@opacedev/ai-content-checker-core/dist/inspect.js +84 -0
  72. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-gb-v1.d.ts +12 -0
  73. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-gb-v1.js +68 -0
  74. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2-data.d.ts +73 -0
  75. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2-data.js +499 -0
  76. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2.d.ts +76 -0
  77. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2.js +1067 -0
  78. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3-data.d.ts +165 -0
  79. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3-data.js +614 -0
  80. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3.d.ts +19 -0
  81. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3.js +542 -0
  82. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-corpus.d.ts +40 -0
  83. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-corpus.js +40 -0
  84. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-data.d.ts +138 -0
  85. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-data.js +200 -0
  86. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4.d.ts +71 -0
  87. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4.js +388 -0
  88. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/extract.d.ts +17 -0
  89. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/extract.js +75 -0
  90. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/validate.d.ts +3 -0
  91. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/validate.js +17 -0
  92. package/node_modules/@opacedev/ai-content-checker-core/dist/provenance/c2pa-text.d.ts +55 -0
  93. package/node_modules/@opacedev/ai-content-checker-core/dist/provenance/c2pa-text.js +86 -0
  94. package/node_modules/@opacedev/ai-content-checker-core/dist/receipts/build.d.ts +48 -0
  95. package/node_modules/@opacedev/ai-content-checker-core/dist/receipts/build.js +61 -0
  96. package/node_modules/@opacedev/ai-content-checker-core/dist/report/checker-result.d.ts +241 -0
  97. package/node_modules/@opacedev/ai-content-checker-core/dist/report/checker-result.js +419 -0
  98. package/node_modules/@opacedev/ai-content-checker-core/dist/source/offsets.d.ts +34 -0
  99. package/node_modules/@opacedev/ai-content-checker-core/dist/source/offsets.js +51 -0
  100. package/node_modules/@opacedev/ai-content-checker-core/dist/source/utf8.d.ts +3 -0
  101. package/node_modules/@opacedev/ai-content-checker-core/dist/source/utf8.js +58 -0
  102. package/node_modules/@opacedev/ai-content-checker-core/dist/source/visible-text.d.ts +12 -0
  103. package/node_modules/@opacedev/ai-content-checker-core/dist/source/visible-text.js +30 -0
  104. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/data.d.ts +22 -0
  105. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/data.js +137 -0
  106. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/inspect.d.ts +16 -0
  107. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/inspect.js +84 -0
  108. package/node_modules/@opacedev/ai-content-checker-core/dist/verdict/combine.d.ts +162 -0
  109. package/node_modules/@opacedev/ai-content-checker-core/dist/verdict/combine.js +501 -0
  110. package/node_modules/@opacedev/ai-content-checker-core/package.json +62 -0
  111. package/node_modules/canonicalize/LICENSE +201 -0
  112. package/node_modules/canonicalize/README.md +65 -0
  113. package/node_modules/canonicalize/bin/canonicalize.js +17 -0
  114. package/node_modules/canonicalize/lib/canonicalize.d.ts +2 -0
  115. package/node_modules/canonicalize/lib/canonicalize.js +74 -0
  116. package/node_modules/canonicalize/package.json +64 -0
  117. package/node_modules/entities/LICENSE +11 -0
  118. package/node_modules/entities/lib/decode.d.ts +211 -0
  119. package/node_modules/entities/lib/decode.d.ts.map +1 -0
  120. package/node_modules/entities/lib/decode.js +536 -0
  121. package/node_modules/entities/lib/decode.js.map +1 -0
  122. package/node_modules/entities/lib/decode_codepoint.d.ts +19 -0
  123. package/node_modules/entities/lib/decode_codepoint.d.ts.map +1 -0
  124. package/node_modules/entities/lib/decode_codepoint.js +76 -0
  125. package/node_modules/entities/lib/decode_codepoint.js.map +1 -0
  126. package/node_modules/entities/lib/encode.d.ts +22 -0
  127. package/node_modules/entities/lib/encode.d.ts.map +1 -0
  128. package/node_modules/entities/lib/encode.js +77 -0
  129. package/node_modules/entities/lib/encode.js.map +1 -0
  130. package/node_modules/entities/lib/escape.d.ts +43 -0
  131. package/node_modules/entities/lib/escape.d.ts.map +1 -0
  132. package/node_modules/entities/lib/escape.js +122 -0
  133. package/node_modules/entities/lib/escape.js.map +1 -0
  134. package/node_modules/entities/lib/esm/decode.d.ts +211 -0
  135. package/node_modules/entities/lib/esm/decode.d.ts.map +1 -0
  136. package/node_modules/entities/lib/esm/decode.js +496 -0
  137. package/node_modules/entities/lib/esm/decode.js.map +1 -0
  138. package/node_modules/entities/lib/esm/decode_codepoint.d.ts +19 -0
  139. package/node_modules/entities/lib/esm/decode_codepoint.d.ts.map +1 -0
  140. package/node_modules/entities/lib/esm/decode_codepoint.js +71 -0
  141. package/node_modules/entities/lib/esm/decode_codepoint.js.map +1 -0
  142. package/node_modules/entities/lib/esm/encode.d.ts +22 -0
  143. package/node_modules/entities/lib/esm/encode.d.ts.map +1 -0
  144. package/node_modules/entities/lib/esm/encode.js +69 -0
  145. package/node_modules/entities/lib/esm/encode.js.map +1 -0
  146. package/node_modules/entities/lib/esm/escape.d.ts +43 -0
  147. package/node_modules/entities/lib/esm/escape.d.ts.map +1 -0
  148. package/node_modules/entities/lib/esm/escape.js +116 -0
  149. package/node_modules/entities/lib/esm/escape.js.map +1 -0
  150. package/node_modules/entities/lib/esm/generated/decode-data-html.d.ts +3 -0
  151. package/node_modules/entities/lib/esm/generated/decode-data-html.d.ts.map +1 -0
  152. package/node_modules/entities/lib/esm/generated/decode-data-html.js +7 -0
  153. package/node_modules/entities/lib/esm/generated/decode-data-html.js.map +1 -0
  154. package/node_modules/entities/lib/esm/generated/decode-data-xml.d.ts +3 -0
  155. package/node_modules/entities/lib/esm/generated/decode-data-xml.d.ts.map +1 -0
  156. package/node_modules/entities/lib/esm/generated/decode-data-xml.js +7 -0
  157. package/node_modules/entities/lib/esm/generated/decode-data-xml.js.map +1 -0
  158. package/node_modules/entities/lib/esm/generated/encode-html.d.ts +8 -0
  159. package/node_modules/entities/lib/esm/generated/encode-html.d.ts.map +1 -0
  160. package/node_modules/entities/lib/esm/generated/encode-html.js +10 -0
  161. package/node_modules/entities/lib/esm/generated/encode-html.js.map +1 -0
  162. package/node_modules/entities/lib/esm/index.d.ts +96 -0
  163. package/node_modules/entities/lib/esm/index.d.ts.map +1 -0
  164. package/node_modules/entities/lib/esm/index.js +99 -0
  165. package/node_modules/entities/lib/esm/index.js.map +1 -0
  166. package/node_modules/entities/lib/esm/package.json +1 -0
  167. package/node_modules/entities/lib/generated/decode-data-html.d.ts +3 -0
  168. package/node_modules/entities/lib/generated/decode-data-html.d.ts.map +1 -0
  169. package/node_modules/entities/lib/generated/decode-data-html.js +9 -0
  170. package/node_modules/entities/lib/generated/decode-data-html.js.map +1 -0
  171. package/node_modules/entities/lib/generated/decode-data-xml.d.ts +3 -0
  172. package/node_modules/entities/lib/generated/decode-data-xml.d.ts.map +1 -0
  173. package/node_modules/entities/lib/generated/decode-data-xml.js +9 -0
  174. package/node_modules/entities/lib/generated/decode-data-xml.js.map +1 -0
  175. package/node_modules/entities/lib/generated/encode-html.d.ts +8 -0
  176. package/node_modules/entities/lib/generated/encode-html.d.ts.map +1 -0
  177. package/node_modules/entities/lib/generated/encode-html.js +12 -0
  178. package/node_modules/entities/lib/generated/encode-html.js.map +1 -0
  179. package/node_modules/entities/lib/index.d.ts +96 -0
  180. package/node_modules/entities/lib/index.d.ts.map +1 -0
  181. package/node_modules/entities/lib/index.js +126 -0
  182. package/node_modules/entities/lib/index.js.map +1 -0
  183. package/node_modules/entities/package.json +90 -0
  184. package/node_modules/entities/readme.md +122 -0
  185. package/package.json +91 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Opace Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ # @opacedev/ai-content-checker-browser
2
+
3
+ ![Free Opace AI Content Checker, Detector and Watermark Tools](https://raw.githubusercontent.com/OpaceDigitalAgency/opace-ai-content-checker-detector/main/docs/assets/opace-ai-content-checker-detector-hero-v3.png)
4
+
5
+ Browser adapter for Opace AI Content Checker & Detector. It provides deterministic DOM visible-text projection and a module Worker client over the frozen core and contracts.
6
+
7
+ - Consumes `@opacedev/ai-content-checker-core` and `@opacedev/ai-content-checker-contracts`
8
+ - No analysis-time telemetry, provider call or remote module
9
+ - Default Worker target is packaged at `dist/worker/entry.js`
10
+ - Licence: MIT for the Opace-authored package
11
+
12
+ Dependency and provenance evidence is recorded in the project-root `THIRD_PARTY_NOTICES.md`, `MODEL_AND_DATA_PROVENANCE.md` and `docs/legal/DEPENDENCY-LEDGER.md`.
13
+
14
+ > Release state: a 0.1.0 npm candidate is prepared locally but is not published. The install command below applies only after owner-approved publication.
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install @opacedev/ai-content-checker-browser
20
+ ```
21
+
22
+ ## Use
23
+
24
+ The adapter projects rendered, visible body text with stable block and line-break semantics, excluding scripts, styles, templates, noscript and hidden descendants. Its Worker client carries schema-valid messages and supports cancellation without retaining content.
25
+
26
+ ```js
27
+ import {
28
+ projectDomVisibleText, // (document) => DomVisibleTextProjection
29
+ createInspectionWorker, // () => InspectionWorkerClient
30
+ supportsWorkerInspection, // () => boolean feature test
31
+ } from '@opacedev/ai-content-checker-browser';
32
+
33
+ if (supportsWorkerInspection()) {
34
+ const projection = projectDomVisibleText(document);
35
+ // hand projection.text to the Worker client for inspection
36
+ }
37
+ ```
38
+
39
+ Create the Worker only after an explicit user action. The default worker asset is packaged at `dist/worker/entry.js`, so consumers do not need a remote script or CDN. `supportsWorkerInspection()` returns `false` outside a Worker-capable environment (verified: it returns `false` under plain Node), letting a consumer fall back to calling the core directly, as the live checker's watchdog does.
40
+
41
+ The package targets modern ESM browsers with module Worker support. It does not render a toolbar, request host permissions or read a page by itself; the consuming product must obtain an explicit user action and pass the intended document.
42
+
43
+ ## Verify
44
+
45
+ ```sh
46
+ npm ci
47
+ npm run typecheck
48
+ npm test
49
+ ```
50
+
51
+ Rendered consumers should also test their own CSP, Worker URL resolution, cancellation, narrow layouts and network boundary. A successful projection does not authorise content storage or transmission.
52
+
53
+ ## Accessibility, security and licence
54
+
55
+ This library does not render controls. The consuming toolbar or extension owns accessible names, focus order, live status and reduced-motion behaviour. Report vulnerabilities through the repository [security policy](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/SECURITY.md). Opace-authored code is available under the [MIT Licence](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/LICENSE).
56
+
57
+ For non-sensitive help, use [Opace AI Content Checker & Detector support](https://opace.agency/get-in-touch/). Changes should follow the repository [contribution guide](../../CONTRIBUTING.md) and [changelog](../../CHANGELOG.md).
58
+
59
+ ## Troubleshooting and links
60
+
61
+ - **Worker creation fails:** confirm the built `dist/worker/entry.js` asset is copied and allowed by the site's Content Security Policy.
62
+ - **Projected text is shorter than expected:** hidden descendants, scripts, styles, templates and `noscript` content are excluded deliberately.
63
+ - **Cancellation appears late:** abort the request through the client and dispose of the Worker when the surface closes.
64
+
65
+ [Opace AI Content Checker & Detector](https://opace.agency/tools/ai/content-verification-integrity/) · [Browser checker](https://opace.agency/tools/ai/content-verification-integrity/checker/) · [Capability register](../../docs/CAPABILITIES.md) · [Chrome extension](https://opace.agency/tools/ai/content-verification-integrity/chrome-extension/) · [AI and automation services](https://opace.agency/services/artificial-intelligence/) · [Opace](https://opace.agency/) · [Opace Digital Agency on GitHub](https://github.com/OpaceDigitalAgency) · [Related core package](../core/README.md)
66
+
67
+ ## Attribution
68
+
69
+ This engine was built on existing open-source work by deliberate choice, so the projects it
70
+ stands on are named here rather than only in a licence file.
71
+
72
+ - [avoid-ai-writing](https://github.com/conorbronsdon/avoid-ai-writing) — MIT, Conor Bronsdon and contributors. 46 of the 51 v2 writing-pattern rule categories, the stylometric methods, the weights and the classifier logic, adapted to TypeScript, plus Cyrillic and Greek lookalike map data. One upstream bug was fixed in the port.
73
+ - [watermarks-remover](https://github.com/guillaumemeyer/watermarks-remover) — MIT, Guillaume Meyer. The invisible-character and space-substitute carrier tables and the explicit-carrier inspection model. Table data only; no upstream code is distributed.
74
+ - [Unicode Consortium character data](https://www.unicode.org/Public/UCD/latest/) — Unicode licence. The 415-code-point carrier inventory across 38 rules and the 60-entry confusable set.
75
+ - [antislop-sampler](https://github.com/sam-paech/antislop-sampler) — Apache-2.0, Sam Paech. Fiction phrase and over-represented name data behind the `fiction-slop-phrase` and `fiction-promptonym` rules.
76
+ - [slop-forensics](https://github.com/sam-paech/slop-forensics) — MIT, Sam Paech. Per-model observations corroborating the fiction-lane rules.
77
+ - [SLOP_Detector](https://github.com/SicariusSicariiStuff/SLOP_Detector) — Apache-2.0, SicariusSicariiStuff. The graded penalty-class weighting approach.
78
+ - [slop-gate](https://github.com/hwajongpark/slop-gate) — MIT, hwajongpark. Promotional-register and buzz-phrase pattern data.
79
+ - [anti-ai-writing](https://github.com/avectats7/anti-ai-writing) — MIT, avectats7. Buzz-phrase and weak-verb observation data.
80
+ - [anti-slop](https://github.com/kjmagnan1s/anti-slop) — MIT, kjmagnan1s. Faux-insight phrase data and the protect-list and context-profile design.
81
+ - [claude-slop-detector](https://github.com/aplaceforallmystuff/claude-slop-detector) — MIT, aplaceforallmystuff. Staccato-fragment and tripled-negation observations.
82
+ - [Wikipedia, *Signs of AI writing*](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing) — CC BY-SA 4.0. Editorial guidance independently re-expressed with no verbatim excerpts, credited as the licence requires.
83
+ - [Project Gutenberg](https://www.gutenberg.org) public-domain texts, all published before 1929 — the embedded human-prose reference corpus behind the rhythm and register signals.
84
+ - [canonicalize](https://github.com/cyberphone/json-canonicalization) — Apache-2.0. RFC 8785 canonicalisation.
85
+ - Published academic findings by Liang et al. (ICML 2024), Kobak et al. (*Science Advances* 2025), Juzek & Ward (COLING 2025), Reinhart et al. (PNAS 2025), Geng & Trotta (2024) and Pew Research (2026), used as rule thresholds and lexicon facts.
86
+
87
+ Several well-known detector repositories were cloned and read during the research phase and are
88
+ credited as exactly that — read, not used. Nothing in this package derives from `fast-detect-gpt`,
89
+ `Binoculars`, `RADAR`, `DIPPER`, `ai-detector-bench`, `BIRA`, `SIRA` or `MarkLLM`.
90
+
91
+ Full records, with versions, snapshot commits and file-level destinations:
92
+ [THIRD_PARTY_NOTICES.md](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/THIRD_PARTY_NOTICES.md) ·
93
+ [DEPENDENCY-LEDGER.md](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/docs/legal/DEPENDENCY-LEDGER.md).
94
+
95
+ ## Where this is weakest, measured
96
+
97
+ This package ships the deterministic character forensics and editorial writing rules. It does
98
+ **not** contain the trained model that produces an AI reading; Cycle 5 runs only in the web
99
+ checker. The model results are disclosed here so package users can distinguish the two systems.
100
+
101
+ **The writing rules are editorial feedback, not detection.** On 922 machine and 1,200 held-out
102
+ human long-form documents, they detect 45.1% of machine writing while flagging **24.8% of human
103
+ writing** — one human document in four. `computeEditorialSignals` returns a writing score, never
104
+ an authorship reading, and must not be presented as one.
105
+
106
+ The deployed Cycle 5 model (`tier3-cycle5-v1`, margin rule
107
+ `max(m1, m2+0.34) >= 3.571`) was measured separately through both live runtimes on the full
108
+ 5,558-document long-form evaluation corpus. That corpus is not wholly independent: 654/922 AI
109
+ documents are independent of every Cycle 2 split and 268 are not; 11/4,636 human documents
110
+ overlap. The separate topic-matched held-out slice is the independent evasion measurement.
111
+
112
+ | Cycle 5 measurement | result | denominator and runtime |
113
+ |---|---:|---|
114
+ | AI documents flagged | **97.8%** | 902/922, EU server fp32 |
115
+ | human documents wrongly flagged | **0.99%** | 46/4,636, EU server fp32 |
116
+ | AI documents flagged | **97.6%** | 900/922, browser int8 |
117
+ | human documents wrongly flagged | **1.57%** | 73/4,636, browser int8 |
118
+ | topic-matched held-out AI flagged | **86.9%** | 153/176, server evaluation route |
119
+ | structured human partners wrongly flagged | **0.2%** | 1/418, server evaluation route |
120
+ | human fiction wrongly flagged | **3.1% / 3.5%** | 7/227 server / 8/227 browser |
121
+ | held-out 100-word AI flagged | **76.8%** | 43/56, server evaluation route |
122
+ | heavy AI edits of human originals flagged | **28.5%** | 39/137, server evaluation view |
123
+ | academic human documents wrongly flagged | **0.8%** | 15/1,992, server evaluation view |
124
+
125
+ The 100-word cell is small, fiction remains higher-risk than the overall human set, and treating
126
+ heavy AI edits as machine-assisted is a product boundary rather than proof of authorship. Do not
127
+ use a result for an academic misconduct decision about one student.
128
+
129
+ *Historical, not current:* Cycle 2 (`tier3-cycle2-v1`, live 28 August – 1 September 2026) produced
130
+ the earlier 883/922 server, 889/922 browser, 45/4,636 server-human and 90/4,636 browser-human
131
+ figures. Its fiction and length rows are retired and must not be mixed with Cycle 5.
132
+
133
+ Complete list with sources: [Honest limitations](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector#honest-limitations).
134
+
135
+ ## Evidence
136
+
137
+ Every accuracy figure this project publishes is measured, carries its denominator and names its
138
+ source report. The six result charts, the per-register detection and false-positive tables and the
139
+ complete weakness list are on the [repository front page](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector#what-it-measures-and-where-it-fails).
140
+
141
+ - [Capability register](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/docs/CAPABILITIES.md) — the exhaustive technical inventory
142
+ - [Evidence index](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/docs/EVIDENCE-INDEX.md) — every test result and research artefact, with paths
143
+ - [Test evidence](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/docs/TEST-EVIDENCE.md) — verbatim suite totals and the current-model appendix
144
+ - [Route parity](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector/blob/main/docs/measurements/ROUTE-PARITY.md) — browser int8 against server fp32, all disagreements written out
145
+ - [Honest limitations](https://github.com/OpaceDigitalAgency/opace-ai-content-checker-detector#honest-limitations) — where the tool is weakest, ranked, with denominators
@@ -0,0 +1,14 @@
1
+ export interface DomSourceRun {
2
+ text: string;
3
+ node_path: number[];
4
+ start_utf16: number;
5
+ end_utf16: number;
6
+ visible_start_utf16: number;
7
+ visible_end_utf16: number;
8
+ }
9
+ export interface DomVisibleTextProjection {
10
+ text: string;
11
+ runs: DomSourceRun[];
12
+ limitations: string[];
13
+ }
14
+ export declare function projectDomVisibleText(root: Node): DomVisibleTextProjection;
@@ -0,0 +1,47 @@
1
+ const EXCLUDED = new Set(["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"]);
2
+ const BLOCKS = new Set(["ADDRESS", "ARTICLE", "ASIDE", "BLOCKQUOTE", "DIV", "DL", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HR", "LI", "MAIN", "NAV", "OL", "P", "PRE", "SECTION", "TABLE", "TR", "UL"]);
3
+ export function projectDomVisibleText(root) {
4
+ let text = "";
5
+ const runs = [];
6
+ const pathOf = (node) => { const path = []; let current = node; while (current && current !== root) {
7
+ const p = current.parentNode;
8
+ if (!p)
9
+ break;
10
+ path.unshift(Array.prototype.indexOf.call(p.childNodes, current));
11
+ current = p;
12
+ } return path; };
13
+ const append = (value, node, startUtf16, endUtf16) => { if (!value)
14
+ return; const start = text.length; text += value; runs.push({ text: value, node_path: pathOf(node), start_utf16: startUtf16, end_utf16: endUtf16, visible_start_utf16: start, visible_end_utf16: text.length }); };
15
+ const separator = (node) => { if (text && !text.endsWith("\n"))
16
+ append("\n", node, 0, 0); };
17
+ const visit = (node, hidden) => { if (node.nodeType === 1) {
18
+ const element = node;
19
+ const excluded = hidden || EXCLUDED.has(element.tagName) || element.hidden || element.getAttribute("aria-hidden") === "true";
20
+ if (excluded)
21
+ return;
22
+ if (element.tagName === "BR") {
23
+ separator(node);
24
+ return;
25
+ }
26
+ const block = BLOCKS.has(element.tagName);
27
+ if (block)
28
+ separator(node);
29
+ for (const child of Array.from(node.childNodes))
30
+ visit(child, false);
31
+ if (block)
32
+ separator(node);
33
+ return;
34
+ } if (node.nodeType === 3) {
35
+ const value = node.nodeValue ?? "";
36
+ append(value, node, 0, value.length);
37
+ return;
38
+ } for (const child of Array.from(node.childNodes))
39
+ visit(child, hidden); };
40
+ visit(root, false);
41
+ text = text.replace(/\n+$/g, "");
42
+ while (runs.length && runs.at(-1).visible_start_utf16 >= text.length)
43
+ runs.pop();
44
+ if (runs.length)
45
+ runs.at(-1).visible_end_utf16 = Math.min(runs.at(-1).visible_end_utf16, text.length);
46
+ return { text, runs, limitations: ["Visibility uses hidden and aria-hidden markers; computed CSS visibility is host-dependent."] };
47
+ }
@@ -0,0 +1,3 @@
1
+ export { createInspectionWorker, type InspectionWorkerClient } from "./worker/client.js";
2
+ export { projectDomVisibleText, type DomVisibleTextProjection, type DomSourceRun } from "./dom/visible-text.js";
3
+ export declare const supportsWorkerInspection: () => boolean;
@@ -0,0 +1,99 @@
1
+ // src/worker/protocol.ts
2
+ var WORKER_PROTOCOL_VERSION = "1.0";
3
+
4
+ // src/worker/client.ts
5
+ function createInspectionWorker(options = {}) {
6
+ if (typeof Worker === "undefined") throw new Error("worker_unsupported");
7
+ const worker = new Worker(options.workerUrl ?? new URL("./worker/entry.js", import.meta.url), { type: "module" });
8
+ const pending = /* @__PURE__ */ new Map();
9
+ worker.onmessage = (event) => {
10
+ const m = event.data, p = pending.get(m.id);
11
+ if (!p || m.protocol_version !== WORKER_PROTOCOL_VERSION) return;
12
+ if (m.type === "progress") p.onProgress?.(m.phase);
13
+ else {
14
+ pending.delete(m.id);
15
+ m.type === "result" ? p.resolve(m.result) : p.reject(new Error(m.code));
16
+ }
17
+ };
18
+ return { inspect(request, opts = {}) {
19
+ const id = `inspect_${Date.now()}_${Math.random().toString(36).slice(2)}`;
20
+ return new Promise((resolve, reject) => {
21
+ pending.set(id, { resolve, reject, onProgress: opts.onProgress });
22
+ const abort = () => {
23
+ worker.postMessage({ protocol_version: WORKER_PROTOCOL_VERSION, type: "cancel", id });
24
+ pending.delete(id);
25
+ reject(new DOMException("Inspection cancelled", "AbortError"));
26
+ };
27
+ if (opts.signal?.aborted) return abort();
28
+ opts.signal?.addEventListener("abort", abort, { once: true });
29
+ worker.postMessage({ protocol_version: WORKER_PROTOCOL_VERSION, type: "inspect", id, request });
30
+ });
31
+ }, dispose() {
32
+ for (const p of pending.values()) p.reject(new Error("worker_disposed"));
33
+ pending.clear();
34
+ worker.terminate();
35
+ } };
36
+ }
37
+
38
+ // src/dom/visible-text.ts
39
+ var EXCLUDED = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"]);
40
+ var BLOCKS = /* @__PURE__ */ new Set(["ADDRESS", "ARTICLE", "ASIDE", "BLOCKQUOTE", "DIV", "DL", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HR", "LI", "MAIN", "NAV", "OL", "P", "PRE", "SECTION", "TABLE", "TR", "UL"]);
41
+ function projectDomVisibleText(root) {
42
+ let text = "";
43
+ const runs = [];
44
+ const pathOf = (node) => {
45
+ const path = [];
46
+ let current = node;
47
+ while (current && current !== root) {
48
+ const p = current.parentNode;
49
+ if (!p) break;
50
+ path.unshift(Array.prototype.indexOf.call(p.childNodes, current));
51
+ current = p;
52
+ }
53
+ return path;
54
+ };
55
+ const append = (value, node, startUtf16, endUtf16) => {
56
+ if (!value) return;
57
+ const start = text.length;
58
+ text += value;
59
+ runs.push({ text: value, node_path: pathOf(node), start_utf16: startUtf16, end_utf16: endUtf16, visible_start_utf16: start, visible_end_utf16: text.length });
60
+ };
61
+ const separator = (node) => {
62
+ if (text && !text.endsWith("\n")) append("\n", node, 0, 0);
63
+ };
64
+ const visit = (node, hidden) => {
65
+ if (node.nodeType === 1) {
66
+ const element = node;
67
+ const excluded = hidden || EXCLUDED.has(element.tagName) || element.hidden || element.getAttribute("aria-hidden") === "true";
68
+ if (excluded) return;
69
+ if (element.tagName === "BR") {
70
+ separator(node);
71
+ return;
72
+ }
73
+ const block = BLOCKS.has(element.tagName);
74
+ if (block) separator(node);
75
+ for (const child of Array.from(node.childNodes)) visit(child, false);
76
+ if (block) separator(node);
77
+ return;
78
+ }
79
+ if (node.nodeType === 3) {
80
+ const value = node.nodeValue ?? "";
81
+ append(value, node, 0, value.length);
82
+ return;
83
+ }
84
+ for (const child of Array.from(node.childNodes)) visit(child, hidden);
85
+ };
86
+ visit(root, false);
87
+ text = text.replace(/\n+$/g, "");
88
+ while (runs.length && runs.at(-1).visible_start_utf16 >= text.length) runs.pop();
89
+ if (runs.length) runs.at(-1).visible_end_utf16 = Math.min(runs.at(-1).visible_end_utf16, text.length);
90
+ return { text, runs, limitations: ["Visibility uses hidden and aria-hidden markers; computed CSS visibility is host-dependent."] };
91
+ }
92
+
93
+ // src/index.ts
94
+ var supportsWorkerInspection = () => typeof Worker !== "undefined" && typeof URL !== "undefined";
95
+ export {
96
+ createInspectionWorker,
97
+ projectDomVisibleText,
98
+ supportsWorkerInspection
99
+ };
@@ -0,0 +1,12 @@
1
+ import type { AnalysisRequest, AnalysisResult } from "@opacedev/ai-content-checker-contracts";
2
+ import { type InspectionPhase } from "./protocol.js";
3
+ export interface InspectionWorkerClient {
4
+ inspect(request: AnalysisRequest, options?: {
5
+ signal?: AbortSignal;
6
+ onProgress?: (phase: InspectionPhase) => void;
7
+ }): Promise<AnalysisResult>;
8
+ dispose(): void;
9
+ }
10
+ export declare function createInspectionWorker(options?: {
11
+ workerUrl?: URL;
12
+ }): InspectionWorkerClient;
@@ -0,0 +1,17 @@
1
+ import { WORKER_PROTOCOL_VERSION } from "./protocol.js";
2
+ export function createInspectionWorker(options = {}) {
3
+ if (typeof Worker === "undefined")
4
+ throw new Error("worker_unsupported");
5
+ const worker = new Worker(options.workerUrl ?? new URL("./worker/entry.js", import.meta.url), { type: "module" });
6
+ const pending = new Map();
7
+ worker.onmessage = (event) => { const m = event.data, p = pending.get(m.id); if (!p || m.protocol_version !== WORKER_PROTOCOL_VERSION)
8
+ return; if (m.type === "progress")
9
+ p.onProgress?.(m.phase);
10
+ else {
11
+ pending.delete(m.id);
12
+ m.type === "result" ? p.resolve(m.result) : p.reject(new Error(m.code));
13
+ } };
14
+ return { inspect(request, opts = {}) { const id = `inspect_${Date.now()}_${Math.random().toString(36).slice(2)}`; return new Promise((resolve, reject) => { pending.set(id, { resolve, reject, onProgress: opts.onProgress }); const abort = () => { worker.postMessage({ protocol_version: WORKER_PROTOCOL_VERSION, type: "cancel", id }); pending.delete(id); reject(new DOMException("Inspection cancelled", "AbortError")); }; if (opts.signal?.aborted)
15
+ return abort(); opts.signal?.addEventListener("abort", abort, { once: true }); worker.postMessage({ protocol_version: WORKER_PROTOCOL_VERSION, type: "inspect", id, request }); }); }, dispose() { for (const p of pending.values())
16
+ p.reject(new Error("worker_disposed")); pending.clear(); worker.terminate(); } };
17
+ }