@fiodos/web-core 0.1.14 → 0.1.17

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.
@@ -6,7 +6,7 @@
6
6
  * way. The only platform dependency is `fetch` + `AbortController` (native in
7
7
  * the browser). No client source code is ever sent — only the manifest payload.
8
8
  */
9
- import { type AgentBackendClient, type AppManifest } from '@fiodos/core';
9
+ import { type ActionRegistries, type AgentBackendClient, type AppManifest } from '@fiodos/core';
10
10
  export interface FiodosBackendClientOptions {
11
11
  baseUrl: string;
12
12
  /** Client API key sent as x-api-key. */
@@ -19,8 +19,15 @@ export interface FiodosBackendClientOptions {
19
19
  ttsTimeoutMs?: number;
20
20
  }
21
21
  export declare function createFiodosBackendClient(options: FiodosBackendClientOptions): AgentBackendClient;
22
- /** Serializes the manifest payloads for a turn request (used by AgentController). */
23
- export declare function buildManifestPayload(manifest: AppManifest): {
22
+ /**
23
+ * Serializes the manifest payloads for a turn request (used by AgentController).
24
+ *
25
+ * When the live registries are provided, actions with no registered handler
26
+ * are dropped from the payload (runtime "wired or nonexistent"): the LLM never
27
+ * hears about an action this mounted app cannot execute, so it can never
28
+ * promise it. Fail-open without registries.
29
+ */
30
+ export declare function buildManifestPayload(manifest: AppManifest, registries?: ActionRegistries | null): {
24
31
  appFlow?: string | undefined;
25
32
  appType?: string | undefined;
26
33
  manifestVersion: string;
@@ -145,6 +145,10 @@ function createFiodosBackendClient(options) {
145
145
  intent: request.lastStep.intent,
146
146
  ...(request.lastStep.label ? { label: request.lastStep.label } : {}),
147
147
  success: request.lastStep.success,
148
+ // Bounded result payload of the executed step (product search
149
+ // results…): the backend renders it so the model can read real
150
+ // ids for the NEXT step's parameters.
151
+ ...(request.lastStep.data ? { data: request.lastStep.data } : {}),
148
152
  };
149
153
  }
150
154
  }
@@ -202,15 +206,23 @@ function createFiodosBackendClient(options) {
202
206
  },
203
207
  };
204
208
  }
