@agent-native/core 0.98.2 → 0.98.3

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 (64) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +10 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +2 -2
  5. package/corpus/core/src/client/index.ts +2 -0
  6. package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -6
  7. package/corpus/core/src/client/settings/SettingsPanel.tsx +31 -211
  8. package/corpus/core/src/client/settings/agent-settings-search.ts +219 -0
  9. package/corpus/core/src/client/settings/index.ts +4 -0
  10. package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
  11. package/corpus/core/src/demo/config.ts +6 -6
  12. package/corpus/core/src/demo/redact.ts +18 -76
  13. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +21 -4
  14. package/corpus/templates/analytics/AGENTS.md +6 -6
  15. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +429 -244
  16. package/corpus/templates/analytics/app/components/layout/command-palette-search.ts +93 -0
  17. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +2 -1
  18. package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +46 -33
  19. package/corpus/templates/analytics/app/pages/Settings.tsx +8 -45
  20. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +205 -70
  21. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +32 -16
  22. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +126 -0
  23. package/corpus/templates/analytics/changelog/2026-07-12-command-menu-loading-placeholders-now-stay-below-available-r.md +6 -0
  24. package/corpus/templates/analytics/changelog/2026-07-12-command-menu-search-now-prioritizes-the-best-match.md +6 -0
  25. package/corpus/templates/analytics/changelog/2026-07-12-demo-charts-preserve-source-movement.md +6 -0
  26. package/corpus/templates/analytics/changelog/2026-07-12-replay-storage-settings-stay-readable-at-narrow-widths.md +6 -0
  27. package/corpus/templates/analytics/changelog/2026-07-12-session-replays-preserve-recorded-styling-and-keep-malformed.md +6 -0
  28. package/dist/agent/production-agent.js +2 -2
  29. package/dist/agent/production-agent.js.map +1 -1
  30. package/dist/client/index.d.ts +1 -1
  31. package/dist/client/index.d.ts.map +1 -1
  32. package/dist/client/index.js +1 -1
  33. package/dist/client/index.js.map +1 -1
  34. package/dist/client/settings/DemoModeSection.d.ts +3 -3
  35. package/dist/client/settings/DemoModeSection.js +4 -4
  36. package/dist/client/settings/DemoModeSection.js.map +1 -1
  37. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  38. package/dist/client/settings/SettingsPanel.js +41 -191
  39. package/dist/client/settings/SettingsPanel.js.map +1 -1
  40. package/dist/client/settings/agent-settings-search.d.ts +16 -0
  41. package/dist/client/settings/agent-settings-search.d.ts.map +1 -0
  42. package/dist/client/settings/agent-settings-search.js +178 -0
  43. package/dist/client/settings/agent-settings-search.js.map +1 -0
  44. package/dist/client/settings/index.d.ts +1 -0
  45. package/dist/client/settings/index.d.ts.map +1 -1
  46. package/dist/client/settings/index.js +1 -0
  47. package/dist/client/settings/index.js.map +1 -1
  48. package/dist/collab/routes.d.ts +2 -2
  49. package/dist/collab/struct-routes.d.ts +1 -1
  50. package/dist/demo/actions/toggle-demo-mode.js +2 -2
  51. package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
  52. package/dist/demo/config.js +6 -6
  53. package/dist/demo/config.js.map +1 -1
  54. package/dist/demo/redact.d.ts +7 -5
  55. package/dist/demo/redact.d.ts.map +1 -1
  56. package/dist/demo/redact.js +19 -54
  57. package/dist/demo/redact.js.map +1 -1
  58. package/dist/notifications/routes.d.ts +1 -1
  59. package/dist/progress/routes.d.ts +1 -1
  60. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  61. package/dist/resources/handlers.d.ts +3 -3
  62. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  63. package/dist/server/transcribe-voice.d.ts +1 -1
  64. package/package.json +2 -2
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Pure, dependency-free, deterministic demo-mode redactor.
3
3
  *
4
- * Replaces sensitive values (names, emails, and free numbers) with stable fake
4
+ * Replaces sensitive values (emails and free numbers) with stable fake
5
5
  * substitutes so a demo looks coherent — the same input always maps to the
6
6
  * same fake. Crucially, it NEVER rewrites identifiers, structural tokens, or
7
- * timestamps: under-redaction is safe, corrupting an ID is not. The string
8
- * redactor uses a protect-first strategy (mask IDs with opaque placeholders
9
- * before any transform runs, restore them byte-identical afterwards), and the
10
- * structure-aware walker additionally protects leaf values by key name.
7
+ * timestamps. Names and other free text are deliberately left alone because
8
+ * guessing whether arbitrary text is a person's name is too inaccurate. The
9
+ * string redactor uses a protect-first strategy (mask IDs with opaque
10
+ * placeholders before any transform runs, restore them byte-identical
11
+ * afterwards), and the structure-aware walker additionally protects leaf
12
+ * values by key name.
11
13
  */
