@reticlehq/browser 2.13.1 → 2.14.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 (196) hide show
  1. package/dist/actions/actions-dom.d.ts +0 -20
  2. package/dist/actions/actions-dom.js +0 -66
  3. package/dist/actions/actions-press.d.ts +10 -0
  4. package/dist/actions/actions-press.js +77 -0
  5. package/dist/actions/actions.d.ts +0 -89
  6. package/dist/actions/actions.js +24 -358
  7. package/dist/actions/appeared-text.d.ts +0 -20
  8. package/dist/actions/appeared-text.js +0 -36
  9. package/dist/actions/danger-context.d.ts +3 -0
  10. package/dist/actions/danger-context.js +39 -0
  11. package/dist/actions/scroll.d.ts +0 -9
  12. package/dist/actions/scroll.js +0 -7
  13. package/dist/actions/synthetic-input.d.ts +0 -7
  14. package/dist/actions/synthetic-input.js +0 -27
  15. package/dist/actions/value-input.d.ts +0 -30
  16. package/dist/actions/value-input.js +0 -33
  17. package/dist/commands/commands.d.ts +0 -2
  18. package/dist/commands/commands.js +0 -67
  19. package/dist/connect-options.d.ts +2 -96
  20. package/dist/dom/a11y.d.ts +0 -29
  21. package/dist/dom/a11y.js +1 -88
  22. package/dist/dom/auto-anchor.d.ts +0 -40
  23. package/dist/dom/auto-anchor.js +0 -33
  24. package/dist/dom/chart.d.ts +0 -61
  25. package/dist/dom/chart.js +0 -60
  26. package/dist/dom/desktop-capture.d.ts +0 -30
  27. package/dist/dom/desktop-capture.js +0 -72
  28. package/dist/dom/dom-ignore.d.ts +0 -30
  29. package/dist/dom/dom-ignore.js +0 -47
  30. package/dist/dom/occlusion.d.ts +0 -7
  31. package/dist/dom/occlusion.js +0 -7
  32. package/dist/dom/query.d.ts +0 -14
  33. package/dist/dom/query.js +48 -214
  34. package/dist/dom/realm.d.ts +0 -20
  35. package/dist/dom/realm.js +0 -28
  36. package/dist/dom/refs.d.ts +0 -76
  37. package/dist/dom/refs.js +2 -133
  38. package/dist/dom/shadow-registry.d.ts +0 -33
  39. package/dist/dom/shadow-registry.js +2 -72
  40. package/dist/dom/snapshot.d.ts +2 -43
  41. package/dist/dom/snapshot.js +26 -151
  42. package/dist/dom/source.d.ts +0 -44
  43. package/dist/dom/source.js +0 -45
  44. package/dist/dom/theme.d.ts +0 -37
  45. package/dist/dom/theme.js +2 -52
  46. package/dist/dom/virtualized.d.ts +0 -17
  47. package/dist/dom/virtualized.js +0 -27
  48. package/dist/edit-epoch.d.ts +0 -22
  49. package/dist/edit-epoch.js +0 -52
  50. package/dist/index.js +1 -23
  51. package/dist/observers/animation.d.ts +0 -1
  52. package/dist/observers/animation.js +0 -3
  53. package/dist/observers/blind-spots.d.ts +0 -26
  54. package/dist/observers/blind-spots.js +2 -61
  55. package/dist/observers/console.d.ts +1 -2
  56. package/dist/observers/console.js +41 -30
  57. package/dist/observers/context-open.d.ts +1 -11
  58. package/dist/observers/context-open.js +10 -14
  59. package/dist/observers/departure.d.ts +3 -0
  60. package/dist/observers/departure.js +52 -0
  61. package/dist/observers/dialogs.d.ts +2 -0
  62. package/dist/observers/dialogs.js +39 -0
  63. package/dist/observers/dom.js +0 -44
  64. package/dist/observers/download.d.ts +1 -2
  65. package/dist/observers/download.js +38 -55
  66. package/dist/observers/focus.d.ts +0 -6
  67. package/dist/observers/focus.js +0 -7
  68. package/dist/observers/frames.d.ts +0 -9
  69. package/dist/observers/frames.js +1 -28
  70. package/dist/observers/health.d.ts +0 -5
  71. package/dist/observers/health.js +1 -33
  72. package/dist/observers/install-all.d.ts +2 -0
  73. package/dist/observers/install-all.js +26 -27
  74. package/dist/observers/ipc.d.ts +0 -25
  75. package/dist/observers/ipc.js +0 -65
  76. package/dist/observers/navigation.d.ts +2 -0
  77. package/dist/observers/navigation.js +33 -0
  78. package/dist/observers/network-body.d.ts +5 -28
  79. package/dist/observers/network-body.js +11 -108
  80. package/dist/observers/network-redact.d.ts +1 -20
  81. package/dist/observers/network-redact.js +1 -80
  82. package/dist/observers/network-stream.d.ts +0 -21
  83. package/dist/observers/network-stream.js +1 -44
  84. package/dist/observers/network.d.ts +2 -26
  85. package/dist/observers/network.js +177 -294
  86. package/dist/observers/perf.d.ts +0 -10
  87. package/dist/observers/perf.js +0 -16
  88. package/dist/observers/route.d.ts +1 -2
  89. package/dist/observers/route.js +13 -19
  90. package/dist/observers/scroll.d.ts +0 -1
  91. package/dist/observers/scroll.js +0 -9
  92. package/dist/observers/sdk-failure.d.ts +1 -9
  93. package/dist/observers/sdk-failure.js +1 -30
  94. package/dist/observers/state.d.ts +1 -11
  95. package/dist/observers/state.js +13 -41
  96. package/dist/observers/storage.d.ts +0 -14
  97. package/dist/observers/storage.js +10 -34
  98. package/dist/observers/types.d.ts +0 -15
  99. package/dist/observers/types.js +0 -14
  100. package/dist/patching/capture-method.d.ts +0 -24
  101. package/dist/patching/capture-method.js +0 -26
  102. package/dist/presenter/overlay.d.ts +0 -1
  103. package/dist/presenter/overlay.js +0 -1
  104. package/dist/presenter/presenter-brand.d.ts +0 -19
  105. package/dist/presenter/presenter-brand.js +0 -21
  106. package/dist/presenter/presenter-config.d.ts +1 -105
  107. package/dist/presenter/presenter-config.js +2 -91
  108. package/dist/presenter/presenter-controls.d.ts +0 -29
  109. package/dist/presenter/presenter-controls.js +3 -131
  110. package/dist/presenter/presenter-dock-layout.d.ts +0 -3
  111. package/dist/presenter/presenter-dock-layout.js +0 -4
  112. package/dist/presenter/presenter-drag.d.ts +2 -24
  113. package/dist/presenter/presenter-drag.js +0 -26
  114. package/dist/presenter/presenter-effects.d.ts +0 -9
  115. package/dist/presenter/presenter-effects.js +0 -9
  116. package/dist/presenter/presenter-glow.d.ts +0 -21
  117. package/dist/presenter/presenter-glow.js +1 -27
  118. package/dist/presenter/presenter-heroicons-data.d.ts +0 -8
  119. package/dist/presenter/presenter-heroicons-data.js +0 -8
  120. package/dist/presenter/presenter-hud-chrome.d.ts +0 -10
  121. package/dist/presenter/presenter-hud-chrome.js +0 -10
  122. package/dist/presenter/presenter-icons.d.ts +0 -6
  123. package/dist/presenter/presenter-icons.js +0 -6
  124. package/dist/presenter/presenter-log.d.ts +0 -38
  125. package/dist/presenter/presenter-log.js +1 -45
  126. package/dist/presenter/presenter-report-copy.d.ts +5 -66
  127. package/dist/presenter/presenter-report-copy.js +5 -66
  128. package/dist/presenter/presenter-report-styles.d.ts +0 -6
  129. package/dist/presenter/presenter-report-styles.js +6 -6
  130. package/dist/presenter/presenter-report.d.ts +4 -7
  131. package/dist/presenter/presenter-report.js +18 -76
  132. package/dist/presenter/presenter-run-state.d.ts +0 -7
  133. package/dist/presenter/presenter-run-state.js +0 -5
  134. package/dist/presenter/presenter-settings-styles.d.ts +0 -1
  135. package/dist/presenter/presenter-settings-styles.js +0 -1
  136. package/dist/presenter/presenter-settings.d.ts +1 -23
  137. package/dist/presenter/presenter-settings.js +1 -31
  138. package/dist/presenter/presenter-shell-styles.js +2 -5
  139. package/dist/presenter/presenter-shell.d.ts +2 -18
  140. package/dist/presenter/presenter-shell.js +1 -73
  141. package/dist/presenter/presenter-styles.d.ts +0 -9
  142. package/dist/presenter/presenter-styles.js +0 -9
  143. package/dist/presenter/presenter-tally.d.ts +0 -5
  144. package/dist/presenter/presenter-tally.js +0 -4
  145. package/dist/presenter/presenter-test-helpers.d.ts +0 -2
  146. package/dist/presenter/presenter-test-helpers.js +0 -2
  147. package/dist/presenter/presenter-verbs.js +0 -7
  148. package/dist/presenter/presenter-workspace.d.ts +0 -4
  149. package/dist/presenter/presenter-workspace.js +1 -8
  150. package/dist/presenter/presenter.d.ts +0 -60
  151. package/dist/presenter/presenter.js +4 -118
  152. package/dist/recorder/recorder-styles.d.ts +0 -6
  153. package/dist/recorder/recorder-styles.js +0 -6
  154. package/dist/recorder/recorder.d.ts +0 -34
  155. package/dist/recorder/recorder.js +0 -49
  156. package/dist/registry/adapters.d.ts +0 -10
  157. package/dist/registry/adapters.js +0 -10
  158. package/dist/registry/auto-testids.d.ts +0 -19
  159. package/dist/registry/auto-testids.js +1 -21
  160. package/dist/registry/capabilities.d.ts +0 -31
  161. package/dist/registry/capabilities.js +1 -46
  162. package/dist/registry/commit-and-signal.d.ts +0 -6
  163. package/dist/registry/commit-and-signal.js +1 -7
  164. package/dist/registry/domains.d.ts +0 -8
  165. package/dist/registry/domains.js +0 -7
  166. package/dist/registry/emitter.d.ts +0 -14
  167. package/dist/registry/emitter.js +0 -10
  168. package/dist/registry/store-adapters.d.ts +0 -186
  169. package/dist/registry/store-adapters.js +0 -163
  170. package/dist/registry/stores.d.ts +0 -33
  171. package/dist/registry/stores.js +0 -59
  172. package/dist/reticle-presenter-helpers.d.ts +0 -13
  173. package/dist/reticle-presenter-helpers.js +0 -14
  174. package/dist/reticle.d.ts +1 -52
  175. package/dist/reticle.js +20 -145
  176. package/dist/review/annotator-styles.js +0 -7
  177. package/dist/review/annotator.d.ts +2 -13
  178. package/dist/review/annotator.js +0 -40
  179. package/dist/review/mark-anchor.d.ts +0 -11
  180. package/dist/review/mark-anchor.js +0 -8
  181. package/dist/security/serialization.d.ts +0 -20
  182. package/dist/security/serialization.js +1 -66
  183. package/dist/session-continuity.d.ts +0 -16
  184. package/dist/session-continuity.js +1 -20
  185. package/dist/timers/clock.d.ts +1 -14
  186. package/dist/timers/clock.js +59 -60
  187. package/dist/timers/native-console.js +0 -4
  188. package/dist/timers/native-timers.d.ts +0 -25
  189. package/dist/timers/native-timers.js +3 -33
  190. package/dist/transport/transport.d.ts +0 -51
  191. package/dist/transport/transport.js +3 -129
  192. package/dist/transport/unreachable-message.d.ts +0 -17
  193. package/dist/transport/unreachable-message.js +0 -17
  194. package/dist/util/captured-method.d.ts +0 -35
  195. package/dist/util/captured-method.js +0 -35
  196. package/package.json +2 -2