205
- /** Serializes the manifest payloads for a turn request (used by AgentController). */
206
- function buildManifestPayload(manifest) {
209
+ /**
210
+ * Serializes the manifest payloads for a turn request (used by AgentController).
211
+ *
212
+ * When the live registries are provided, actions with no registered handler
213
+ * are dropped from the payload (runtime "wired or nonexistent"): the LLM never
214
+ * hears about an action this mounted app cannot execute, so it can never
215
+ * promise it. Fail-open without registries.
216
+ */
217
+ function buildManifestPayload(manifest, registries) {
218
+ const effective = (0, core_1.withExecutableActions)(manifest, registries);
207
219
  return {
208
- manifestVersion: manifest.version,
209
- manifestRoutes: (0, core_1.manifestRoutesForBackend)(manifest),
210
- manifestActions: (0, core_1.manifestActionsForBackend)(manifest),
211
- appName: manifest.appName,
212
- appDescription: manifest.appDescription,
213
- ...(manifest.appType ? { appType: manifest.appType } : {}),
214
- ...(manifest.appFlow ? { appFlow: manifest.appFlow } : {}),
220
+ manifestVersion: effective.version,
221
+ manifestRoutes: (0, core_1.manifestRoutesForBackend)(effective),
222
+ manifestActions: (0, core_1.manifestActionsForBackend)(effective),
223
+ appName: effective.appName,
224
+ appDescription: effective.appDescription,
225
+ ...(effective.appType ? { appType: effective.appType } : {}),
226
+ ...(effective.appFlow ? { appFlow: effective.appFlow } : {}),
215
227
  };
216
228
  }
@@ -44,6 +44,13 @@ export interface FiodosWebAgentConfig {
44
44
  manifest: AppManifest;
45
45
  /** Agent locale (reply language, message catalogs). MANDATORY. */
46
46
  locale: string;
47
+ /**
48
+ * LIVE locale resolver. When set, the orb re-reads it periodically so the
49
+ * thought bubble follows the app's ACTIVE language (host language switcher,
50
+ * `<html lang>`, i18n runtime) without remounting. Falls back to `locale`
51
+ * whenever it returns nothing.
52
+ */
53
+ getLocale?: () => string | null | undefined;
47
54
  /** Speech recognition locale, e.g. 'en-US', 'es-ES'. MANDATORY. */
48
55
  sttLocale: string;
49
56
  /** Device-TTS fallback locale. Defaults to sttLocale. */
@@ -716,9 +716,22 @@ class AgentController {
716
716
  /* keep the assistant's guiding reply; never speak a refusal. */
717
717
  }
718
718
  else if (r.message) {
719
+ // FEEDBACK GUARANTEE: the handler's outcome message (success,
720
+ // idempotent or failure) is the ground truth that the action ran;
721
+ // speak it instead of the LLM's optimistic reply.
719
722
  outReply = r.message;
720
723
  outAudio = null;
721
724
  }
725
+ else if (!r.success) {
726
+ // Same guarantee for a handler that failed WITHOUT setting `message`
727
+ // (e.g. a declarative Shopify/OData `execution` call that hit a
728
+ // network/HTTP/template error — apiActions.ts never throws, so
729
+ // runManifestAction's catch never runs either). Without this, the
730
+ // LLM's pre-action narration ("Adding X to your cart…") stays on
731
+ // screen as if it had happened — a silent false success.
732
+ outReply = this.messages.actionFailed;
733
+ outAudio = null;
734
+ }
722
735
  // Chain signal: only a cleanly EXECUTED, non-recoverable step may advance,
723
736
  // and only when the backend asked to continue (task_status=in_progress).
724
737
  if (turn.action &&
@@ -726,11 +739,16 @@ class AgentController {
726
739
  r.success &&
727
740
  !r.recoverable) {
728
741
  const intent = turn.action.intent ?? '';
742
+ // Feed the step's RESULT DATA back to the next chain turn (bounded):
743
+ // it is how the model reads real technical values (variant ids, line
744
+ // numbers) out of a data action instead of inventing them.
745
+ const chainData = (0, core_1.trimChainStepData)(r.data);
729
746
  lastStep = {
730
747
  type: turn.action.type,
731
748
  intent,
732
749
  label: turn.action.type === 'navigate' ? this.routeLabel(intent) : this.actionLabel(intent),
733
750
  success: true,
751
+ ...(chainData ? { data: chainData } : {}),
734
752
  };
735
753
  actionKey = `${turn.action.type}:${intent}`;
736
754
  canContinue = turn.taskStatus === 'in_progress';
@@ -830,7 +848,7 @@ class AgentController {
830
848
  lastStep,
831
849
  platformCapabilities: this.platformCapabilities(),
832
850
  sessionContext: this.sessionContext(),
833
- ...(0, backendClient_1.buildManifestPayload)(this.config.manifest),
851
+ ...(0, backendClient_1.buildManifestPayload)(this.config.manifest, this.config.registries),
834
852
  }, { signal: controller.signal });
835
853
  oc = await this.applyOutcome(turn, routeNow, snap?.context ?? {}, state.userMessage);
836
854
  }
@@ -989,7 +1007,7 @@ class AgentController {
989
1007
  sessionId: this.sessionId,
990
1008
  platformCapabilities: this.platformCapabilities(),
991
1009
  sessionContext: this.sessionContext(),
992
- ...(0, backendClient_1.buildManifestPayload)(this.config.manifest),
1010
+ ...(0, backendClient_1.buildManifestPayload)(this.config.manifest, this.config.registries),
993
1011
  }, { signal: controller.signal });
994
1012
  outcome = await this.applyOutcome(turn, currentRoute, snapshot?.context ?? {}, text);
995
1013
  }
@@ -32,6 +32,11 @@ function browserLocale() {
32
32
  const lang = typeof navigator !== 'undefined' && navigator.language ? navigator.language : 'en-US';
33
33
  return { locale: lang.split('-')[0] || 'en', sttLocale: lang };
34
34
  }
