@reticlehq/browser 3.7.0 → 3.8.1

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 (104) hide show
  1. package/dist/actions/action-report.d.ts +10 -0
  2. package/dist/actions/action-report.js +59 -14
  3. package/dist/actions/actions-dom.d.ts +4 -0
  4. package/dist/actions/actions-dom.js +20 -4
  5. package/dist/actions/actions.d.ts +1 -0
  6. package/dist/actions/actions.js +94 -46
  7. package/dist/actions/appeared-text.js +2 -2
  8. package/dist/actions/click-target.d.ts +1 -0
  9. package/dist/actions/click-target.js +14 -0
  10. package/dist/actions/danger-context.d.ts +2 -1
  11. package/dist/actions/danger-context.js +43 -17
  12. package/dist/actions/scroll.d.ts +2 -0
  13. package/dist/actions/scroll.js +10 -2
  14. package/dist/activate.d.ts +14 -0
  15. package/dist/activate.js +43 -0
  16. package/dist/busy.d.ts +3 -0
  17. package/dist/busy.js +42 -0
  18. package/dist/commands/commands.js +45 -17
  19. package/dist/connect-options.d.ts +12 -1
  20. package/dist/connect-options.js +18 -1
  21. package/dist/dom/a11y.d.ts +0 -2
  22. package/dist/dom/a11y.js +103 -363
  23. package/dist/dom/chart.d.ts +3 -2
  24. package/dist/dom/chart.js +3 -2
  25. package/dist/dom/dom-ignore.d.ts +6 -0
  26. package/dist/dom/dom-ignore.js +60 -5
  27. package/dist/dom/layout-audit.d.ts +15 -0
  28. package/dist/dom/layout-audit.js +208 -0
  29. package/dist/dom/paint-context.d.ts +15 -0
  30. package/dist/dom/paint-context.js +78 -0
  31. package/dist/dom/query-finders.d.ts +6 -0
  32. package/dist/dom/query-finders.js +216 -0
  33. package/dist/dom/query.d.ts +1 -1
  34. package/dist/dom/query.js +154 -227
  35. package/dist/dom/shadow-registry.d.ts +1 -0
  36. package/dist/dom/shadow-registry.js +27 -0
  37. package/dist/dom/snapshot.d.ts +5 -0
  38. package/dist/dom/snapshot.js +87 -35
  39. package/dist/dom/stable-selector.d.ts +1 -0
  40. package/dist/dom/stable-selector.js +19 -0
  41. package/dist/dom/text-match.d.ts +12 -0
  42. package/dist/dom/text-match.js +115 -0
  43. package/dist/dom/theme.d.ts +1 -0
  44. package/dist/dom/theme.js +21 -9
  45. package/dist/dom/virtualized.js +5 -1
  46. package/dist/dom/visibility.d.ts +7 -0
  47. package/dist/dom/visibility.js +322 -0
  48. package/dist/index.d.ts +4 -2
  49. package/dist/index.js +7 -1
  50. package/dist/observers/blind-spots.js +3 -2
  51. package/dist/observers/canvas.d.ts +18 -0
  52. package/dist/observers/canvas.js +76 -0
  53. package/dist/observers/dom.js +68 -19
  54. package/dist/observers/field.d.ts +11 -0
  55. package/dist/observers/field.js +117 -13
  56. package/dist/observers/install-all.js +4 -0
  57. package/dist/observers/media.d.ts +2 -0
  58. package/dist/observers/media.js +32 -0
  59. package/dist/observers/net-detail/network-body.d.ts +2 -0
  60. package/dist/observers/net-detail/network-body.js +36 -9
  61. package/dist/observers/net-detail/network-event-stream.d.ts +5 -0
  62. package/dist/observers/net-detail/network-event-stream.js +55 -0
  63. package/dist/observers/net-detail/network-frames.d.ts +2 -0
  64. package/dist/observers/net-detail/network-frames.js +39 -0
  65. package/dist/observers/net-detail/network-response-body.d.ts +3 -0
  66. package/dist/observers/net-detail/network-response-body.js +97 -0
  67. package/dist/observers/network.js +111 -101
  68. package/dist/observers/route.d.ts +2 -0
  69. package/dist/observers/route.js +33 -4
  70. package/dist/presenter/chrome/presenter-report-copy.d.ts +0 -5
  71. package/dist/presenter/chrome/presenter-report-copy.js +0 -14
  72. package/dist/presenter/hud-telemetry.js +1 -1
  73. package/dist/presenter/presenter-chat-views.d.ts +3 -4
  74. package/dist/presenter/presenter-chat-views.js +47 -20
  75. package/dist/presenter/presenter-config.d.ts +1 -0
  76. package/dist/presenter/presenter-config.js +1 -0
  77. package/dist/presenter/presenter-frame.d.ts +3 -2
  78. package/dist/presenter/presenter-frame.js +24 -15
  79. package/dist/presenter/presenter-harness-row.d.ts +4 -15
  80. package/dist/presenter/presenter-harness-row.js +11 -50
  81. package/dist/presenter/presenter-report-styles.js +0 -17
  82. package/dist/presenter/presenter-report.d.ts +1 -2
  83. package/dist/presenter/presenter-report.js +4 -54
  84. package/dist/presenter/presenter-run-state.js +2 -1
  85. package/dist/presenter/presenter-settings.d.ts +2 -3
  86. package/dist/presenter/presenter-settings.js +14 -17
  87. package/dist/presenter/presenter-shell.d.ts +6 -4
  88. package/dist/presenter/presenter-shell.js +37 -7
  89. package/dist/presenter/presenter.js +32 -11
  90. package/dist/registry/store-adapters.d.ts +5 -2
  91. package/dist/registry/store-adapters.js +40 -10
  92. package/dist/reticle-inject.js +700 -645
  93. package/dist/reticle.d.ts +4 -2
  94. package/dist/reticle.js +36 -6
  95. package/dist/security/page-location.d.ts +2 -0
  96. package/dist/security/page-location.js +7 -0
  97. package/dist/security/serialization.js +1 -1
  98. package/dist/storage-keys.d.ts +1 -0
  99. package/dist/storage-keys.js +1 -0
  100. package/dist/timers/native/native-timers.d.ts +1 -0
  101. package/dist/timers/native/native-timers.js +5 -0
  102. package/dist/transport/transport.d.ts +1 -0
  103. package/dist/transport/transport.js +18 -0
  104. package/package.json +6 -2
