@myscheme/voice-navigation-sdk 0.1.0 → 0.1.2

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.
package/dist/actions.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { NavigationAction, ActionContext, ActionResult, AgentActionResponse } from "./types.js";
2
+ import type { PageRegistry } from "./services/page-registry.js";
3
+ export declare const setPageRegistry: (registry: PageRegistry) => void;
4
+ export declare const getPageRegistry: () => PageRegistry | null;
2
5
  export declare const setZoomScale: (scale: number) => number;
3
6
  export declare const adjustZoom: (delta: number) => number;
4
7
  export declare const formatActionLabel: (action: string) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACpB,MAAM,YAAY,CAAC;AA0EpB,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAS5C,CAAC;AAKF,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAM1C,CAAC;AAKF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,MAElD,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,gBAAgB,EACxB,UAAS,aAAkB,KAC1B,YAoaF,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,KAAG,mBAAmB,GAAG,IAgCtE,CAAC;AAKF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAAM,IAAI,gBAE1D,CAAC"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAEhB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAsChE,eAAO,MAAM,eAAe,GAAI,UAAU,YAAY,KAAG,IAOxD,CAAC;AAKF,eAAO,MAAM,eAAe,QAAO,YAAY,GAAG,IAEjD,CAAC;AAkCF,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAS5C,CAAC;AAKF,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAM1C,CAAC;AAKF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,MAElD,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,gBAAgB,EACxB,UAAS,aAAkB,KAC1B,YA4VF,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,KAAG,mBAAmB,GAAG,IAiCtE,CAAC;AAKF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAAM,IAAI,gBAY1D,CAAC"}
