@reticlehq/browser 3.6.0 → 3.7.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 (83) hide show
  1. package/dist/actions/action-report.d.ts +54 -0
  2. package/dist/actions/action-report.js +60 -0
  3. package/dist/actions/actions-dom.d.ts +1 -0
  4. package/dist/actions/actions-dom.js +12 -0
  5. package/dist/actions/actions-press.d.ts +1 -0
  6. package/dist/actions/actions-press.js +25 -1
  7. package/dist/actions/actions.d.ts +1 -37
  8. package/dist/actions/actions.js +24 -66
  9. package/dist/actions/appeared-text.js +3 -0
  10. package/dist/actions/danger-context.d.ts +2 -0
  11. package/dist/actions/danger-context.js +28 -2
  12. package/dist/actions/value-input.d.ts +1 -0
  13. package/dist/actions/value-input.js +11 -0
  14. package/dist/commands/commands.js +4 -0
  15. package/dist/dom/a11y.js +251 -10
  16. package/dist/dom/addressing/coverage-markers.d.ts +6 -0
  17. package/dist/dom/addressing/coverage-markers.js +13 -0
  18. package/dist/dom/dom-ignore.d.ts +2 -0
  19. package/dist/dom/dom-ignore.js +15 -2
  20. package/dist/dom/query.d.ts +1 -1
  21. package/dist/dom/query.js +78 -16
  22. package/dist/dom/render-page.d.ts +12 -0
  23. package/dist/dom/render-page.js +135 -0
  24. package/dist/dom/snapshot.js +55 -7
  25. package/dist/observers/dom.js +32 -16
  26. package/dist/observers/field.js +3 -21
  27. package/dist/observers/network.js +56 -7
  28. package/dist/observers/perf.js +53 -1
  29. package/dist/observers/route.js +6 -5
  30. package/dist/presenter/carousel/carousel.js +2 -7
  31. package/dist/presenter/carousel/panel-slides.d.ts +2 -4
  32. package/dist/presenter/carousel/panel-slides.js +17 -15
  33. package/dist/presenter/chrome/log-memory.d.ts +1 -0
  34. package/dist/presenter/chrome/presenter-log.d.ts +7 -0
  35. package/dist/presenter/chrome/presenter-log.js +48 -8
  36. package/dist/presenter/chrome/presenter-morph.d.ts +1 -0
  37. package/dist/presenter/chrome/presenter-morph.js +34 -0
  38. package/dist/presenter/chrome/presenter-report-copy.d.ts +17 -4
  39. package/dist/presenter/chrome/presenter-report-copy.js +26 -4
  40. package/dist/presenter/hud-telemetry.js +55 -4
  41. package/dist/presenter/presenter-account-styles.js +3 -3
  42. package/dist/presenter/presenter-account.d.ts +27 -5
  43. package/dist/presenter/presenter-account.js +83 -6
  44. package/dist/presenter/presenter-agent-link.d.ts +9 -0
  45. package/dist/presenter/presenter-agent-link.js +62 -0
  46. package/dist/presenter/presenter-chat-views.d.ts +27 -5
  47. package/dist/presenter/presenter-chat-views.js +312 -72
  48. package/dist/presenter/presenter-config.d.ts +2 -0
  49. package/dist/presenter/presenter-config.js +1 -0
  50. package/dist/presenter/presenter-controls.d.ts +0 -1
  51. package/dist/presenter/presenter-controls.js +13 -91
  52. package/dist/presenter/presenter-design-tokens.js +1 -1
  53. package/dist/presenter/presenter-frame.d.ts +1 -1
  54. package/dist/presenter/presenter-frame.js +8 -10
  55. package/dist/presenter/presenter-harness-row.d.ts +67 -0
  56. package/dist/presenter/presenter-harness-row.js +209 -0
  57. package/dist/presenter/presenter-personas.d.ts +35 -0
  58. package/dist/presenter/presenter-personas.js +95 -0
  59. package/dist/presenter/presenter-plan.js +25 -24
  60. package/dist/presenter/presenter-report-styles.js +40 -22
  61. package/dist/presenter/presenter-report.d.ts +2 -1
  62. package/dist/presenter/presenter-report.js +83 -17
  63. package/dist/presenter/presenter-settings-styles.js +8 -4
  64. package/dist/presenter/presenter-settings.d.ts +7 -6
  65. package/dist/presenter/presenter-settings.js +75 -34
  66. package/dist/presenter/presenter-shell-styles.js +12 -3
  67. package/dist/presenter/presenter-shell.d.ts +10 -4
  68. package/dist/presenter/presenter-shell.js +31 -23
  69. package/dist/presenter/presenter-styles.js +0 -2
  70. package/dist/presenter/presenter-workspace.js +10 -3
  71. package/dist/presenter/presenter.d.ts +3 -2
  72. package/dist/presenter/presenter.js +81 -20
  73. package/dist/registry/declared-platform.js +2 -0
  74. package/dist/reticle-inject.js +777 -700
  75. package/dist/reticle.js +36 -2
  76. package/dist/storage-keys.d.ts +3 -2
  77. package/dist/storage-keys.js +7 -3
  78. package/dist/transport/transport.js +5 -3
  79. package/dist/transport/unreachable-message.d.ts +1 -1
  80. package/dist/transport/unreachable-message.js +3 -5
  81. package/package.json +2 -2
  82. package/dist/presenter/carousel/offer-card.d.ts +0 -21
  83. package/dist/presenter/carousel/offer-card.js +0 -56
