Help
RSS
API
Feed
Maltego
Contact
Domain > agx.com
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2020-01-06
159.8.40.55
(
ClassC
)
2026-01-29
176.126.122.206
(
ClassC
)
Port 80
HTTP/1.1 301 Moved PermanentlyServer: openresty/1.27.1.2Date: Thu, 29 Jan 2026 15:06:50 GMTContent-Type: text/htmlContent-Length: 175Connection: keep-aliveLocation: https://agx.com/ html>head>title>301 Moved Permanently/title>/head>body>center>h1>301 Moved Permanently/h1>/center>hr>center>openresty/1.27.1.2/center>/body>/html>
Port 443
HTTP/1.1 200 OKServer: openresty/1.27.1.2Date: Thu, 29 Jan 2026 15:06:50 GMTContent-Type: text/html; charsetUTF-8Transfer-Encoding: chunkedConnection: keep-aliveVary: Accept-EncodingLink: https://agx.com/wp-json/>; relhttps://api.w.org/Strict-Transport-Security: max-age31536000; includeSubDomains; preloadReferrer-Policy: strict-origin-when-cross-originX-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINPermissions-Policy: geolocation(), microphone(), camera(), payment()Content-Security-Policy-Report-Only: default-src self; img-src self data: https:; font-src self data: https:; style-src self unsafe-inline https:; script-src self unsafe-inline unsafe-eval https:; connect-src self https://agx.com https://moltbot.agx.com wss://agx.com wss://moltbot.agx.com; frame-ancestors self; base-uri self; form-action self !DOCTYPE html>html langen-US>head> meta charsetUTF-8 /> meta nameviewport contentwidthdevice-width, initial-scale1 />meta namerobots contentmax-image-preview:large /> style>img:is(sizesauto i, sizes^auto, i) { contain-intrinsic-size: 3000px 1500px }/style> title>AGX/title>link relalternate typeapplication/rss+xml titleAGX » Feed hrefhttps://agx.com/feed/ />link relalternate typeapplication/rss+xml titleAGX » Comments Feed hrefhttps://agx.com/comments/feed/ />script>window._wpemojiSettings {baseUrl:https:\/\/s.w.org\/images\/core\/emoji\/16.0.1\/72x72\/,ext:.png,svgUrl:https:\/\/s.w.org\/images\/core\/emoji\/16.0.1\/svg\/,svgExt:.svg,source:{concatemoji:https:\/\/agx.com\/wp-includes\/js\/wp-emoji-release.min.js?ver6.8.3}};/*! This file is auto-generated */!function(s,n){var o,i,e;function c(e){try{var t{supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function p(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var tnew Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data),a(e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0),new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data));return t.every(function(e,t){return eat})}function u(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);for(var ne.getImageData(16,16,1,1),a0;an.data.length;a++)if(0!n.dataa)return!1;return!0}function f(e,t,n,a){switch(t){caseflag:return n(e,\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f,\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f)?!1:!n(e,\ud83c\udde8\ud83c\uddf6,\ud83c\udde8\u200b\ud83c\uddf6)&&!n(e,\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f,\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f);caseemoji:return!a(e,\ud83e\udedf)}return!1}function g(e,t,n,a){var rundefined!typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):s.createElement(canvas),or.getContext(2d,{willReadFrequently:!0}),i(o.textBaselinetop,o.font600 32px Arial,{});return e.forEach(function(e){iet(o,e,n,a)}),i}function t(e){var ts.createElement(script);t.srce,t.defer!0,s.head.appendChild(t)}undefined!typeof Promise&&(owpEmojiSettingsSupports,iflag,emoji,n.supports{everything:!0,everythingExceptFlag:!0},enew Promise(function(e){s.addEventListener(DOMContentLoaded,e,{once:!0})}),new Promise(function(t){var nfunction(){try{var eJSON.parse(sessionStorage.getItem(o));if(objecttypeof e&&numbertypeof e.timestamp&&(new Date).valueOf()e.timestamp+604800&&objecttypeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(undefined!typeof Worker&&undefined!typeof OffscreenCanvas&&undefined!typeof URL&&URL.createObjectURL&&undefined!typeof Blob)try{var epostMessage(+g.toString()+(+JSON.stringify(i),f.toString(),p.toString(),u.toString().join(,)+));,anew Blob(e,{type:text/javascript}),rnew Worker(URL.createObjectURL(a),{name:wpTestEmojiSupports});return void(r.onmessagefunction(e){c(ne.data),r.terminate(),t(n)})}catch(e){}c(ng(i,f,p,u))}t(n)}).then(function(e){for(var t in e)n.supportstet,n.supports.everythingn.supports.everything&&n.supportst,flag!t&&(n.supports.everythingExceptFlagn.supports.everythingExceptFlag&&n.supportst);n.supports.everythingExceptFlagn.supports.everythingExceptFlag&&!n.supports.flag,n.DOMReady!1,n.readyCallbackfunction(){n.DOMReady!0}}).then(function(){return e}).then(function(){var e;n.supports.everything||(n.readyCallback(),(en.source||{}).concatemoji?t(e.concatemoji):e.wpemoji&&e.twemoji&&(t(e.twemoji),t(e.wpemoji)))}))}((window,document),window._wpemojiSettings);/script>style idwp-block-site-title-inline-css>.wp-block-site-title{box-sizing:border-box}.wp-block-site-title :where(a){color:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;text-decoration:inherit}/style>style idwp-block-page-list-inline-css>.wp-block-navigation .wp-block-page-list{align-items:var(--navigation-layout-align,initial);background-color:inherit;display:flex;flex-direction:var(--navigation-layout-direction,initial);flex-wrap:var(--navigation-layout-wrap,wrap);justify-content:var(--navigation-layout-justify,initial)}.wp-block-navigation .wp-block-navigation-item{background-color:inherit}.wp-block-page-list{box-sizing:border-box}/style>link relstylesheet idwp-block-navigation-css hrefhttps://agx.com/wp-includes/blocks/navigation/style.min.css?ver6.8.3 mediaall />style idwp-block-group-inline-css>.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}/style>style idwp-block-heading-inline-css>h1.has-background,h2.has-background,h3.has-background,h4.has-background,h5.has-background,h6.has-background{padding:1.25em 2.375em}h1.has-text-align-leftstyle*writing-mode:where(style*vertical-lr),h1.has-text-align-rightstyle*writing-mode:where(style*vertical-rl),h2.has-text-align-leftstyle*writing-mode:where(style*vertical-lr),h2.has-text-align-rightstyle*writing-mode:where(style*vertical-rl),h3.has-text-align-leftstyle*writing-mode:where(style*vertical-lr),h3.has-text-align-rightstyle*writing-mode:where(style*vertical-rl),h4.has-text-align-leftstyle*writing-mode:where(style*vertical-lr),h4.has-text-align-rightstyle*writing-mode:where(style*vertical-rl),h5.has-text-align-leftstyle*writing-mode:where(style*vertical-lr),h5.has-text-align-rightstyle*writing-mode:where(style*vertical-rl),h6.has-text-align-leftstyle*writing-mode:where(style*vertical-lr),h6.has-text-align-rightstyle*writing-mode:where(style*vertical-rl){rotate:180deg}/style>style idwp-block-post-featured-image-inline-css>.wp-block-post-featured-image{margin-left:0;margin-right:0}.wp-block-post-featured-image a{display:block;height:100%}.wp-block-post-featured-image :where(img){box-sizing:border-box;height:auto;max-width:100%;vertical-align:bottom;width:100%}.wp-block-post-featured-image.alignfull img,.wp-block-post-featured-image.alignwide img{width:100%}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim{background-color:#000;inset:0;position:absolute}.wp-block-post-featured-image{position:relative}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-gradient{background-color:initial}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-0{opacity:0}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-10{opacity:.1}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-20{opacity:.2}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-30{opacity:.3}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-40{opacity:.4}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-50{opacity:.5}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-60{opacity:.6}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-70{opacity:.7}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-80{opacity:.8}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-90{opacity:.9}.wp-block-post-featured-image .wp-block-post-featured-image__overlay.has-background-dim-100{opacity:1}.wp-block-post-featured-image:where(.alignleft,.alignright){width:100%}/style>style idwp-block-post-title-inline-css>.wp-block-post-title{box-sizing:border-box;word-break:break-word}.wp-block-post-title :where(a){display:inline-block;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;text-decoration:inherit}/style>style idwp-block-paragraph-inline-css>.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-leftstyle*writing-mode:vertical-lr,p.has-text-align-rightstyle*writing-mode:vertical-rl{rotate:180deg}/style>style idwp-block-post-content-inline-css>.wp-block-post-content{display:flow-root}/style>style idwp-block-post-date-inline-css>.wp-block-post-date{box-sizing:border-box}/style>style idwp-block-post-template-inline-css>.wp-block-post-template{box-sizing:border-box;list-style:none;margin-bottom:0;margin-top:0;max-width:100%;padding:0}.wp-block-post-template.is-flex-container{display:flex;flex-direction:row;flex-wrap:wrap;gap:1.25em}.wp-block-post-template.is-flex-container>li{margin:0;width:100%}@media (min-width:600px){.wp-block-post-template.is-flex-container.is-flex-container.columns-2>li{width:calc(50% - .625em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-3>li{width:calc(33.33333% - .83333em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-4>li{width:calc(25% - .9375em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-5>li{width:calc(20% - 1em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-6>li{width:calc(16.66667% - 1.04167em)}}@media (max-width:600px){.wp-block-post-template-is-layout-grid.wp-block-post-template-is-layout-grid.wp-block-post-template-is-layout-grid.wp-block-post-template-is-layout-grid{grid-template-columns:1fr}}.wp-block-post-template-is-layout-constrained>li>.alignright,.wp-block-post-template-is-layout-flow>li>.alignright{float:right;margin-inline-end:0;margin-inline-start:2em}.wp-block-post-template-is-layout-constrained>li>.alignleft,.wp-block-post-template-is-layout-flow>li>.alignleft{float:left;margin-inline-end:2em;margin-inline-start:0}.wp-block-post-template-is-layout-constrained>li>.aligncenter,.wp-block-post-template-is-layout-flow>li>.aligncenter{margin-inline-end:auto;margin-inline-start:auto}/style>style idwp-block-query-pagination-inline-css>.wp-block-query-pagination.is-content-justification-space-between>.wp-block-query-pagination-next:last-of-type{margin-inline-start:auto}.wp-block-query-pagination.is-content-justification-space-between>.wp-block-query-pagination-previous:first-child{margin-inline-end:auto}.wp-block-query-pagination .wp-block-query-pagination-previous-arrow{display:inline-block;margin-right:1ch}.wp-block-query-pagination .wp-block-query-pagination-previous-arrow:not(.is-arrow-chevron){transform:scaleX(1)}.wp-block-query-pagination .wp-block-query-pagination-next-arrow{display:inline-block;margin-left:1ch}.wp-block-query-pagination .wp-block-query-pagination-next-arrow:not(.is-arrow-chevron){transform:scaleX(1)}.wp-block-query-pagination.aligncenter{justify-content:center}/style>style idwp-block-site-logo-inline-css>.wp-block-site-logo{box-sizing:border-box;line-height:0}.wp-block-site-logo a{display:inline-block;line-height:0}.wp-block-site-logo.is-default-size img{height:auto;width:120px}.wp-block-site-logo img{height:auto;max-width:100%}.wp-block-site-logo a,.wp-block-site-logo img{border-radius:inherit}.wp-block-site-logo.aligncenter{margin-left:auto;margin-right:auto;text-align:center}:root :where(.wp-block-site-logo.is-style-rounded){border-radius:9999px}/style>style idwp-block-site-tagline-inline-css>.wp-block-site-tagline{box-sizing:border-box}/style>style idwp-block-spacer-inline-css>.wp-block-spacer{clear:both}/style>style idwp-block-columns-inline-css>.wp-block-columns{align-items:normal!important;box-sizing:border-box;display:flex;flex-wrap:wrap!important}@media (min-width:782px){.wp-block-columns{flex-wrap:nowrap!important}}.wp-block-columns.are-vertically-aligned-top{align-items:flex-start}.wp-block-columns.are-vertically-aligned-center{align-items:center}.wp-block-columns.are-vertically-aligned-bottom{align-items:flex-end}@media (max-width:781px){.wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-column{flex-basis:100%!important}}@media (min-width:782px){.wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-column{flex-basis:0;flex-grow:1}.wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-columnstyle*flex-basis{flex-grow:0}}.wp-block-columns.is-not-stacked-on-mobile{flex-wrap:nowrap!important}.wp-block-columns.is-not-stacked-on-mobile>.wp-block-column{flex-basis:0;flex-grow:1}.wp-block-columns.is-not-stacked-on-mobile>.wp-block-columnstyle*flex-basis{flex-grow:0}:where(.wp-block-columns){margin-bottom:1.75em}:where(.wp-block-columns.has-background){padding:1.25em 2.375em}.wp-block-column{flex-grow:1;min-width:0;overflow-wrap:break-word;word-break:break-word}.wp-block-column.is-vertically-aligned-top{align-self:flex-start}.wp-block-column.is-vertically-aligned-center{align-self:center}.wp-block-column.is-vertically-aligned-bottom{align-self:flex-end}.wp-block-column.is-vertically-aligned-stretch{align-self:stretch}.wp-block-column.is-vertically-aligned-bottom,.wp-block-column.is-vertically-aligned-center,.wp-block-column.is-vertically-aligned-top{width:100%}/style>style idwp-block-navigation-link-inline-css>.wp-block-navigation .wp-block-navigation-item__label{overflow-wrap:break-word}.wp-block-navigation .wp-block-navigation-item__description{display:none}.link-ui-tools{border-top:1px solid #f0f0f0;padding:8px}.link-ui-block-inserter{padding-top:8px}.link-ui-block-inserter__back{margin-left:8px;text-transform:uppercase}/style>style idwp-emoji-styles-inline-css> img.wp-smiley, img.emoji { display: inline !important; border: none !important; box-shadow: none !important; height: 1em !important; width: 1em !important; margin: 0 0.07em !important; vertical-align: -0.1em !important; background: none !important; padding: 0 !important; }/style>style idwp-block-library-inline-css>:root{--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,161;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px;--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color)}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}.has-text-align-center{text-align:center}.has-text-align-left{text-align:left}.has-text-align-right{text-align:right}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where(style*border-top-color){border-top-style:solid}html :where(style*border-right-color){border-right-style:solid}html :where(style*border-bottom-color){border-bottom-style:solid}html :where(style*border-left-color){border-left-style:solid}html :where(style*border-width){border-style:solid}html :where(style*border-top-width){border-top-style:solid}html :where(style*border-right-width){border-right-style:solid}html :where(style*border-bottom-width){border-bottom-style:solid}html :where(style*border-left-width){border-left-style:solid}html :where(imgclass*wp-image-){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}/style>style idglobal-styles-inline-css>:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--base: #FFFFFF;--wp--preset--color--contrast: #111111;--wp--preset--color--accent-1: #FFEE58;--wp--preset--color--accent-2: #F6CFF4;--wp--preset--color--accent-3: #503AA8;--wp--preset--color--accent-4: #686868;--wp--preset--color--accent-5: #FBFAF3;--wp--preset--color--accent-6: color-mix(in srgb, currentColor 20%, transparent);--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 0.875rem;--wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.2rem) * 0.196), 1.125rem);--wp--preset--font-size--large: clamp(1.125rem, 1.125rem + ((1vw - 0.2rem) * 0.392), 1.375rem);--wp--preset--font-size--x-large: clamp(1.75rem, 1.75rem + ((1vw - 0.2rem) * 0.392), 2rem);--wp--preset--font-size--xx-large: clamp(2.15rem, 2.15rem + ((1vw - 0.2rem) * 1.333), 3rem);--wp--preset--font-family--manrope: Manrope, sans-serif;--wp--preset--font-family--fira-code: Fira Code, monospace;--wp--preset--spacing--20: 10px;--wp--preset--spacing--30: 20px;--wp--preset--spacing--40: 30px;--wp--preset--spacing--50: clamp(30px, 5vw, 50px);--wp--preset--spacing--60: clamp(30px, 7vw, 70px);--wp--preset--spacing--70: clamp(50px, 7vw, 90px);--wp--preset--spacing--80: clamp(70px, 10vw, 140px);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1);--wp--preset--shadow--crisp: 6px 6px 0px rgba(0, 0, 0, 1);}:root { --wp--style--global--content-size: 645px;--wp--style--global--wide-size: 1340px; }:where(body) { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 1.2rem; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 1.2rem; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 1.2rem;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 1.2rem;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 1.2rem;}:root :where(.is-layout-grid){gap: 1.2rem;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{background-color: var(--wp--preset--color--base);color: var(--wp--preset--color--contrast);font-family: var(--wp--preset--font-family--manrope);font-size: var(--wp--preset--font-size--large);font-weight: 300;letter-spacing: -0.1px;line-height: 1.4;--wp--style--root--padding-top: 0px;--wp--style--root--padding-right: var(--wp--preset--spacing--50);--wp--style--root--padding-bottom: 0px;--wp--style--root--padding-left: var(--wp--preset--spacing--50);}a:where(:not(.wp-element-button)){color: currentColor;text-decoration: underline;}:root :where(a:where(:not(.wp-element-button)):hover){text-decoration: none;}h1, h2, h3, h4, h5, h6{font-weight: 400;letter-spacing: -0.1px;line-height: 1.125;}h1{font-size: var(--wp--preset--font-size--xx-large);}h2{font-size: var(--wp--preset--font-size--x-large);}h3{font-size: var(--wp--preset--font-size--large);}h4{font-size: var(--wp--preset--font-size--medium);}h5{font-size: var(--wp--preset--font-size--small);letter-spacing: 0.5px;}h6{font-size: var(--wp--preset--font-size--small);font-weight: 700;letter-spacing: 1.4px;text-transform: uppercase;}:root :where(.wp-element-button, .wp-block-button__link){background-color: var(--wp--preset--color--contrast);border-width: 0;color: var(--wp--preset--color--base);font-family: inherit;font-size: var(--wp--preset--font-size--medium);line-height: inherit;padding-top: 1rem;padding-right: 2.25rem;padding-bottom: 1rem;padding-left: 2.25rem;text-decoration: none;}:root :where(.wp-element-button:hover, .wp-block-button__link:hover){background-color: color-mix(in srgb, var(--wp--preset--color--contrast) 85%, transparent);border-color: transparent;color: var(--wp--preset--color--base);}:root :where(.wp-element-button:focus, .wp-block-button__link:focus){outline-color: var(--wp--preset--color--accent-4);outline-offset: 2px;}:root :where(.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption){font-size: var(--wp--preset--font-size--small);line-height: 1.4;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-base-color{color: var(--wp--preset--color--base) !important;}.has-contrast-color{color: var(--wp--preset--color--contrast) !important;}.has-accent-1-color{color: var(--wp--preset--color--accent-1) !important;}.has-accent-2-color{color: var(--wp--preset--color--accent-2) !important;}.has-accent-3-color{color: var(--wp--preset--color--accent-3) !important;}.has-accent-4-color{color: var(--wp--preset--color--accent-4) !important;}.has-accent-5-color{color: var(--wp--preset--color--accent-5) !important;}.has-accent-6-color{color: var(--wp--preset--color--accent-6) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-background-color{background-color: var(--wp--preset--color--base) !important;}.has-contrast-background-color{background-color: var(--wp--preset--color--contrast) !important;}.has-accent-1-background-color{background-color: var(--wp--preset--color--accent-1) !important;}.has-accent-2-background-color{background-color: var(--wp--preset--color--accent-2) !important;}.has-accent-3-background-color{background-color: var(--wp--preset--color--accent-3) !important;}.has-accent-4-background-color{background-color: var(--wp--preset--color--accent-4) !important;}.has-accent-5-background-color{background-color: var(--wp--preset--color--accent-5) !important;}.has-accent-6-background-color{background-color: var(--wp--preset--color--accent-6) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-border-color{border-color: var(--wp--preset--color--base) !important;}.has-contrast-border-color{border-color: var(--wp--preset--color--contrast) !important;}.has-accent-1-border-color{border-color: var(--wp--preset--color--accent-1) !important;}.has-accent-2-border-color{border-color: var(--wp--preset--color--accent-2) !important;}.has-accent-3-border-color{border-color: var(--wp--preset--color--accent-3) !important;}.has-accent-4-border-color{border-color: var(--wp--preset--color--accent-4) !important;}.has-accent-5-border-color{border-color: var(--wp--preset--color--accent-5) !important;}.has-accent-6-border-color{border-color: var(--wp--preset--color--accent-6) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-manrope-font-family{font-family: var(--wp--preset--font-family--manrope) !important;}.has-fira-code-font-family{font-family: var(--wp--preset--font-family--fira-code) !important;}:root :where(.wp-block-columns-is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.wp-block-columns-is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-flow) > *{margin-block-start: var(--wp--preset--spacing--50);margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.wp-block-columns-is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-constrained) > *{margin-block-start: var(--wp--preset--spacing--50);margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-flex){gap: var(--wp--preset--spacing--50);}:root :where(.wp-block-columns-is-layout-grid){gap: var(--wp--preset--spacing--50);}:root :where(.wp-block-post-date){color: var(--wp--preset--color--accent-4);font-size: var(--wp--preset--font-size--small);}:root :where(.wp-block-post-date a:where(:not(.wp-element-button))){color: var(--wp--preset--color--accent-4);text-decoration: none;}:root :where(.wp-block-post-date a:where(:not(.wp-element-button)):hover){text-decoration: underline;}:root :where(.wp-block-post-title a:where(:not(.wp-element-button))){text-decoration: none;}:root :where(.wp-block-post-title a:where(:not(.wp-element-button)):hover){text-decoration: underline;}:root :where(.wp-block-query-pagination){font-size: var(--wp--preset--font-size--medium);font-weight: 500;}:root :where(.wp-block-site-tagline){font-size: var(--wp--preset--font-size--medium);}:root :where(.wp-block-site-title){font-weight: 700;letter-spacing: -.5px;}:root :where(.wp-block-site-title a:where(:not(.wp-element-button))){text-decoration: none;}:root :where(.wp-block-site-title a:where(:not(.wp-element-button)):hover){text-decoration: underline;}:root :where(.wp-block-navigation){font-size: var(--wp--preset--font-size--medium);}:root :where(.wp-block-navigation a:where(:not(.wp-element-button))){text-decoration: none;}:root :where(.wp-block-navigation a:where(:not(.wp-element-button)):hover){text-decoration: underline;}/style>style idcore-block-supports-inline-css>.wp-container-core-navigation-is-layout-fc306653{justify-content:flex-end;}.wp-container-core-group-is-layout-f4c28e8b{flex-wrap:nowrap;gap:var(--wp--preset--spacing--10);justify-content:flex-end;}.wp-container-core-group-is-layout-8165f36a{flex-wrap:nowrap;justify-content:space-between;}.wp-container-core-columns-is-layout-28f84493{flex-wrap:nowrap;}.wp-container-core-navigation-is-layout-fe9cc265{flex-direction:column;align-items:flex-start;}.wp-container-core-group-is-layout-570722b2{gap:var(--wp--preset--spacing--80);justify-content:space-between;align-items:flex-start;}.wp-container-core-group-is-layout-e5edad21{justify-content:space-between;align-items:flex-start;}.wp-container-core-group-is-layout-91e87306{gap:var(--wp--preset--spacing--20);justify-content:space-between;}/style>style idwp-block-template-skip-link-inline-css> .skip-link.screen-reader-text { border: 0; clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute !important; width: 1px; word-wrap: normal !important; } .skip-link.screen-reader-text:focus { background-color: #eee; clip-path: none; color: #444; display: block; font-size: 1em; height: auto; left: 5px; line-height: normal; padding: 15px 23px 14px; text-decoration: none; top: 5px; width: auto; z-index: 100000; }/style>style idagx-chat-inline-css>.agx-chat-wrap{position:fixed;right:16px;bottom:16px;max-width:90vw;min-width:280px;min-height:260px;background:#ffffffcc;border:3px solid #0d6efd;border-radius:12px;box-shadow:0 0 0 1px rgba(13,110,253,0.2),0 12px 28px rgba(2,6,23,0.20);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);display:flex;flex-direction:column;resize:both;overflow:hidden;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;font-size:14px;color:#0f172a;z-index:9999}.agx-chat-header{padding:10px 12px;border-bottom:1px solid #eaeaea;background:#f8fafc;font-weight:600;cursor:move;border-top-left-radius:10px;border-top-right-radius:10px;-webkit-user-select:none;user-select:none;display:flex;align-items:center}.agx-chat-transcript{flex:1 1 auto;min-height:160px;overflow:auto;padding:12px}.agx-chat-form{display:flex;gap:8px;border-top:1px solid #eee;padding:8px}.agx-chat-input{flex:1 1 auto;padding:8px;border:1px solid #ddd;border-radius:6px;color:#0f172a}.agx-chat-send{padding:8px 12px;border:1px solid #0d6efd;background:#0d6efd;color:#fff;border-radius:6px;cursor:pointer}.agx-chat-send:disabled{opacity:.6;cursor:not-allowed}/* Mic (voice input) button: placed before input in the form row */.agx-chat-mic{padding:8px 10px;border:1px solid #0d6efd;background:#e9f2ff;color:#0d6efd;border-radius:6px;cursor:pointer}.agx-chat-mic.listening{background:#0d6efd;color:#fff;box-shadow:0 0 0 3px rgba(13,110,253,0.15) inset}.agx-chat-mic:disabled{opacity:.6;cursor:not-allowed}.agx-chat-bubble{margin:6px 0;padding:8px 10px;border-radius:10px;max-width:90%;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}.agx-chat-bubble.user{background:#e7f1ff;margin-left:auto}.agx-chat-bubble.assistant{background:#f6f6f6;margin-right:auto}.agx-chat-wrap.dragging{box-shadow:0 0 0 1px rgba(13,110,253,0.25),0 16px 36px rgba(2,6,23,0.24)}/* Toggle button aligned to far right */.agx-chat-toggle{margin-left:auto;min-width:28px;line-height:1;padding:4px 8px;border:1px solid #0d6efd;background:#e9f2ff;color:#0d6efd;border-radius:6px;cursor:pointer}/* Collapsed state */.agx-chat-wrap.collapsed{width:240px;height:auto;min-height:0}.agx-chat-wrap.collapsed .agx-chat-transcript,.agx-chat-wrap.collapsed .agx-chat-form{display:none}.agx-chat-wrap.collapsed .agx-chat-header{cursor:default}/* Hard anchor override: ensure bottom-right float but allow dragging to override via left/top */.agx-chat-wrap{ position:fixed !important; right:16px !important; bottom:16px !important; z-index:9999 !important; /* Critical visual + layout properties to prevent theme overrides */ max-width:90vw !important; min-width:280px !important; min-height:260px !important; display:flex !important; flex-direction:column !important; background:#ffffffcc !important; border:3px solid #0d6efd !important; border-radius:12px !important; overflow:hidden !important; box-shadow:0 0 0 1px rgba(13,110,253,0.2),0 12px 28px rgba(2,6,23,0.20) !important;}/* While dragging, free right/bottom so inline left/top can take effect */.agx-chat-wrap.dragging{ right:auto !important; bottom:auto !important;}/* Header robustness */.agx-chat-header{ display:flex !important; align-items:center !important; background:#f8fafc !important; border-bottom:1px solid #eaeaea !important;}/* Toggle alignment */.agx-chat-toggle{ margin-left:auto !important; }/* Fullscreen control button (aligned to the right like the minimize toggle) */.agx-chat-fullscreen{ min-width:28px; line-height:1; padding:4px 8px; border:1px solid #0d6efd; background:#e9f2ff; color:#0d6efd; border-radius:6px; cursor:pointer; margin-left:auto !important; }/* Give a small gap between fullscreen and minimize toggles when both present */.agx-chat-header .agx-chat-toggle{ margin-left:8px !important; }/* Fullscreen mode: occupy entire viewport; remove window chrome */.agx-chat-wrap.fullscreen{ position:fixed !important; top:0 !important; left:0 !important; right:0 !important; bottom:0 !important; width:auto !important; height:auto !important; max-width:none !important; max-height:none !important; min-width:0 !important; min-height:0 !important; border:0 !important; border-radius:0 !important; box-shadow:none !important; background:#fff !important;}.agx-chat-wrap.fullscreen .agx-chat-header{ cursor:default !important; border-bottom:1px solid #eaeaea !important;}/* In fullscreen, dragging is a no-op; keep edges pinned */.agx-chat-wrap.fullscreen.dragging{ right:0 !important; bottom:0 !important;}/* Voice mode toggle button (header) */.agx-chat-voice{ min-width:28px; line-height:1; padding:4px 8px; border:1px solid #0d6efd; background:#e9f2ff; color:#0d6efd; border-radius:6px; cursor:pointer; }.agx-chat-voice.on{ background:#0d6efd; color:#fff; }.agx-chat-voice:disabled{ opacity:.6; cursor:not-allowed }/* Provide small gap between Voice and its neighbors */.agx-chat-header .agx-chat-voice{ margin-left:8px !important; }/style>style idagx-docs-embed-inline-css>.agx-docs-embed{max-width:980px;margin:0 auto;}.agx-docs-embed pre{overflow-x:auto;background:#f7f7f7;padding:12px;border-radius:8px;}.agx-docs-embed code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;}.agx-docs-embed a{color:#0b5ed7;}/style>link relstylesheet idtwentytwentyfive-style-css hrefhttps://agx.com/wp-content/themes/twentytwentyfive/style.css?ver1.3 mediaall />link relhttps://api.w.org/ hrefhttps://agx.com/wp-json/ />link relEditURI typeapplication/rsd+xml titleRSD hrefhttps://agx.com/xmlrpc.php?rsd />meta namegenerator contentWordPress 6.8.3 />script typeimportmap idwp-importmap>{imports:{@wordpress\/interactivity:https:\/\/agx.com\/wp-includes\/js\/dist\/script-modules\/interactivity\/index.min.js?ver55aebb6e0a16726baffb}}/script>script typemodule srchttps://agx.com/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver61572d447d60c0aa5240 id@wordpress/block-library/navigation/view-js-module>/script>link relmodulepreload hrefhttps://agx.com/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver55aebb6e0a16726baffb id@wordpress/interactivity-js-modulepreload> style idagx-site-logo-css> .agx-site-logo{position:fixed;top:12px;left:12px;z-index:9998;display:inline-block;text-decoration:none} .agx-site-logo img{height:96px;width:auto;display:block} @media (max-width:1024px){ .agx-site-logo img{height:78px} } @media (max-width:480px){ .agx-site-logo img{height:60px} } body.admin-bar .agx-site-logo{ top:44px } @media (max-width:782px){ body.admin-bar .agx-site-logo{ top:58px } } /style> style classwp-fonts-local>@font-face{font-family:Manrope;font-style:normal;font-weight:200 800;font-display:fallback;src:url(https://agx.com/wp-content/themes/twentytwentyfive/assets/fonts/manrope/Manrope-VariableFont_wght.woff2) format(woff2);}@font-face{font-family:Fira Code;font-style:normal;font-weight:300 700;font-display:fallback;src:url(https://agx.com/wp-content/themes/twentytwentyfive/assets/fonts/fira-code/FiraCode-VariableFont_wght.woff2) format(woff2);}/style>/head>body classhome blog wp-embed-responsive wp-theme-twentytwentyfive>style idagx-chat-inline-css-fallback>.agx-chat-wrap{position:fixed;right:16px;bottom:16px;max-width:90vw;min-width:280px;min-height:260px;background:#ffffffcc;border:3px solid #0d6efd;border-radius:12px;box-shadow:0 0 0 1px rgba(13,110,253,0.2),0 12px 28px rgba(2,6,23,0.20);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);display:flex;flex-direction:column;resize:both;overflow:hidden;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;font-size:14px;color:#0f172a;z-index:9999}.agx-chat-header{padding:10px 12px;border-bottom:1px solid #eaeaea;background:#f8fafc;font-weight:600;cursor:move;border-top-left-radius:10px;border-top-right-radius:10px;-webkit-user-select:none;user-select:none;display:flex;align-items:center}.agx-chat-transcript{flex:1 1 auto;min-height:160px;overflow:auto;padding:12px}.agx-chat-form{display:flex;gap:8px;border-top:1px solid #eee;padding:8px}.agx-chat-input{flex:1 1 auto;padding:8px;border:1px solid #ddd;border-radius:6px;color:#0f172a}.agx-chat-send{padding:8px 12px;border:1px solid #0d6efd;background:#0d6efd;color:#fff;border-radius:6px;cursor:pointer}.agx-chat-send:disabled{opacity:.6;cursor:not-allowed}/* Mic (voice input) button: placed before input in the form row */.agx-chat-mic{padding:8px 10px;border:1px solid #0d6efd;background:#e9f2ff;color:#0d6efd;border-radius:6px;cursor:pointer}.agx-chat-mic.listening{background:#0d6efd;color:#fff;box-shadow:0 0 0 3px rgba(13,110,253,0.15) inset}.agx-chat-mic:disabled{opacity:.6;cursor:not-allowed}.agx-chat-bubble{margin:6px 0;padding:8px 10px;border-radius:10px;max-width:90%;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}.agx-chat-bubble.user{background:#e7f1ff;margin-left:auto}.agx-chat-bubble.assistant{background:#f6f6f6;margin-right:auto}.agx-chat-wrap.dragging{box-shadow:0 0 0 1px rgba(13,110,253,0.25),0 16px 36px rgba(2,6,23,0.24)}/* Toggle button aligned to far right */.agx-chat-toggle{margin-left:auto;min-width:28px;line-height:1;padding:4px 8px;border:1px solid #0d6efd;background:#e9f2ff;color:#0d6efd;border-radius:6px;cursor:pointer}/* Collapsed state */.agx-chat-wrap.collapsed{width:240px;height:auto;min-height:0}.agx-chat-wrap.collapsed .agx-chat-transcript,.agx-chat-wrap.collapsed .agx-chat-form{display:none}.agx-chat-wrap.collapsed .agx-chat-header{cursor:default}/* Hard anchor override: ensure bottom-right float but allow dragging to override via left/top */.agx-chat-wrap{ position:fixed !important; right:16px !important; bottom:16px !important; z-index:9999 !important; /* Critical visual + layout properties to prevent theme overrides */ max-width:90vw !important; min-width:280px !important; min-height:260px !important; display:flex !important; flex-direction:column !important; background:#ffffffcc !important; border:3px solid #0d6efd !important; border-radius:12px !important; overflow:hidden !important; box-shadow:0 0 0 1px rgba(13,110,253,0.2),0 12px 28px rgba(2,6,23,0.20) !important;}/* While dragging, free right/bottom so inline left/top can take effect */.agx-chat-wrap.dragging{ right:auto !important; bottom:auto !important;}/* Header robustness */.agx-chat-header{ display:flex !important; align-items:center !important; background:#f8fafc !important; border-bottom:1px solid #eaeaea !important;}/* Toggle alignment */.agx-chat-toggle{ margin-left:auto !important; }/* Fullscreen control button (aligned to the right like the minimize toggle) */.agx-chat-fullscreen{ min-width:28px; line-height:1; padding:4px 8px; border:1px solid #0d6efd; background:#e9f2ff; color:#0d6efd; border-radius:6px; cursor:pointer; margin-left:auto !important; }/* Give a small gap between fullscreen and minimize toggles when both present */.agx-chat-header .agx-chat-toggle{ margin-left:8px !important; }/* Fullscreen mode: occupy entire viewport; remove window chrome */.agx-chat-wrap.fullscreen{ position:fixed !important; top:0 !important; left:0 !important; right:0 !important; bottom:0 !important; width:auto !important; height:auto !important; max-width:none !important; max-height:none !important; min-width:0 !important; min-height:0 !important; border:0 !important; border-radius:0 !important; box-shadow:none !important; background:#fff !important;}.agx-chat-wrap.fullscreen .agx-chat-header{ cursor:default !important; border-bottom:1px solid #eaeaea !important;}/* In fullscreen, dragging is a no-op; keep edges pinned */.agx-chat-wrap.fullscreen.dragging{ right:0 !important; bottom:0 !important;}/* Voice mode toggle button (header) */.agx-chat-voice{ min-width:28px; line-height:1; padding:4px 8px; border:1px solid #0d6efd; background:#e9f2ff; color:#0d6efd; border-radius:6px; cursor:pointer; }.agx-chat-voice.on{ background:#0d6efd; color:#fff; }.agx-chat-voice:disabled{ opacity:.6; cursor:not-allowed }/* Provide small gap between Voice and its neighbors */.agx-chat-header .agx-chat-voice{ margin-left:8px !important; }/style>div idagx-chat-widget aria-livepolite>/div>script idagx-chat-js-fallback>try{ if(!window.__agxChatBootstrapped){ window.__agxChatBootstrapped1;window.AgxChatCfg {ws_url:wss:\/\/agx.com\/api\/chat\/ws?sessionpublic-chat};(() > { const cfg (typeof window ! undefined && window.AgxChatCfg) ? window.AgxChatCfg : { ws_url: }; const WS_URL cfg.ws_url || wss://agx.com/api/chat/ws?sessionpublic-chat; // Detailed logging controls (opt-in content logging; events enabled by default unless explicitly disabled) const LOG_EVENTS (typeof cfg.log_events boolean) ? cfg.log_events : true; const LOG_CONTENT !!cfg.log_content; const SESSION (() > { try { const u new URL(WS_URL); return u.searchParams.get(session) || ; } catch(_) { return ; } })(); function __agxLogEvent(event, data){ if (!LOG_EVENTS) return; try { var msgObj { event: String(event||), session: SESSION, ts: Date.now() }; if (data && typeof data object) { if (typeof data.len number) msgObj.len data.len|0; if (typeof data.chunk_len number) msgObj.chunk_len data.chunk_len|0; if (typeof data.total number) msgObj.total data.total|0; if (typeof data.count number) msgObj.count data.count|0; if (typeof data.duration_ms number) msgObj.duration_ms data.duration_ms|0; if (typeof data.ws_url string) msgObj.ws_url data.ws_url; if (typeof data.error string) msgObj.error data.error; if (typeof data.code string || typeof data.code number) msgObj.code data.code; if (LOG_CONTENT && typeof data.text string) { msgObj.text String(data.text).slice(0,2000); } } var payload { type: widget_event, message: JSON.stringify(msgObj), url: String(location && location.href || ) }; var body JSON.stringify(payload); if (navigator.sendBeacon) { var blob new Blob(body, { type: application/json }); navigator.sendBeacon(/wp-json/agx/v1/log, blob); } else { fetch(/wp-json/agx/v1/log, { method: POST, headers: { Content-Type: application/json }, body: body, keepalive: true }).catch(function(){}); } } catch(_){} } function h(tag, attrs {}, children ) { const el document.createElement(tag); Object.entries(attrs).forEach((k, v) > { if (k class) el.className v; else if (k text) el.textContent v; else el.setAttribute(k, v); }); (Array.isArray(children) ? children : children).forEach(c > { if (c) el.appendChild(c); }); return el; } function init() { const root document.getElementById(agx-chat-widget); if (!root) return; root.innerHTML ; const wrap h(div, { class: agx-chat-wrap }); // Anchor bottom-right by default; clear any left/top that may override try { // Force floating even if CSS fails to load wrap.style.position fixed; wrap.style.zIndex 9999; wrap.style.right 16px; wrap.style.bottom 16px; wrap.style.left auto; wrap.style.top auto; } catch(_) {} // Header with text-only title (logo removed) const header h(div, { class: agx-chat-header }); const title h(span, { class: agx-chat-title, text: Ask AGX }); // Thinking indicator (shown on {type:thinking,state:start}, hidden on complete/error/final) const thinking h(span, { class: agx-chat-thinking, text: thinking… }); try { thinking.style.display none; thinking.setAttribute(aria-live,polite); } catch(_) {} // Minimize/expand toggle const toggle h(button, { class: agx-chat-toggle, type: button, title: Minimize chat, aria-label: Minimize chat, text: – }); // Fullscreen toggle (maximize/restore) const FS_KEY agxChatFullscreen; const fsBtn h(button, { class: agx-chat-fullscreen, type: button, title: Maximize, aria-label: Maximize, text: ⛶ }); function updateFullscreenBtn(){ try { const on wrap.classList.contains(fullscreen); fsBtn.textContent on ? 🗗 : ⛶; const label on ? Restore size : Maximize; fsBtn.setAttribute(aria-label, label); fsBtn.title label; } catch(_) {} } function setFullscreen(on){ try { if (on) wrap.classList.add(fullscreen); else wrap.classList.remove(fullscreen); sessionStorage.setItem(FS_KEY, on ? 1 : 0); updateFullscreenBtn(); } catch(_) {} } // Voice mode (header toggle) — skeleton (no TTS yet) const VOICE_KEY agxChatVoice; const voiceBtn h(button, { class: agx-chat-voice, type: button, title: Voice off, aria-label: Voice off, aria-pressed: false, text: 🔊 }); let voiceOn false; try { voiceOn sessionStorage.getItem(VOICE_KEY) 1; } catch(_) {} function updateVoiceBtn(){ try { if (voiceOn) { voiceBtn.classList.add(on); voiceBtn.setAttribute(aria-pressed,true); voiceBtn.title Voice on; voiceBtn.setAttribute(aria-label,Voice on); } else { voiceBtn.classList.remove(on); voiceBtn.setAttribute(aria-pressed,false); voiceBtn.title Voice off; voiceBtn.setAttribute(aria-label,Voice off); } } catch(_) {} } function setVoice(on){ voiceOn !!on; updateVoiceBtn(); try { sessionStorage.setItem(VOICE_KEY, voiceOn ? 1 : 0); } catch(_) {} } // Basic support check: disable when SpeechSynthesis is unavailable try { const synthOk (typeof window ! undefined) && (speechSynthesis in window) && (typeof window.SpeechSynthesisUtterance function); if (!synthOk) { voiceBtn.disabled true; voiceBtn.title Voice not supported in this browser; } } catch(_) {} voiceBtn.addEventListener(click, (e)>{ try{ e.stopPropagation(); e.preventDefault(); }catch(_){} setVoice(!voiceOn); }); // Place the button before Fullscreen in the header try { header.insertBefore(voiceBtn, fsBtn); } catch(_) {} // Collapsed state helpers const COLLAPSE_KEY agxChatCollapsed; function updateToggle(){ try { const collapsed wrap.classList.contains(collapsed); toggle.textContent collapsed ? + : –; const label collapsed ? Expand chat : Minimize chat; toggle.setAttribute(aria-label, label); toggle.title label; } catch(_) {} } function setCollapsed(on){ try { if (on) wrap.classList.add(collapsed); else wrap.classList.remove(collapsed); sessionStorage.setItem(COLLAPSE_KEY, on ? 1 : 0); updateToggle(); } catch(_) {} } // Initialize collapsed from sessionStorage try { if (sessionStorage.getItem(COLLAPSE_KEY) 1) wrap.classList.add(collapsed); } catch(_) {} updateToggle(); toggle.addEventListener(click, (e) > { try { e.stopPropagation(); setCollapsed(!wrap.classList.contains(collapsed)); } catch(_) {} }); // Fullscreen listeners/init fsBtn.addEventListener(click, (e) > { try { e.stopPropagation(); setFullscreen(!wrap.classList.contains(fullscreen)); } catch(_) {} }); try { if (sessionStorage.getItem(FS_KEY) 1) { wrap.classList.add(fullscreen); } } catch(_) {} updateFullscreenBtn(); const transcript h(div, { class: agx-chat-transcript, role: log }); const form h(div, { class: agx-chat-form }); const input h(input, { class: agx-chat-input, type: text, placeholder: Ask me anything… }); const send h(button, { class: agx-chat-send, type: button, text: Send }); // Voice input (mic) button — with optional Web Speech API support const mic h(button, { class: agx-chat-mic, type: button, title: Voice input (press and hold), aria-label: Voice input, aria-pressed: false, text: 🎤 }); // Insert mic before input: micinputSend form.appendChild(mic); form.appendChild(input); form.appendChild(send); // Mic state and handlers (press-and-hold; STT when available) let micListening false; const SR (window.SpeechRecognition || window.webkitSpeechRecognition); const sttSupported !!SR; let rec null; function ensureRecognizer(){ if (!sttSupported) return null; if (rec) return rec; try { rec new SR(); rec.interimResults true; // We prefer short utterances; continuousfalse gives end events promptly try { rec.continuous false; } catch(_) {} // Seed language from html lang> if present try { const lang (document.documentElement && document.documentElement.lang) ? String(document.documentElement.lang) : ; if (lang) rec.lang lang; } catch(_) {} rec.onresult (ev) > { try { let finalTxt ; let interimTxt ; for (let i ev.resultIndex; i ev.results.length; i++) { const r ev.resultsi; if (r.isFinal) finalTxt + r0.transcript; else interimTxt + r0.transcript; } if (finalTxt) { const cur (input.value || ).trim(); input.value (cur ? (cur + + finalTxt) : finalTxt).trim(); } // We avoid rendering interim into the UI to keep it simple/no flicker } catch(_) {} }; rec.onerror (_ev) > { // On error, stop listening state gracefully try { micListening false; updateMicBtn(); } catch(_) {} }; rec.onend () > { // Recognizer naturally ends after a short phrase try { micListening false; updateMicBtn(); } catch(_) {} }; } catch(_) { rec null; } return rec; } // If unsupported, clarify via tooltip (button still works as visual affordance) if (!sttSupported) { try { mic.title Voice input (unsupported in this browser); } catch(_) {} } // MediaRecorder fallback for browsers without Web Speech API let mediaStream null; let mediaRecorder null; let mediaChunks ; async function startRecording(){ try { // Request mic; keep it simple (let server normalize if needed) mediaStream await (navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia({ audio: true }) : Promise.reject(new Error(getUserMedia unavailable))); mediaChunks ; // Prefer webm/opus; fall back to ogg/opus when available const typeCandidates audio/webm;codecsopus,audio/webm,audio/ogg;codecsopus,audio/ogg; let selType ; if (window.MediaRecorder && typeof MediaRecorder.isTypeSupported function) { for (const t of typeCandidates){ if (MediaRecorder.isTypeSupported(t)) { selType t; break; } } } mediaRecorder new MediaRecorder(mediaStream, selType ? { mimeType: selType } : undefined); mediaRecorder.ondataavailable (e)>{ if (e && e.data && e.data.size > 0) { mediaChunks.push(e.data); } }; mediaRecorder.start(); } catch(e) { try { mic.title Microphone permission denied or unsupported; } catch(_) {} // Stop listening state immediately on failure micListening false; updateMicBtn(); } } async function stopRecordingAndTranscribe(){ try { if (mediaRecorder && mediaRecorder.state ! inactive) { await new Promise(resolve > { const done ()>{ try { resolve(); } catch(_) {} }; mediaRecorder.addEventListener(stop, done, { once: true }); try { mediaRecorder.stop(); } catch(_) { done(); } }); } } catch(_) { /* ignore */ } try { if (mediaStream) { mediaStream.getTracks().forEach(t>{ try { t.stop(); } catch(_) {} }); } } catch(_) {} const blob mediaChunks && mediaChunks.length ? new Blob(mediaChunks, { type: (mediaChunks0 && mediaChunks0.type) || audio/webm }) : null; mediaRecorder null; mediaStream null; mediaChunks ; if (!blob) { return; } try { const fd new FormData(); // Filename hint helps server-side MIME detection fallbacks const fname (blob.type && blob.type.indexOf(ogg)>0) ? voice.ogg : (blob.type && blob.type.indexOf(webm)>0 ? voice.webm : voice.wav); fd.append(file, blob, fname); const resp await fetch(/api/voice/transcribe_meta, { method: POST, body: fd }); const v await resp.json().catch(()>({})); if (resp.ok && v && v.ok && typeof v.text string && v.text.trim().length){ const cur (input.value || ).trim(); input.value (cur ? (cur + + v.text) : v.text).trim(); } else { // Graceful: keep silent; set an informative title for long-press info try { mic.title Dictation fallback failed; } catch(_) {} } } catch(_) { try { mic.title Dictation network error; } catch(_) {} } } function updateMicBtn(){ try { if (micListening) { mic.classList.add(listening); mic.setAttribute(aria-pressed,true); // Reuse the header badge to reflect mic state for now try { thinking.textContent listening…; thinking.style.display ; } catch(_) {} } else { mic.classList.remove(listening); mic.setAttribute(aria-pressed,false); // Restore/hide the header badge when not listening try { thinking.textContent thinking…; thinking.style.display none; } catch(_) {} } } catch(_) {} } function startListening(){ micListening true; updateMicBtn(); try { input.blur(); } catch(_) {} const r ensureRecognizer(); if (r) { try { r.start(); } catch(_) {} } else { // Fallback to MediaRecorder when Web Speech is unavailable startRecording(); } } function stopListening(){ if (!micListening) return; const r rec; micListening false; updateMicBtn(); if (r) { try { r.stop(); } catch(_) {} } else { stopRecordingAndTranscribe(); } } function toggleListening(){ if (!micListening) { startListening(); } else { stopListening(); } } // Touch/click dedupe to avoid double-triggering on mobile (touchstart -> click) let micTouching false; mic.addEventListener(mousedown, (e)>{ try{ e.preventDefault(); }catch(_){} startListening(); }); window.addEventListener(mouseup, stopListening); mic.addEventListener(touchstart, (e)>{ try{ e.preventDefault(); }catch(_){} micTouching true; startListening(); }, { passive: false }); window.addEventListener(touchend, ()>{ stopListening(); setTimeout(()>{ micTouching false; }, 120); }, { passive: true }); mic.addEventListener(click, (e)>{ try{ e.preventDefault(); }catch(_){} if (micTouching) return; toggleListening(); }); // Keyboard accessibility: Space/Enter toggles mic.addEventListener(keydown, (e)>{ const k e.key || e.code; if (k || k Spacebar || k Enter) { try{ e.preventDefault(); }catch(_){} toggleListening(); } }); header.appendChild(title); header.appendChild(thinking); // Ensure Voice toggle is in the header before fullscreen (insertBefore earlier may have no effect if fsBtn wasnt attached yet) try { if (!voiceBtn.parentNode) header.appendChild(voiceBtn); } catch(_) {} header.appendChild(fsBtn); header.appendChild(toggle); wrap.appendChild(header); wrap.appendChild(transcript); wrap.appendChild(form); root.appendChild(wrap); // Infinite history state and loader let histCursor null; // server-provided cursor let histDone false; let histLoading false; async function requestHistory(){ if (histLoading || histDone) return; histLoading true; try { // Ask the same WS for history const payload { type: history }; if (histCursor) payload.cursor histCursor; payload.limit 50; ensureWS(); // If socket not open yet, wait briefly if (!ws || ws.readyState ! WebSocket.OPEN) { await new Promise((resolve) > { const t setInterval(() > { if (ws && ws.readyState WebSocket.OPEN) { clearInterval(t); resolve(); } }, 100); setTimeout(() > { clearInterval(t); resolve(); }, 1500); }); } if (ws && ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify(payload)); } } catch(_) {} finally { histLoading false; } } // Preserve scroll position when prepending nodes function prependMessages(msgs){ if (!Array.isArray(msgs) || msgs.length 0) return; const prevScroll transcript.scrollTop; const prevHeight transcript.scrollHeight; // Build nodes in a fragment to minimize reflow const frag document.createDocumentFragment(); for (const m of msgs){ const role (m && m.role) || assistant; const text (m && m.content) || ; const bubble h(div, { class: agx-chat-bubble + (role user ? user : assistant) }); bubble.style.whiteSpace pre-wrap; bubble.textContent text; frag.appendChild(bubble); } // Insert at beginning if (transcript.firstChild) transcript.insertBefore(frag, transcript.firstChild); else transcript.appendChild(frag); // Adjust scroll to keep viewport anchored const newHeight transcript.scrollHeight; transcript.scrollTop newHeight - prevHeight + prevScroll; } // Attach scroll handler to top-load history transcript.addEventListener(scroll, () > { try { if (transcript.scrollTop 0 && !histDone && !histLoading) { requestHistory(); } } catch(_) {} }); // demo notice removed (streaming via CEP is now live) let ws null; let busy false; let currentChunks ; let streamBubble null; // live-updated assistant bubble during streaming function addMsg(role, text) { const bubble h(div, { class: agx-chat-bubble + (role user ? user : assistant) }); // Preserve newlines in messages bubble.style.whiteSpace pre-wrap; bubble.textContent text; transcript.appendChild(bubble); transcript.scrollTop transcript.scrollHeight; } function ensureWS() { if (ws && ws.readyState WebSocket.OPEN) return; try { if (ws) ws.close(); } catch(_) {} ws new WebSocket(WS_URL); ws.addEventListener(open, () > { try { __agxLogEvent(ws_open, { ws_url: WS_URL }); } catch(_) {} }); ws.addEventListener(message, (ev) > { try { const obj JSON.parse(ev.data); if (obj.type ai_chunk) { const chunk String(obj.text || ); currentChunks.push(chunk); if (!streamBubble) { streamBubble h(div, { class: agx-chat-bubble assistant }); streamBubble.style.whiteSpace pre-wrap; transcript.appendChild(streamBubble); } streamBubble.textContent currentChunks.join(); transcript.scrollTop transcript.scrollHeight; try { __agxLogEvent(ai_chunk, { chunk_len: chunk.length|0, count: currentChunks.length|0, text: chunk }); } catch(_) {} } else if (obj.type history) { try { const msgs Array.isArray(obj.messages) ? obj.messages : ; if (msgs.length > 0) { prependMessages(msgs); } histCursor (obj.cursor null || typeof obj.cursor undefined) ? null : obj.cursor; histDone !!obj.done || msgs.length 0; } catch(_) {} } else if (obj.type final) { const text (currentChunks.join() || ).trim(); if (streamBubble) { streamBubble.textContent text; } else { addMsg(assistant, text); } currentChunks ; streamBubble null; busy false; send.disabled false; input.disabled false; // hide thinking on final try { thinking.style.display none; } catch(_) {} // Optional TTS: speak the final reply when Voice mode is ON and supported try { if (voiceOn && typeof window ! undefined && speechSynthesis in window && typeof window.SpeechSynthesisUtterance function) { try { window.speechSynthesis.cancel(); } catch(_) {} const u new SpeechSynthesisUtterance(text); try { u.lang (document.documentElement && document.documentElement.lang) ? String(document.documentElement.lang) : u.lang; } catch(_) {} window.speechSynthesis.speak(u); } } catch(_) {} try { __agxLogEvent(final, { len: (text||).length|0 }); } catch(_) {} } else if (obj.type busy) { // server-side busy; keep disabled until a final arrives try { __agxLogEvent(busy, {}); } catch(_) {} } else if (obj.type thinking) { try { const st String(obj.state||); if (st start) { thinking.style.display ; } else { thinking.style.display none; } __agxLogEvent(thinking, { state: st }); } catch(_) {} } else if (obj.type error) { if (streamBubble) { streamBubble null; } const emsg String(obj.message || unknown); addMsg(assistant, Error: + emsg); currentChunks ; busy false; send.disabled false; input.disabled false; try { __agxLogEvent(error, { error: emsg }); } catch(_) {} } } catch (e) { // ignore } }); ws.addEventListener(close, () > { // lazy reconnect on next send try { __agxLogEvent(ws_close, { ws_url: WS_URL }); } catch(_) {} }); ws.addEventListener(error, () > { // noop try { __agxLogEvent(ws_error, {}); } catch(_) {} }); } function submit() { const text (input.value || ).trim(); if (!text || busy) return; ensureWS(); if (!ws || ws.readyState ! WebSocket.OPEN) { // slight delay to open then send const t setInterval(() > { if (ws && ws.readyState WebSocket.OPEN) { clearInterval(t); doSend(text); } }, 100); setTimeout(() > clearInterval(t), 3000); } else { doSend(text); } } function doSend(text) { try { ws.send(JSON.stringify({ type: user, text })); addMsg(user, text); input.value ; busy true; send.disabled true; input.disabled true; currentChunks ; streamBubble null; } catch (e) { addMsg(assistant, Failed to send.); } } // Enable dragging (header is the handle). Converts fixed right/bottom to left/top during drag. enableDrag(wrap, header); // JS resize fallback: drag near bottom-right corner to resize (works even when CSS resize is ignored) (function enableAutoResize(box){ let resizing false; let sx 0, sy 0, sw 0, sh 0; const MIN_W 280, MIN_H 260; // match CSS minima function begin(x, y) { try { const r box.getBoundingClientRect(); sx x; sy y; sw r.width; sh r.height; resizing true; // Ensure explicit sizes so we can adjust via JS box.style.width sw + px; box.style.height sh + px; } catch(_) {} } function move(x, y) { if (!resizing) return; const dx x - sx, dy y - sy; const vw window.innerWidth, vh window.innerHeight; let nw Math.max(MIN_W, sw + dx); let nh Math.max(MIN_H, sh + dy); nw Math.min(nw, Math.floor(vw * 0.95)); nh Math.min(nh, Math.floor(vh * 0.95)); box.style.width nw + px; box.style.height nh + px; } function end(){ if (!resizing) return; resizing false; } // Mouse handlers box.addEventListener(mousedown, (e) > { const r box.getBoundingClientRect(); // Hit area: 18px square at bottom-right if (e.clientX > r.right - 18 && e.clientY > r.bottom - 18) { e.preventDefault(); begin(e.clientX, e.clientY); } }); window.addEventListener(mousemove, (e) > move(e.clientX, e.clientY)); window.addEventListener(mouseup, end); // Touch handlers box.addEventListener(touchstart, (e) > { const t e.touches && e.touches0; if (!t) return; const r box.getBoundingClientRect(); if (t.clientX > r.right - 24 && t.clientY > r.bottom - 24) { try { e.preventDefault(); } catch(_) {} begin(t.clientX, t.clientY); } }, { passive: false }); window.addEventListener(touchmove, (e) > { const t e.touches && e.touches0; if (t) move(t.clientX, t.clientY); }, { passive: true }); window.addEventListener(touchend, end); })(wrap); function enableDrag(box, handle) { let dragging false; let startX 0, startY 0, startLeft 0, startTop 0; const toNum (v) > (typeof v number ? v : parseFloat(String(v)||0)); function begin(x, y) { try { const rect box.getBoundingClientRect(); box.style.left rect.left + px; box.style.top rect.top + px; box.style.right auto; box.style.bottom auto; startX x; startY y; startLeft toNum(box.style.left); startTop toNum(box.style.top); dragging true; box.classList.add(dragging); } catch {} } function move(x, y) { if (!dragging) return; const dx x - startX, dy y - startY; const vw window.innerWidth, vh window.innerHeight; const rect box.getBoundingClientRect(); let nl startLeft + dx, nt startTop + dy; nl Math.max(0, Math.min(nl, vw - rect.width)); nt Math.max(0, Math.min(nt, vh - rect.height)); box.style.left nl + px; box.style.top nt + px; } function end() { if (!dragging) return; dragging false; box.classList.remove(dragging); } function shouldIgnoreStart(ev){ try { const t ev.target; if (!t) return false; if (t.closest && t.closest(.agx-chat-form)) return true; const tn (t.tagName || ).toString().toLowerCase(); return tn input || tn textarea || tn button || tn a; } catch(_) { return false; } } // Mouse (header handle) handle.addEventListener(mousedown, (e) > { if (shouldIgnoreStart(e)) return; e.preventDefault(); begin(e.clientX, e.clientY); }); // Mouse (wrapper fallback - only when starting on header region) box.addEventListener(mousedown, (e) > { if (shouldIgnoreStart(e)) return; const isHeader e.target && e.target.closest && e.target.closest(.agx-chat-header); if (!isHeader) return; e.preventDefault(); begin(e.clientX, e.clientY); }); window.addEventListener(mousemove, (e) > move(e.clientX, e.clientY)); window.addEventListener(mouseup, end); // Touch (header handle) handle.addEventListener(touchstart, (e) > { const t e.touches && e.touches0; if (!t || shouldIgnoreStart(e)) return; e.preventDefault(); begin(t.clientX, t.clientY); }, { passive: false }); // Touch (wrapper fallback - only when starting on header region) box.addEventListener(touchstart, (e) > { const t e.touches && e.touches0; if (!t || shouldIgnoreStart(e)) return; const isHeader e.target && e.target.closest && e.target.closest(.agx-chat-header); if (!isHeader) return; try { e.preventDefault(); } catch(_) {} begin(t.clientX, t.clientY); }, { passive: false }); window.addEventListener(touchmove, (e) > { const t e.touches && e.touches0; if (t) move(t.clientX, t.clientY); }, { passive: true }); window.addEventListener(touchend, end); } send.addEventListener(click, submit); input.addEventListener(keydown, (e) > { if (e.key Enter) submit(); }); } if (document.readyState loading) { document.addEventListener(DOMContentLoaded, init); } else { init(); } // to the local REST endpoint /wp-json/agx/v1/log on the same origin. // Client-side error logging: forward runtime errors and unhandled rejections var endpoint /wp-json/agx/v1/log; (function setupAgxChatErrorLogging(){ var endpoint /wp-json/agx/v1/log; function sendLog(payload){ try { var body JSON.stringify(payload); if (navigator.sendBeacon) { var blob new Blob(body, { type: application/json }); navigator.sendBeacon(endpoint, blob); } else { // keepalive helps during unload/navigation fetch(endpoint, { method: POST, headers: { Content-Type: application/json }, body: body, keepalive: true }).catch(function(){}); } } catch(_){ /* ignore */ } } // Runtime errors try { window.addEventListener(error, function(ev){ try { var msg (ev && ev.message) || error; var src (ev && ev.filename) || ; var ln (ev && ev.lineno) || 0; var cn (ev && ev.colno) || 0; var stack ev && ev.error && ev.error.stack ? String(ev.error.stack) : ; sendLog({ type: error, message: String(msg || ), stack: stack ? String(stack).slice(0, 4000) : , source: String(src || ), lineno: ln|0, colno: cn|0, url: String(location && location.href || ), ua: String(navigator && navigator.userAgent || ), ts: Date.now() }); } catch(_){ /* ignore */ } }, true); } catch(_){ /* ignore */ } // Unhandled promise rejections try { window.addEventListener(unhandledrejection, function(ev){ try { var reason ev && ev.reason; var msg ; var stack ; if (reason) { if (typeof reason string) { msg reason; } else if (reason && reason.message) { msg String(reason.message); } if (reason && reason.stack) { stack String(reason.stack); } } sendLog({ type: unhandledrejection, message: String(msg || unhandledrejection), stack: stack ? String(stack).slice(0, 4000) : , source: , lineno: 0, colno: 0, url: String(location && location.href || ), ua: String(navigator && navigator.userAgent || ), ts: Date.now() }); } catch(_){ /* ignore */ } }, true); } catch(_){ /* ignore */ } })();})(); } }catch(e){ console.error(agx-chat inline fallback failed, e); }/script>div classwp-site-blocks>header classwp-block-template-part>div classwp-block-group alignfull is-layout-flow wp-block-group-is-layout-flow> div classwp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained> div classwp-block-group alignwide is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-8165f36a wp-block-group-is-layout-flex stylepadding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)> p classwp-block-site-title>a hrefhttps://agx.com target_self relhome aria-currentpage>AGX/a>/p> div classwp-block-group is-content-justification-right is-nowrap is-layout-flex wp-container-core-group-is-layout-f4c28e8b wp-block-group-is-layout-flex> nav classis-responsive items-justified-right wp-block-navigation is-content-justification-right is-layout-flex wp-container-core-navigation-is-layout-fc306653 wp-block-navigation-is-layout-flex data-wp-interactivecore/navigation data-wp-context{overlayOpenedBy:{click:false,hover:false,focus:false},type:overlay,roleAttribute:,ariaLabel:Menu}>button aria-haspopupdialog aria-labelOpen menu classwp-block-navigation__responsive-container-open data-wp-on-async--clickactions.openMenuOnClick data-wp-on--keydownactions.handleMenuKeydown >svg width24 height24 xmlnshttp://www.w3.org/2000/svg viewBox0 0 24 24 aria-hiddentrue focusablefalse>rect x4 y7.5 width16 height1.5 />rect x4 y15 width16 height1.5 />/svg>/button> div classwp-block-navigation__responsive-container has-text-color has-contrast-color has-background has-base-background-color idmodal-1 data-wp-class--has-modal-openstate.isMenuOpen data-wp-class--is-menu-openstate.isMenuOpen data-wp-watchcallbacks.initMenu data-wp-on--keydownactions.handleMenuKeydown data-wp-on-async--focusoutactions.handleMenuFocusout tabindex-1 > div classwp-block-navigation__responsive-close tabindex-1> div classwp-block-navigation__responsive-dialog data-wp-bind--aria-modalstate.ariaModal data-wp-bind--aria-labelstate.ariaLabel data-wp-bind--rolestate.roleAttribute > button aria-labelClose menu classwp-block-navigation__responsive-container-close data-wp-on-async--clickactions.closeMenuOnClick >svg xmlnshttp://www.w3.org/2000/svg viewBox0 0 24 24 width24 height24 aria-hiddentrue focusablefalse>path dm13.06 12 6.47-6.47-1.06-1.06L12 10.94 5.53 4.47 4.47 5.53 10.94 12l-6.47 6.47 1.06 1.06L12 13.06l6.47 6.47 1.06-1.06L13.06 12Z>/path>/svg>/button> div classwp-block-navigation__responsive-container-content data-wp-watchcallbacks.focusFirstElement idmodal-1-content> ul classwp-block-navigation__container is-responsive items-justified-right wp-block-navigation>ul classwp-block-page-list>li classwp-block-pages-list__item wp-block-navigation-item open-on-hover-click>a classwp-block-pages-list__item__link wp-block-navigation-item__content hrefhttps://agx.com/sample-page/>Sample Page/a>/li>/ul>/ul> /div> /div> /div> /div>/nav> /div> /div> /div> /div>/header>main classwp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained stylemargin-top:var(--wp--preset--spacing--60)> h1 classwp-block-heading has-text-align-left>Blog/h1> div classwp-block-query alignfull is-layout-flow wp-block-query-is-layout-flow> ul classalignfull wp-block-post-template is-layout-flow wp-block-post-template-is-layout-flow>li classwp-block-post post-1 post type-post status-publish format-standard hentry category-uncategorized> div classwp-block-group alignfull has-global-padding is-layout-constrained wp-block-group-is-layout-constrained stylepadding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)> h2 classwp-block-post-title has-x-large-font-size>a hrefhttps://agx.com/2026/01/25/hello-world/ target_self >Hello world!/a>/h2> div classentry-content alignfull wp-block-post-content has-medium-font-size has-global-padding is-layout-constrained wp-block-post-content-is-layout-constrained>p>Welcome to a hrefhttps://agx.com/>WordPress/a>. This is your first post. Edit or delete it, then start writing!/p>/div> div stylemargin-top:var(--wp--preset--spacing--40); classwp-block-post-date has-small-font-size>time datetime2026-01-25T15:09:28+00:00>a hrefhttps://agx.com/2026/01/25/hello-world/>January 25, 2026/a>/time>/div> /div> /li>/ul> div classwp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained stylepadding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)> /div> div classwp-block-group alignwide has-global-padding is-layout-constrained wp-block-group-is-layout-constrained> /div> /div>/main>footer classwp-block-template-part>div classwp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained stylepadding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--50)> div classwp-block-group alignwide is-layout-flow wp-block-group-is-layout-flow> div classwp-block-group alignfull is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-e5edad21 wp-block-group-is-layout-flex> div classwp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex> div classwp-block-column is-layout-flow wp-block-column-is-layout-flow styleflex-basis:100%>h2 classwp-block-site-title>a hrefhttps://agx.com target_self relhome aria-currentpage>AGX/a>/h2> /div> div classwp-block-column is-layout-flow wp-block-column-is-layout-flow> div styleheight:var(--wp--preset--spacing--40);width:0px aria-hiddentrue classwp-block-spacer>/div> /div> /div> div classwp-block-group is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-570722b2 wp-block-group-is-layout-flex> nav classis-vertical wp-block-navigation is-layout-flex wp-container-core-navigation-is-layout-fe9cc265 wp-block-navigation-is-layout-flex>ul classwp-block-navigation__container is-vertical wp-block-navigation>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>Blog/span>/a>/li>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>About/span>/a>/li>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>FAQs/span>/a>/li>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>Authors/span>/a>/li>/ul>/nav> nav classis-vertical wp-block-navigation is-layout-flex wp-container-core-navigation-is-layout-fe9cc265 wp-block-navigation-is-layout-flex>ul classwp-block-navigation__container is-vertical wp-block-navigation>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>Events/span>/a>/li>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>Shop/span>/a>/li>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>Patterns/span>/a>/li>li class wp-block-navigation-item wp-block-navigation-link>a classwp-block-navigation-item__content href#>span classwp-block-navigation-item__label>Themes/span>/a>/li>/ul>/nav> /div> /div> div styleheight:var(--wp--preset--spacing--70) aria-hiddentrue classwp-block-spacer>/div> div classwp-block-group alignfull is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-91e87306 wp-block-group-is-layout-flex> p classhas-small-font-size>Twenty Twenty-Five/p> p classhas-small-font-size> Designed with a hrefhttps://wordpress.org relnofollow>WordPress/a> /p> /div> /div> /div>/footer>/div>script typespeculationrules>{prefetch:{source:document,where:{and:{href_matches:\/*},{not:{href_matches:\/wp-*.php,\/wp-admin\/*,\/wp-content\/uploads\/*,\/wp-content\/*,\/wp-content\/plugins\/*,\/wp-content\/themes\/twentytwentyfive\/*,\/*\\?(.+)}},{not:{selector_matches:arel~\nofollow\}},{not:{selector_matches:.no-prefetch, .no-prefetch a}}},eagerness:conservative}}/script>script idwp-block-template-skip-link-js-after> ( function() { var skipLinkTarget document.querySelector( main ), sibling, skipLinkTargetID, skipLink; // Early exit if a skip-link target cant be located. if ( ! skipLinkTarget ) { return; } /* * Get the site wrapper. * The skip-link will be injected in the beginning of it. */ sibling document.querySelector( .wp-site-blocks ); // Early exit if the root element was not found. if ( ! sibling ) { return; } // Get the skip-link targets ID, and generate one if it doesnt exist. skipLinkTargetID skipLinkTarget.id; if ( ! skipLinkTargetID ) { skipLinkTargetID wp--skip-link--target; skipLinkTarget.id skipLinkTargetID; } // Create the skip link. skipLink document.createElement( a ); skipLink.classList.add( skip-link, screen-reader-text ); skipLink.id wp-skip-link; skipLink.href # + skipLinkTargetID; skipLink.innerText Skip to content; // Inject the skip link. sibling.parentElement.insertBefore( skipLink, sibling ); }() ); /script>script idagx-chat-js-before>window.AgxChatCfg {ws_url:wss:\/\/agx.com\/api\/chat\/ws?sessionpublic-chat,log_events:true,log_content:false};(() > { const cfg (typeof window ! undefined && window.AgxChatCfg) ? window.AgxChatCfg : { ws_url: }; const WS_URL cfg.ws_url || wss://agx.com/api/chat/ws?sessionpublic-chat; // Detailed logging controls (opt-in content logging; events enabled by default unless explicitly disabled) const LOG_EVENTS (typeof cfg.log_events boolean) ? cfg.log_events : true; const LOG_CONTENT !!cfg.log_content; const SESSION (() > { try { const u new URL(WS_URL); return u.searchParams.get(session) || ; } catch(_) { return ; } })(); function __agxLogEvent(event, data){ if (!LOG_EVENTS) return; try { var msgObj { event: String(event||), session: SESSION, ts: Date.now() }; if (data && typeof data object) { if (typeof data.len number) msgObj.len data.len|0; if (typeof data.chunk_len number) msgObj.chunk_len data.chunk_len|0; if (typeof data.total number) msgObj.total data.total|0; if (typeof data.count number) msgObj.count data.count|0; if (typeof data.duration_ms number) msgObj.duration_ms data.duration_ms|0; if (typeof data.ws_url string) msgObj.ws_url data.ws_url; if (typeof data.error string) msgObj.error data.error; if (typeof data.code string || typeof data.code number) msgObj.code data.code; if (LOG_CONTENT && typeof data.text string) { msgObj.text String(data.text).slice(0,2000); } } var payload { type: widget_event, message: JSON.stringify(msgObj), url: String(location && location.href || ) }; var body JSON.stringify(payload); if (navigator.sendBeacon) { var blob new Blob(body, { type: application/json }); navigator.sendBeacon(/wp-json/agx/v1/log, blob); } else { fetch(/wp-json/agx/v1/log, { method: POST, headers: { Content-Type: application/json }, body: body, keepalive: true }).catch(function(){}); } } catch(_){} } function h(tag, attrs {}, children ) { const el document.createElement(tag); Object.entries(attrs).forEach((k, v) > { if (k class) el.className v; else if (k text) el.textContent v; else el.setAttribute(k, v); }); (Array.isArray(children) ? children : children).forEach(c > { if (c) el.appendChild(c); }); return el; } function init() { const root document.getElementById(agx-chat-widget); if (!root) return; root.innerHTML ; const wrap h(div, { class: agx-chat-wrap }); // Anchor bottom-right by default; clear any left/top that may override try { // Force floating even if CSS fails to load wrap.style.position fixed; wrap.style.zIndex 9999; wrap.style.right 16px; wrap.style.bottom 16px; wrap.style.left auto; wrap.style.top auto; } catch(_) {} // Header with text-only title (logo removed) const header h(div, { class: agx-chat-header }); const title h(span, { class: agx-chat-title, text: Ask AGX }); // Thinking indicator (shown on {type:thinking,state:start}, hidden on complete/error/final) const thinking h(span, { class: agx-chat-thinking, text: thinking… }); try { thinking.style.display none; thinking.setAttribute(aria-live,polite); } catch(_) {} // Minimize/expand toggle const toggle h(button, { class: agx-chat-toggle, type: button, title: Minimize chat, aria-label: Minimize chat, text: – }); // Fullscreen toggle (maximize/restore) const FS_KEY agxChatFullscreen; const fsBtn h(button, { class: agx-chat-fullscreen, type: button, title: Maximize, aria-label: Maximize, text: ⛶ }); function updateFullscreenBtn(){ try { const on wrap.classList.contains(fullscreen); fsBtn.textContent on ? 🗗 : ⛶; const label on ? Restore size : Maximize; fsBtn.setAttribute(aria-label, label); fsBtn.title label; } catch(_) {} } function setFullscreen(on){ try { if (on) wrap.classList.add(fullscreen); else wrap.classList.remove(fullscreen); sessionStorage.setItem(FS_KEY, on ? 1 : 0); updateFullscreenBtn(); } catch(_) {} } // Voice mode (header toggle) — skeleton (no TTS yet) const VOICE_KEY agxChatVoice; const voiceBtn h(button, { class: agx-chat-voice, type: button, title: Voice off, aria-label: Voice off, aria-pressed: false, text: 🔊 }); let voiceOn false; try { voiceOn sessionStorage.getItem(VOICE_KEY) 1; } catch(_) {} function updateVoiceBtn(){ try { if (voiceOn) { voiceBtn.classList.add(on); voiceBtn.setAttribute(aria-pressed,true); voiceBtn.title Voice on; voiceBtn.setAttribute(aria-label,Voice on); } else { voiceBtn.classList.remove(on); voiceBtn.setAttribute(aria-pressed,false); voiceBtn.title Voice off; voiceBtn.setAttribute(aria-label,Voice off); } } catch(_) {} } function setVoice(on){ voiceOn !!on; updateVoiceBtn(); try { sessionStorage.setItem(VOICE_KEY, voiceOn ? 1 : 0); } catch(_) {} } // Basic support check: disable when SpeechSynthesis is unavailable try { const synthOk (typeof window ! undefined) && (speechSynthesis in window) && (typeof window.SpeechSynthesisUtterance function); if (!synthOk) { voiceBtn.disabled true; voiceBtn.title Voice not supported in this browser; } } catch(_) {} voiceBtn.addEventListener(click, (e)>{ try{ e.stopPropagation(); e.preventDefault(); }catch(_){} setVoice(!voiceOn); }); // Place the button before Fullscreen in the header try { header.insertBefore(voiceBtn, fsBtn); } catch(_) {} // Collapsed state helpers const COLLAPSE_KEY agxChatCollapsed; function updateToggle(){ try { const collapsed wrap.classList.contains(collapsed); toggle.textContent collapsed ? + : –; const label collapsed ? Expand chat : Minimize chat; toggle.setAttribute(aria-label, label); toggle.title label; } catch(_) {} } function setCollapsed(on){ try { if (on) wrap.classList.add(collapsed); else wrap.classList.remove(collapsed); sessionStorage.setItem(COLLAPSE_KEY, on ? 1 : 0); updateToggle(); } catch(_) {} } // Initialize collapsed from sessionStorage try { if (sessionStorage.getItem(COLLAPSE_KEY) 1) wrap.classList.add(collapsed); } catch(_) {} updateToggle(); toggle.addEventListener(click, (e) > { try { e.stopPropagation(); setCollapsed(!wrap.classList.contains(collapsed)); } catch(_) {} }); // Fullscreen listeners/init fsBtn.addEventListener(click, (e) > { try { e.stopPropagation(); setFullscreen(!wrap.classList.contains(fullscreen)); } catch(_) {} }); try { if (sessionStorage.getItem(FS_KEY) 1) { wrap.classList.add(fullscreen); } } catch(_) {} updateFullscreenBtn(); const transcript h(div, { class: agx-chat-transcript, role: log }); const form h(div, { class: agx-chat-form }); const input h(input, { class: agx-chat-input, type: text, placeholder: Ask me anything… }); const send h(button, { class: agx-chat-send, type: button, text: Send }); // Voice input (mic) button — with optional Web Speech API support const mic h(button, { class: agx-chat-mic, type: button, title: Voice input (press and hold), aria-label: Voice input, aria-pressed: false, text: 🎤 }); // Insert mic before input: micinputSend form.appendChild(mic); form.appendChild(input); form.appendChild(send); // Mic state and handlers (press-and-hold; STT when available) let micListening false; const SR (window.SpeechRecognition || window.webkitSpeechRecognition); const sttSupported !!SR; let rec null; function ensureRecognizer(){ if (!sttSupported) return null; if (rec) return rec; try { rec new SR(); rec.interimResults true; // We prefer short utterances; continuousfalse gives end events promptly try { rec.continuous false; } catch(_) {} // Seed language from html lang> if present try { const lang (document.documentElement && document.documentElement.lang) ? String(document.documentElement.lang) : ; if (lang) rec.lang lang; } catch(_) {} rec.onresult (ev) > { try { let finalTxt ; let interimTxt ; for (let i ev.resultIndex; i ev.results.length; i++) { const r ev.resultsi; if (r.isFinal) finalTxt + r0.transcript; else interimTxt + r0.transcript; } if (finalTxt) { const cur (input.value || ).trim(); input.value (cur ? (cur + + finalTxt) : finalTxt).trim(); } // We avoid rendering interim into the UI to keep it simple/no flicker } catch(_) {} }; rec.onerror (_ev) > { // On error, stop listening state gracefully try { micListening false; updateMicBtn(); } catch(_) {} }; rec.onend () > { // Recognizer naturally ends after a short phrase try { micListening false; updateMicBtn(); } catch(_) {} }; } catch(_) { rec null; } return rec; } // If unsupported, clarify via tooltip (button still works as visual affordance) if (!sttSupported) { try { mic.title Voice input (unsupported in this browser); } catch(_) {} } // MediaRecorder fallback for browsers without Web Speech API let mediaStream null; let mediaRecorder null; let mediaChunks ; async function startRecording(){ try { // Request mic; keep it simple (let server normalize if needed) mediaStream await (navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia({ audio: true }) : Promise.reject(new Error(getUserMedia unavailable))); mediaChunks ; // Prefer webm/opus; fall back to ogg/opus when available const typeCandidates audio/webm;codecsopus,audio/webm,audio/ogg;codecsopus,audio/ogg; let selType ; if (window.MediaRecorder && typeof MediaRecorder.isTypeSupported function) { for (const t of typeCandidates){ if (MediaRecorder.isTypeSupported(t)) { selType t; break; } } } mediaRecorder new MediaRecorder(mediaStream, selType ? { mimeType: selType } : undefined); mediaRecorder.ondataavailable (e)>{ if (e && e.data && e.data.size > 0) { mediaChunks.push(e.data); } }; mediaRecorder.start(); } catch(e) { try { mic.title Microphone permission denied or unsupported; } catch(_) {} // Stop listening state immediately on failure micListening false; updateMicBtn(); } } async function stopRecordingAndTranscribe(){ try { if (mediaRecorder && mediaRecorder.state ! inactive) { await new Promise(resolve > { const done ()>{ try { resolve(); } catch(_) {} }; mediaRecorder.addEventListener(stop, done, { once: true }); try { mediaRecorder.stop(); } catch(_) { done(); } }); } } catch(_) { /* ignore */ } try { if (mediaStream) { mediaStream.getTracks().forEach(t>{ try { t.stop(); } catch(_) {} }); } } catch(_) {} const blob mediaChunks && mediaChunks.length ? new Blob(mediaChunks, { type: (mediaChunks0 && mediaChunks0.type) || audio/webm }) : null; mediaRecorder null; mediaStream null; mediaChunks ; if (!blob) { return; } try { const fd new FormData(); // Filename hint helps server-side MIME detection fallbacks const fname (blob.type && blob.type.indexOf(ogg)>0) ? voice.ogg : (blob.type && blob.type.indexOf(webm)>0 ? voice.webm : voice.wav); fd.append(file, blob, fname); const resp await fetch(/api/voice/transcribe_meta, { method: POST, body: fd }); const v await resp.json().catch(()>({})); if (resp.ok && v && v.ok && typeof v.text string && v.text.trim().length){ const cur (input.value || ).trim(); input.value (cur ? (cur + + v.text) : v.text).trim(); } else { // Graceful: keep silent; set an informative title for long-press info try { mic.title Dictation fallback failed; } catch(_) {} } } catch(_) { try { mic.title Dictation network error; } catch(_) {} } } function updateMicBtn(){ try { if (micListening) { mic.classList.add(listening); mic.setAttribute(aria-pressed,true); // Reuse the header badge to reflect mic state for now try { thinking.textContent listening…; thinking.style.display ; } catch(_) {} } else { mic.classList.remove(listening); mic.setAttribute(aria-pressed,false); // Restore/hide the header badge when not listening try { thinking.textContent thinking…; thinking.style.display none; } catch(_) {} } } catch(_) {} } function startListening(){ micListening true; updateMicBtn(); try { input.blur(); } catch(_) {} const r ensureRecognizer(); if (r) { try { r.start(); } catch(_) {} } else { // Fallback to MediaRecorder when Web Speech is unavailable startRecording(); } } function stopListening(){ if (!micListening) return; const r rec; micListening false; updateMicBtn(); if (r) { try { r.stop(); } catch(_) {} } else { stopRecordingAndTranscribe(); } } function toggleListening(){ if (!micListening) { startListening(); } else { stopListening(); } } // Touch/click dedupe to avoid double-triggering on mobile (touchstart -> click) let micTouching false; mic.addEventListener(mousedown, (e)>{ try{ e.preventDefault(); }catch(_){} startListening(); }); window.addEventListener(mouseup, stopListening); mic.addEventListener(touchstart, (e)>{ try{ e.preventDefault(); }catch(_){} micTouching true; startListening(); }, { passive: false }); window.addEventListener(touchend, ()>{ stopListening(); setTimeout(()>{ micTouching false; }, 120); }, { passive: true }); mic.addEventListener(click, (e)>{ try{ e.preventDefault(); }catch(_){} if (micTouching) return; toggleListening(); }); // Keyboard accessibility: Space/Enter toggles mic.addEventListener(keydown, (e)>{ const k e.key || e.code; if (k || k Spacebar || k Enter) { try{ e.preventDefault(); }catch(_){} toggleListening(); } }); header.appendChild(title); header.appendChild(thinking); // Ensure Voice toggle is in the header before fullscreen (insertBefore earlier may have no effect if fsBtn wasnt attached yet) try { if (!voiceBtn.parentNode) header.appendChild(voiceBtn); } catch(_) {} header.appendChild(fsBtn); header.appendChild(toggle); wrap.appendChild(header); wrap.appendChild(transcript); wrap.appendChild(form); root.appendChild(wrap); // Infinite history state and loader let histCursor null; // server-provided cursor let histDone false; let histLoading false; async function requestHistory(){ if (histLoading || histDone) return; histLoading true; try { // Ask the same WS for history const payload { type: history }; if (histCursor) payload.cursor histCursor; payload.limit 50; ensureWS(); // If socket not open yet, wait briefly if (!ws || ws.readyState ! WebSocket.OPEN) { await new Promise((resolve) > { const t setInterval(() > { if (ws && ws.readyState WebSocket.OPEN) { clearInterval(t); resolve(); } }, 100); setTimeout(() > { clearInterval(t); resolve(); }, 1500); }); } if (ws && ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify(payload)); } } catch(_) {} finally { histLoading false; } } // Preserve scroll position when prepending nodes function prependMessages(msgs){ if (!Array.isArray(msgs) || msgs.length 0) return; const prevScroll transcript.scrollTop; const prevHeight transcript.scrollHeight; // Build nodes in a fragment to minimize reflow const frag document.createDocumentFragment(); for (const m of msgs){ const role (m && m.role) || assistant; const text (m && m.content) || ; const bubble h(div, { class: agx-chat-bubble + (role user ? user : assistant) }); bubble.style.whiteSpace pre-wrap; bubble.textContent text; frag.appendChild(bubble); } // Insert at beginning if (transcript.firstChild) transcript.insertBefore(frag, transcript.firstChild); else transcript.appendChild(frag); // Adjust scroll to keep viewport anchored const newHeight transcript.scrollHeight; transcript.scrollTop newHeight - prevHeight + prevScroll; } // Attach scroll handler to top-load history transcript.addEventListener(scroll, () > { try { if (transcript.scrollTop 0 && !histDone && !histLoading) { requestHistory(); } } catch(_) {} }); // demo notice removed (streaming via CEP is now live) let ws null; let busy false; let currentChunks ; let streamBubble null; // live-updated assistant bubble during streaming function addMsg(role, text) { const bubble h(div, { class: agx-chat-bubble + (role user ? user : assistant) }); // Preserve newlines in messages bubble.style.whiteSpace pre-wrap; bubble.textContent text; transcript.appendChild(bubble); transcript.scrollTop transcript.scrollHeight; } function ensureWS() { if (ws && ws.readyState WebSocket.OPEN) return; try { if (ws) ws.close(); } catch(_) {} ws new WebSocket(WS_URL); ws.addEventListener(open, () > { try { __agxLogEvent(ws_open, { ws_url: WS_URL }); } catch(_) {} }); ws.addEventListener(message, (ev) > { try { const obj JSON.parse(ev.data); if (obj.type ai_chunk) { const chunk String(obj.text || ); currentChunks.push(chunk); if (!streamBubble) { streamBubble h(div, { class: agx-chat-bubble assistant }); streamBubble.style.whiteSpace pre-wrap; transcript.appendChild(streamBubble); } streamBubble.textContent currentChunks.join(); transcript.scrollTop transcript.scrollHeight; try { __agxLogEvent(ai_chunk, { chunk_len: chunk.length|0, count: currentChunks.length|0, text: chunk }); } catch(_) {} } else if (obj.type history) { try { const msgs Array.isArray(obj.messages) ? obj.messages : ; if (msgs.length > 0) { prependMessages(msgs); } histCursor (obj.cursor null || typeof obj.cursor undefined) ? null : obj.cursor; histDone !!obj.done || msgs.length 0; } catch(_) {} } else if (obj.type final) { const text (currentChunks.join() || ).trim(); if (streamBubble) { streamBubble.textContent text; } else { addMsg(assistant, text); } currentChunks ; streamBubble null; busy false; send.disabled false; input.disabled false; // hide thinking on final try { thinking.style.display none; } catch(_) {} // Optional TTS: speak the final reply when Voice mode is ON and supported try { if (voiceOn && typeof window ! undefined && speechSynthesis in window && typeof window.SpeechSynthesisUtterance function) { try { window.speechSynthesis.cancel(); } catch(_) {} const u new SpeechSynthesisUtterance(text); try { u.lang (document.documentElement && document.documentElement.lang) ? String(document.documentElement.lang) : u.lang; } catch(_) {} window.speechSynthesis.speak(u); } } catch(_) {} try { __agxLogEvent(final, { len: (text||).length|0 }); } catch(_) {} } else if (obj.type busy) { // server-side busy; keep disabled until a final arrives try { __agxLogEvent(busy, {}); } catch(_) {} } else if (obj.type thinking) { try { const st String(obj.state||); if (st start) { thinking.style.display ; } else { thinking.style.display none; } __agxLogEvent(thinking, { state: st }); } catch(_) {} } else if (obj.type error) { if (streamBubble) { streamBubble null; } const emsg String(obj.message || unknown); addMsg(assistant, Error: + emsg); currentChunks ; busy false; send.disabled false; input.disabled false; try { __agxLogEvent(error, { error: emsg }); } catch(_) {} } } catch (e) { // ignore } }); ws.addEventListener(close, () > { // lazy reconnect on next send try { __agxLogEvent(ws_close, { ws_url: WS_URL }); } catch(_) {} }); ws.addEventListener(error, () > { // noop try { __agxLogEvent(ws_error, {}); } catch(_) {} }); } function submit() { const text (input.value || ).trim(); if (!text || busy) return; ensureWS(); if (!ws || ws.readyState ! WebSocket.OPEN) { // slight delay to open then send const t setInterval(() > { if (ws && ws.readyState WebSocket.OPEN) { clearInterval(t); doSend(text); } }, 100); setTimeout(() > clearInterval(t), 3000); } else { doSend(text); } } function doSend(text) { try { ws.send(JSON.stringify({ type: user, text })); addMsg(user, text); input.value ; busy true; send.disabled true; input.disabled true; currentChunks ; streamBubble null; } catch (e) { addMsg(assistant, Failed to send.); } } // Enable dragging (header is the handle). Converts fixed right/bottom to left/top during drag. enableDrag(wrap, header); // JS resize fallback: drag near bottom-right corner to resize (works even when CSS resize is ignored) (function enableAutoResize(box){ let resizing false; let sx 0, sy 0, sw 0, sh 0; const MIN_W 280, MIN_H 260; // match CSS minima function begin(x, y) { try { const r box.getBoundingClientRect(); sx x; sy y; sw r.width; sh r.height; resizing true; // Ensure explicit sizes so we can adjust via JS box.style.width sw + px; box.style.height sh + px; } catch(_) {} } function move(x, y) { if (!resizing) return; const dx x - sx, dy y - sy; const vw window.innerWidth, vh window.innerHeight; let nw Math.max(MIN_W, sw + dx); let nh Math.max(MIN_H, sh + dy); nw Math.min(nw, Math.floor(vw * 0.95)); nh Math.min(nh, Math.floor(vh * 0.95)); box.style.width nw + px; box.style.height nh + px; } function end(){ if (!resizing) return; resizing false; } // Mouse handlers box.addEventListener(mousedown, (e) > { const r box.getBoundingClientRect(); // Hit area: 18px square at bottom-right if (e.clientX > r.right - 18 && e.clientY > r.bottom - 18) { e.preventDefault(); begin(e.clientX, e.clientY); } }); window.addEventListener(mousemove, (e) > move(e.clientX, e.clientY)); window.addEventListener(mouseup, end); // Touch handlers box.addEventListener(touchstart, (e) > { const t e.touches && e.touches0; if (!t) return; const r box.getBoundingClientRect(); if (t.clientX > r.right - 24 && t.clientY > r.bottom - 24) { try { e.preventDefault(); } catch(_) {} begin(t.clientX, t.clientY); } }, { passive: false }); window.addEventListener(touchmove, (e) > { const t e.touches && e.touches0; if (t) move(t.clientX, t.clientY); }, { passive: true }); window.addEventListener(touchend, end); })(wrap); function enableDrag(box, handle) { let dragging false; let startX 0, startY 0, startLeft 0, startTop 0; const toNum (v) > (typeof v number ? v : parseFloat(String(v)||0)); function begin(x, y) { try { const rect box.getBoundingClientRect(); box.style.left rect.left + px; box.style.top rect.top + px; box.style.right auto; box.style.bottom auto; startX x; startY y; startLeft toNum(box.style.left); startTop toNum(box.style.top); dragging true; box.classList.add(dragging); } catch {} } function move(x, y) { if (!dragging) return; const dx x - startX, dy y - startY; const vw window.innerWidth, vh window.innerHeight; const rect box.getBoundingClientRect(); let nl startLeft + dx, nt startTop + dy; nl Math.max(0, Math.min(nl, vw - rect.width)); nt Math.max(0, Math.min(nt, vh - rect.height)); box.style.left nl + px; box.style.top nt + px; } function end() { if (!dragging) return; dragging false; box.classList.remove(dragging); } function shouldIgnoreStart(ev){ try { const t ev.target; if (!t) return false; if (t.closest && t.closest(.agx-chat-form)) return true; const tn (t.tagName || ).toString().toLowerCase(); return tn input || tn textarea || tn button || tn a; } catch(_) { return false; } } // Mouse (header handle) handle.addEventListener(mousedown, (e) > { if (shouldIgnoreStart(e)) return; e.preventDefault(); begin(e.clientX, e.clientY); }); // Mouse (wrapper fallback - only when starting on header region) box.addEventListener(mousedown, (e) > { if (shouldIgnoreStart(e)) return; const isHeader e.target && e.target.closest && e.target.closest(.agx-chat-header); if (!isHeader) return; e.preventDefault(); begin(e.clientX, e.clientY); }); window.addEventListener(mousemove, (e) > move(e.clientX, e.clientY)); window.addEventListener(mouseup, end); // Touch (header handle) handle.addEventListener(touchstart, (e) > { const t e.touches && e.touches0; if (!t || shouldIgnoreStart(e)) return; e.preventDefault(); begin(t.clientX, t.clientY); }, { passive: false }); // Touch (wrapper fallback - only when starting on header region) box.addEventListener(touchstart, (e) > { const t e.touches && e.touches0; if (!t || shouldIgnoreStart(e)) return; const isHeader e.target && e.target.closest && e.target.closest(.agx-chat-header); if (!isHeader) return; try { e.preventDefault(); } catch(_) {} begin(t.clientX, t.clientY); }, { passive: false }); window.addEventListener(touchmove, (e) > { const t e.touches && e.touches0; if (t) move(t.clientX, t.clientY); }, { passive: true }); window.addEventListener(touchend, end); } send.addEventListener(click, submit); input.addEventListener(keydown, (e) > { if (e.key Enter) submit(); }); } if (document.readyState loading) { document.addEventListener(DOMContentLoaded, init); } else { init(); } // to the local REST endpoint /wp-json/agx/v1/log on the same origin. // Client-side error logging: forward runtime errors and unhandled rejections var endpoint /wp-json/agx/v1/log; (function setupAgxChatErrorLogging(){ var endpoint /wp-json/agx/v1/log; function sendLog(payload){ try { var body JSON.stringify(payload); if (navigator.sendBeacon) { var blob new Blob(body, { type: application/json }); navigator.sendBeacon(endpoint, blob); } else { // keepalive helps during unload/navigation fetch(endpoint, { method: POST, headers: { Content-Type: application/json }, body: body, keepalive: true }).catch(function(){}); } } catch(_){ /* ignore */ } } // Runtime errors try { window.addEventListener(error, function(ev){ try { var msg (ev && ev.message) || error; var src (ev && ev.filename) || ; var ln (ev && ev.lineno) || 0; var cn (ev && ev.colno) || 0; var stack ev && ev.error && ev.error.stack ? String(ev.error.stack) : ; sendLog({ type: error, message: String(msg || ), stack: stack ? String(stack).slice(0, 4000) : , source: String(src || ), lineno: ln|0, colno: cn|0, url: String(location && location.href || ), ua: String(navigator && navigator.userAgent || ), ts: Date.now() }); } catch(_){ /* ignore */ } }, true); } catch(_){ /* ignore */ } // Unhandled promise rejections try { window.addEventListener(unhandledrejection, function(ev){ try { var reason ev && ev.reason; var msg ; var stack ; if (reason) { if (typeof reason string) { msg reason; } else if (reason && reason.message) { msg String(reason.message); } if (reason && reason.stack) { stack String(reason.stack); } } sendLog({ type: unhandledrejection, message: String(msg || unhandledrejection), stack: stack ? String(stack).slice(0, 4000) : , source: , lineno: 0, colno: 0, url: String(location && location.href || ), ua: String(navigator && navigator.userAgent || ), ts: Date.now() }); } catch(_){ /* ignore */ } }, true); } catch(_){ /* ignore */ } })();})();/script> script idagx-hide-text-logo> (function(){try{ var sels .site-title,h1.site-title,h1.wp-block-site-title,p.wp-block-site-title,.wp-block-site-title a; for (var i0;isels.length;i++){ var nodes document.querySelectorAll(selsi); for (var j0;jnodes.length;j++){ var el nodesj; var txt (el.textContent||).trim(); if (txt AGX Network){ el.style.displaynone; } } } }catch(e){} })(); /script> /body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]