12
14
 
13
15
  export interface RedactOptions {
@@ -66,7 +68,7 @@ function pick<T>(rng: () => number, pool: readonly T[]): T {
66
68
  * round-trips back through redaction (e.g. you edit a draft that's
67
69
  * already showing fake text, it autosaves, then refetches) it is passed
68
70
  * through UNCHANGED instead of being re-faked into something new. This
69
- * is what stops names/emails drifting on every edit.
71
+ * is what stops emails drifting on every edit.
70
72
  *
71
73
  * Leak-free by construction: no timers, a hard size cap, and lazy TTL purge
72
74
  * on write. Works the same per-tab in the browser and per-process on the
@@ -242,15 +244,6 @@ const LAST_NAMES: readonly string[] = [
242
244
  * Fake value generators (deterministic in value + salt)
243
245
  * ------------------------------------------------------------------ */
244
246
 
245
- function fakeName(original: string, salt: string): string {
246
- return memoFake("name", original, salt, () => {
247
- const rng = seededRng(`name:${original}`, salt);
248
- const first = pick(rng, FIRST_NAMES);
249
- const last = pick(rng, LAST_NAMES);
250
- return `${first} ${last}`;
251
- });
252
- }
253
-
254
247
  // Realistic-looking but safe stand-in domains: real consumer providers (the
255
248
  // local part is fake, so no real mailbox is implied) plus the canonical
256
249
  // reserved fictional-company domains (Contoso/Fabrikam/Northwind), which read
@@ -484,10 +477,6 @@ function unprotect(text: string, restore: Map<string, string>): string {
484
477
 
485
478
  const EMAIL_RE = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
486
479
 
487
- // 2+ capitalized words (each starts uppercase, allows internal lowercase /
488
- // apostrophes / hyphens). Single-letter middle initials allowed: `Sarah J Connor`.
489
- const FULL_NAME_RE = /\b[A-Z][a-zA-Z'’-]*(?:\s+[A-Z](?:[a-zA-Z'’-]*)?){1,3}\b/g;
490
-
491
480
  // A standalone numeric token: optional currency + sign, digits with optional
492
481
  // comma grouping and a single optional decimal part. Bounded so it is NOT
493
482
  // adjacent to a letter (placeholders already removed letter-mixed tokens, but
@@ -499,10 +488,6 @@ function transformEmails(text: string, salt: string): string {
499
488
  return text.replace(EMAIL_RE, (match) => fakeEmail(match, salt));
500
489
  }
501
490
 
502
- function transformNames(text: string, salt: string): string {
503
- return text.replace(FULL_NAME_RE, (match) => fakeName(match, salt));
504
- }
505
-
506
491
  function isProbablyYear(digits: string): boolean {
507
492
  if (digits.length !== 4) return false;
508
493
  const n = Number(digits);
@@ -547,23 +532,18 @@ function transformNumbers(text: string, salt: string): string {
547
532
  * Public: string redactor
548
533
  * ------------------------------------------------------------------ */
549
534
 
550
- function redactDemoStringInternal(
551
- text: string,
552
- salt: string,
553
- redactNames: boolean,
554
- ): string {
535
+ function redactDemoStringInternal(text: string, salt: string): string {
555
536
  if (typeof text !== "string" || text.length === 0) return text;
556
537
 
557
538
  const { text: masked, restore } = protect(text);
558
539
  let out = masked;
559
540
  out = transformEmails(out, salt);
560
- if (redactNames) out = transformNames(out, salt);
561
541
  out = transformNumbers(out, salt);
562
542
  return unprotect(out, restore);
563
543
  }
564
544
 
565
545
  export function redactDemoString(text: string, opts?: RedactOptions): string {
566
- return redactDemoStringInternal(text, opts?.salt ?? "", true);
546
+ return redactDemoStringInternal(text, opts?.salt ?? "");
567
547
  }
568
548
 
569
549
  /* ------------------------------------------------------------------ *
@@ -573,35 +553,14 @@ export function redactDemoString(text: string, opts?: RedactOptions): string {
573
553
  // Keys whose leaf values must NEVER be transformed (still recurse into nested
574
554
  // objects/arrays under them). Besides ids/urls/timestamps this also covers
575
555
  // code/query-bearing keys (`sql`, `query`, `expression`, `formula`, `code`):
576
- // prose-redacting a SQL string injects fake emails/numbers into it and rewrites
577
- // its leading tokens, producing `syntax error at or near "Henry"` /
578
- // `queries must start with SELECT or WITH`. The query must run untouched; its
579
- // RESULTS are what get redacted so the chart still shows fake values.
556
+ // redacting a SQL string mutates literals and can make the query semantically
557
+ // wrong or invalid. The query must run untouched; its RESULTS are what get
558
+ // redacted so the chart still shows fake values.
580
559
  const PROTECTED_KEY_RE =
581
560
  /^id$|(^|_)id$|Id$|Ids$|uuid|guid|slug|token|secret|password|passwd|apikey|api_key|hash|sha\d*|etag|cursor|nonce|sessionid|messageid|threadid|nodeid|(^|_)key$|keyid|(^|_)ref$|url$|uri$|href$|src$|path$|filename$|mimetype|mime|^sql$|sql$|query|expression|formula|^code$|createdat|updatedat|deletedat|expiresat|timestamp|.+at$|.+_at$/i;
582
561
 
583
- // UI labels and chart/dashboard titles often look like person names ("Maya
584
- // Davis (First-party)", "Clicks by Henry Moore") but are structural labels. Keep
585
- // them stable while still redacting free-text and contact-style person fields.
586
- const LABEL_TEXT_KEY_RE = /^(name|title|label)$/i;
587
-
588
562
  const MAX_DEPTH = 64;
589
563
 
590
- function isStandalonePersonNameLabel(text: string): boolean {
591
- const trimmed = text.trim();
592
- if (!trimmed) return false;
593
- return /^[A-Z][a-zA-Z'’-]+(?:\s+[A-Z]\.?)?\s+[A-Z][a-zA-Z'’-]+$/.test(
594
- trimmed,
595
- );
596
- }
597
-
598
- function shouldRedactNamesForChild(key: string, entry: unknown): boolean {
599
- if (!LABEL_TEXT_KEY_RE.test(key)) return true;
600
- // Preserve structural labels/titles, but do not let common contact-shaped
601
- // fields like `{ name: "Olivia Parker" }` leak real person names.
602
- return typeof entry === "string" ? isStandalonePersonNameLabel(entry) : true;
603
- }
604
-
605
564
  function isPlainObject(value: unknown): value is Record<string, unknown> {
606
565
  if (value === null || typeof value !== "object") return false;
607
566
  if (Array.isArray(value)) return false;
@@ -610,14 +569,6 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
610
569
  return proto === Object.prototype || proto === null;
611
570
  }
612
571
 
613
- function redactDemoStringWithSalt(
614
- text: string,
615
- salt: string,
616
- redactNames: boolean,
617
- ): string {
618
- return redactDemoStringInternal(text, salt, redactNames);
619
- }
620
-
621
572
  function redactNumberLeaf(value: number, salt: string): number {
622
573
  if (!Number.isFinite(value)) return value;
623
574
  const repr = String(value);
@@ -635,7 +586,6 @@ function walk(
635
586
  salt: string,
636
587
  depth: number,
637
588
  seen: WeakSet<object>,
638
- redactNames: boolean,
639
589
  ): unknown {
640
590
  if (depth > MAX_DEPTH) return value;
641
591
 
@@ -644,7 +594,7 @@ function walk(
644
594
  const t = typeof value;
645
595
 
646
596
  if (t === "string") {
647
- return redactDemoStringWithSalt(value as string, salt, redactNames);
597
+ return redactDemoStringInternal(value as string, salt);
648
598
  }
649
599
 
650
600
  if (t === "number") {
@@ -660,9 +610,7 @@ function walk(
660
610
  if (Array.isArray(value)) {
661
611
  if (seen.has(value)) return value;
662
612
  seen.add(value);
663
- const out = value.map((item) =>
664
- walk(item, salt, depth + 1, seen, redactNames),
665
- );
613
+ const out = value.map((item) => walk(item, salt, depth + 1, seen));
666
614
  seen.delete(value);
667
615
  return out;
668
616
  }
@@ -681,20 +629,14 @@ function walk(
681
629
  ) {
682
630
  // Still recurse into nested structures, but the protected key does
683
631
  // not transform its own leaf value.
684
- out[key] = walk(entry, salt, depth + 1, seen, redactNames);
632
+ out[key] = walk(entry, salt, depth + 1, seen);
685
633
  } else {
686
634
  // Leaf under a protected key: pass through completely untouched.
687
635
  out[key] = entry;
688
636
  }
689
637
  continue;
690
638
  }
691
- out[key] = walk(
692
- entry,
693
- salt,
694
- depth + 1,
695
- seen,
696
- redactNames && shouldRedactNamesForChild(key, entry),
697
- );
639
+ out[key] = walk(entry, salt, depth + 1, seen);
698
640
  }
699
641
  seen.delete(value);
700
642
  return out;
@@ -706,7 +648,7 @@ function walk(
706
648
 
707
649
  export function redactDemoData<T>(value: T, opts?: RedactOptions): T {
708
650
  const salt = opts?.salt ?? "";
709
- return walk(value, salt, 0, new WeakSet<object>(), true) as T;
651
+ return walk(value, salt, 0, new WeakSet<object>()) as T;
710
652
  }
711
653
 
712
654
  /**
@@ -104,10 +104,27 @@ agent answers about browser recordings in the Analytics template.
104
104
  rebuilding the player mid-load desyncs the scrubber and playhead.
105
105
  - Pass events to `Replayer` untouched. rrweb rebuilds them in a sandboxed iframe;
106
106
  pre-processing DOM, stylesheet, resource, or mutation payloads makes playback
107
- diverge from the captured page. Let rrweb own iframe sizing via Meta /
108
- ViewportResize, and keep the outer wrapper in that same coordinate system for
109
- fit-to-stage scaling. Never clamp only one layer, rewrite Meta widths, or force
110
- iframe dimensions that desyncs the FullSnapshot DOM and breaks the stage.
107
+ diverge from the captured page. In particular, never rewrite `href`, `src`,
108
+ `_cssText`, CSS `url()`, or Meta URLs to `about:blank`; that exact remediation
109
+ broke historical replay CSS in PR #2040. Handle request privacy at capture or
110
+ the sandbox boundary instead of mutating stored rrweb events. Historical
111
+ captures without inlined resources require live stylesheet/image/font
112
+ requests for accurate rendering; the viewer accepts that fidelity tradeoff,
113
+ uses rrweb's script-disabled sandbox plus `referrerpolicy="no-referrer"`, and
114
+ must never add credentials or proxy those URLs through a privileged server.
115
+ - Let rrweb own normal iframe sizing via Meta / ViewportResize, and keep the
116
+ outer wrapper in that same coordinate system for fit-to-stage scaling. Some
117
+ legacy recordings contain demonstrably corrupt 4,000–7,500px-wide viewport
118
+ values from ordinary desktop sessions. Only widths of at least 4,000px with
119
+ aspect ratios above 4:1 are recovered to a 16:10 viewport; real 32:9 and
120
+ mobile portrait captures remain untouched. The resize handler applies the
121
+ recovery to **both** rrweb's iframe and the outer stage.
122
+ Never remove this recovery, clamp only one layer, or broadly rewrite event
123
+ geometry: those changes repeatedly recreated the ribbon/clipping regression.
124
+ - Keep the realistic fidelity and malformed-viewport tests in
125
+ `SessionDetailPage.spec.ts`. Do not change their expectations merely to bless
126
+ a new sanitizer or raw ultra-wide sizing; validate the affected replay in a
127
+ browser first.
111
128
  - The event timeline soft-highlights the active marker, auto-scrolls it into
112
129
  view (pausing briefly after manual scroll), and supports search. It appears
113
130
  beside the player from ~880px content width upward.
@@ -41,12 +41,12 @@ details live in `.agents/skills/`.
41
41
  unless the user explicitly asks to inspect the demo dashboard.
42
42
  - Framework Demo mode is separate from the built-in `demo` source. While Demo
43
43
  mode is enabled, Analytics line and area charts reshape each returned numeric
44
- series into a deterministic, natural-looking upward trend using that series'
45
- actual minimum, maximum, and normalized volatility. Smooth source series stay
46
- smooth; spiky source series keep larger swings. This is presentation-only:
47
- query results, tables, metrics, exports, and non-time-series charts remain
48
- unchanged, so do not cite the displayed intermediate line/area points as
49
- retrieved source values.
44
+ series into a deterministic upward trend using that series' actual minimum,
45
+ maximum, and local step pattern. Source spikes and dips remain at the same
46
+ x-positions while the smallest necessary linear drift moves the series up and
47
+ to the right. This is presentation-only: query results, tables, metrics,
48
+ exports, and non-time-series charts remain unchanged, so do not cite the
49
+ displayed intermediate line/area points as retrieved source values.
50
50
  - Every analytical answer should include enough audit context for the user to
51
51
  trust it: source(s), time window, filters, sample size or row count,
52
52
  join/match method when relevant, and caveats/gaps.