35
+ function documentLocale() {
36
+ return typeof document !== 'undefined'
37
+ ? document.documentElement.lang?.split(/[-_]/)[0]?.trim() || ''
38
+ : '';
39
+ }
35
40
  function resolveAgentLocale(options) {
36
41
  const fromApp = options.getLocale?.()?.trim();
37
42
  if (fromApp)
@@ -41,12 +46,16 @@ function resolveAgentLocale(options) {
41
46
  if (options.localeDetection === 'browser')
42
47
  return browserLocale().locale;
43
48
  if (options.localeDetection === 'document') {
44
- const docLang = typeof document !== 'undefined'
45
- ? document.documentElement.lang?.split(/[-_]/)[0]?.trim()
46
- : '';
49
+ const docLang = documentLocale();
47
50
  if (docLang)
48
51
  return docLang;
49
52
  }
53
+ // Default (nothing configured): follow the HOST PAGE's language when the
54
+ // page declares one (<html lang>), so the orb bubble speaks the language the
55
+ // end user is actually browsing in. English only as the last resort.
56
+ const docLang = documentLocale();
57
+ if (docLang)
58
+ return docLang;
50
59
  return 'en';
51
60
  }
52
61
  /** Build the controller + adapters from a known manifest (shared by both paths). */
@@ -67,6 +76,10 @@ function assemble(options, manifest, baseUrl) {
67
76
  const config = {
68
77
  manifest,
69
78
  locale,
79
+ // Live resolver: re-runs the same resolution chain (app getLocale → static
80
+ // locale → detection) on every read, so the bubble follows the host app's
81
+ // language switcher / <html lang> without remounting the agent.
82
+ getLocale: () => resolveAgentLocale(options),
70
83
  sttLocale,
71
84
  navigation: (0, webNavigationAdapter_1.createWebNavigationAdapter)({
72
85
  navigate: options.navigate ?? defaultNavigate,
@@ -27,7 +27,7 @@ import { createScreenContextStore } from '../context/screenContextStore';
27
27
  import { connectDynamicsFormContext, dynamicsFormSnapshot, normalizeDynamicsId } from './dynamics';
28
28
  import { buildApiActionRegistries, validateManifest } from '@fiodos/core';
29
29
  declare const api: {
30
- readonly version: "0.1.14";
30
+ readonly version: "0.1.17";
31
31
  readonly createFiodosAgent: typeof createFiodosAgent;
32
32
  readonly mountOrb: typeof mountOrb;
33
33
  readonly AgentController: typeof AgentController;
@@ -213,12 +213,13 @@ function mountOrb(controller, options = {}) {
213
213
  const prompt = resolveCurrentBubblePrompt();
214
214
  openTextPanel(prompt
215
215
  ? (0, core_1.bubblePromptToAffirmative)(prompt, {
216
- locale: controller.config.locale,
216
+ locale: currentBubbleLocale(),
217
217
  messages: controller.messages,
218
218
  })
219
219
  : undefined);
220
220
  return;
221
221
  }
222
+ userTriedVoice = true;
222
223
  void controller.toggleListening();
223
224
  });
224
225
  thoughtBubbleEl.addEventListener('keydown', (event) => {
@@ -397,6 +398,13 @@ function mountOrb(controller, options = {}) {
397
398
  // SAME "active/listening" visual state and reveals the keyboard chip — the
398
399
  // user is never blocked, text is always an open avenue (Change 1).
399
400
  let micFallbackActive = false;
401
+ // True once the user actually ATTEMPTED voice (tapped the orb/bubble to
402
+ // listen). The voiceBlocked→keyboard fallback in render() must only fire for
403
+ // a mic that failed MID-ATTEMPT: the upfront Permissions-API detection also
404
+ // sets voiceBlocked at mount (e.g. the site has the mic permission denied),
405
+ // and without this guard the conversation panel would pop open on page load
406
+ // with zero interaction.
407
+ let userTriedVoice = false;
400
408
  const bubble = document.createElement('div');
401
409
  bubble.className = 'fyodos-bubble';
402
410
  bubble.style.display = 'none';
@@ -465,6 +473,12 @@ function mountOrb(controller, options = {}) {
465
473
  signature.endsWith(renderedSignature);
466
474
  const fromBottom = exchangeScroll.scrollHeight - exchangeScroll.scrollTop;
467
475
  renderedSignature = signature;
476
+ // Theme colors INLINE at node creation: the embed lives inside arbitrary
477
+ // host pages (e.g. Shopify themes) whose CSS sets `color` on everything,
478
+ // so a freshly-rendered message must never wait for the next
479
+ // applyBubbleTheme() pass to become readable (it would inherit the host's
480
+ // ink — black on our dark panel — until the next config poll).
481
+ const t = { ...DEFAULT_MODAL_THEME, ...(modalTheme ?? {}) };
468
482
  exchangeList.replaceChildren();
469
483
  for (const ex of exchanges) {
470
484
  const block = document.createElement('div');
@@ -472,11 +486,13 @@ function mountOrb(controller, options = {}) {
472
486
  const user = document.createElement('div');
473
487
  user.className = 'fyodos-bubble-user';
474
488
  user.textContent = ex.userText;
489
+ user.style.color = t.bodyColor;
475
490
  block.append(user);
476
491
  if (ex.replyText) {
477
492
  const reply = document.createElement('div');
478
493
  reply.className = 'fyodos-bubble-reply';
479
494
  reply.textContent = ex.replyText;
495
+ reply.style.color = t.titleColor;
480
496
  block.append(reply);
481
497
  }
482
498
  exchangeList.append(block);
@@ -583,7 +599,8 @@ function mountOrb(controller, options = {}) {
583
599
  if (active) {
584
600
  micBtn.style.background = accent;
585
601
  micBtn.style.border = 'none';
586
- micBtn.style.color = '#000';
602
+ // Ink readable on the (customizable) accent — dark accents need light ink.
603
+ micBtn.style.color = (0, core_1.fiodosInputInkColor)(accent);
587
604
  micBtn.style.borderRadius = sendRadius;
588
605
  }
589
606
  else {
@@ -682,6 +699,9 @@ function mountOrb(controller, options = {}) {
682
699
  textInput.style.color = (0, core_1.fiodosInputInkColor)(inputBg);
683
700
  textInput.style.setProperty('--fyodos-input-placeholder', (0, core_1.fiodosInputMutedInkColor)(inputBg));
684
701
  sendBtn.style.background = accent;
702
+ // The glyph (↑ / ◼) must stay readable on ANY published accent: dark ink
703
+ // on light accents, light ink on dark ones (never the hardcoded black).
704
+ sendBtn.style.color = (0, core_1.fiodosInputInkColor)(accent);
685
705
  sendBtn.style.borderRadius = `${modalTheme?.sendButtonRadius ?? 10}px`;
686
706
  syncMicButton();
687
707
  busyRow.querySelectorAll('span').forEach((s) => {
@@ -690,13 +710,44 @@ function mountOrb(controller, options = {}) {
690
710
  // Live-activity line: white (reply color), italic — not the faint gray.
691
711
  busyStep.style.color = t.titleColor;
692
712
  }
713
+ // The panel's open state survives FULL PAGE LOADS within the tab session:
714
+ // on multi-page hosts (Shopify storefronts…) every agent navigation reloads
715
+ // the document, and without this the conversation panel closed mid-dialogue
716
+ // on every step. sessionStorage is per-tab and cleared when the tab closes,
717
+ // so a fresh visit never starts with the panel open.
718
+ const panelOpenKey = `fyodos:panel-open:${controller.config.manifest.appId}`;
719
+ function persistPanelOpen(open) {
720
+ try {
721
+ if (open)
722
+ window.sessionStorage.setItem(panelOpenKey, '1');
723
+ else
724
+ window.sessionStorage.removeItem(panelOpenKey);
725
+ }
726
+ catch {
727
+ /* storage unavailable (private mode) — persistence is best-effort */
728
+ }
729
+ }
730
+ function wasPanelOpen() {
731
+ try {
732
+ return window.sessionStorage.getItem(panelOpenKey) === '1';
733
+ }
734
+ catch {
735
+ return false;
736
+ }
737
+ }
693
738
  function setBubble(open) {
694
739
  bubbleOpen = open;
695
740
  bubble.style.display = open ? 'flex' : 'none';
741
+ persistPanelOpen(open);
696
742
  if (open) {
697
743
  applyBubbleTheme();
698
744
  positionBubble();
699
745
  textInput.focus();
746
+ // Paint the CURRENT conversation immediately: exchanges are only
747
+ // rendered inside render() while the panel is open, so without this
748
+ // the restored history stayed invisible (an apparently stuck panel)
749
+ // until the next controller event — i.e. until the user typed.
750
+ render(controller.getState());
700
751
  }
701
752
  else {
702
753
  bubbleDictation.stop();
@@ -710,6 +761,13 @@ function mountOrb(controller, options = {}) {
710
761
  setBubbleExpanded(false);
711
762
  }
712
763
  syncChip(controller.getState());
764
+ // Re-resolve the thought bubble NOW: closing the panel must bring the
765
+ // screen-aware CTA back immediately. Nothing else is guaranteed to run
766
+ // right after close (controller state does not change on close, the route
767
+ // poll only fires on changes, the config poll every ~12s) — without this
768
+ // the orb sat "naked" until some unrelated event. Opening hides it (the
769
+ // resolver returns null while the panel is open), so one call covers both.
770
+ syncThoughtBubble(controller.getState());
713
771
  }
714
772
  function submitText() {
715
773
  // While a turn is in flight the button is a STOP button; never start a new
@@ -774,8 +832,22 @@ function mountOrb(controller, options = {}) {
774
832
  applyDeployment();
775
833
  }
776
834
  // ── Thought bubble (screen-aware CTA beside the orb) ─────────────────────────
835
+ // LIVE locale: re-read the host app's active language on every resolution so
836
+ // the bubble text (analyzer `bubblePrompts` translations) follows the app's
837
+ // language switcher without remounting. Falls back to the mount-time locale.
838
+ function currentBubbleLocale() {
839
+ try {
840
+ const live = controller.config.getLocale?.();
841
+ if (typeof live === 'string' && live.trim())
842
+ return live.trim();
843
+ }
844
+ catch {
845
+ /* live locale read is best-effort */
846
+ }
847
+ return controller.config.locale;
848
+ }
777
849
  function resolveCurrentBubblePrompt() {
778
- return (0, core_1.resolveBubblePrompt)((0, core_1.matchRouteIntent)(currentRoute, controller.config.manifest.routes), controller.messages, { locale: controller.config.locale, actions: controller.config.manifest.actions });
850
+ return (0, core_1.resolveBubblePrompt)((0, core_1.matchRouteIntent)(currentRoute, controller.config.manifest.routes), controller.messages, { locale: currentBubbleLocale(), actions: controller.config.manifest.actions });
779
851
  }
780
852
  // Render the CTA on ONE line at its NATURAL size. When the phrase is too long
781
853
  // to fit the max width, scroll it (marquee, right-to-left, looping) instead of
@@ -990,6 +1062,7 @@ function mountOrb(controller, options = {}) {
990
1062
  // denied/blocked, in which case we skip voice and go straight to the keyboard.
991
1063
  if (controller.voiceAvailable && voiceInputEnabled) {
992
1064
  micFallbackActive = false;
1065
+ userTriedVoice = true;
993
1066
  void controller.toggleListening();
994
1067
  return;
995
1068
  }
@@ -1001,6 +1074,7 @@ function mountOrb(controller, options = {}) {
1001
1074
  openTextPanel();
1002
1075
  return;
1003
1076
  }
1077
+ userTriedVoice = true;
1004
1078
  void controller.toggleListening();
1005
1079
  };
1006
1080
  sendBtn.onclick = () => {
@@ -1039,11 +1113,15 @@ function mountOrb(controller, options = {}) {
1039
1113
  // Track phase transitions to fire haptics: start/stop listening + response.
1040
1114
  let lastPhase = null;
1041
1115
  function render(state) {
1042
- // Mic just became unusable mid-attempt (no Permissions API): fall back to the
1043
- // keyboard so the user always has a working channel.
1116
+ // Mic just became unusable MID-ATTEMPT (no Permissions API): fall back to
1117
+ // the keyboard so the user always has a working channel. Guarded by
1118
+ // userTriedVoice: the upfront permission detection also sets voiceBlocked
1119
+ // right at mount (site-level mic denial), and that must NEVER auto-open
1120
+ // the conversation panel on page load — the user did nothing yet; their
1121
+ // first orb tap will route to the keyboard via the voiceAvailable check.
1044
1122
  if (state.voiceBlocked && !lastVoiceBlocked) {
1045
1123
  lastVoiceBlocked = true;
1046
- if (enableTextInput && !bubbleOpen && !state.isBusy) {
1124
+ if (userTriedVoice && enableTextInput && !bubbleOpen && !state.isBusy) {
1047
1125
  openTextPanel();
1048
1126
  return;
1049
1127
  }
@@ -1112,9 +1190,18 @@ function mountOrb(controller, options = {}) {
1112
1190
  }
1113
1191
  const unsubscribe = controller.subscribe(render);
1114
1192
  render(controller.getState());
1115
- // Track the current route so the thought bubble re-resolves its CTA on
1116
- // navigation. No universal navigation event exists, so poll + listen to
1117
- // popstate / visibility for snappier updates.
1193
+ // Re-open the conversation panel after a full page load when it was open
1194
+ // before (MPA hosts reload on every navigation): the dialogue visually
1195
+ // continues where it was the persisted conversation restores async in the
1196
+ // controller and repaints via emit → render once loaded.
1197
+ if (enableTextInput && wasPanelOpen()) {
1198
+ setBubble(true);
1199
+ }
1200
+ // Track the current route AND the app's live locale so the thought bubble
1201
+ // re-resolves its CTA on navigation and on language switches. No universal
1202
+ // navigation/i18n event exists, so poll + listen to popstate / visibility
1203
+ // for snappier updates.
1204
+ let lastBubbleLocale = currentBubbleLocale();
1118
1205
  const readRoute = () => {
1119
1206
  let next = null;
1120
1207
  try {
@@ -1123,8 +1210,10 @@ function mountOrb(controller, options = {}) {
1123
1210
  catch {
1124
1211
  /* navigation read is best-effort */
1125
1212
  }
1126
- if (next !== currentRoute) {
1213
+ const nextLocale = currentBubbleLocale();
1214
+ if (next !== currentRoute || nextLocale !== lastBubbleLocale) {
1127
1215
  currentRoute = next;
1216
+ lastBubbleLocale = nextLocale;
1128
1217
  syncThoughtBubble(controller.getState());
1129
1218
  }
1130
1219
  };
@@ -79,13 +79,14 @@ function buildDoubleBorder(opts) {
79
79
  return { root, fill };
80
80
  }
81
81
  /**
82
- * Neutral Fiodos brand mark (idle, no logo). Breathes very gently on web — a
82
+ * Fiodos brand mark (idle, no logo). Breathes very gently on web — a
83
83
  * direct port of the landing hero orbs' waveform easing (see
84
84
  * `fiodosOrbMarkIdleHalfHeight` in @fiodos/core) — deliberately subtler than
85
85
  * and distinct from the volume-reactive waveform shown while listening.
86
+ * Tinted with the same "audio color" (waveformColor) as that waveform.
86
87
  */
87
- function buildOrbMark(orbSize) {
88
- const { bars, color, radius } = (0, core_1.resolveFiodosOrbMarkAnimatedBars)(orbSize);
88
+ function buildOrbMark(orbSize, markColor) {
89
+ const { bars, color, radius } = (0, core_1.resolveFiodosOrbMarkAnimatedBars)(orbSize, markColor);
89
90
  const svg = svgEl('svg', {
90
91
  width: orbSize,
91
92
  height: orbSize,
@@ -489,7 +490,7 @@ function createOrbVisual(initial) {
489
490
  return;
490
491
  }
491
492
  if (state === 'idle') {
492
- markAnim = buildOrbMark(size);
493
+ markAnim = buildOrbMark(size, appearance.waveformColor);
493
494
  fillEl.appendChild(markAnim.el);
494
495
  markAnim.start();
495
496
  }
@@ -5,5 +5,5 @@
5
5
  * Auto-generated by scripts/sync-sdk-version.mjs from package.json. Do not edit
6
6
  * by hand — change package.json `version` and re-run the sync/release script.
7
7
  */
8
- export declare const SDK_VERSION = "0.1.14";
8
+ export declare const SDK_VERSION = "0.1.17";
9
9
  export declare const SDK_PACKAGE = "@fiodos/web-core";
@@ -8,5 +8,5 @@ exports.SDK_PACKAGE = exports.SDK_VERSION = void 0;
8
8
  * Auto-generated by scripts/sync-sdk-version.mjs from package.json. Do not edit
9
9
  * by hand — change package.json `version` and re-run the sync/release script.
10
10
  */
11
- exports.SDK_VERSION = '0.1.14';
11
+ exports.SDK_VERSION = '0.1.17';
12
12
  exports.SDK_PACKAGE = '@fiodos/web-core';