@@ -16,13 +16,8 @@ import { renderTally } from './presenter-tally.js';
16
16
  import { CONTROLS_BANNER_HTML, CONTROLS_FLOWS_HTML, CONTROLS_FOOT_HTML, ENDED_FADE_MS, ControlPanel, } from './presenter-controls.js';
17
17
  import { statusTheme, blockerHtml, getPresenterSettings, OutputDetail, syncPageBlocker, } from './presenter-settings.js';
18
18
  import { Annotator } from '../review/annotator.js';
19
- // Re-export the config surface so the public import path (`./presenter.js`) is unchanged.
20
19
  export { GlowPhase } from './presenter-config.js';
21
20
  export { LOG_KIND, LOG_RESULT } from './presenter-log.js';
22
- // Presenter / transparency layer: a human watches the agent work. Glowing border while
23
- // active, a synthetic cursor that flies to targets, click/hover/type effects, and a HUD that
24
- // shows the current action + the agent's narrated intent. All nodes carry data-reticle-* attrs
25
- // so they're excluded from snapshots/observers (see dom-ignore.ts).
26
21
  export class Presenter {
27
22
  #paceMs;
28
23
  #root;
@@ -32,12 +27,9 @@ export class Presenter {
32
27
  #hud;
33
28
  #actLine;
34
29
  #actStrip;
35
- /** Label inside the minimised-chat capsule; mirrors the act strip. */
36
30
  #chatPillText;
37
- /** Age of the last action, shown in the capsule's own slot. */
38
31
  #chatPillTime;
39
32
  #chip;
40
- /** Live verdict tally (✓N ✗M) in the header - the running testing score the human watches. */
41
33
  #tally;
42
34
  #tallied = { passes: 0, fails: 0 };
43
35
  #mode = PresenterMode.IDLE;
@@ -45,25 +37,18 @@ export class Presenter {
45
37
  #heartbeatMs;
46
38
  #idleNoticeMs;
47
39
  #borderMode;
48
- /** The glow / activity state machine (border shimmer + cursor visibility from activity timing). */
49
40
  #glowCtl;
50
- /** Liveness: the most recent action text + a 1s ticker that ages it into an "idle · {dur}" clock. */
51
41
  #lastActionText = '';
52
42
  #heartbeatTimer;
53
- /** Session lifecycle: idle-end window (tweakable), session id, start/end cursors, structured run log. */
54
43
  #idleEndMs;
55
44
  #sessionId;
56
45
  #startMs;
57
46
  #endMs;
58
47
  #runLog = [];
59
- /** Tracks sessionStart/sessionEnd so both are idempotent (no strobe / no spurious off-write). */
60
48
  #sessionActive = false;
61
- // v2: narration + action status accumulate in a persistent, timestamped, scrollable log.
62
49
  #logMax;
63
50
  #log;
64
- /** now of the first row, the baseline for the +elapsed timestamps. */
65
51
  #logBaseMs;
66
- // Live-control panel: the two-way control surface (Pause/Resume + End + message Send).
67
52
  #onControl;
68
53
  #panel;
69
54
  #shell;
@@ -110,26 +95,18 @@ export class Presenter {
110
95
  },
111
96
  });
112
97
  }