package/dist/reticle.js CHANGED
@@ -1,4 +1,6 @@
1
- import { setPresenterVisible } from './dom/dom-ignore.js';
1
+ import { setPresenterViewer, setPresenterVisible } from './dom/dom-ignore.js';
2
+ import { setOutwardPolicy } from './actions/danger-context.js';
3
+ import { coverageMarkers, notApplicableFor } from './dom/addressing/coverage-markers.js';
2
4
  import { EventType, RETICLE_DEFAULT_PORT, RETICLE_PROTOCOL_VERSION, RETICLE_URL_PARAM, RETICLE_WS_PATH, bridgeWsUrl, ReticleCommand, MessageKind, SESSION_AUTO, SessionState, TRANSPORT_LIMITS, buildRedactionPolicy, isLoopbackHostname, isLocalPage, setActiveRedactionPolicy, wireRedactionKeys, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, CONTRACT_FINGERPRINT, CONTRACT_PARTS, newDocumentId, NO_EDITS_OBSERVED, PresenterMode, CommandActor, HudCorner, HudVisibility, } from '@reticlehq/core';
3
5
  import { rememberSessionLabel } from './session-continuity.js';
4
6
  import { editEpoch } from './edit-epoch.js';
@@ -73,6 +75,7 @@ export function connectionPolicy(pageHostname, bridgeUrl, allowNonLocalhost, tok
73
75
  }
74
76
  return { allowed: true };
75
77
  }
78
+ const MAX_PENDING_NARRATIONS = 20;
76
79
  const KILLED_MESSAGE = '[Reticle] killed for this page. Restart your dev server or reload the page to bring it back.';
77
80
  const BRIDGE_LOST_SUMMARY = 'Session ended - lost connection to Reticle (the agent is no longer running).';