@@ -134,7 +134,10 @@ function settingsHelpIcon() {
134
134
  }
135
135
  const SETTING_TEXT = {
136
136
  outputDetail: ['Output detail', 'How much a copied run includes.'],
137
- autoOpenChat: ['Open the log automatically', 'Opens this panel when a session starts.'],
137
+ autoOpenChat: [
138
+ 'Open the log automatically',
139
+ 'Opens this panel when an agent starts to act in this tab.',
140
+ ],
138
141
  showTimestamps: ['Show timestamps', 'Shows how long ago each log row happened.'],
139
142
  showTally: ['Show pass/fail count', 'A score pill for this session in the toolbar.'],
140
143
  reactComponents: ['React component names', 'Adds component names to copied runs.'],
@@ -144,10 +147,7 @@ const SETTING_TEXT = {
144
147
  ],
145
148
  clearOnCopy: ['Clear notes after copying', 'Starts fresh once your notes are copied.'],
146
149
  hideUntilRestart: ['Hide until reload', 'Hides the HUD until this page reloads.'],
147
- harnessEnabled: [
148
- 'Allow Reticle to drive',
149
- 'Off by default. Unlocks at 80% instrumentation; lets Run Harness test this project.',
150
- ],
150
+ harnessEnabled: ['Allow Reticle to drive', 'Off by default. Lets Run Harness test this project.'],
151
151
  reduceMotion: ['Reduce motion', 'Fewer HUD animations.'],
152
152
  ambientGlow: ['Page glow', 'Tints the page edges while a session runs.'],
153
153
  workspace: ['Workspace', 'The Reticle account this machine is signed in to.'],
@@ -184,7 +184,7 @@ export function creditsLeft(credits) {
184
184
  return creditsSpent(credits) ? creditsUsedText(credits).said : creditsLeftText(credits);
185
185
  }
186
186
  const ACCOUNT_HELP = `Whether this machine is signed in to a Reticle workspace. Press ${ACCOUNT_TEXT.SIGNED_OUT} to sign in through your browser.`;
187
- export function paintHarnessRow(root, config, gate) {
187
+ export function paintHarnessRow(root, config) {
188
188
  const row = root.querySelector(`[${SETTINGS_HARNESS_ROW_ATTR}]`);
189
189
  if (!(row instanceof HTMLElement))
190
190
  return;
@@ -196,20 +196,17 @@ export function paintHarnessRow(root, config, gate) {
196
196
  const toggle = row.querySelector(`[${SETTING_KEY_ATTR}="harnessEnabled"]`);
197
197
  if (!(toggle instanceof HTMLElement))
198
198
  return;
199
- const locked = gate !== undefined && !gate.unlocked ? gate.reason : undefined;
200
- const usable = config.harnessEntitled && locked === undefined;
199
+ const usable = config.harnessEntitled;
201
200
  toggle.setAttribute('aria-checked', config.harnessEnabled && usable ? 'true' : 'false');
202
201
  toggle.setAttribute('aria-disabled', usable ? 'false' : 'true');
203
202
  const help = row.querySelector('[data-reticle-settings-help]');
204
203
  const credits = creditsLeft(config.credits);
205
204
  if (help instanceof HTMLElement)
206
- help.title =
207
- locked ??
208
- (usable
209
- ? [HARNESS_HELP, credits].filter((t) => 0 < t.length).join(' ')
210
- : creditsSpent(config.credits)
211
- ? credits
212
- : HARNESS_LOCKED_HELP);
205
+ help.title = usable
206
+ ? [HARNESS_HELP, credits].filter((t) => 0 < t.length).join(' ')
207
+ : creditsSpent(config.credits)
208
+ ? credits
209
+ : HARNESS_LOCKED_HELP;
213
210
  }
214
211
  export function paintSettingsAccount(root, account, dashboardUrl, details = {}) {
215
212
  const row = root.querySelector(`[${SETTINGS_ACCOUNT_ROW_ATTR}]`);
@@ -502,11 +499,11 @@ export class PresenterSettingsPanel {
502
499
  applyPresenterSettings(root, activeSettings);
503
500
  this.#host.onSettingsChange?.(activeSettings);
504
501
  }
505
- paintHarness(config, gate) {
502
+ paintHarness(config) {
506
503
  const root = this.#panel;
507
504
  if (root === undefined)
508
505
  return;
509
- paintHarnessRow(root, config, gate);
506
+ paintHarnessRow(root, config);
510
507
  }
511
508
  teardown() {
512
509
  this.#root = undefined;
@@ -1,5 +1,4 @@
1
- import type { AgentLink, HarnessConfig, HarnessDrive } from '@reticlehq/core';
2
- import type { HarnessGateView } from './presenter-harness-row.js';
1
+ import type { AgentLink, HarnessConfig, HarnessDrive, LastDrive } from '@reticlehq/core';
3
2
  import { PresenterReport } from './presenter-report.js';
4
3
  import type { AccountState } from '@reticlehq/core';
5
4
  import { type AccountDetails } from './presenter-account.js';
@@ -23,9 +22,9 @@ interface HudShellCallbacks {
23
22
  export declare class HudShell {
24
23
  #private;
25
24
  setProjectId(projectId: string | undefined): void;
26
- paintHarnessDrive(drive: HarnessDrive | undefined): void;
25
+ paintHarnessDrive(drive: HarnessDrive | undefined, last?: LastDrive): void;
27
26
  paintAgent(link: AgentLink | undefined): void;
28
- paintHarness(config: HarnessConfig | undefined, gate?: HarnessGateView): void;
27
+ paintHarness(config: HarnessConfig | undefined): void;
29
28
  paintImpact(verdicts: number): void;
30
29
  paintAnnotations(items: readonly AnnotationItem[]): void;
31
30
  openView(view: ChatView): void;
@@ -50,5 +49,8 @@ export declare class HudShell {
50
49
  }
51
50
  export declare function wasMinimised(): boolean;
52
51
  export declare function rememberMinimised(minimised: boolean): void;
52
+ export declare function wasLeftOpen(): boolean;
53
+ export declare function rememberLeftOpen(open: boolean): void;
54
+ export declare function shouldReopenPanel(): boolean;
53
55
  export declare function shouldAutoOpenChat(autoOpenChat: boolean): boolean;
54
56
  export {};
@@ -3,10 +3,10 @@ import { PresenterReport, reportPanelHtml } from './presenter-report.js';
3
3
  import { paintToolbarAccount, TOOLBAR_ACCOUNT_ATTR } from './presenter-workspace.js';
4
4
  import { mountAccountControl, TEAM_SLOT_ATTR } from './presenter-account.js';
5
5
  import { paintSettingsAccount } from './presenter-settings.js';
6
- import { CHAT_MIN_ATTR, CHAT_PILL_ATTR, REPORT_ATTR, ANNOTATE_BTN_ATTR, CHAT_ATTR, CHAT_TOGGLE_ATTR, CHAT_PANEL_ATTR, DOCK_ATTR, FAB_ATTR, MIN_ATTR, SETTINGS_ATTR, SETTINGS_BTN_ATTR, MINIMISED_STORAGE_KEY, } from './presenter-config.js';
6
+ import { CHAT_MIN_ATTR, CHAT_PILL_ATTR, REPORT_ATTR, ANNOTATE_BTN_ATTR, CHAT_ATTR, CHAT_TOGGLE_ATTR, CHAT_PANEL_ATTR, DOCK_ATTR, FAB_ATTR, MIN_ATTR, SETTINGS_ATTR, SETTINGS_BTN_ATTR, MINIMISED_STORAGE_KEY, OPEN_STORAGE_KEY, } from './presenter-config.js';
7
7
  import { paintCarousel } from './carousel/carousel.js';
8
8
  import { panelSlides } from './carousel/panel-slides.js';
9
- import { paintRail, RAIL_HTML, RAIL_PROMO_ATTR } from './presenter-frame.js';
9
+ import { paintRail, RAIL_HTML, RAIL_PROMO_ATTR, RAIL_SIGNIN_HTML } from './presenter-frame.js';
10
10
  import { FAB_TOGGLE_HTML, MARK_SVG } from './chrome/presenter-brand.js';
11
11
  import { settleLogAtLatest } from './chrome/presenter-log.js';
12
12
  import { installHudDragHandles, installHudPositionGuards } from './presenter-drag.js';
@@ -67,16 +67,16 @@ export class HudShell {
67
67
  setProjectId(projectId) {
68
68
  this.#chatViews.setProjectId(projectId);
69
69
  }
70
- paintHarnessDrive(drive) {
71
- this.#chatViews.paintDrive(drive);
70
+ paintHarnessDrive(drive, last) {
71
+ this.#chatViews.paintDrive(drive, last);
72
72
  }
73
73
  paintAgent(link) {
74
74
  this.#chatViews.paintAgent(link);
75
75
  }
76
- paintHarness(config, gate) {
76
+ paintHarness(config) {
77
77
  this.#pushedHarness = config;
78
- this.#settings.paintHarness(config, gate);
79
- this.#chatViews.paintHarness(config, gate);
78
+ this.#settings.paintHarness(config);
79
+ this.#chatViews.paintHarness(config);
80
80
  this.#repaintPromoIfMoved();
81
81
  }
82
82
  #promoLinks() {
@@ -174,6 +174,7 @@ export class HudShell {
174
174
  <div class="${HUD_LOG_WELL_CLASS}"><div ${logAttr}></div></div>
175
175
  ${flowsHtml}
176
176
  </section>
177
+ ${RAIL_SIGNIN_HTML}
177
178
  ${footHtml}
178
179
  </div>
179
180
  ${FLOWS_PAGE_HTML}
@@ -270,12 +271,14 @@ export class HudShell {
270
271
  pillEl?.addEventListener('click', (e) => {
271
272
  e.preventDefault();
272
273
  e.stopPropagation();
274
+ rememberLeftOpen(true);
273
275
  this.showChat();
274
276
  }, { signal });
275
277
  const chatMinEl = root.querySelector(`[${CHAT_MIN_ATTR}]`);
276
278
  chatMinEl?.addEventListener('click', (e) => {
277
279
  e.preventDefault();
278
280
  e.stopPropagation();
281
+ rememberLeftOpen(false);
279
282
  this.closeChat();
280
283
  }, { signal });
281
284
  const collapseEl = root.querySelector('[data-reticle-min-btn]');
@@ -300,10 +303,12 @@ export class HudShell {
300
303
  this.#suppressFabClick = false;
301
304
  return;
302
305
  }
306
+ rememberLeftOpen(true);
303
307
  this.expand();
304
308
  }, { signal });
305
309
  this.#collapseBtn?.addEventListener('click', (e) => {
306
310
  e.stopPropagation();
311
+ rememberLeftOpen(false);
307
312
  this.collapse();
308
313
  }, { signal });
309
314
  const toolbarDrag = root.querySelector(`.${DRAG_HANDLE_CLASS}`);
@@ -455,6 +460,7 @@ export class HudShell {
455
460
  return;
456
461
  }
457
462
  if (this.isChatOpen()) {
463
+ rememberLeftOpen(false);
458
464
  this.closeChat();
459
465
  return;
460
466
  }
@@ -463,6 +469,7 @@ export class HudShell {
463
469
  return;
464
470
  }
465
471
  if (!this.isCollapsed()) {
472
+ rememberLeftOpen(false);
466
473
  this.collapse();
467
474
  }
468
475
  };
@@ -485,6 +492,29 @@ export function rememberMinimised(minimised) {
485
492
  catch {
486
493
  }
487
494
  }
495
+ export function wasLeftOpen() {
496
+ try {
497
+ return '1' === globalThis.localStorage.getItem(OPEN_STORAGE_KEY);
498
+ }
499
+ catch {
500
+ return false;
501
+ }
502
+ }
503
+ export function rememberLeftOpen(open) {
504
+ try {
505
+ if (open) {
506
+ globalThis.localStorage.setItem(OPEN_STORAGE_KEY, '1');
507
+ }
508
+ else {
509
+ globalThis.localStorage.removeItem(OPEN_STORAGE_KEY);
510
+ }
511
+ }
512
+ catch {
513
+ }
514
+ }
515
+ export function shouldReopenPanel() {
516
+ return wasLeftOpen() && !wasMinimised();
517
+ }
488
518
  export function shouldAutoOpenChat(autoOpenChat) {
489
519
  return autoOpenChat && !wasMinimised();
490
520
  }
@@ -1,4 +1,4 @@
1
- import { ActionType, HudCorner, HudVisibility, HumanControlKind, ReticleCommand, PresenterMode, SessionState, isPresenterTone, isSessionState, } from '@reticlehq/core';
1
+ import { ActionType, HudCorner, HudVisibility, HUD_RELOAD_TO_RESTORE, HumanControlKind, ReticleCommand, PresenterMode, SessionState, isPresenterTone, isSessionState, } from '@reticlehq/core';
2
2
  import { refs } from '../dom/addressing/refs.js';
3
3
  import { UNREACHABLE_HEADLINE, unreachableMessage } from '../transport/unreachable-message.js';
4
4
  import { actionVerb } from './chrome/presenter-verbs.js';
@@ -6,7 +6,7 @@ import { nativeSetTimeout, nativeClearTimeout, nativeNow } from '../timers/nativ
6
6
  import { LOG_KIND, CHIP_LABEL, LOG_ACTOR, DATA_RETICLE_LOG, clampLogMax, formatElapsed, humanDuration, appendLogRow, clearLogRows, trimLogRows, } from './chrome/presenter-log.js';
7
7
  import { PRESENTER_CSS } from './presenter-styles.js';
8
8
  import { HudShell } from './presenter-shell.js';
9
- import { HARNESS_LOG_ATTR, harnessGateIn } from './presenter-harness-row.js';
9
+ import { HARNESS_LOG_ATTR } from './presenter-harness-row.js';
10
10
  import { platformDetails } from './presenter-account.js';
11
11
  import { parseImpactSnapshot } from './chrome/presenter-report-copy.js';
12
12
  import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
@@ -20,7 +20,7 @@ import { renderTally } from './chrome/presenter-tally.js';
20
20
  import { CONTROLS_BANNER_HTML, CONTROLS_MARKS_HTML, CONTROLS_FOOT_HTML, CONTROLS_STATUS_HTML, ENDED_FADE_MS, ControlPanel, } from './presenter-controls.js';
21
21
  import { statusTheme, blockerHtml, getPresenterSettings, OutputDetail, syncPageBlocker, paintSdkVersion, } from './presenter-settings.js';
22
22
  import { Annotator } from '../review/annotator.js';
23
- import { shouldAutoOpenChat } from './presenter-shell.js';
23
+ import { shouldAutoOpenChat, shouldReopenPanel } from './presenter-shell.js';
24
24
  import { installHudTelemetry } from './hud-telemetry.js';
25
25
  import { PlanBoard, parsePlanView } from './presenter-plan.js';
26
26
  import { claimLog, readRememberedLog, rememberLog, } from './chrome/log-memory.js';
@@ -56,6 +56,7 @@ export class Presenter {
56
56
  #endMs;
57
57
  #runLog = [];
58
58
  #sessionActive = false;
59
+ #agentOpened = false;
59
60
  #logMax;
60
61
  #log;
61
62
  #harnessLog;
@@ -70,6 +71,7 @@ export class Presenter {
70
71
  #panel;
71
72
  #shell;
72
73
  #annotator;
74
+ #removed = false;
73
75
  constructor(options = {}) {
74
76
  this.#paceMs = effectivePaceMs(options.paceMs);
75
77
  this.#now = options.now ?? nativeNow;
@@ -161,8 +163,8 @@ export class Presenter {
161
163
  if (snapshot !== undefined) {
162
164
  this.#shell.report.setSnapshot(snapshot);
163
165
  this.#shell.paintNotices(snapshot.notices);
164
- this.#shell.paintHarness(snapshot.harnessConfig, harnessGateIn(snapshot.instrumentation, snapshot.harnessConfig));
165
- this.#shell.paintHarnessDrive(snapshot.harnessDrive);
166
+ this.#shell.paintHarness(snapshot.harnessConfig);
167
+ this.#shell.paintHarnessDrive(snapshot.harnessDrive, snapshot.lastDrive);
166
168
  this.#paintHarnessRun(snapshot.harnessDrive);
167
169
  this.#shell.paintAgent(snapshot.agent);
168
170
  this.#shell.paintImpact(snapshot.project.counts.verdicts);
@@ -201,7 +203,7 @@ export class Presenter {
201
203
  document.head.appendChild(style);
202
204
  const root = document.createElement('div');
203
205
  root.setAttribute('data-reticle-overlay', '');
204
- 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>${CONTROLS_BANNER_HTML}<span class="reticle-chip" data-reticle-chip></span><div class="reticle-act-actions">${CONTROLS_STATUS_HTML}</div></div>`;
206
+ const actStrip = `<div class="reticle-act-strip" data-liveness="idle"><span class="reticle-act-dot" aria-hidden="true"></span><span class="reticle-act" data-reticle-act-line>${ACT_STRIP.READY}</span>${CONTROLS_BANNER_HTML}<span class="reticle-chip" data-reticle-chip></span><div class="reticle-act-actions">${CONTROLS_STATUS_HTML}</div></div>`;
205
207
  root.innerHTML = `
206
208
  ${blockerHtml()}
207
209
  <div data-reticle-glow></div>
@@ -340,7 +342,7 @@ export class Presenter {
340
342
  this.#showSession();
341
343
  this.#glowCtl.resetActivity(this.#now());
342
344
  this.#startHeartbeat();
343
- if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat)) {
345
+ if (shouldReopenPanel()) {
344
346
  this.#shell.openChat();
345
347
  }
346
348
  }
@@ -354,8 +356,9 @@ export class Presenter {
354
356
  this.#lastActionText = UNREACHABLE_HEADLINE;
355
357
  this.#paintActStrip(this.#lastActionText, true);
356
358
  this.#actLine?.setAttribute('title', unreachableMessage(url, attempts));
357
- if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat))
359
+ if (shouldReopenPanel()) {
358
360
  this.#shell.openChat();
361
+ }
359
362
  }
360
363
  #showSession() {
361
364
  const dock = this.#root?.querySelector('[data-reticle-dock]');
@@ -432,11 +435,15 @@ export class Presenter {
432
435
  this.#paintActStrip(text, false);
433
436
  }
434
437
  #paintHarnessRun(drive) {
438
+ const log = this.#harnessLog;
435
439
  if (drive !== undefined && drive.runId !== this.#harnessRunId) {
436
440
  this.#harnessRunId = drive.runId;
437
- if (this.#harnessLog !== undefined)
438
- clearLogRows(this.#harnessLog);
441
+ if (log !== undefined)
442
+ clearLogRows(log);
439
443
  }
444
+ if (log !== undefined && 0 === log.childElementCount)
445
+ for (const line of drive?.lines ?? [])
446
+ appendLogRow(log, LOG_KIND.NARRATION, line, '', this.#logMax, LOG_ACTOR.HARNESS);
440
447
  const was = this.#harnessSteps;
441
448
  this.#harnessSteps = drive?.steps;
442
449
  if (drive !== undefined)
@@ -507,10 +514,14 @@ export class Presenter {
507
514
  }
508
515
  placeHud(visibility, corner) {
509
516
  const root = this.#root;
510
- if (root === undefined)
517
+ if (root === undefined) {
518
+ if (this.#removed)
519
+ return [HUD_RELOAD_TO_RESTORE];
511
520
  return [];
521
+ }
512
522
  const applied = [];
513
523
  if (HudVisibility.REMOVED === visibility) {
524
+ this.#removed = true;
514
525
  this.destroy();
515
526
  return [visibility];
516
527
  }
@@ -632,6 +643,7 @@ export class Presenter {
632
643
  while (this.#remembered.length > this.#logMax)
633
644
  this.#remembered.shift();
634
645
  rememberLog(this.#remembered);
646
+ this.#openForAgent(kind);
635
647
  if (this.#shell.isCollapsed())
636
648
  this.#shell.pulseFab(true);
637
649
  this.#renderTally();
@@ -645,6 +657,15 @@ export class Presenter {
645
657
  },
646
658
  };
647
659
  }
660
+ #openForAgent(kind) {
661
+ if (this.#agentOpened || LOG_KIND.HUMAN === kind) {
662
+ return;
663
+ }
664
+ this.#agentOpened = true;
665
+ if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat)) {
666
+ this.#shell.openChat();
667
+ }
668
+ }
648
669
  #renderTally() {
649
670
  if (!getPresenterSettings().showTally) {
650
671
  this.#tally?.setAttribute('hidden', '');
@@ -9,6 +9,7 @@ interface QueryLike {
9
9
  message?: string;
10
10
  } | null;
11
11
  data?: unknown;
12
+ isInvalidated?: boolean;
12
13
  };
13
14
  isStale?: () => boolean;
14
15
  }
@@ -23,16 +24,18 @@ export interface QuerySnapshot {
23
24
  status: string;
24
25
  fetchStatus: string | undefined;
25
26
  isStale: boolean | undefined;
27
+ isInvalidated: boolean | undefined;
26
28
  dataUpdatedAt: number | undefined;
27
29
  error: string | null;
28
30
  data: unknown;
29
31
  }
30
32
  export declare function tanstackQueryStore(client: QueryClientLike): StoreLike;
33
+ type Deferred<T> = [T][T extends unknown ? 0 : never];
31
34
  interface JotaiStoreLike<A> {
32
35
  get: (atom: A) => unknown;
33
36
  sub: (atom: A, listener: () => void) => () => void;
34
37
  }
35
- export declare function jotaiStore<A>(store: JotaiStoreLike<A>, atoms: Record<string, A>): StoreLike;
38
+ export declare function jotaiStore<A>(store: JotaiStoreLike<A>, atoms: Record<string, Deferred<A>>): StoreLike;
36
39
  interface ActorLike {
37
40
  getSnapshot: () => unknown;
38
41
  subscribe: (listener: () => void) => {
@@ -54,7 +57,7 @@ export interface RecoilAtomSnapshot {
54
57
  value: unknown;
55
58
  error: string | null;
56
59
  }
57
- export declare function recoilStore<A>(atoms: Record<string, A>, snapshot: () => RecoilSnapshotLike<A>, subscribe: StoreSubscribe): StoreLike;
60
+ export declare function recoilStore<A>(atoms: Record<string, Deferred<A>>, snapshot: () => RecoilSnapshotLike<A>, subscribe: StoreSubscribe): StoreLike;
58
61
  interface SvelteReadable {
59
62
  subscribe: (run: (value: unknown) => void) => (() => void) | {
60
63
  unsubscribe: () => void;
@@ -9,37 +9,63 @@ function snapshotLive(value) {
9
9
  }
10
10
  }
11
11
  function copyPlain(value, seen) {
12
- if ('object' !== typeof value || null === value)
12
+ if ('object' !== typeof value || null === value) {
13
13
  return value;
14
+ }
14
15
  const done = seen.get(value);
15
- if (done !== undefined)
16
+ if (done !== undefined) {
16
17
  return done;
18
+ }
17
19
  if (Array.isArray(value)) {
18
20
  const out = [];
19
21
  seen.set(value, out);
20
- for (const item of value)
22
+ for (const item of value) {
21
23
  out.push(copyPlain(item, seen));
24
+ }
22
25
  return out;
23
26
  }
24
27
  const proto = Object.getPrototypeOf(value);
25
- if (proto !== Object.prototype && proto !== null)
28
+ if (proto !== Object.prototype && proto !== null) {
26
29
  return value;
30
+ }
27
31
  const out = {};
28
32
  seen.set(value, out);
29
- for (const [key, item] of Object.entries(value))
33
+ for (const [key, item] of Object.entries(value)) {
30
34
  out[key] = copyPlain(item, seen);
35
+ }
31
36
  return out;
32
37
  }
38
+ function queryKeyPart(part) {
39
+ if ('object' !== typeof part || null === part) {
40
+ return String(part);
41
+ }
42
+ return JSON.stringify(part, (_key, value) => {
43
+ if ('object' !== typeof value || null === value || Array.isArray(value)) {
44
+ return value;
45
+ }
46
+ const proto = Object.getPrototypeOf(value);
47
+ if (proto !== Object.prototype && proto !== null) {
48
+ return value;
49
+ }
50
+ const plain = value;
51
+ const sorted = {};
52
+ for (const key of Object.keys(plain).sort()) {
53
+ sorted[key] = plain[key];
54
+ }
55
+ return sorted;
56
+ });
57
+ }
33
58
  export function tanstackQueryStore(client) {
34
59
  const store = {
35
60
  getState: () => {
36
61
  const out = {};
37
62
  for (const query of client.getQueryCache().getAll()) {
38
- const key = query.queryKey.map((part) => String(part)).join('/');
63
+ const key = query.queryKey.map(queryKeyPart).join('/');
39
64
  out[key] = {
40
65
  status: query.state.status,
41
66
  fetchStatus: query.state.fetchStatus,
42
67
  isStale: query.isStale?.(),
68
+ isInvalidated: query.state.isInvalidated,
43
69
  dataUpdatedAt: query.state.dataUpdatedAt,
44
70
  error: query.state.error?.message ?? null,
45
71
  data: query.state.data,
@@ -56,15 +82,17 @@ export function jotaiStore(store, atoms) {
56
82
  return {
57
83
  getState: () => {
58
84
  const out = {};
59
- for (const [name, atom] of Object.entries(atoms))
85
+ for (const [name, atom] of Object.entries(atoms)) {
60
86
  out[name] = store.get(atom);
87
+ }
61
88
  return out;
62
89
  },
63
90
  subscribe: (listener) => {
64
91
  const unsubs = Object.values(atoms).map((atom) => store.sub(atom, listener));
65
92
  return () => {
66
- for (const unsub of unsubs)
93
+ for (const unsub of unsubs) {
67
94
  unsub();
95
+ }
68
96
  };
69
97
  },
70
98
  };
@@ -151,8 +179,9 @@ export function svelteStore(readable, warn = nativeWarn) {
151
179
  subscribe: (listener) => {
152
180
  let inSubscribeCall = true;
153
181
  const handle = readable.subscribe(() => {
154
- if (!inSubscribeCall)
182
+ if (!inSubscribeCall) {
155
183
  listener();
184
+ }
156
185
  });
157
186
  inSubscribeCall = false;
158
187
  return () => stopSubscription(handle);
@@ -177,8 +206,9 @@ export function pushStore(initial) {
177
206
  store: { getState: () => current, subscribe },
178
207
  push: (value) => {
179
208
  current = value;
180
- for (const listener of listeners)
209
+ for (const listener of listeners) {
181
210
  listener();
211
+ }
182
212
  },
183
213
  };
184
214
  }