package/dist/actions.js CHANGED
@@ -1,4 +1,4 @@
1
- const ALLOWED_ACTIONS = new Set([
1
+ const CORE_ACTIONS = new Set([
2
2
  "zoom_in",
3
3
  "zoom_out",
4
4
  "scroll_up",
@@ -23,20 +23,20 @@ const ALLOWED_ACTIONS = new Set([
23
23
  "pause_media",
24
24
  "mute_media",
25
25
  "unmute_media",
26
- "navigate_home",
27
26
  "search_content",
28
- "navigate_search",
29
- "navigate_faqs",
30
- "navigate_help",
31
- "navigate_contact",
32
- "navigate_about",
33
- "navigate_screen_reader",
34
- "navigate_accessibility",
35
- "navigate_disclaimer",
36
- "navigate_terms_conditions",
37
27
  "stop",
38
28
  "unknown",
39
29
  ]);
30
+ let globalPageRegistry = null;
31
+ export const setPageRegistry = (registry) => {
32
+ globalPageRegistry = registry;
33
+ if (typeof window !== "undefined") {
34
+ window.__navigatePageRegistry = registry;
35
+ }
36
+ };
37
+ export const getPageRegistry = () => {
38
+ return globalPageRegistry;
39
+ };
40
40
  const ZOOM_STEP = 0.1;
41
41
  const MIN_ZOOM = 0.5;
42
42
  const MAX_ZOOM = 2.0;
@@ -337,92 +337,12 @@ export const performAgentAction = (action, context = {}) => {
337
337
  }
338
338
  break;
339
339
  }
340
- case "navigate_home": {
341
- notifyNavigationIntent(context, "/");
342
- window.location.href = "/";
343
- console.log("Navigating to home");
344
- info.navigated = true;
345
- performed = true;
346
- break;
347
- }
348
340
  case "search_content": {
349
341
  console.log("Vector search action delegated to controller");
350
342
  info.searchPending = true;
351
343
  performed = false;
352
344
  break;
353
345
  }
354
- case "navigate_search": {
355
- notifyNavigationIntent(context, "/search");
356
- window.location.href = "/search";
357
- console.log("Navigating to search");
358
- info.navigated = true;
359
- performed = true;
360
- break;
361
- }
362
- case "navigate_faqs": {
363
- notifyNavigationIntent(context, "/faqs");
364
- window.location.href = "/faqs";
365
- console.log("Navigating to FAQs");
366
- info.navigated = true;
367
- performed = true;
368
- break;
369
- }
370
- case "navigate_help": {
371
- notifyNavigationIntent(context, "/help");
372
- window.location.href = "/help";
373
- console.log("Navigating to help");
374
- info.navigated = true;
375
- performed = true;
376
- break;
377
- }
378
- case "navigate_contact": {
379
- notifyNavigationIntent(context, "/contact");
380
- window.location.href = "/contact";
381
- console.log("Navigating to contact");
382
- info.navigated = true;
383
- performed = true;
384
- break;
385
- }
386
- case "navigate_about": {
387
- notifyNavigationIntent(context, "/about");
388
- window.location.href = "/about";
389
- console.log("Navigating to about");
390
- info.navigated = true;
391
- performed = true;
392
- break;
393
- }
394
- case "navigate_screen_reader": {
395
- notifyNavigationIntent(context, "/screen-reader-access");
396
- window.location.href = "/screen-reader-access";
397
- console.log("Navigating to screen reader");
398
- info.navigated = true;
399
- performed = true;
400
- break;
401
- }
402
- case "navigate_accessibility": {
403
- notifyNavigationIntent(context, "/accessibility");
404
- window.location.href = "/accessibility";
405
- console.log("Navigating to accessibility");
406
- info.navigated = true;
407
- performed = true;
408
- break;
409
- }
410
- case "navigate_disclaimer": {
411
- notifyNavigationIntent(context, "/disclaimer");
412
- window.location.href = "/disclaimer";
413
- console.log("Navigating to disclaimer");
414
- info.navigated = true;
415
- performed = true;
416
- break;
417
- }
418
- case "navigate_terms_conditions": {
419
- notifyNavigationIntent(context, "/terms-and-conditions");
420
- window.location.href = "/terms-and-conditions";
421
- console.log("Navigating to terms and conditions");
422
- info.navigated = true;
423
- performed = true;
424
- break;
425
- }
426
346
  case "stop": {
427
347
  console.log("Stop command received");
428
348
  if (onStop) {
@@ -432,6 +352,22 @@ export const performAgentAction = (action, context = {}) => {
432
352
  break;
433
353
  }
434
354
  default: {
355
+ if (action.startsWith("navigate_") && globalPageRegistry) {
356
+ const pageId = globalPageRegistry.getPageIdFromAction(action);
357
+ if (pageId) {
358
+ const page = globalPageRegistry.getPageById(pageId);
359
+ if (page) {
360
+ notifyNavigationIntent(context, page.path);
361
+ window.location.href = page.path;
362
+ console.log(`Navigating to ${page.name} (${page.path})`);
363
+ info.navigated = true;
364
+ info.pageName = page.name;
365
+ info.pagePath = page.path;
366
+ performed = true;
367
+ break;
368
+ }
369
+ }
370
+ }
435
371
  console.log(`Unknown action: ${action}`);
436
372
  performed = false;
437
373
  break;
@@ -446,8 +382,10 @@ export const extractAgentAction = (result) => {
446
382
  if (!result) {
447
383
  return null;
448
384
  }
385
+ if (typeof result === "object" && !Array.isArray(result) && result.action) {
386
+ return result;
387
+ }
449
388
  let raw = "";
450
- console.log("Extracting action from result:", result, typeof result);
451
389
  if (typeof result === "string") {
452
390
  raw = result;
453
391
  }
@@ -457,10 +395,6 @@ export const extractAgentAction = (result) => {
457
395
  else if (result.text) {
458
396
  raw = result.text;
459
397
  }
460
- else if (typeof result === "object" && result?.action) {
461
- raw = result?.action;
462
- }
463
- console.log("raw reslt", raw, result);
464
398
  if (!raw) {
465
399
  return null;
466
400
  }
@@ -474,5 +408,11 @@ export const extractAgentAction = (result) => {
474
408
  }
475
409
  };
476
410
  export const isAllowedAction = (action) => {
477
- return ALLOWED_ACTIONS.has(action);
411
+ if (CORE_ACTIONS.has(action)) {
412
+ return true;
413
+ }
414
+ if (action.startsWith("navigate_") && globalPageRegistry) {
415
+ return globalPageRegistry.isPageNavigationAction(action);
416
+ }
417
+ return false;
478
418
  };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,10 @@ export { VoiceNavigationController } from "./navigation-controller.js";
2
2
  export { MicrophoneHandler } from "./microphone-handler.js";
3
3
  export { AzureSpeechService } from "./services/azure-speech.js";
4
4
  export { BedrockService } from "./services/bedrock.js";
5
+ export { PageRegistry } from "./services/page-registry.js";
6
+ export type { NavigablePage } from "./services/page-registry.js";
7
+ export { parseNavigationXML, fetchNavigationXML, loadNavigationPages, } from "./services/xml-parser.js";
8
+ export type { PageXMLConfig } from "./services/xml-parser.js";
5
9
  export * from "./types.js";
6
10
  export * from "./actions.js";
7
11
  export * from "./ui.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA2JnD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,CA0E3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA8JnD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,CAuE3B"}
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@ export { VoiceNavigationController } from "./navigation-controller.js";
2
2
  export { MicrophoneHandler } from "./microphone-handler.js";
3
3
  export { AzureSpeechService } from "./services/azure-speech.js";
4
4
  export { BedrockService } from "./services/bedrock.js";
5
+ export { PageRegistry } from "./services/page-registry.js";
6
+ export { parseNavigationXML, fetchNavigationXML, loadNavigationPages, } from "./services/xml-parser.js";
5
7
  export * from "./types.js";
6
8
  export * from "./actions.js";
7
9
  export * from "./ui.js";
@@ -32,6 +34,7 @@ const buildPersistableConfig = (config) => {
32
34
  opensearch: normalizedConfig.opensearch
33
35
  ? { ...normalizedConfig.opensearch }
34
36
  : undefined,
37
+ pages: normalizedConfig.pages ? { ...normalizedConfig.pages } : undefined,
35
38
  language: normalizedConfig.language,
36
39
  autoStart: normalizedConfig.autoStart,
37
40
  };
@@ -109,7 +112,8 @@ export function initNavigationOnMicrophone(config) {
109
112
  const currentOpenSearchConfig = resolveOpenSearchConfig(normalizedConfig);
110
113
  const hadVectorSearch = Boolean(previousOpenSearchConfig);
111
114
  const wantsVectorSearch = Boolean(currentOpenSearchConfig);
112
- const existingHasVectorSearch = Boolean(existingController?.vectorSearchService);
115
+ const existingHasVectorSearch = Boolean(existingController
116
+ ?.vectorSearchService);
113
117
  const vectorConfigChanged = hadVectorSearch &&
114
118
  wantsVectorSearch &&
115
119
  describeOpenSearchConfig(previousOpenSearchConfig) !==
@@ -4,6 +4,7 @@ export declare class VoiceNavigationController {
4
4
  private microphoneHandler;
5
5
  private azureSpeechService;
6
6
  private bedrockService;
7
+ private pageRegistry;
7
8
  private ui;
8
9
  private lastTranscript;
9
10
  private isProcessing;
@@ -12,12 +13,14 @@ export declare class VoiceNavigationController {
12
13
  private autoStartPendingUserInteraction;
13
14
  private removeAutoStartListeners;
14
15
  private vectorSearchService;
16
+ private pagesInitialized;
15
17
  constructor(config: NavigationConfig);
16
18
  private shouldAutoStart;
17
19
  setAutoStart(enabled: boolean): void;
18
20
  prepareForNavigation(options?: {
19
21
  target?: string;
20
22
  }): void;
23
+ private initializePages;
21
24
  private setupEventHandlers;
22
25
  private toggleRecording;
23
26
  start(options?: {
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-controller.d.ts","sourceRoot":"","sources":["../src/navigation-controller.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,YAAY,CAAC;AAuDpB,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,qBAAqB,CAAkB;IAC/C,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,+BAA+B,CAAkB;IACzD,OAAO,CAAC,wBAAwB,CAA6B;IAC7D,OAAO,CAAC,mBAAmB,CAAoC;gBAEnD,MAAM,EAAE,gBAAgB;IA0EpC,OAAO,CAAC,eAAe;IAoBhB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAepC,oBAAoB,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;IAOpE,OAAO,CAAC,kBAAkB;YASZ,eAAe;IAkBhB,KAAK,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiEhE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BlC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,aAAa;YAWP,aAAa;YAOb,wBAAwB;IAuCtC,OAAO,CAAC,WAAW;YAiBL,iBAAiB;YAwBjB,iBAAiB;IA0HxB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;YAI5B,yBAAyB;IAgMvC,OAAO,CAAC,oBAAoB;IAqD5B,OAAO,CAAC,yBAAyB;IAoCjC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,4BAA4B;IAiB7B,OAAO,IAAI,IAAI;IAgBtB,OAAO,CAAC,kBAAkB;IA+B1B,OAAO,CAAC,kBAAkB;IAwD1B,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,uBAAuB;IAoD/B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;CAgBjC"}
1
+ {"version":3,"file":"navigation-controller.d.ts","sourceRoot":"","sources":["../src/navigation-controller.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,YAAY,CAAC;AA0DpB,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,qBAAqB,CAAkB;IAC/C,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,+BAA+B,CAAkB;IACzD,OAAO,CAAC,wBAAwB,CAA6B;IAC7D,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,gBAAgB,CAAgB;gBAE5B,MAAM,EAAE,gBAAgB;IAgFpC,OAAO,CAAC,eAAe;IAoBhB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAepC,oBAAoB,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;YAOtD,eAAe;IAmE7B,OAAO,CAAC,kBAAkB;YASZ,eAAe;IAkBhB,KAAK,CAChB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAO,GACzC,OAAO,CAAC,IAAI,CAAC;IA0EH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BlC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,aAAa;YAWP,aAAa;YAOb,wBAAwB;IAuCtC,OAAO,CAAC,WAAW;YAoBL,iBAAiB;YAwBjB,iBAAiB;IA0HxB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;YAI5B,yBAAyB;IAgMvC,OAAO,CAAC,oBAAoB;IAqD5B,OAAO,CAAC,yBAAyB;IAoCjC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,4BAA4B;IAiB7B,OAAO,IAAI,IAAI;IAgBtB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,kBAAkB;IAwD1B,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,uBAAuB;IAkD/B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;CAmBjC"}
@@ -1,8 +1,10 @@
1
1
  import { MicrophoneHandler } from "./microphone-handler.js";
2
2
  import { AzureSpeechService } from "./services/azure-speech.js";
3
3
  import { BedrockService } from "./services/bedrock.js";
4
+ import { PageRegistry } from "./services/page-registry.js";
5
+ import { loadNavigationPages } from "./services/xml-parser.js";
4
6
  import { VectorSearchService, } from "./services/vector-search.js";
5
- import { performAgentAction, formatActionLabel, extractAgentAction, } from "./actions.js";
7
+ import { performAgentAction, formatActionLabel, extractAgentAction, setPageRegistry, } from "./actions.js";
6
8
  import { createFloatingControl, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
7
9
  const AUTO_START_STORAGE_KEY = "__navigate_auto_start";
8
10
  const RESUME_STATE_STORAGE_KEY = "__navigate_resume_state";
@@ -61,6 +63,8 @@ export class VoiceNavigationController {
61
63
  ? undefined
62
64
  : this.config.aws.embeddingModelId,
63
65
  });
66
+ this.pageRegistry = new PageRegistry([]);
67
+ this.pagesInitialized = this.initializePages();
64
68
  this.microphoneHandler = new MicrophoneHandler({
65
69
  azureSpeechService: this.azureSpeechService,
66
70
  bedrockService: this.bedrockService,
@@ -114,6 +118,46 @@ export class VoiceNavigationController {
114
118
  prepareForNavigation(options = {}) {
115
119
  this.persistResumeState(options.target);
116
120
  }
121
+ async initializePages() {
122
+ try {
123
+ const pagesConfig = this.config.pages;
124
+ if (!pagesConfig) {
125
+ flowLog("No pages configuration provided, using empty page registry");
126
+ setPageRegistry(this.pageRegistry);
127
+ this.bedrockService.setPageRegistry(this.pageRegistry);
128
+ return;
129
+ }
130
+ if (pagesConfig.pages && Array.isArray(pagesConfig.pages)) {
131
+ flowLog(`Loading ${pagesConfig.pages.length} pages from configuration`);
132
+ this.pageRegistry.loadPages(pagesConfig.pages);
133
+ }
134
+ else if (pagesConfig.xml) {
135
+ const xmlType = pagesConfig.xmlType || "url";
136
+ flowLog(`Loading pages from XML (${xmlType}): ${clipText(pagesConfig.xml, 60)}`);
137
+ const pages = await loadNavigationPages({
138
+ source: pagesConfig.xml,
139
+ type: xmlType,
140
+ });
141
+ flowLog(`✓ Loaded ${pages.length} pages from XML`);
142
+ this.pageRegistry.loadPages(pages);
143
+ }
144
+ setPageRegistry(this.pageRegistry);
145
+ this.bedrockService.setPageRegistry(this.pageRegistry);
146
+ flowLog(`✓ Page registry initialized with ${this.pageRegistry.size} pages`);
147
+ if (this.pageRegistry.size > 0) {
148
+ const actions = this.pageRegistry.getNavigationActions();
149
+ flowLog(`✓ Available page navigation actions: ${actions
150
+ .slice(0, 5)
151
+ .join(", ")}${actions.length > 5 ? `, ... (${actions.length} total)` : ""}`);
152
+ }
153
+ }
154
+ catch (error) {
155
+ console.error("Failed to load navigation pages:", error);
156
+ flowLog("⚠ Continuing without dynamic page navigation");
157
+ setPageRegistry(this.pageRegistry);
158
+ this.bedrockService.setPageRegistry(this.pageRegistry);
159
+ }
160
+ }
117
161
  setupEventHandlers() {
118
162
  this.ui.button.addEventListener("click", () => {
119
163
  this.toggleRecording();
@@ -133,6 +177,12 @@ export class VoiceNavigationController {
133
177
  async start(options = {}) {
134
178
  const reason = options.reason ?? "user";
135
179
  this.lastStartReason = reason;
180
+ try {
181
+ await this.pagesInitialized;
182
+ }
183
+ catch (error) {
184
+ console.error("Failed to initialize pages:", error);
185
+ }
136
186
  if (this.ui.root.dataset.state === "listening") {
137
187
  return;
138
188
  }
@@ -258,7 +308,8 @@ export class VoiceNavigationController {
258
308
  }
259
309
  handleError(error) {
260
310
  console.error("Microphone error:", error);
261
- if (this.lastStartReason === "auto" && this.isPermissionRelatedError(error)) {
311
+ if (this.lastStartReason === "auto" &&
312
+ this.isPermissionRelatedError(error)) {
262
313
  setState(this.ui, "idle");
263
314
  this.handleAutoStartFallback("auto");
264
315
  updateStatus(this.ui, "Tap to start voice control");
@@ -670,8 +721,7 @@ export class VoiceNavigationController {
670
721
  };
671
722
  sessionStorage.setItem(RESUME_STATE_STORAGE_KEY, JSON.stringify(payload));
672
723
  }
673
- catch (error) {
674
- }
724
+ catch (error) { }
675
725
  }
676
726
  consumeResumeState() {
677
727
  if (typeof window === "undefined") {
@@ -737,9 +787,7 @@ export class VoiceNavigationController {
737
787
  setState(this.ui, "idle");
738
788
  const resume = () => {
739
789
  this.clearAutoStartFallback();
740
- void this.microphoneHandler
741
- .unlockAudioContext(true)
742
- .finally(() => {
790
+ void this.microphoneHandler.unlockAudioContext(true).finally(() => {
743
791
  void this.start({ reason: "user" });
744
792
  });
745
793
  };
@@ -774,7 +822,8 @@ export class VoiceNavigationController {
774
822
  if (message.includes("audiocontext") && message.includes("not allowed")) {
775
823
  return true;
776
824
  }
777
- if (message.includes("must be resumed") && message.includes("user gesture")) {
825
+ if (message.includes("must be resumed") &&
826
+ message.includes("user gesture")) {
778
827
  return true;
779
828
  }
780
829
  return false;
@@ -1,4 +1,5 @@
1
1
  import type { AgentActionResponse } from "../types.js";
2
+ import type { PageRegistry } from "./page-registry.js";
2
3
  export interface BedrockConfig {
3
4
  region: string;
4
5
  accessKeyId: string;
@@ -10,7 +11,10 @@ export declare class BedrockService {
10
11
  private client;
11
12
  private actionModelId;
12
13
  private embeddingModelId;
14
+ private pageRegistry;
13
15
  constructor(config: BedrockConfig);
16
+ setPageRegistry(registry: PageRegistry): void;
17
+ private buildSystemPrompt;
14
18
  extractAction(userInput: string): Promise<AgentActionResponse>;
15
19
  static create(config: BedrockConfig): BedrockService;
16
20
  embedText(text: string): Promise<number[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"bedrock.d.ts","sourceRoot":"","sources":["../../src/services/bedrock.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,aAAa,CAAC;AAExE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAmBD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,gBAAgB,CAAgB;gBAE5B,MAAM,EAAE,aAAa;IAuB3B,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiEpE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc;IAO9C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAyDjD"}
1
+ {"version":3,"file":"bedrock.d.ts","sourceRoot":"","sources":["../../src/services/bedrock.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAiCD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,YAAY,CAA6B;gBAErC,MAAM,EAAE,aAAa;IAwBjC,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAO7C,OAAO,CAAC,iBAAiB;IA+BnB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAmEpE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc;IAO9C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAwDjD"}
@@ -1,21 +1,35 @@
1
1
  import { BedrockRuntimeClient, InvokeModelCommand, } from "@aws-sdk/client-bedrock-runtime";
2
- const SYSTEM_PROMPT = `You are an action extraction assistant.
2
+ const CORE_ACTIONS_LIST = `"zoom_in", "zoom_out", "scroll_up", "scroll_down", "scroll_left", "scroll_right", "page_up", "page_down", "scroll_top", "scroll_bottom", "go_back", "go_forward", "reload_page", "print_page", "copy_url", "open_menu", "close_menu", "focus_search", "toggle_fullscreen", "exit_fullscreen", "play_media", "pause_media", "mute_media", "unmute_media", "search_content", "stop"`;
3
+ const BASE_SYSTEM_PROMPT = `You are an action extraction assistant.
3
4
 
4
- VERY IMPORTANT INSTRUCTIONS:
5
- - RESPOND ONLY IN THE SPECIFIED JSON FORMAT.
6
- - ALWAYS RETURN A SINGLE JSON OBJECT.
7
- - IF THE USER INPUT DOES NOT CLEARLY INDICATE AN ACTION, RESPOND WITH {"action": "unknown"}.
8
- - DO NOT RESPOND WITH ANYTHING OTHER THAN THE JSON OBJECT.
9
- - CHOOSE THE MOST SPECIFIC ACTION FROM THE ALLOWED LIST. IF MULTIPLE ACTIONS APPLY, CHOOSE THE ONE THAT BEST MATCHES THE USER REQUEST.
10
- - DO NOT INVENT YOUR OWN ACTION. IT MUST BE ONE OF THE GIVEN ACTIONS OR "unknown".
11
- - WHEN THE USER CLEARLY EXPRESSES A DESIRE TO SEARCH OR FIND INFORMATION, RESPOND WITH {"action": "search_content", "query": "concise keywords"}.
12
- - WHEN YOU RETURN THE "search_content" ACTION, THE "query" FIELD MUST BE A SHORT PHRASE CAPTURING THE USER'S INTENT WITHOUT FILLER WORDS.
5
+ CRITICAL RULES:
6
+ 1. RESPOND ONLY WITH A SINGLE JSON OBJECT
7
+ 2. THE JSON MUST HAVE AN "action" FIELD
8
+ 3. THE ACTION MUST BE EXACTLY ONE OF THE ACTIONS LISTED BELOW
9
+ 4. DO NOT CREATE NEW ACTION NAMES - USE ONLY THE EXACT ACTION NAMES PROVIDED
10
+ 5. DO NOT USE GENERIC ACTIONS LIKE "go_to_page" OR "navigate_to_page"
11
+ 6. FOR PAGE NAVIGATION, USE THE EXACT "navigate_<id>" ACTION PROVIDED FOR THAT PAGE
12
+ 7. IF UNCERTAIN, RESPOND WITH {"action": "unknown"}
13
13
 
14
- The only possible actions are:
15
- "zoom_in", "zoom_out", "scroll_up", "scroll_down", "scroll_left", "scroll_right", "page_up", "page_down", "scroll_top", "scroll_bottom", "go_back", "go_forward", "reload_page", "print_page", "copy_url", "open_menu", "close_menu", "focus_search", "toggle_fullscreen", "exit_fullscreen", "play_media", "pause_media", "mute_media", "unmute_media", "navigate_home", "search_content", "navigate_search", "navigate_faqs", "navigate_help", "navigate_contact", "navigate_about", "navigate_screen_reader", "navigate_accessibility", "navigate_disclaimer", "navigate_terms_conditions", "stop".`;
14
+ RESPONSE FORMAT:
15
+ - Core actions: {"action": "action_name"}
16
+ - Search: {"action": "search_content", "query": "keywords"}
17
+ - Page navigation: {"action": "navigate_<page_id>"}
18
+
19
+ EXAMPLES:
20
+ - User says "go to dashboard" → {"action": "navigate_dashboard"}
21
+ - User says "show me the team page" → {"action": "navigate_team"}
22
+ - User says "scroll down" → {"action": "scroll_down"}
23
+ - User says "find schemes" → {"action": "search_content", "query": "schemes"}
24
+
25
+ MATCHING RULES FOR PAGE NAVIGATION:
26
+ - Match user's words to page names and keywords
27
+ - Choose the navigate_ action that best matches
28
+ - If user says "go to X" or "open X" or "show me X", find the page with that name or keyword`;
16
29
  const DEFAULT_EMBEDDING_MODEL_ID = "cohere.embed-multilingual-v3";
17
30
  export class BedrockService {
18
31
  constructor(config) {
32
+ this.pageRegistry = null;
19
33
  this.client = new BedrockRuntimeClient({
20
34
  region: config.region,
21
35
  credentials: {
@@ -29,17 +43,39 @@ export class BedrockService {
29
43
  }
30
44
  else if (typeof config.embeddingModelId === "string") {
31
45
  const trimmed = config.embeddingModelId.trim();
32
- this.embeddingModelId = trimmed.length > 0 ? trimmed : DEFAULT_EMBEDDING_MODEL_ID;
46
+ this.embeddingModelId =
47
+ trimmed.length > 0 ? trimmed : DEFAULT_EMBEDDING_MODEL_ID;
33
48
  }
34
49
  else {
35
50
  this.embeddingModelId = DEFAULT_EMBEDDING_MODEL_ID;
36
51
  }
37
52
  }
53
+ setPageRegistry(registry) {
54
+ this.pageRegistry = registry;
55
+ }
56
+ buildSystemPrompt() {
57
+ let prompt = BASE_SYSTEM_PROMPT;
58
+ prompt += `\n\n=== CORE ACTIONS ===\n${CORE_ACTIONS_LIST}`;
59
+ if (this.pageRegistry && this.pageRegistry.size > 0) {
60
+ const pages = this.pageRegistry.getAllPages();
61
+ const pageActions = pages.map((page) => {
62
+ const keywords = page.keywords?.length
63
+ ? ` [matches: ${page.keywords.join(", ")}]`
64
+ : "";
65
+ return `"navigate_${page.id}" for "${page.name}"${keywords}`;
66
+ });
67
+ prompt += `\n\n=== PAGE NAVIGATION ACTIONS ===\n${pageActions.join("\n")}`;
68
+ prompt += `\n\nWhen user wants to navigate to a page, use the EXACT "navigate_<id>" action shown above.`;
69
+ }
70
+ prompt += `\n\n=== FINAL REMINDER ===\nYou MUST use one of the actions listed above. DO NOT create new action names. DO NOT use "go_to_page" or similar generic actions.`;
71
+ return prompt;
72
+ }
38
73
  async extractAction(userInput) {
74
+ const systemPrompt = this.buildSystemPrompt();
39
75
  const body = {
40
76
  anthropic_version: "bedrock-2023-05-31",
41
77
  max_tokens: 70,
42
- system: SYSTEM_PROMPT,
78
+ system: systemPrompt,
43
79
  messages: [
44
80
  {
45
81
  role: "user",
@@ -118,7 +154,8 @@ export class BedrockService {
118
154
  (Array.isArray(decoded.embeddings)
119
155
  ? decoded.embeddings
120
156
  : decoded.embeddings?.values) ||
121
- decoded.results?.find((item) => Array.isArray(item.embedding))?.embedding;
157
+ decoded.results?.find((item) => Array.isArray(item.embedding))
158
+ ?.embedding;
122
159
  if (!Array.isArray(embedding)) {
123
160
  throw new Error("Unexpected embedding response structure");
124
161
  }
@@ -0,0 +1,25 @@
1
+ export interface NavigablePage {
2
+ id: string;
3
+ name: string;
4
+ path: string;
5
+ keywords?: string[];
6
+ description?: string;
7
+ }
8
+ export declare class PageRegistry {
9
+ private pages;
10
+ private pathToId;
11
+ private keywordIndex;
12
+ constructor(pages?: NavigablePage[]);
13
+ loadPages(pages: NavigablePage[]): void;
14
+ addPage(page: NavigablePage): void;
15
+ getPageById(id: string): NavigablePage | undefined;
16
+ getPageByPath(path: string): NavigablePage | undefined;
17
+ findPageByKeyword(keyword: string): NavigablePage | undefined;
18
+ getAllPages(): NavigablePage[];
19
+ getNavigationActions(): string[];
20
+ isPageNavigationAction(action: string): boolean;
21
+ getPageIdFromAction(action: string): string | null;
22
+ clear(): void;
23
+ get size(): number;
24
+ }
25
+ //# sourceMappingURL=page-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-registry.d.ts","sourceRoot":"","sources":["../../src/services/page-registry.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAyC;IACtD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,YAAY,CAAuC;gBAE/C,KAAK,GAAE,aAAa,EAAO;IAOvC,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI;IAavC,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAuBlC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAOlD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAQtD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAwB7D,WAAW,IAAI,aAAa,EAAE;IAO9B,oBAAoB,IAAI,MAAM,EAAE;IAOhC,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAW/C,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAWlD,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,82 @@
1
+ export class PageRegistry {
2
+ constructor(pages = []) {
3
+ this.pages = new Map();
4
+ this.pathToId = new Map();
5
+ this.keywordIndex = new Map();
6
+ this.loadPages(pages);
7
+ }
8
+ loadPages(pages) {
9
+ this.pages.clear();
10
+ this.pathToId.clear();
11
+ this.keywordIndex.clear();
12
+ for (const page of pages) {
13
+ this.addPage(page);
14
+ }
15
+ }
16
+ addPage(page) {
17
+ this.pages.set(page.id, page);
18
+ this.pathToId.set(page.path, page.id);
19
+ const keywords = page.keywords || [];
20
+ const allKeywords = [
21
+ page.name.toLowerCase(),
22
+ page.id.toLowerCase(),
23
+ ...keywords.map((k) => k.toLowerCase()),
24
+ ];
25
+ for (const keyword of allKeywords) {
26
+ if (!this.keywordIndex.has(keyword)) {
27
+ this.keywordIndex.set(keyword, new Set());
28
+ }
29
+ this.keywordIndex.get(keyword).add(page.id);
30
+ }
31
+ }
32
+ getPageById(id) {
33
+ return this.pages.get(id);
34
+ }
35
+ getPageByPath(path) {
36
+ const id = this.pathToId.get(path);
37
+ return id ? this.pages.get(id) : undefined;
38
+ }
39
+ findPageByKeyword(keyword) {
40
+ const normalized = keyword.toLowerCase().trim();
41
+ const pageIds = this.keywordIndex.get(normalized);
42
+ if (pageIds && pageIds.size > 0) {
43
+ const firstId = Array.from(pageIds)[0];
44
+ return this.pages.get(firstId);
45
+ }
46
+ for (const [key, ids] of this.keywordIndex.entries()) {
47
+ if (key.includes(normalized) || normalized.includes(key)) {
48
+ const firstId = Array.from(ids)[0];
49
+ return this.pages.get(firstId);
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ getAllPages() {
55
+ return Array.from(this.pages.values());
56
+ }
57
+ getNavigationActions() {
58
+ return Array.from(this.pages.keys()).map((id) => `navigate_${id}`);
59
+ }
60
+ isPageNavigationAction(action) {
61
+ if (!action.startsWith("navigate_")) {
62
+ return false;
63
+ }
64
+ const pageId = action.replace(/^navigate_/, "");
65
+ return this.pages.has(pageId);
66
+ }
67
+ getPageIdFromAction(action) {
68
+ if (!action.startsWith("navigate_")) {
69
+ return null;
70
+ }
71
+ const pageId = action.replace(/^navigate_/, "");
72
+ return this.pages.has(pageId) ? pageId : null;
73
+ }
74
+ clear() {
75
+ this.pages.clear();
76
+ this.pathToId.clear();
77
+ this.keywordIndex.clear();
78
+ }
79
+ get size() {
80
+ return this.pages.size;
81
+ }
82
+ }
@@ -0,0 +1,9 @@
1
+ import type { NavigablePage } from "./page-registry.js";
2
+ export interface PageXMLConfig {
3
+ source: string | URL;
4
+ type: "url" | "string";
5
+ }
6
+ export declare function parseNavigationXML(xmlString: string): NavigablePage[];
7
+ export declare function fetchNavigationXML(url: string): Promise<NavigablePage[]>;
8
+ export declare function loadNavigationPages(config: PageXMLConfig): Promise<NavigablePage[]>;
9
+ //# sourceMappingURL=xml-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml-parser.d.ts","sourceRoot":"","sources":["../../src/services/xml-parser.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;CACxB;AAKD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE,CAsDrE;AAKD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,EAAE,CAAC,CAgB1B;AAKD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,EAAE,CAAC,CAM1B"}