Legal-i commited on
Commit
85f7b02
·
1 Parent(s): b5a44a9

Day 54+: ChatGPT hallucination filter (Tier 0+2)

Browse files

- /v1/verify_citations endpoint — batch corpus existence check
- chatgpt-overlay v0.4.0 — verifies every cite via 4-tier fallback:
corpus → wikisource → wikipedia → Google→court.gov.il
- Color-coded chip: 🟡 checking → 🟢 verified / 🔴 not_found
- background.js: POST support + JSON content-type + legal-eye/HF allowlist
- manifest 0.4.0: host_permissions for backend API access

tau_rag/api/fastapi_app.py CHANGED
@@ -23123,6 +23123,66 @@ def judgment_reader_by_cite(cite: str): # type: ignore
23123
  })
23124
 
23125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23126
  @app.get("/v1/judgment/{doc_id:path}/render")
23127
  def judgment_reader_render(doc_id: str): # type: ignore
23128
  """Day 51 — full structured render of a single judgment for the
 
23123
  })
23124
 
23125
 
23126
+ class _VerifyCitationsRequest(BaseModel): # type: ignore
23127
+ cites: List[str]
23128
+
23129
+
23130
+ @app.post("/v1/verify_citations")
23131
+ def verify_citations(body: _VerifyCitationsRequest): # type: ignore
23132
+ """Day 54 — bulk citation existence check, powers the ChatGPT
23133
+ hallucination filter. For each cite, returns:
23134
+ - status='verified_corpus' + doc_id (found in our corpus)
23135
+ - status='not_found_locally' (extension should try web)
23136
+
23137
+ Designed to be fast: no parsing, no external fetches. Just a corpus
23138
+ index lookup per cite. Wikisource/Wikipedia/court.gov.il are checked
23139
+ by the extension itself (which has the helper privileges).
23140
+ """
23141
+ try:
23142
+ from ..pipeline import get_pipeline
23143
+ from ..citation_network import get_or_build
23144
+ pipe = get_pipeline()
23145
+ cn = get_or_build(pipe)
23146
+
23147
+ results = []
23148
+ for raw_cite in (body.cites or [])[:100]: # cap at 100 per request
23149
+ raw = (raw_cite or "").strip()
23150
+ if not raw:
23151
+ results.append({"cite": raw_cite, "status": "empty"})
23152
+ continue
23153
+ m = _JR_CASE_RX.search(raw)
23154
+ prefix = m.group(1).strip() if m else raw
23155
+ candidates = [
23156
+ prefix,
23157
+ prefix.replace('"', '"').replace("'", "׳"),
23158
+ prefix.replace('"', '"').replace("'", "'"),
23159
+ _re.sub(r"\s+", "", prefix),
23160
+ ]
23161
+ doc_id = None
23162
+ for c in candidates:
23163
+ doc_id = cn.doc_for_citation.get(c)
23164
+ if doc_id:
23165
+ break
23166
+ if doc_id:
23167
+ results.append({
23168
+ "cite": raw_cite,
23169
+ "prefix": prefix,
23170
+ "status": "verified_corpus",
23171
+ "doc_id": doc_id,
23172
+ })
23173
+ else:
23174
+ results.append({
23175
+ "cite": raw_cite,
23176
+ "prefix": prefix,
23177
+ "status": "not_found_locally",
23178
+ })
23179
+ return {"results": results}
23180
+ except Exception as e:
23181
+ return JSONResponse(status_code=500, content={
23182
+ "ok": False, "error": f"{type(e).__name__}: {e}"
23183
+ })
23184
+
23185
+
23186
  @app.get("/v1/judgment/{doc_id:path}/render")
23187
  def judgment_reader_render(doc_id: str): # type: ignore
23188
  """Day 51 — full structured render of a single judgment for the
tau_rag/chrome-extension/background.js CHANGED
@@ -7,18 +7,28 @@
7
  // We only allow fetches to the small set of legal sites declared in
8
  // host_permissions — anything else gets refused.
9
 
10
- const ALLOWED_HOSTS = [
11
- 'www.court.gov.il',
12
- 'supreme.court.gov.il',
 
13
  'www.google.com',
14
  'html.duckduckgo.com',
15
  'www.pador.co.il',
16
  'www.psakdin.co.il',
17
  'www.takdin.co.il',
18
  'www.lawdata.co.il',
 
19
  'he.wikipedia.org',
20
  'he.wikisource.org',
21
- ];
 
 
 
 
 
 
 
 
22
 
