gowtham0992 Codex commited on
Commit
358049d
·
1 Parent(s): c6f4ab1

Improve trusted handoff copy

Browse files

Co-authored-by: Codex <codex@openai.com>

Files changed (3) hide show
  1. app.py +32 -4
  2. style.css +27 -14
  3. tests/test_app_guard.py +15 -1
app.py CHANGED
@@ -206,6 +206,26 @@ def render_save_status(message: str = "") -> str:
206
  return f"<div class='save-status'>{message}</div>"
207
 
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  def should_use_heuristic_guard(
210
  model_analysis: ScamAnalysis,
211
  heuristic: ScamAnalysis,
@@ -243,7 +263,7 @@ def analyze_message(
243
  analysis = analysis_error(exc)
244
  return (
245
  render_analysis_html(message, analysis),
246
- analysis.trusted_person_message,
247
  render_memory_html(analysis, memory),
248
  memory,
249
  {},
@@ -256,7 +276,13 @@ def analyze_message(
256
  entry = last_scan["entry"]
257
  if not memory or memory[-1].get("text") != entry.get("text"):
258
  memory.append(entry)
259
- return render_analysis_html(message, analysis), analysis.trusted_person_message, render_memory_html(analysis, memory), memory, last_scan
 
 
 
 
 
 
260
 
261
 
262
  def analysis_error(exc: Exception) -> ScamAnalysis:
@@ -352,9 +378,11 @@ def build_app() -> gr.Blocks:
352
  </div>
353
  """
354
  )
 
355
  trusted_message = gr.Textbox(
356
- label="Send to someone you trust",
357
- lines=3,
 
358
  interactive=False,
359
  buttons=["copy"],
360
  elem_classes=["trusted-output"],
 
206
  return f"<div class='save-status'>{message}</div>"
207
 
208
 
209
+ def build_handoff_message(message: str, analysis: ScamAnalysis) -> str:
210
+ cleaned = " ".join(message.strip().split())
211
+ if len(cleaned) > 500:
212
+ cleaned = cleaned[:497].rstrip() + "..."
213
+
214
+ risk = analysis.risk_level.replace("_", " ")
215
+ scam_type = analysis.scam_type.replace("_", " ")
216
+ risk_line = f"Jawbreaker marked it as {risk}"
217
+ if scam_type and scam_type != "none":
218
+ risk_line += f" ({scam_type})"
219
+
220
+ return (
221
+ "Can you check this message with me before I do anything?\n\n"
222
+ f"Message I received:\n\"{cleaned}\"\n\n"
223
+ f"{risk_line}.\n"
224
+ f"Safest next step: {analysis.safest_action}\n\n"
225
+ "I have not clicked any links, replied, or sent anything."
226
+ )
227
+
228
+
229
  def should_use_heuristic_guard(
230
  model_analysis: ScamAnalysis,
231
  heuristic: ScamAnalysis,
 
263
  analysis = analysis_error(exc)
264
  return (
265
  render_analysis_html(message, analysis),
266
+ build_handoff_message(message, analysis),
267
  render_memory_html(analysis, memory),
268
  memory,
269
  {},
 
276
  entry = last_scan["entry"]
277
  if not memory or memory[-1].get("text") != entry.get("text"):
278
  memory.append(entry)
279
+ return (
280
+ render_analysis_html(message, analysis),
281
+ build_handoff_message(message, analysis),
282
+ render_memory_html(analysis, memory),
283
+ memory,
284
+ last_scan,
285
+ )
286
 
287
 
288
  def analysis_error(exc: Exception) -> ScamAnalysis:
 
378
  </div>
379
  """
380
  )
381
+ gr.HTML("<div class='handoff-header'>Send to someone you trust</div>")
382
  trusted_message = gr.Textbox(
383
+ label=None,
384
+ show_label=False,
385
+ lines=6,
386
  interactive=False,
387
  buttons=["copy"],
388
  elem_classes=["trusted-output"],
style.css CHANGED
@@ -548,11 +548,20 @@ button.primary:hover {
548
  line-height: 1.45;
549
  }
550
 
 
 
 
 
 
 
 
 
551
  .trusted-output {
552
- margin-top: 14px;
553
  }
554
 
555
  .trusted-output textarea {
 
556
  border-color: rgba(29, 29, 31, 0.1) !important;
557
  border-radius: 12px !important;
558
  background: linear-gradient(135deg, #fffdf8, #fff5df) !important;
@@ -561,22 +570,23 @@ button.primary:hover {
561
  line-height: 1.42 !important;
562
  }
563
 
564
- .trusted-output label span.svelte-jdcl7l {
565
- max-width: calc(100% - 42px);
566
- white-space: normal;
567
  }
568
 
569
  .trusted-output button[aria-label="Copy"],
570
  .trusted-output button[title="Copy"],
571
  .trusted-output .icon-button,
572
  .trusted-output button:has(svg) {
573
- width: 40px !important;
574
- height: 40px !important;
575
- border: none !important;
576
- border-radius: 10px !important;
577
- background: var(--jb-blue) !important;
578
- box-shadow: 0 2px 8px rgba(37, 99, 235, 0.2) !important;
579
- color: #ffffff !important;
 
580
  cursor: pointer !important;
581
  }
582
 
@@ -584,15 +594,18 @@ button.primary:hover {
584
  .trusted-output button[title="Copy"] svg,
585
  .trusted-output .icon-button svg,
586
  .trusted-output button:has(svg) svg {
587
- color: #ffffff !important;
588
- stroke: #ffffff !important;
 
 
589
  }
590
 
591
  .trusted-output button[aria-label="Copy"]:hover,
592
  .trusted-output button[title="Copy"]:hover,
593
  .trusted-output .icon-button:hover,
594
  .trusted-output button:has(svg):hover {
595
- background: #1d4ed8 !important;
 
596
  }
597
 
598
  .save-status {
 
548
  line-height: 1.45;
549
  }
550
 
551
+ .handoff-header {
552
+ margin: 14px 0 8px;
553
+ color: var(--jb-text);
554
+ font-size: 17px;
555
+ font-weight: 850;
556
+ line-height: 1.2;
557
+ }
558
+
559
  .trusted-output {
560
+ margin-top: 0;
561
  }
562
 
563
  .trusted-output textarea {
564
+ min-height: 132px !important;
565
  border-color: rgba(29, 29, 31, 0.1) !important;
566
  border-radius: 12px !important;
567
  background: linear-gradient(135deg, #fffdf8, #fff5df) !important;
 
570
  line-height: 1.42 !important;
571
  }
572
 
573
+ .trusted-output label,
574
+ .trusted-output .label-wrap {
575
+ display: none !important;
576
  }
577
 
578
  .trusted-output button[aria-label="Copy"],
579
  .trusted-output button[title="Copy"],
580
  .trusted-output .icon-button,
581
  .trusted-output button:has(svg) {
582
+ width: 34px !important;
583
+ height: 34px !important;
584
+ min-width: 34px !important;
585
+ border: 1px solid rgba(37, 99, 235, 0.16) !important;
586
+ border-radius: 999px !important;
587
+ background: #ffffff !important;
588
+ box-shadow: 0 2px 8px rgba(29, 29, 31, 0.08) !important;
589
+ color: var(--jb-blue) !important;
590
  cursor: pointer !important;
591
  }
592
 
 
594
  .trusted-output button[title="Copy"] svg,
595
  .trusted-output .icon-button svg,
596
  .trusted-output button:has(svg) svg {
597
+ width: 17px !important;
598
+ height: 17px !important;
599
+ color: var(--jb-blue) !important;
600
+ stroke: var(--jb-blue) !important;
601
  }
602
 
603
  .trusted-output button[aria-label="Copy"]:hover,
604
  .trusted-output button[title="Copy"]:hover,
605
  .trusted-output .icon-button:hover,
606
  .trusted-output button:has(svg):hover {
607
+ border-color: rgba(37, 99, 235, 0.28) !important;
608
+ background: #eff6ff !important;
609
  }
610
 
611
  .save-status {
tests/test_app_guard.py CHANGED
@@ -1,4 +1,4 @@
1
- from app import remember_current, should_use_heuristic_guard
2
  from jawbreaker.schema import ScamAnalysis
3
 
4
 
@@ -61,3 +61,17 @@ def test_remember_current_saves_last_scan_without_reanalysis(monkeypatch) -> Non
61
  assert "Saved this scam pattern for this session." in status
62
  assert len(memory) == 1
63
  assert "Session scam memory" in memory_html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app import build_handoff_message, remember_current, should_use_heuristic_guard
2
  from jawbreaker.schema import ScamAnalysis
3
 
4
 
 
61
  assert "Saved this scam pattern for this session." in status
62
  assert len(memory) == 1
63
  assert "Session scam memory" in memory_html
64
+
65
+
66
+ def test_handoff_message_includes_original_text_and_safe_action() -> None:
67
+ message = "Hi Grandma, I lost my phone. This is my new number. Can you send $800 today?"
68
+ analysis = ScamAnalysis.from_heuristics(message)
69
+
70
+ handoff = build_handoff_message(message, analysis)
71
+
72
+ assert "Message I received:" in handoff
73
+ assert message in handoff
74
+ assert "Jawbreaker marked it as dangerous (family impersonation)." in handoff
75
+ assert "Safest next step:" in handoff
76
+ assert analysis.safest_action in handoff
77
+ assert "I have not clicked any links, replied, or sent anything." in handoff