78
81
  export function resolveSessionLabel(option, gen) {
@@ -141,6 +144,7 @@ export class Reticle {
141
144
  #overlay;
142
145
  #presenter;
143
146
  #pendingPushes = new Map();
147
+ #pendingNarrations = [];
144
148
  #pendingHud;
145
149
  #readPanelSettings;
146
150
  #panelRequested = false;
@@ -281,6 +285,8 @@ export class Reticle {
281
285
  if (options.idleEndMs !== undefined)
282
286
  panelOptions.idleEndMs = options.idleEndMs;
283
287
  panelOptions.sessionId = this.#session;
288
+ if (this.#projectId !== undefined)
289
+ panelOptions.projectId = this.#projectId;
284
290
  panelOptions.onControl = (intent) => this.#emit(EventType.HUMAN_CONTROL, intent.text !== undefined
285
291
  ? { kind: intent.kind, text: intent.text }
286
292
  : { kind: intent.kind });
@@ -292,9 +298,14 @@ export class Reticle {
292
298
  const panel = new Presenter(panelOptions);
293
299
  this.#presenter = panel;
294
300
  panel.mount();
301
+ panel.showSdkVersion(this.#sdkVersion);
295
302
  for (const buffered of this.#pendingPushes.values())
296
303
  panel.handlePush(buffered);
297
304
  this.#pendingPushes.clear();
305
+ if (0 < this.#pendingNarrations.length)
306
+ panel.sessionStart();
307
+ for (const early of this.#pendingNarrations.splice(0))
308
+ panel.narrate(early.text, early.level, early.actor, early.summary);
298
309
  if (this.#pendingHud !== undefined)
299
310
  panel.placeHud(this.#pendingHud, undefined);
300
311
  this.#pendingHud = undefined;
@@ -423,9 +434,25 @@ export class Reticle {
423
434
  };
424
435
  }
425
436
  async #handleCommand(command) {
437
+ if (command.name === ReticleCommand.MARKERS) {
438
+ const markers = coverageMarkers(document);
439
+ const buildPlugin = this.#sdkVersion !== undefined || this.#sourceMapping !== undefined;
440
+ const na = notApplicableFor({ buildPlugin, adapters: adapterNames(), markers });
441
+ return { ok: true, result: { markers, ...(0 === na.length ? {} : { notApplicable: na }) } };
442
+ }
426
443
  if (command.name === ReticleCommand.NARRATE) {
444
+ if (this.#presenter === undefined && this.#panelRequested) {
445
+ if (this.#pendingNarrations.length < MAX_PENDING_NARRATIONS)
446
+ this.#pendingNarrations.push({
447
+ text: str(command.args['text']),
448
+ level: str(command.args['level'], 'info'),
449
+ actor: narrationActor(command.args),
450
+ summary: true === command.args['summary'],
451
+ });
452
+ return { ok: true, result: { shown: false, buffered: true } };
453
+ }
427
454
  this.#presenter?.sessionStart();
428
- this.#presenter?.narrate(str(command.args['text']), str(command.args['level'], 'info'));
455
+ this.#presenter?.narrate(str(command.args['text']), str(command.args['level'], 'info'), narrationActor(command.args), true === command.args['summary']);
429
456
  return { ok: true, result: { shown: this.#presenter !== undefined } };
430
457
  }
431
458
  if (command.name === ReticleCommand.SESSION_CONFIG) {
@@ -471,6 +498,8 @@ export class Reticle {
471
498
  }
472
499
  this.#presenter?.sessionStart();
473
500
  await this.#presentBefore(command);
501
+ setPresenterViewer(CommandActor.HARNESS === command.by);
502
+ setOutwardPolicy(command.outward?.allowed);
474
503
  try {
475
504
  const result = await handler(command.args);
476
505
  this.#actHandle?.result(LOG_RESULT.PASS);
@@ -481,6 +510,8 @@ export class Reticle {
481
510
  return { ok: false, error: error instanceof Error ? error.message : String(error) };
482
511
  }
483
512
  finally {
513
+ setPresenterViewer(false);
514
+ setOutwardPolicy(undefined);
484
515
  this.#actHandle = undefined;
485
516
  this.#presenter?.scheduleIdle();
486
517
  }
@@ -515,6 +546,9 @@ export class Reticle {
515
546
  }
516
547
  }
517
548
  }
549
+ function narrationActor(args) {
550
+ return CommandActor.HARNESS === args['actor'] ? LOG_ACTOR.HARNESS : LOG_ACTOR.AGENT;
551
+ }
518
552
  function oneOf(values, raw) {
519
553
  return Object.values(values).includes(raw) ? raw : undefined;
520
554
  }
@@ -5,10 +5,11 @@ export declare const ReticleStorageKey: {
5
5
  readonly PRESENTER_LOG_OWNER: "reticle-presenter-log-owner";
6
6
  readonly PRESENTER_SETTINGS: "reticle-presenter-settings";
7
7
  readonly PRESENTER_MINIMISED: "reticle-presenter-minimised";
8
+ readonly PRESENTER_VIEW: "reticle-presenter-view";
8
9
  readonly CAROUSEL_DISMISSED: "reticle.carousel.dismissed";
9
- readonly HARNESS_OFFER_DISMISSED: "reticle.harnessOffer.dismissed";
10
10
  readonly ANNOTATION_HISTORY: "reticle.annotations.history.v1";
11
- readonly RECENT_PLAYS: "reticle.flow.recent.v1";
12
11
  };
13
12
  export declare const TOUR_SEEN_KEY_PREFIX = "reticle.tour.seen.";
13
+ export declare const PERSONA_KEY_PREFIX = "reticle.harness.persona.";
14
+ export declare const IMPACT_SEEN_KEY_PREFIX = "reticle.impact.seen.";
14
15
  export declare function isReticleStorageKey(key: string): boolean;
@@ -5,13 +5,17 @@ export const ReticleStorageKey = {
5
5
  PRESENTER_LOG_OWNER: 'reticle-presenter-log-owner',
6
6
  PRESENTER_SETTINGS: 'reticle-presenter-settings',
7
7
  PRESENTER_MINIMISED: 'reticle-presenter-minimised',
8
+ PRESENTER_VIEW: 'reticle-presenter-view',
8
9
  CAROUSEL_DISMISSED: 'reticle.carousel.dismissed',
9
- HARNESS_OFFER_DISMISSED: 'reticle.harnessOffer.dismissed',
10
10
  ANNOTATION_HISTORY: 'reticle.annotations.history.v1',
11
- RECENT_PLAYS: 'reticle.flow.recent.v1',
12
11
  };
13
12
  export const TOUR_SEEN_KEY_PREFIX = 'reticle.tour.seen.';
13
+ export const PERSONA_KEY_PREFIX = 'reticle.harness.persona.';
14
+ export const IMPACT_SEEN_KEY_PREFIX = 'reticle.impact.seen.';
14
15
  const OWN_KEYS = new Set(Object.values(ReticleStorageKey));
15
16
  export function isReticleStorageKey(key) {
16
- return OWN_KEYS.has(key) || key.startsWith(TOUR_SEEN_KEY_PREFIX);
17
+ return (OWN_KEYS.has(key) ||
18
+ key.startsWith(TOUR_SEEN_KEY_PREFIX) ||
19
+ key.startsWith(PERSONA_KEY_PREFIX) ||
20
+ key.startsWith(IMPACT_SEEN_KEY_PREFIX));
17
21
  }
@@ -26,6 +26,7 @@ export class Transport {
26
26
  #signalQueue = [];
27
27
  #insertOrder = 0;
28
28
  #closed = false;
29
+ #refused = false;
29
30
  #disconnectedSince;
30
31
  #reconnectDelay = RECONNECT_DELAY_MS;
31
32
  #lost = false;
@@ -55,6 +56,7 @@ export class Transport {
55
56
  this.#reconnectDelay = RECONNECT_DELAY_MS;
56
57
  if (this.#ws !== undefined)
57
58
  return;
59
+ this.#refused = false;
58
60
  this.#open();
59
61
  }
60
62
  #open() {
@@ -92,9 +94,9 @@ export class Transport {
92
94
  ws.onclose = (event) => {
93
95
  this.#ws = undefined;
94
96
  if (event.code === WS_POLICY_VIOLATION) {
95
- this.#closed = true;
97
+ this.#refused = true;
96
98
  const reason = event.reason.length > 0 ? event.reason : 'policy violation';
97
- nativeWarn(`[reticle] bridge refused the connection: ${reason} — not retrying.`);
99
+ nativeWarn(`[reticle] bridge refused the connection: ${reason} — not retrying until you come back to this tab.`);
98
100
  this.#deps.onConnectionLost?.();
99
101
  return;
100
102
  }
@@ -107,7 +109,7 @@ export class Transport {
107
109
  };
108
110
  }
109
111
  #scheduleReopen() {
110
- if (this.#closed)
112
+ if (this.#closed || this.#refused)
111
113
  return;
112
114
  const delay = this.#reconnectDelay;
113
115
  this.#reconnectDelay = nextReconnectDelay(delay);
@@ -1,2 +1,2 @@
1
1
  export declare function unreachableMessage(url: string, attempts: number): string;
2
- export declare function unreachableStripText(url: string, attempts: number): string;
2
+ export declare const UNREACHABLE_HEADLINE = "Can't reach Reticle from this page. Run reticle status to check, or reticle serve to start it.";
@@ -5,9 +5,7 @@ export function unreachableMessage(url, attempts) {
5
5
  `that is not there from one it is not allowed to reach, so this is not evidence about the ` +
6
6
  `daemon. What answers it: run \`npx @reticlehq/server status\` to see whether one is listening ` +
7
7
  `and on which port. If your app runs in a container, devcontainer or WSL, the daemon is on a ` +
8
- `different host, so set the URL explicitly (Vite: VITE_RETICLE_WS_URL, or ` +
9
- `reticle.connect({ url })). An https page cannot open a ws:// socket at all. Still retrying…`);
10
- }
11
- export function unreachableStripText(url, attempts) {
12
- return `no bridge at ${url} — ${String(attempts)} attempt${1 === attempts ? '' : 's'}`;
8
+ `different host, so point the page at it: RETICLE_PORT or reticle({ port }) with the Vite ` +
9
+ `plugin, reticle.connect({ url }) by hand. An https page cannot open a ws:// socket. Still retrying…`);
13
10
  }
11
+ export const UNREACHABLE_HEADLINE = "Can't reach Reticle from this page. Run reticle status to check, or reticle serve to start it.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reticlehq/browser",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Reticle browser SDK — installs observers, builds semantic snapshots, executes actions, talks to the bridge.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  }
19
19
  },
20
20
  "dependencies": {
21
- "@reticlehq/core": "3.6.0"
21
+ "@reticlehq/core": "3.7.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@iconify-json/heroicons-outline": "^1.2.1",
@@ -1,21 +0,0 @@
1
- export declare const OFFER_ATTR = "data-reticle-offer";
2
- export declare const OFFER_CLAIM_ATTR = "data-reticle-offer-claim";
3
- export declare const OFFER_DISMISSED_KEY: "reticle.harnessOffer.dismissed";
4
- export declare const OFFER_TEXT: {
5
- readonly HEADLINE: "Get Harness free for 3 months";
6
- readonly BODY: "Explores your app and saves every journey as a free replay.";
7
- readonly CLAIM: "Claim now";
8
- readonly DISMISS: "Not now";
9
- readonly claimed: (days: number) => string;
10
- readonly ending: (days: number) => string;
11
- };
12
- export declare function offerDismissed(storage: Pick<Storage, 'getItem'> | undefined): boolean;
13
- export interface OfferState {
14
- claimed: boolean;
15
- daysRemaining?: number | undefined;
16
- claimUrl?: string | undefined;
17
- eligible?: boolean | undefined;
18
- drivable?: boolean | undefined;
19
- }
20
- export declare function offerHtml(offer: OfferState | undefined, dismissed: boolean): string;
21
- export declare const OFFER_CSS: string;
@@ -1,56 +0,0 @@
1
- import { ReticleStorageKey } from '../../storage-keys.js';
2
- export const OFFER_ATTR = 'data-reticle-offer';
3
- export const OFFER_CLAIM_ATTR = 'data-reticle-offer-claim';
4
- export const OFFER_DISMISSED_KEY = ReticleStorageKey.HARNESS_OFFER_DISMISSED;
5
- export const OFFER_TEXT = {
6
- HEADLINE: 'Get Harness free for 3 months',
7
- BODY: 'Explores your app and saves every journey as a free replay.',
8
- CLAIM: 'Claim now',
9
- DISMISS: 'Not now',
10
- claimed: (days) => `Harness active — ${String(days)} days left`,
11
- ending: (days) => 1 >= days
12
- ? 'Harness free period ends today'
13
- : `Harness free period ends in ${String(days)} days`,
14
- };
15
- const ENDING_SOON_DAYS = 14;
16
- export function offerDismissed(storage) {
17
- try {
18
- return '1' === storage?.getItem(OFFER_DISMISSED_KEY);
19
- }
20
- catch {
21
- return false;
22
- }
23
- }
24
- const escape = (text) => text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
25
- export function offerHtml(offer, dismissed) {
26
- if (offer === undefined)
27
- return '';
28
- if (offer.claimed) {
29
- const days = offer.daysRemaining;
30
- if (days === undefined || 0 > days)
31
- return '';
32
- const text = ENDING_SOON_DAYS >= days ? OFFER_TEXT.ending(days) : OFFER_TEXT.claimed(days);
33
- return `<div ${OFFER_ATTR} class="reticle-offer reticle-offer-claimed">${escape(text)}</div>`;
34
- }
35
- if (false === offer.eligible)
36
- return '';
37
- if (false === offer.drivable)
38
- return '';
39
- if (dismissed)
40
- return '';
41
- const url = offer.claimUrl;
42
- if (url === undefined || 0 === url.length)
43
- return '';
44
- return `<div ${OFFER_ATTR} class="reticle-offer" role="note">
45
- <span class="reticle-offer-title">${escape(OFFER_TEXT.HEADLINE)}</span>
46
- <p class="reticle-offer-body">${escape(OFFER_TEXT.BODY)}</p>
47
- <a ${OFFER_CLAIM_ATTR} class="reticle-offer-claim" href="${escape(url)}" target="_blank" rel="noopener noreferrer">${escape(OFFER_TEXT.CLAIM)}</a>
48
- </div>`;
49
- }
50
- export const OFFER_CSS = `
51
- .reticle-offer{margin:8px 10px;padding:10px 12px;border:1px solid var(--reticle-line,#2a2f3a);border-radius:10px;background:var(--reticle-surface-inset,rgba(255,255,255,.03));}
52
- .reticle-offer-title{display:block;font-weight:600;font-size:12px;padding-right:18px;}
53
- .reticle-offer-body{margin:6px 0 8px;font-size:11px;line-height:1.45;opacity:.8;}
54
- .reticle-offer-claim{display:inline-block;font-size:11px;font-weight:600;padding:4px 10px;border-radius:6px;background:var(--reticle-accent,#4f7cff);color:#fff;text-decoration:none;}
55
- .reticle-offer-claimed{margin:6px 10px;font-size:10px;opacity:.6;}
56
- `;