23
  // Simple per-host rate limit: max 20 fetches/host/minute.
24
  // Prevents the page (or a bug) from hammering a site.
@@ -44,7 +54,7 @@ chrome.runtime.onMessage.addListener((req, _sender, sendResponse) => {
44
  (async () => {
45
  try {
46
  const u = new URL(req.url);
47
- if (!ALLOWED_HOSTS.includes(u.hostname)) {
48
  sendResponse({ error: `host_not_allowed: ${u.hostname}` });
49
  return;
50
  }
@@ -52,15 +62,23 @@ chrome.runtime.onMessage.addListener((req, _sender, sendResponse) => {
52
  sendResponse({ error: 'rate_limited' });
53
  return;
54
  }
 
 
 
 
 
55
  const r = await fetch(req.url, {
 
56
  credentials: 'include',
57
  redirect: 'follow',
58
- headers: {
59
- // Mirror what a real user's browser would send
60
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
61
- 'Accept-Language': 'he-IL,he;q=0.9,en;q=0.8',
62
- 'Cache-Control': 'no-cache',
63
- },
 
 
64
  });
65
  const html = await r.text();
66
  sendResponse({
 
7
  // We only allow fetches to the small set of legal sites declared in
8
  // host_permissions — anything else gets refused.
9
 
10
+ // Allowed hosts: exact matches OR any subdomain of *.court.gov.il
11
+ // (covers www., supreme., elyon1., labor., etc. — many famous Israeli
12
+ // judgments live on the older elyon1 subdomain).
13
+ const ALLOWED_EXACT = new Set([
14
  'www.google.com',
15
  'html.duckduckgo.com',
16
  'www.pador.co.il',
17
  'www.psakdin.co.il',
18
  'www.takdin.co.il',
19
  'www.lawdata.co.il',
20
+ 'www.nlrc.gov.il',
21
  'he.wikipedia.org',
22
  'he.wikisource.org',
23
+ // v0.4.0 — backend API access (for ChatGPT hallucination filter)
24
+ 'legal-i-legal-eye.hf.space',
25
+ 'legal-eye.1bigfam.com',
26
+ ]);
27
+ function isHostAllowed(host) {
28
+ if (ALLOWED_EXACT.has(host)) return true;
29
+ if (/\.court\.gov\.il$/.test(host)) return true;
30
+ return false;
31
+ }
32
 
33
  // Simple per-host rate limit: max 20 fetches/host/minute.
34
  // Prevents the page (or a bug) from hammering a site.
 
54
  (async () => {
55
  try {
56
  const u = new URL(req.url);
57
+ if (!isHostAllowed(u.hostname)) {
58
  sendResponse({ error: `host_not_allowed: ${u.hostname}` });
59
  return;
60
  }
 
62
  sendResponse({ error: 'rate_limited' });
63
  return;
64
  }
65
+ // v0.4.0 — support POST + custom headers/body (for the
66
+ // verify_citations API call). Defaults still match a real
67
+ // browser for HTML scraping use case.
68
+ const method = req.method || 'GET';
69
+ const isApi = req.contentType === 'application/json';
70
  const r = await fetch(req.url, {
71
+ method,
72
  credentials: 'include',
73
  redirect: 'follow',
74
+ headers: isApi
75
+ ? { 'Content-Type': 'application/json', 'Accept': 'application/json' }
76
+ : {
77
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
78
+ 'Accept-Language': 'he-IL,he;q=0.9,en;q=0.8',
79
+ 'Cache-Control': 'no-cache',
80
+ },
81
+ body: req.body || undefined,
82
  });
83
  const html = await r.text();
84
  sendResponse({
tau_rag/chrome-extension/chatgpt-overlay.js ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Legal Eye Helper — ChatGPT overlay v0.4.0
2
+ // ──────────────────────────────────────────────────────────────────────
3
+ // Hallucination filter for Israeli legal citations in ChatGPT.
4
+ //
5
+ // For every Hebrew case citation ChatGPT produces, we verify it exists:
6
+ // Tier 0: backend lookup against our corpus (instant)
7
+ // Tier 1: Wikisource title check (fast)
8
+ // Tier 2: Wikipedia title check (fast)
9
+ // Tier 3: Google search → court.gov.il / pador / psakdin (helper)
10
+ //
11
+ // Color-coded result chip:
12
+ // 🟢 verified — exists in at least one source
13
+ // 🟡 checking — verification in flight
14
+ // 🔴 not_found — likely a ChatGPT hallucination
15
+ //
16
+ // Lawyer use case: never cite a ruling to court without confirming it
17
+ // exists. ChatGPT routinely makes up citations.
18
+
19
+ (function () {
20
+ const LE_CITE_RE = /(?:בג"?ץ|ע"?א|ע"?פ|ע"?ע|דנ"?א|דנ"?פ|רע"?א|רע"?פ|רע"?ב|דב"?ע|בש"?א|בש"?פ|תפ"?ח|ת"?א|ה"?פ|ע"?מ|ע"?ר|בר"?ע)\s+(?:[א-ת]+\s*\/\s*\d+\s*-\s*\d+|\d+\s*[\/\-]\s*\d+(?:\s*-\s*\d+)?)/g;
21
+
22
+ const API_BASE = 'https://legal-i-legal-eye.hf.space';
23
+ const LEGAL_EYE_URL = 'https://legal-eye.1bigfam.com/?cite=';
24
+ const MARK_CLASS = 'legal-eye-cite-mark';
25
+
26
+ // Verification result cache (session-scoped, in-memory only).
27
+ // Map<cite, {status, source?}>
28
+ const VERIFY_CACHE = new Map();
29
+
30
+ // ── Helpers ────────────────────────────────────────────────────────
31
+ function alreadyMarked(node) {
32
+ return node?.parentElement?.closest?.('.' + MARK_CLASS);
33
+ }
34
+
35
+ // chrome.runtime.sendMessage Promise wrapper
36
+ function helperFetch(url, opts) {
37
+ return new Promise((resolve, reject) => {
38
+ try {
39
+ chrome.runtime.sendMessage({
40
+ op: 'fetch',
41
+ url,
42
+ method: opts?.method || 'GET',
43
+ contentType: opts?.contentType || null,
44
+ body: opts?.body || null,
45
+ }, (resp) => {
46
+ if (chrome.runtime.lastError) {
47
+ reject(new Error(chrome.runtime.lastError.message));
48
+ return;
49
+ }
50
+ if (!resp || resp.error) {
51
+ reject(new Error(resp?.error || 'helper_fetch_failed'));
52
+ return;
53
+ }
54
+ resolve(resp);
55
+ });
56
+ } catch (e) { reject(e); }
57
+ });
58
+ }
59
+
60
+ // ── Verification chain ─────────────────────────────────────────────
61
+ async function verifyOne(cite) {
62
+ if (VERIFY_CACHE.has(cite)) return VERIFY_CACHE.get(cite);
63
+ let result;
64
+ try {
65
+ // Tier 0 — backend corpus check
66
+ const resp = await helperFetch(API_BASE + '/v1/verify_citations', {
67
+ method: 'POST',
68
+ contentType: 'application/json',
69
+ body: JSON.stringify({ cites: [cite] }),
70
+ });
71
+ const data = JSON.parse(resp.html);
72
+ const row = (data.results || [])[0];
73
+ if (row && row.status === 'verified_corpus') {
74
+ result = { status: 'verified', source: 'corpus', doc_id: row.doc_id };
75
+ }
76
+ } catch (e) { /* fall through */ }
77
+
78
+ if (!result) {
79
+ // Tier 1 — Wikisource title check (HEAD-ish: API JSON)
80
+ try {
81
+ const prefix = extractPrefix(cite);
82
+ const wsUrl = 'https://he.wikisource.org/w/api.php?action=query&format=json&list=search&srsearch=' +
83
+ encodeURIComponent('"' + prefix + '"') + '&srlimit=3&origin=*';
84
+ const wsResp = await helperFetch(wsUrl);
85
+ const ws = JSON.parse(wsResp.html);
86
+ if (ws.query?.search?.length > 0) {
87
+ result = { status: 'verified', source: 'wikisource' };
88
+ }
89
+ } catch (e) { /* fall through */ }
90
+ }
91
+
92
+ if (!result) {
93
+ // Tier 2 — Wikipedia title check
94
+ try {
95
+ const prefix = extractPrefix(cite);
96
+ const wpUrl = 'https://he.wikipedia.org/w/api.php?action=query&format=json&list=search&srsearch=' +
97
+ encodeURIComponent('"' + prefix + '"') + '&srlimit=3&origin=*';
98
+ const wpResp = await helperFetch(wpUrl);
99
+ const wp = JSON.parse(wpResp.html);
100
+ if (wp.query?.search?.length > 0) {
101
+ result = { status: 'verified', source: 'wikipedia' };
102
+ }
103
+ } catch (e) { /* fall through */ }
104
+ }
105
+
106
+ if (!result) {
107
+ // Tier 3 — Google search → look for court.gov.il / pador / psakdin / etc.
108
+ try {
109
+ const prefix = extractPrefix(cite);
110
+ const gUrl = 'https://www.google.com/search?q=' +
111
+ encodeURIComponent('"' + prefix + '"') + '&hl=he';
112
+ const gResp = await helperFetch(gUrl);
113
+ if (gResp.html && !/sorry\/index|recaptcha|unusual traffic/i.test(gResp.html)) {
114
+ const HOST_RX = /(?:[a-z0-9-]+\.court\.gov\.il|www\.pador\.co\.il|www\.psakdin\.co\.il|www\.takdin\.co\.il|www\.lawdata\.co\.il|www\.nevo\.co\.il)/i;
115
+ // Look in Google's wrapped URLs first
116
+ let found = false;
117
+ for (const m of gResp.html.matchAll(/\/url\?q=([^&"]+)/g)) {
118
+ try {
119
+ const decoded = decodeURIComponent(m[1]);
120
+ if (HOST_RX.test(decoded)) { found = true; break; }
121
+ } catch {}
122
+ }
123
+ if (!found) {
124
+ for (const m of gResp.html.matchAll(/href="(https?:\/\/[^"]+)"/g)) {
125
+ if (HOST_RX.test(m[1])) { found = true; break; }
126
+ }
127
+ }
128
+ if (found) {
129
+ result = { status: 'verified', source: 'court_external' };
130
+ }
131
+ }
132
+ } catch (e) { /* fall through */ }
133
+ }
134
+
135
+ if (!result) {
136
+ result = { status: 'not_found' };
137
+ }
138
+ VERIFY_CACHE.set(cite, result);
139
+ return result;
140
+ }
141
+
142
+ function extractPrefix(cite) {
143
+ // Same regex as backend — the cite text WE detected already IS just the prefix
144
+ return cite.trim();
145
+ }
146
+
147
+ // ── Chip construction ──────────────────────────────────────────────
148
+ function buildChipFragment(text) {
149
+ const frag = document.createDocumentFragment();
150
+ let lastIdx = 0;
151
+ LE_CITE_RE.lastIndex = 0;
152
+ let m;
153
+ while ((m = LE_CITE_RE.exec(text)) !== null) {
154
+ if (m.index > lastIdx) {
155
+ frag.appendChild(document.createTextNode(text.slice(lastIdx, m.index)));
156
+ }
157
+ const cite = m[0];
158
+ const wrap = document.createElement('span');
159
+ wrap.className = MARK_CLASS;
160
+ wrap.setAttribute('dir', 'rtl');
161
+ wrap.dataset.cite = cite;
162
+ // Start in "checking" state (yellow)
163
+ applyChipState(wrap, { status: 'checking' }, cite);
164
+ frag.appendChild(wrap);
165
+ lastIdx = m.index + cite.length;
166
+ // Kick off verification (async)
167
+ verifyOne(cite).then(res => {
168
+ applyChipState(wrap, res, cite);
169
+ }).catch(() => {
170
+ applyChipState(wrap, { status: 'not_found' }, cite);
171
+ });
172
+ }
173
+ if (lastIdx < text.length) {
174
+ frag.appendChild(document.createTextNode(text.slice(lastIdx)));
175
+ }
176
+ return frag;
177
+ }
178
+
179
+ const STYLES = {
180
+ checking: {
181
+ bg: 'linear-gradient(135deg,#fef3c7,#fde68a)',
182
+ border: '#d97706',
183
+ ink: '#78350f',
184
+ tagBg: '#bf9b30',
185
+ icon: '⏳',
186
+ label: 'בודק…',
187
+ title: 'מאמת קיום ב-corpus / Wikipedia / court.gov.il',
188
+ },
189
+ verified: {
190
+ bg: 'linear-gradient(135deg,#d1fae5,#a7f3d0)',
191
+ border: '#059669',
192
+ ink: '#065f46',
193
+ tagBg: '#059669',
194
+ icon: '✓',
195
+ label: 'אומת',
196
+ title: '✓ הציטוט אומת — קיים במקור משפטי',
197
+ },
198
+ not_found: {
199
+ bg: 'linear-gradient(135deg,#fee2e2,#fecaca)',
200
+ border: '#dc2626',
201
+ ink: '#7f1d1d',
202
+ tagBg: '#dc2626',
203
+ icon: '❌',
204
+ label: 'לא נמצא',
205
+ title: '❌ הציטוט לא נמצא בשום מקור — כנראה הלוסינציה של ChatGPT',
206
+ },
207
+ };
208
+
209
+ function applyChipState(wrap, res, cite) {
210
+ const state = STYLES[res.status] || STYLES.checking;
211
+ wrap.innerHTML = '';
212
+ wrap.style.cssText = [
213
+ 'display:inline-flex',
214
+ 'align-items:center',
215
+ 'gap:6px',
216
+ 'background:' + state.bg,
217
+ 'border:1px solid ' + state.border,
218
+ 'border-radius:6px',
219
+ 'padding:1px 4px 1px 8px',
220
+ 'margin:0 3px',
221
+ 'font-weight:600',
222
+ 'color:' + state.ink,
223
+ 'vertical-align:baseline',
224
+ 'white-space:nowrap',
225
+ 'transition:background .15s,border-color .15s',
226
+ ].join(';');
227
+
228
+ wrap.appendChild(document.createTextNode(cite));
229
+
230
+ const tag = document.createElement('a');
231
+ tag.href = LEGAL_EYE_URL + encodeURIComponent(cite);
232
+ tag.target = '_blank';
233
+ tag.rel = 'noopener noreferrer';
234
+ tag.title = state.title +
235
+ (res.source ? `\n(מקור: ${res.source})` : '') +
236
+ '\nלחץ לפתיחה בלגל-אי';
237
+ tag.style.cssText = [
238
+ 'text-decoration:none',
239
+ 'background:' + state.tagBg,
240
+ 'color:white',
241
+ 'border-radius:4px',
242
+ 'padding:1px 7px',
243
+ 'font-size:0.85em',
244
+ 'font-weight:700',
245
+ 'line-height:1.4',
246
+ 'transition:transform .08s,opacity .08s',
247
+ ].join(';');
248
+ tag.textContent = state.icon + ' ' + state.label;
249
+ tag.addEventListener('mouseenter', () => {
250
+ tag.style.opacity = '0.85';
251
+ tag.style.transform = 'translateY(-1px)';
252
+ });
253
+ tag.addEventListener('mouseleave', () => {
254
+ tag.style.opacity = '';
255
+ tag.style.transform = '';
256
+ });
257
+ wrap.appendChild(tag);
258
+ }
259
+
260
+ // ── DOM walker ─────────────────────────────────────────────────────
261
+ function processTextNode(textNode) {
262
+ if (alreadyMarked(textNode)) return;
263
+ const text = textNode.textContent;
264
+ if (!text || text.length < 6) return;
265
+ LE_CITE_RE.lastIndex = 0;
266
+ if (!LE_CITE_RE.test(text)) return;
267
+ const frag = buildChipFragment(text);
268
+ textNode.replaceWith(frag);
269
+ }
270
+
271
+ function walkAndProcess(root) {
272
+ if (!root || root.nodeType !== Node.ELEMENT_NODE) {
273
+ if (root && root.nodeType === Node.TEXT_NODE) processTextNode(root);
274
+ return;
275
+ }
276
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
277
+ acceptNode: (n) => {
278
+ if (alreadyMarked(n)) return NodeFilter.FILTER_REJECT;
279
+ if (!n.textContent || n.textContent.length < 6) return NodeFilter.FILTER_REJECT;
280
+ const p = n.parentElement;
281
+ if (!p) return NodeFilter.FILTER_REJECT;
282
+ const tag = p.tagName;
283
+ if (tag === 'SCRIPT' || tag === 'STYLE' ||
284
+ tag === 'TEXTAREA' || tag === 'INPUT' ||
285
+ p.isContentEditable) {
286
+ return NodeFilter.FILTER_REJECT;
287
+ }
288
+ return NodeFilter.FILTER_ACCEPT;
289
+ },
290
+ });
291
+ const nodes = [];
292
+ let n;
293
+ while ((n = walker.nextNode())) nodes.push(n);
294
+ nodes.forEach(processTextNode);
295
+ }
296
+
297
+ // Debounced — ChatGPT streams char-by-char, wait for settle.
298
+ let debounceTimer = null;
299
+ function scheduleProcess() {
300
+ if (debounceTimer) clearTimeout(debounceTimer);
301
+ debounceTimer = setTimeout(() => {
302
+ walkAndProcess(document.body);
303
+ }, 400);
304
+ }
305
+
306
+ const observer = new MutationObserver(scheduleProcess);
307
+ observer.observe(document.body, {
308
+ childList: true,
309
+ subtree: true,
310
+ characterData: true,
311
+ });
312
+ setTimeout(() => walkAndProcess(document.body), 1500);
313
+
314
+ console.info('[legal-eye] ChatGPT overlay v0.4.0 — hallucination filter active');
315
+ })();
tau_rag/chrome-extension/content.js CHANGED
@@ -11,12 +11,22 @@
11
  // PAGE ← { type: 'LE_HELPER_READY', version } (announces presence on load)
12
 
13
  (function () {
14
- const VERSION = '0.1.0';
15
 
16
  window.addEventListener('message', async (e) => {
17
  if (e.source !== window) return;
18
  const msg = e.data;
19
- if (!msg || msg.type !== 'LE_HELPER_FETCH') return;
 
 
 
 
 
 
 
 
 
 
20
  const { id, url } = msg;
21
  if (!id || !url) return;
22
 
 
11
  // PAGE ← { type: 'LE_HELPER_READY', version } (announces presence on load)
12
 
13
  (function () {
14
+ const VERSION = '0.2.1';
15
 
16
  window.addEventListener('message', async (e) => {
17
  if (e.source !== window) return;
18
  const msg = e.data;
19
+ if (!msg) return;
20
+
21
+ // v0.2.1 — page can probe us with PING; we respond with READY.
22
+ // Fixes timing issue where READY announce fired before page set up
23
+ // its listener (e.g. on install-instructions page).
24
+ if (msg.type === 'LE_HELPER_PING') {
25
+ announce();
26
+ return;
27
+ }
28
+
29
+ if (msg.type !== 'LE_HELPER_FETCH') return;
30
  const { id, url } = msg;
31
  if (!id || !url) return;
32
 
tau_rag/chrome-extension/manifest.json CHANGED
@@ -1,20 +1,21 @@
1
  {
2
  "manifest_version": 3,
3
  "name": "Legal Eye Helper",
4
- "version": "0.1.0",
5
  "description": "מאפשר ל-Legal Eye לטעון פסקי-דין ישירות מאתרי הפסיקה (בית-המשפט, ויקיטקסט, וכו'). דורש הסכמה מפורשת לכל בקשה.",
6
- "default_locale": "he",
7
  "host_permissions": [
8
- "https://www.court.gov.il/*",
9
- "https://supreme.court.gov.il/*",
10
  "https://www.google.com/*",
11
  "https://html.duckduckgo.com/*",
12
  "https://www.pador.co.il/*",
13
  "https://www.psakdin.co.il/*",
14
  "https://www.takdin.co.il/*",
15
  "https://www.lawdata.co.il/*",
 
16
  "https://he.wikipedia.org/*",
17
- "https://he.wikisource.org/*"
 
 
18
  ],
19
  "permissions": ["storage"],
20
  "content_scripts": [{
@@ -24,6 +25,13 @@
24
  ],
25
  "js": ["content.js"],
26
  "run_at": "document_start"
 
 
 
 
 
 
 
27
  }],
28
  "background": {
29
  "service_worker": "background.js"
 
1
  {
2
  "manifest_version": 3,
3
  "name": "Legal Eye Helper",
4
+ "version": "0.4.0",
5
  "description": "מאפשר ל-Legal Eye לטעון פסקי-דין ישירות מאתרי הפסיקה (בית-המשפט, ויקיטקסט, וכו'). דורש הסכמה מפורשת לכל בקשה.",
 
6
  "host_permissions": [
7
+ "https://*.court.gov.il/*",
 
8
  "https://www.google.com/*",
9
  "https://html.duckduckgo.com/*",
10
  "https://www.pador.co.il/*",
11
  "https://www.psakdin.co.il/*",
12
  "https://www.takdin.co.il/*",
13
  "https://www.lawdata.co.il/*",
14
+ "https://www.nlrc.gov.il/*",
15
  "https://he.wikipedia.org/*",
16
+ "https://he.wikisource.org/*",
17
+ "https://legal-i-legal-eye.hf.space/*",
18
+ "https://legal-eye.1bigfam.com/*"
19
  ],
20
  "permissions": ["storage"],
21
  "content_scripts": [{
 
25
  ],
26
  "js": ["content.js"],
27
  "run_at": "document_start"
28
+ }, {
29
+ "matches": [
30
+ "https://chatgpt.com/*",
31
+ "https://chat.openai.com/*"
32
+ ],
33
+ "js": ["chatgpt-overlay.js"],
34
+ "run_at": "document_idle"
35
  }],
36
  "background": {
37
  "service_worker": "background.js"