113
- /** Setter so reticle.ts can wire the control callback after construction. */
114
98
  setControlHandler(handler) {
115
99
  this.#onControl = handler;
116
100
  }
117
- /** Current live-control session state mirrored onto the panel (data-reticle-state). */
118
101
  get state() {
119
102
  return this.#panel.state;
120
103
  }
121
- /** Whether a run is currently being presented (false before the agent's first activity / after end). */
122
104
  get sessionActive() {
123
105
  return this.#sessionActive;
124
106
  }
125
- /** Drive the panel's live-control visual state (server-push / agent path; never emits). */
126
107
  setState(state, text, tone) {
127
108
  this.#panel.setState(state, text, tone);
128
109
  }
129
- /** Apply a bridge→browser presenter push: PRESENTER (state echo) or FLOWS (replay list, the human's
130
- * no-agent replay surface). Owns the wire parsing so the SDK dispatcher stays a thin router;
131
- * setState-only so an echo can't re-emit. */
132
- /** Re-scope the replay-flow chips to the current page (called by the SDK on route change). */
133
110
  refilterFlows() {
134
111
  this.#panel.refilterFlows();
135
112
  }
@@ -149,7 +126,6 @@ export class Presenter {
149
126
  if (isSessionState(state))
150
127
  this.setState(state, text, isPresenterTone(tone) ? tone : undefined);
151
128
  }
152
- /** Current cap on accumulated log rows. */
153
129
  get logMax() {
154
130
  return this.#logMax;
155
131
  }
