@rtrvr-ai/rover 3.0.1 → 4.0.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.
@@ -1,4 +1,7 @@
1
1
  const DEFAULT_EMBED_SCRIPT_URL = 'https://rover.rtrvr.ai/embed.js';
2
+ const DEFAULT_AGENT_BASE = 'https://agent.rtrvr.ai';
3
+ const VOICE_AUTO_STOP_MIN_MS = 800;
4
+ const VOICE_AUTO_STOP_MAX_MS = 5000;
2
5
  function toStringValue(value) {
3
6
  return String(value || '').trim();
4
7
  }
@@ -9,6 +12,12 @@ function escapeHtmlAttr(value) {
9
12
  .replace(/</g, '&lt;')
10
13
  .replace(/>/g, '&gt;');
11
14
  }
15
+ function escapeScriptJson(value) {
16
+ return value
17
+ .replace(/</g, '\\u003c')
18
+ .replace(/>/g, '\\u003e')
19
+ .replace(/&/g, '\\u0026');
20
+ }
12
21
  function parseBooleanAttr(value) {
13
22
  const normalized = toStringValue(value).toLowerCase();
14
23
  if (!normalized)
@@ -28,11 +37,45 @@ function parseCsvList(value) {
28
37
  return undefined;
29
38
  return Array.from(new Set(items));
30
39
  }
40
+ function parseIntegerAttr(value) {
41
+ const parsed = Number(toStringValue(value));
42
+ return Number.isFinite(parsed) ? Math.trunc(parsed) : undefined;
43
+ }
44
+ function normalizeVoiceConfig(value) {
45
+ if (!value || typeof value !== 'object')
46
+ return undefined;
47
+ const voice = {};
48
+ if (typeof value.enabled === 'boolean')
49
+ voice.enabled = value.enabled;
50
+ const language = toStringValue(value.language).replace(/[^a-zA-Z0-9-]/g, '').slice(0, 48);
51
+ if (language)
52
+ voice.language = language;
53
+ const autoStopMs = Number(value.autoStopMs);
54
+ if (Number.isFinite(autoStopMs)) {
55
+ voice.autoStopMs = Math.max(VOICE_AUTO_STOP_MIN_MS, Math.min(VOICE_AUTO_STOP_MAX_MS, Math.trunc(autoStopMs)));
56
+ }
57
+ return Object.keys(voice).length ? voice : undefined;
58
+ }
59
+ function normalizeUiConfig(value) {
60
+ if (!value || typeof value !== 'object')
61
+ return undefined;
62
+ const ui = {};
63
+ const voice = normalizeVoiceConfig(value.voice);
64
+ if (voice)
65
+ ui.voice = voice;
66
+ return Object.keys(ui).length ? ui : undefined;
67
+ }
31
68
  function normalizeBootstrapConfig(config) {
32
- return {
69
+ const next = {
33
70
  ...config,
34
71
  scriptUrl: toStringValue(config.scriptUrl) || DEFAULT_EMBED_SCRIPT_URL,
35
72
  };
73
+ const ui = normalizeUiConfig(config.ui);
74
+ if (ui)
75
+ next.ui = ui;
76
+ else
77
+ delete next.ui;
78
+ return next;
36
79
  }
37
80
  function buildBootstrapPayload(config) {
38
81
  const normalized = normalizeBootstrapConfig(config);
@@ -65,6 +108,8 @@ function buildBootstrapPayload(config) {
65
108
  payload.mode = normalized.mode;
66
109
  if (typeof normalized.allowActions === 'boolean')
67
110
  payload.allowActions = normalized.allowActions;
111
+ if (normalized.ui?.voice)
112
+ payload.ui = { voice: normalized.ui.voice };
68
113
  return payload;
69
114
  }
70
115
  function buildQueueStub() {
@@ -147,7 +192,20 @@ export function createRoverScriptTagSnippet(config) {
147
192
  attrs.push(`data-mode="${escapeHtmlAttr(normalized.mode)}"`);
148
193
  if (typeof normalized.allowActions === 'boolean')
149
194
  attrs.push(`data-allow-actions="${escapeHtmlAttr(String(normalized.allowActions))}"`);
150
- return `<script ${attrs.join(' ')}></script>`;
195
+ if (typeof normalized.ui?.voice?.enabled === 'boolean')
196
+ attrs.push(`data-voice-enabled="${escapeHtmlAttr(String(normalized.ui.voice.enabled))}"`);
197
+ if (normalized.ui?.voice?.language)
198
+ attrs.push(`data-voice-language="${escapeHtmlAttr(normalized.ui.voice.language)}"`);
199
+ if (typeof normalized.ui?.voice?.autoStopMs === 'number')
200
+ attrs.push(`data-voice-auto-stop-ms="${escapeHtmlAttr(String(normalized.ui.voice.autoStopMs))}"`);
201
+ const taskEndpoint = `${toStringValue(normalized.apiBase) || DEFAULT_AGENT_BASE}/v1/tasks`;
202
+ const markerJson = escapeScriptJson(JSON.stringify({ task: taskEndpoint }));
203
+ return [
204
+ `<script type="application/agent+json" data-rover-agent-discovery="marker">${markerJson}</script>`,
205
+ '<link rel="service-desc" href="/.well-known/agent-card.json" type="application/json" data-rover-agent-discovery="service-desc" />',
206
+ '<link rel="service-doc" href="/llms.txt" type="text/markdown" data-rover-agent-discovery="service-doc" />',
207
+ `<script ${attrs.join(' ')}></script>`,
208
+ ].join('\n');
151
209
  }
152
210
  export function readRoverScriptDataAttributes(scriptEl) {
153
211
  const siteId = toStringValue(scriptEl.getAttribute('data-site-id'));
@@ -199,5 +257,17 @@ export function readRoverScriptDataAttributes(scriptEl) {
199
257
  const allowActions = parseBooleanAttr(scriptEl.getAttribute('data-allow-actions'));
200
258
  if (typeof allowActions === 'boolean')
201
259
  config.allowActions = allowActions;
260
+ const voiceEnabled = parseBooleanAttr(scriptEl.getAttribute('data-voice-enabled'));
261
+ const voiceLanguage = toStringValue(scriptEl.getAttribute('data-voice-language'));
262
+ const voiceAutoStopMs = parseIntegerAttr(scriptEl.getAttribute('data-voice-auto-stop-ms'));
263
+ if (typeof voiceEnabled === 'boolean' || voiceLanguage || typeof voiceAutoStopMs === 'number') {
264
+ config.ui = {
265
+ voice: {
266
+ ...(typeof voiceEnabled === 'boolean' ? { enabled: voiceEnabled } : {}),
267
+ ...(voiceLanguage ? { language: voiceLanguage } : {}),
268
+ ...(typeof voiceAutoStopMs === 'number' ? { autoStopMs: voiceAutoStopMs } : {}),
269
+ },
270
+ };
271
+ }
202
272
  return config;
203
273
  }