@@ -166,10 +142,6 @@ export class Presenter {
166
142
  document.head.appendChild(style);
167
143
  const root = document.createElement('div');
168
144
  root.setAttribute('data-reticle-overlay', '');
169
- // The mode rides ON the status row, next to the dot and the elapsed time it already reports.
170
- // It used to be a sibling BELOW the strip, hidden except while reading or acting — so every
171
- // single tool call popped a block into the panel and took it away again, which reads as a
172
- // second UI flashing in rather than as the one status line changing what it says.
173
145
  const actStrip = `<div class="reticle-act-strip" data-liveness="idle"><span class="reticle-act-dot" aria-hidden="true"></span><span class="reticle-act">${ACT_STRIP.READY}</span><span class="reticle-chip" data-reticle-chip></span></div>`;
174
146
  root.innerHTML = `
175
147
  ${blockerHtml()}
@@ -195,12 +167,10 @@ export class Presenter {
195
167
  this.#shell.mount(root);
196
168
  syncPageBlocker(root, getPresenterSettings(), false);
197
169
  this.#glowCtl.setElements(this.#glow, this.#cursor);
198
- // The panel queries its refs, binds listeners, and paints the initial active state.
199
170
  this.#panel.mount(root, this.#glow);
200
171
  this.setMode(this.#mode);
201
172
  this.#renderTally();
202
173
  }
203
- /** Wire annotation chrome; expanding the HUD enters annotate mode. */
204
174
  bindAnnotator(annotator) {
205
175
  this.#annotator = annotator;
206
176
  const root = this.#root;
@@ -220,12 +190,7 @@ export class Presenter {
220
190
  annotator.setAccent(statusTheme(getPresenterSettings().statusThemeId).active);
221
191
  this.#syncAnnotator();
222
192
  }
223
- /** Annotate whenever the HUD is open and the person asked for it - agent or no agent. */
224
193
  #syncAnnotator() {
225
- // Two things have to agree, and both are the user's: the HUD is open, and annotate is switched
226
- // on. Session state used to be a third, so the mode was refused the moment the agent
227
- // disconnected - which is precisely when someone opens the HUD to record what they just saw.
228
- // The button still lit up, so the refusal was invisible: no outline, no composer, no reason.
229
194
  const live = !this.#shell.isCollapsed() && this.#shell.isAnnotateOn();
230
195
  this.#annotator?.toggle(live);
231
196
  if (this.#root !== undefined) {
@@ -246,19 +211,13 @@ export class Presenter {
246
211
  document.querySelectorAll('style[data-reticle-overlay]').forEach((s) => s.remove());
247
212
  this.#root = undefined;
248
213
  }
249
- /**
250
- * Session start: in 'session' border mode this fades the base border IN and keeps it on until
251
- * sessionEnd. Idempotent, and a no-op when unmounted or in 'busy' border mode.
252
- */
253
214
  sessionStart() {
254
- // Returning agent activity after the session ended (idle or explicit) revives it as a fresh run.
255
215
  if (this.state === SessionState.ENDED) {
256
216
  this.#revive();
257
217
  return;
258
218
  }
259
219
  if (this.#sessionActive)
260
220
  return;
261
- // A bridge that answered supersedes whatever the failed connect left on screen.
262
221
  if (UNREACHABLE_STATE === this.#root?.getAttribute(STATE_ATTR))
263
222
  this.#root.removeAttribute(STATE_ATTR);
264
223
  this.#sessionActive = true;
@@ -271,43 +230,25 @@ export class Presenter {
271
230
  this.#shell.openChat();
272
231
  }
273
232
  }
274
- /**
275
- * The bridge never answered: show that, rather than showing nothing.
276
- *
277
- * An instrumented page with a dead bridge used to be indistinguishable from a page with no
278
- * Reticle in it — overlay mounted, dock off, nothing on screen. The user cannot tell "I forgot to
279
- * start the daemon" from "the install did not work", and the commonest cause is the cheapest to
280
- * say: the port. So the HUD appears, states the URL it tried, and marks itself unreachable so it
281
- * is never mistaken for a live session.
282
- *
283
- * Not an error dialog and not modal: the page is the user's, and a dev overlay that shouts is one
284
- * they turn off. It is the same capsule they would have had, saying the one thing it knows.
285
- */
286
233
  showUnreachable(url, attempts) {
287
234
  if (this.#sessionActive)
288
235
  return;
289
236
  this.#root?.setAttribute(STATE_ATTR, UNREACHABLE_STATE);
290
- // Dock and HUD, but NOT the border glow: the glow means the agent is working, and nothing is.
291
237
  const dock = this.#root?.querySelector('[data-reticle-dock]');
292
238
  dock?.setAttribute(DATA_ON, GLOW_ON);
293
239
  this.#hud?.setAttribute(DATA_ON, GLOW_ON);
294
240
  this.#lastActionText = unreachableStripText(url, attempts);
295
241
  this.#paintActStrip(this.#lastActionText, true);
296
- // The message IS the reason this state exists, and a collapsed capsule hides it. Same setting
297
- // a live session honours, so a user who wants the bare toolbar still gets one.
298
242
  if (getPresenterSettings().autoOpenChat)
299
243
  this.#shell.openChat();
300
244
  }
301
- /** Turn the base border (session mode) + the HUD/log on - the visible "session is live" state. */
302
245
  #showSession() {
303
246
  const dock = this.#root?.querySelector('[data-reticle-dock]');
304
247
  dock?.setAttribute(DATA_ON, GLOW_ON);
305
248
  this.#hud?.setAttribute(DATA_ON, GLOW_ON);
306
- // Base border persists in 'session' mode; 'busy' mode leaves it to the busy machine.
307
249
  if (this.#borderMode === BorderMode.SESSION)
308
250
  this.#glow?.setAttribute(DATA_ON, GLOW_ON);
309
251
  }
310
- /** Revive after an ended session (new agent activity): clear the ended state + glow back on. */
311
252
  #revive() {
312
253
  this.#panel.setState(SessionState.ACTIVE);
313
254
  this.#endMs = undefined;
@@ -315,10 +256,6 @@ export class Presenter {
315
256
  this.#glowCtl.resetActivity(this.#now());
316
257
  this.#startHeartbeat();
317
258
  }
318
- /**
319
- * Session end: hides the log/HUD and (in 'session' mode) clears the base border. Idempotent; a
320
- * no-op without a prior sessionStart or when unmounted.
321
- */
322
259
  sessionEnd() {
323
260
  if (!this.#sessionActive)
324
261
  return;
@@ -336,30 +273,18 @@ export class Presenter {
336
273
  this.#glow?.setAttribute(DATA_BUSY, BUSY_OFF);
337
274
  }
338
275
  }
339
- /**
340
- * Record agent activity. Idempotent while busy - only the first activity from idle/fading flips
341
- * the glow on, so a burst never restarts the reticle-pulse animation (no strobe). Subsequent calls
342
- * just refresh the last-activity timestamp and re-arm the idle check.
343
- */
344
276
  markActivity() {
345
277
  this.#glowCtl.markActivity();
346
278
  }
347
- /** Re-arm the quiet-window idle check (kept for reticle.ts's finally block). */
348
279
  scheduleIdle() {
349
280
  this.#glowCtl.scheduleIdle();
350
281
  }
351
- /** Test/diagnostic accessor for the current glow phase. */
352
282
  glowPhase() {
353
283
  return this.#glowCtl.phase();
354
284
  }
355
- /** Current intent (reading vs acting), exposed for tests + the watcher. */
356
285
  get mode() {
357
286
  return this.#mode;
358
287
  }
359
- /**
360
- * Set the presenter intent. READING shows a cyan scan + chip and hides the cursor; ACTING
361
- * keeps the warm cursor/ripple + chip; IDLE clears the chip. Drives color via data-reticle-mode.
362
- */
363
288
  setMode(mode) {
364
289
  this.#mode = mode;
365
290
  this.#root?.setAttribute('data-reticle-mode', mode);
@@ -381,7 +306,6 @@ export class Presenter {
381
306
  this.#chip.appendChild(labelEl);
382
307
  }
383
308
  }
384
- // READING has no real pointer to show (synthetic-hover pointer is native-only) - hide the cursor.
385
309
  if (mode === PresenterMode.READING)
386
310
  this.#cursor?.setAttribute(DATA_ON, GLOW_OFF);
387
311
  }
@@ -392,13 +316,6 @@ export class Presenter {
392
316
  this.#lastActionText = text;
393
317
  this.#paintActStrip(text, false);
394
318
  }
395
- /**
396
- * Sync act-strip text + the live/idle state.
397
- *
398
- * The liveness is mirrored onto the overlay root as well as the strip, because that is what the
399
- * status COLOUR resolves against: the page glow, the collapsed FAB's halo and the minimised
400
- * capsule all live outside the strip and still have to say whether the agent is working.
401
- */
402
319
  #paintActStrip(text, idle) {
403
320
  if (this.#actLine !== undefined)
404
321
  this.#actLine.textContent = text;
@@ -407,9 +324,6 @@ export class Presenter {
407
324
  this.#actStrip.setAttribute('data-liveness', liveness);
408
325
  this.#root?.setAttribute(LIVENESS_ATTR, liveness);
409
326
  if (this.#chatPillText !== undefined) {
410
- // The capsule reads "logo | what the agent did | how long ago | expand", so it takes the
411
- // ACTION, never the composed "idle · 12s since last action" line the strip shows: the age
412
- // lives in its own slot next to it.
413
327
  this.#chatPillText.textContent = idle
414
328
  ? this.#lastActionText !== ''
415
329
  ? this.#lastActionText
@@ -417,12 +331,6 @@ export class Presenter {
417
331
  : text;
418
332
  }
419
333
  }
420
- /**
421
- * Liveness heartbeat (native 1s timer - never rAF, so it ticks in a foreground tab regardless of
422
- * agent activity). Once the agent has been quiet for IDLE_NOTICE_MS, the act strip shows a LIVE,
423
- * growing "◌ idle · {duration} since last action" - the signal that was missing when a stopped
424
- * agent left the panel frozen and indistinguishable from one still thinking.
425
- */
426
334
  #startHeartbeat() {
427
335
  if (this.#heartbeatTimer !== undefined)
428
336
  nativeClearTimeout(this.#heartbeatTimer);
@@ -436,20 +344,19 @@ export class Presenter {
436
344
  if (!this.#sessionActive || this.#actLine === undefined)
437
345
  return;
438
346
  if (this.state === SessionState.ENDED)
439
- return; // already ended - leave the summary
347
+ return;
440
348
  const idleMs = this.#now() - this.#glowCtl.lastActivityMs();
441
349
  if (idleMs >= this.#idleEndMs) {
442
- this.#endIdle(idleMs); // crossed the idle-end window → auto-end (glow off, panel kept)
350
+ this.#endIdle(idleMs);
443
351
  return;
444
352
  }
445
353
  if (idleMs < this.#idleNoticeMs)
446
- return; // still active (or a brief think) - keep the action text
354
+ return;
447
355
  const since = this.#lastActionText !== '' ? ACT_STRIP.SINCE_LAST : '';
448
356
  this.#paintActStrip(`${ACT_STRIP.IDLE_PREFIX}${humanDuration(idleMs)}${since}`, true);
449
357
  if (this.#chatPillTime !== undefined)
450
358
  this.#chatPillTime.textContent = humanDuration(idleMs);
451
359
  }
452
- /** Auto-end after the idle window: stamp the end, drive the panel to ENDED, stop the heartbeat. */
453
360
  #endIdle(idleMs) {
454
361
  this.#endMs = this.#now();
455
362
  this.#panel.setState(SessionState.ENDED, `idle ${humanDuration(idleMs)}`);
@@ -458,17 +365,11 @@ export class Presenter {
458
365
  this.#heartbeatTimer = undefined;
459
366
  }
460
367
  }
461
- /** Agent-tunable idle-end window (reticle_session). Floored so it can't be set uselessly small. */
462
368
  setIdleEndMs(ms) {
463
369
  if (!Number.isFinite(ms))
464
370
  return;
465
371
  this.#idleEndMs = Math.max(IDLE_END_MIN_MS, Math.floor(ms));
466
372
  }
467
- /**
468
- * The exported "run state" for the Copy/Export buttons - everything the page holds about this
469
- * run: session id, url, duration, capability surface, per-kind counts, and the full activity log.
470
- * (The full network/console ring-buffer lives server-side; this is the in-page run summary.)
471
- */
472
373
  runState() {
473
374
  const base = buildRunState({
474
375
  sessionId: this.#sessionId,
@@ -518,11 +419,6 @@ export class Presenter {
518
419
  syncPageBlocker(this.#root, settings, live);
519
420
  }
520
421
  }
521
- /**
522
- * Append an activity-log row. Accumulates (never overwrites): each call adds a timestamped row
523
- * with a mode chip + text. Returns a handle to stamp the row's outcome glyph (✓/✗) later, or
524
- * undefined when unmounted / when the text is empty after trimming.
525
- */
526
422
  log(kind, text, result) {
527
423
  const ms = this.#now();
528
424
  this.#glowCtl.markActivity(ms);
@@ -532,7 +428,6 @@ export class Presenter {
532
428
  if (0 === trimmed.length)
533
429
  return undefined;
534
430
  this.#logBaseMs ??= ms;
535
- // Structured run-log entry (mirrors the DOM row) for the exported run state, capped like the DOM.
536
431
  const entry = result !== undefined
537
432
  ? { at: ms - this.#logBaseMs, kind, text: trimmed, result }
538
433
  : { at: ms - this.#logBaseMs, kind, text: trimmed };
@@ -546,16 +441,14 @@ export class Presenter {
546
441
  if (this.#shell.isCollapsed())
547
442
  this.#shell.pulseFab(true);
548
443
  this.#renderTally();
549
- // Wrap the handle so a later outcome stamp updates BOTH the DOM glyph and the run-log entry.
550
444
  return {
551
445
  result: (r) => {
552
446
  handle.result(r);
553
447
  entry.result = r;
554
- this.#renderTally(); // a deferred ✓/✗ stamp bumps the header tally
448
+ this.#renderTally();
555
449
  },
556
450
  };
557
451
  }
558
- /** Repaint the header verdict tally from the run log; the side that grew gets a one-shot pop. */
559
452
  #renderTally() {
560
453
  if (!getPresenterSettings().showTally) {
561
454
  this.#tally?.setAttribute('hidden', '');
@@ -563,7 +456,6 @@ export class Presenter {
563
456
  }
564
457
  this.#tallied = renderTally(this.#tally, this.#runLog, this.#tallied);
565
458
  }
566
- /** Back-compat: narration appends to the live log (append-only, never overwrites). */
567
459
  narrate(text, level = 'info') {
568
460
  const line = 'info' === level ? text : `[${level}] ${text}`;
569
461
  return this.log(LOG_KIND.NARRATION, line);
@@ -575,11 +467,6 @@ export class Presenter {
575
467
  this.#log.firstElementChild?.remove();
576
468
  }
577
469
  }
578
- /**
579
- * Mirror the server's session.throttled state onto the HUD border. When throttled (tab
580
- * backgrounded or stale), the border turns amber so the developer knows actions are no-oping -
581
- * the same signal the agent already reads from result.session.throttled.
582
- */
583
470
  setThrottled(throttled) {
584
471
  this.#root?.setAttribute(THROTTLED_ATTR, throttled ? '1' : '0');
585
472
  if (throttled && this.#actLine !== undefined) {
@@ -587,7 +474,6 @@ export class Presenter {
587
474
  'Tab backgrounded - actions throttled. Bring tab to front or use `reticle drive`.';
588
475
  }
589
476
  }
590
- /** Fly the cursor to an element, play the action's effect, then pace for the human. */
591
477
  async beforeAct(refId, action, label) {
592
478
  const el = refs.resolve(refId);
593
479
  this.status(`${actionVerb(action)} ${label}`);
@@ -1,9 +1,3 @@
1
- /**
2
- * Inline style strings for the floating recorder toolbar. Browser-local UI
3
- * text (never crosses the wire), split out of recorder.ts to keep that file under the 500-line cap
4
- * (mirrors how presenter.ts extracts presenter-log.ts). All toolbar nodes also carry
5
- * data-reticle-overlay, so they are snapshot-excluded via dom-ignore.ts regardless of these styles.
6
- */
7
1
  export declare const TOOLBAR_CSS: string;
8
2
  export declare const BTN_CSS: string;
9
3
  export declare const NAME_CSS: string;
@@ -1,9 +1,3 @@
1
- /**
2
- * Inline style strings for the floating recorder toolbar. Browser-local UI
3
- * text (never crosses the wire), split out of recorder.ts to keep that file under the 500-line cap
4
- * (mirrors how presenter.ts extracts presenter-log.ts). All toolbar nodes also carry
5
- * data-reticle-overlay, so they are snapshot-excluded via dom-ignore.ts regardless of these styles.
6
- */
7
1
  export const TOOLBAR_CSS = [
8
2
  'position:fixed',
9
3
  'top:8px',
@@ -1,59 +1,25 @@
1
1
  import { AnnotationKind, EventType, RecorderPhase, type FlowAnchor, type FlowFile, type FlowStep } from '@reticlehq/core';
2
- /**
3
- * The human recorder. A floating toolbar (Record / Stop / Annotate) lets a
4
- * human click the golden path; capture-phase listeners map each real interaction to a SEMANTIC
5
- * anchor (testid → role+name → degraded placeholder, NEVER a volatile ref — invariant #1) and
6
- * append a FlowStep. Stop compiles a FlowFile in-page and emits ONE EventType.FLOW_RECORDED event
7
- * the server persists. Four structured annotation kinds compile into expect/dynamic/success.
8
- *
9
- * FIRST CUT scope: structured annotations only (the 4 AnnotationKind values via a menu + a signal
10
- * <select> drawn from registered capabilities). Explicitly FUTURE, not faked here:
11
- * - NL annotation → predicate compiler (CO-OWNED-FLOWS invariant #2 best-UX path).
12
- * - fixtures/preconditions (FlowFile.fixture stays a schema slot, never written).
13
- * - wait-for / ignore-region annotation kinds (only the 4 requested ship).
14
- */
15
- /** Browser-local UI text — never crosses the wire, mirrors presenter.ts CHIP_LABEL precedent. */
16
2
  export declare const RECORDER_EMPTY_MSG = "recorded 0 steps";
17
3
  interface RecorderDeps {
18
- /** Emit a wire event (Reticle.#emit bound). */
19
4
  emit: (type: EventType, data: Record<string, unknown>) => void;
20
- /** Injected clock for FlowFile.createdAt — never Date.now in pure logic (rule 7). */
21
5
  now: () => number;
22
- /** Default flow name when the toolbar field is blank. */
23
6
  defaultName?: string;
24
7
  }
25
8
  export interface RecorderHandle {
26
9
  mount(): void;
27
10
  destroy(): void;
28
- /** Test seam: current phase. */
29
11
  phase(): RecorderPhase;
30
- /** Test seam: steps captured so far in the active span. */
31
12
  steps(): FlowStep[];
32
13
  }
33
- /** A structured annotation captured while recording (compiled into flow fields at Stop). */
34
14
  export interface Annotation {
35
15
  kind: AnnotationKind;
36
16
  anchor: FlowAnchor;
37
- /** Required for ASSERT_SIGNAL / SUCCESS_STATE; the chosen capability signal name. */
38
17
  signal?: string;
39
18
  }
40
- /**
41
- * Pure: map a clicked/typed element → semantic FlowAnchor. testid wins; else role+accessible-name;
42
- * else the DEGRADED_ANCHOR_ROLE placeholder + degraded:true (never a ref — invariant #1).
43
- */
44
19
  export declare function anchorFor(el: Element): {
45
20
  anchor: FlowAnchor;
46
21
  degraded: boolean;
47
22
  };
48
- /**
49
- * Pure: assemble the in-page FlowFile from captured steps + annotations.
50
- * - ASSERT_SIGNAL → the most-recent step's expect.signal.
51
- * - ASSERT_VISIBLE → the most-recent step's expect.element (from the annotation anchor).
52
- * - MARK_DYNAMIC → push the anchor into flow.dynamic[].
53
- * - SUCCESS_STATE → flow.success = { signal }.
54
- * (FUTURE: per-step annotation targeting beyond the most-recent step.)
55
- */
56
23
  export declare function compileRecording(name: string, steps: FlowStep[], annotations: Annotation[], createdAt: number, startPath?: string): FlowFile;
57
- /** Install the floating recorder toolbar. Default off; gated by the host (reticle.ts recorder flag). */
58
24
  export declare function installRecorder(deps: RecorderDeps): RecorderHandle;
59
25
  export {};
@@ -4,20 +4,6 @@ import { getAccessibleName, getRole, isSensitiveField } from '../dom/a11y.js';
4
4
  import { isReticleOverlay } from '../dom/dom-ignore.js';
5
5
  import { getCapabilities } from '../registry/capabilities.js';
6
6
  import { TOOLBAR_CSS, BTN_CSS, NAME_CSS, STATUS_CSS, MENU_CSS } from './recorder-styles.js';
7
- /**
8
- * The human recorder. A floating toolbar (Record / Stop / Annotate) lets a
9
- * human click the golden path; capture-phase listeners map each real interaction to a SEMANTIC
10
- * anchor (testid → role+name → degraded placeholder, NEVER a volatile ref — invariant #1) and
11
- * append a FlowStep. Stop compiles a FlowFile in-page and emits ONE EventType.FLOW_RECORDED event
12
- * the server persists. Four structured annotation kinds compile into expect/dynamic/success.
13
- *
14
- * FIRST CUT scope: structured annotations only (the 4 AnnotationKind values via a menu + a signal
15
- * <select> drawn from registered capabilities). Explicitly FUTURE, not faked here:
16
- * - NL annotation → predicate compiler (CO-OWNED-FLOWS invariant #2 best-UX path).
17
- * - fixtures/preconditions (FlowFile.fixture stays a schema slot, never written).
18
- * - wait-for / ignore-region annotation kinds (only the 4 requested ship).
19
- */
20
- /** Browser-local UI text — never crosses the wire, mirrors presenter.ts CHIP_LABEL precedent. */
21
7
  export const RECORDER_EMPTY_MSG = 'recorded 0 steps';
22
8
  const STATUS_RECORDING = 'recording…';
23
9
  const STATUS_IDLE = 'ready';
@@ -38,16 +24,11 @@ const ANNOTATION_LABEL = {
38
24
  [AnnotationKind.SUCCESS_STATE]: 'success state',
39
25
  [AnnotationKind.INTENT]: 'intent',
40
26
  };
41
- /** Annotation kinds whose compilation needs a signal name (from the capabilities <select>). */
42
27
  const NEEDS_SIGNAL = new Set([
43
28
  AnnotationKind.ASSERT_SIGNAL,
44
29
  AnnotationKind.SUCCESS_STATE,
45
30
  ]);
46
31
  const DEFAULT_NAME = 'recorded-flow';
47
- /**
48
- * Pure: map a clicked/typed element → semantic FlowAnchor. testid wins; else role+accessible-name;
49
- * else the DEGRADED_ANCHOR_ROLE placeholder + degraded:true (never a ref — invariant #1).
50
- */
51
32
  export function anchorFor(el) {
52
33
  const testid = el.getAttribute(TESTID_ATTR);
53
34
  if (testid !== null && testid.length > 0) {
@@ -59,7 +40,6 @@ export function anchorFor(el) {
59
40
  const anchor = name.length > 0 ? { kind: AnchorKind.ROLE, role, name } : { kind: AnchorKind.ROLE, role };
60
41
  return { anchor, degraded: false };
61
42
  }
62
- // No resolvable testid/role/name — keep the step with a legible placeholder, never a ref.
63
43
  return { anchor: { kind: AnchorKind.ROLE, role: DEGRADED_ANCHOR_ROLE }, degraded: true };
64
44
  }
65
45
  function isTextbox(el) {
@@ -86,14 +66,6 @@ function buildStep(el, action, args) {
86
66
  step.degraded = true;
87
67
  return step;
88
68
  }
89
- /**
90
- * Pure: assemble the in-page FlowFile from captured steps + annotations.
91
- * - ASSERT_SIGNAL → the most-recent step's expect.signal.
92
- * - ASSERT_VISIBLE → the most-recent step's expect.element (from the annotation anchor).
93
- * - MARK_DYNAMIC → push the anchor into flow.dynamic[].
94
- * - SUCCESS_STATE → flow.success = { signal }.
95
- * (FUTURE: per-step annotation targeting beyond the most-recent step.)
96
- */
97
69
  export function compileRecording(name, steps, annotations, createdAt, startPath) {
98
70
  const out = steps.map((s) => ({ ...s }));
99
71
  const dynamic = [];
@@ -137,12 +109,10 @@ function anchorToElement(anchor) {
137
109
  }
138
110
  return {};
139
111
  }
140
- /** Live recorder instance: holds the capture buffer, toolbar DOM, and the phase machine. */
141
112
  class Recorder {
142
113
  #deps;
143
114
  #phase = RecorderPhase.IDLE;
144
115
  #steps = [];
145
- /** The pathname the recording began on, so replay can navigate here before step 1. */
146
116
  #startPath;
147
117
  #annotations = [];
148
118
  #pendingFill;
@@ -150,7 +120,6 @@ class Recorder {
150
120
  #root;
151
121
  #statusEl;
152
122
  #menuEl;
153
- /** The annotation being assembled in ANNOTATING phase. */
154
123
  #draft;
155
124
  constructor(deps) {
156
125
  this.#deps = deps;
@@ -183,7 +152,6 @@ class Recorder {
183
152
  this.#draft = undefined;
184
153
  this.#pendingFill = undefined;
185
154
  }
186
- // ---- capture ----
187
155
  #installCapture() {
188
156
  const onClick = (ev) => this.#onClick(ev);
189
157
  const onInput = (ev) => this.#onInput(ev);
@@ -200,7 +168,6 @@ class Recorder {
200
168
  document.removeEventListener('submit', onSubmit, true);
201
169
  });
202
170
  }
203
- /** True if the event should be ignored (toolbar self-click or non-element target). */
204
171
  #ignore(ev) {
205
172
  const target = ev.target;
206
173
  if (!(target instanceof Element))
@@ -219,9 +186,6 @@ class Recorder {
219
186
  }
220
187
  if (this.#phase !== RecorderPhase.RECORDING)
221
188
  return;
222
- // Controls that emit `change` (checkbox, radio, select) are recorded by #onChange as their
223
- // semantic step (CHECK/UNCHECK/SELECT). Recording a CLICK here too would replay any onChange
224
- // side effect twice (analytics, dependent-field resets), so skip the duplicate.
225
189
  if ((target instanceof HTMLInputElement &&
226
190
  ('checkbox' === inputRole(target) || 'radio' === inputRole(target))) ||
227
191
  target instanceof HTMLSelectElement) {
@@ -236,7 +200,6 @@ class Recorder {
236
200
  const target = this.#ignore(ev);
237
201
  if (target === undefined || !isTextbox(target))
238
202
  return;
239
- // Debounce per-keystroke input: keep the latest value, flush on change/blur/next-act.
240
203
  this.#pendingFill = { el: target, value: target.value };
241
204
  }
242
205
  #onChange(ev) {
@@ -279,12 +242,9 @@ class Recorder {
279
242
  if (pending === undefined)
280
243
  return;
281
244
  this.#pendingFill = undefined;
282
- // Never persist a typed secret into a git-checked flow file: password/OTP/cc/sensitive-keyed
283
- // fields record REDACTED_VALUE, the same redaction live snapshots apply (dom/a11y.ts getValue).
284
245
  const value = isSensitiveField(pending.el) ? REDACTED_VALUE : pending.value;
285
246
  this.#steps.push(buildStep(pending.el, ActionType.FILL, { value }));
286
247
  }
287
- // ---- annotation ----
288
248
  #captureAnnotationTarget(target) {
289
249
  const draft = this.#draft;
290
250
  if (draft === undefined)
@@ -299,7 +259,6 @@ class Recorder {
299
259
  this.#setPhase(RecorderPhase.RECORDING);
300
260
  this.#closeMenu();
301
261
  }
302
- /** Annotate-on-prior: confirm the most-recent step as the target (no extra click needed). */
303
262
  #confirmAnnotationOnPrior() {
304
263
  const draft = this.#draft;
305
264
  if (draft === undefined)
@@ -325,9 +284,7 @@ class Recorder {
325
284
  const value = select?.value ?? '';
326
285
  return value.length > 0 ? value : undefined;
327
286
  }
328
- // ---- toolbar lifecycle actions ----
329
287
  #start() {
330
- // A fresh span — no leakage from a previous Record→Stop cycle.
331
288
  this.#steps = [];
332
289
  this.#startPath = 'undefined' === typeof location ? undefined : location.pathname;
333
290
  this.#annotations = [];
@@ -355,7 +312,6 @@ class Recorder {
355
312
  #setPhase(phase) {
356
313
  this.#phase = phase;
357
314
  }
358
- // ---- toolbar DOM (all nodes data-reticle-overlay → snapshot-excluded via dom-ignore.ts) ----
359
315
  #buildToolbar() {
360
316
  const root = document.createElement('div');
361
317
  root.setAttribute('data-reticle-overlay', '');
@@ -396,9 +352,6 @@ class Recorder {
396
352
  return;
397
353
  menu.textContent = '';
398
354
  for (const kind of Object.values(AnnotationKind)) {
399
- // INTENT is free-text (the business goal), not a signal/testid pick — authored via
400
- // reticle_annotate { kind:'intent', text }. The human toolbar's name field is signal/testid only,
401
- // so intent is omitted from this menu rather than shipped as a control that can't capture text.
402
355
  if (kind === AnnotationKind.INTENT)
403
356
  continue;
404
357
  const item = document.createElement('button');
@@ -426,7 +379,6 @@ class Recorder {
426
379
  }
427
380
  menu.appendChild(select);
428
381
  }
429
- // "Confirm on prior step" path (annotate-on-prior); or the next click selects the target.
430
382
  const confirm = document.createElement('button');
431
383
  confirm.setAttribute('data-reticle-action', 'annotate-confirm');
432
384
  confirm.textContent = 'on prior step';
@@ -448,7 +400,6 @@ class Recorder {
448
400
  return value.length > 0 ? value : undefined;
449
401
  }
450
402
  }
451
- /** Install the floating recorder toolbar. Default off; gated by the host (reticle.ts recorder flag). */
452
403
  export function installRecorder(deps) {
453
404
  return new Recorder(deps);
454
405
  }
@@ -1,4 +1,3 @@
1
- /** Framework adapter registry — enriches elements with component identity + source. */
2
1
  export interface ComponentSource {
3
2
  file: string;
4
3
  line: number;
@@ -11,20 +10,11 @@ export interface ComponentInfo {
11
10
  export interface ReticleAdapter {
12
11
  name: string;
13
12
  identify: (el: Element) => ComponentInfo | null;
14
- /** Best-effort: read a component's hook state for a DOM element. */
15
13
  readState?: (el: Element) => unknown;
16
- /** Best-effort: does the element declare framework enter/leave handlers synthetic hover may not fire? */
17
14
  hasHoverHandlers?: (el: Element) => boolean;
18
15
  }
19
- /** Called by @reticlehq/react (and future adapters) to register themselves. */
20
16
  export declare function registerAdapter(adapter: ReticleAdapter): void;
21
- /** First adapter that can identify the element wins. */
22
17
  export declare function identifyComponent(el: Element): ComponentInfo | null;
23
- /** First adapter that returns non-undefined component state for the element wins. */
24
18
  export declare function readComponentState(el: Element): unknown;
25
- /**
26
- * True if any installed adapter reports framework enter/leave hover handlers on the element.
27
- * No-op-safe: returns false when no adapter is installed or none implements the probe.
28
- */
29
19
  export declare function elementHasHoverHandlers(el: Element): boolean;
30
20
  export declare function adapterNames(): string[];