@tomtom-org/maps-sdk-plugin-agent-toolkit 0.2.12 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1,2 +1,2 @@
1
- import{Output as e,ToolLoopAgent as t,generateText as r,stepCountIs as s}from"ai";import{BaseMapModule as o,CustomGeoJSONModule as n,GeometriesModule as i,HillshadeModule as a,POIsModule as l,PlacesModule as c,RoutingModule as d,TrafficAreaAnalyticsModule as u,TrafficFlowModule as p,TrafficIncidentOverlayModule as h,TrafficIncidentsModule as m,baseMapLayerGroupNames as y,reachableRangeGeometryConfig as f,standardStyleIDs as g,themedGeometryConfig as b}from"@tomtom-org/maps-sdk/map";import{POPULATED_AREA_TAGS as w,alongRouteSearch as v,calculateReachableRanges as I,calculateRoute as S,explorationSearch as E,geocode as M,geocodeOne as x,geometryData as P,getPOICategoryCodes as D,reverseGeocode as _,routeTypes as T,search as A,searchOne as C,trafficAreaAnalytics as k,trafficIncidentDetails as O}from"@tomtom-org/maps-sdk/services";import{TomTomConfig as R,avoidableTypes as L,bboxFromGeoJSON as F,calculateProgressAtRoutePoint as N,formatDistance as j,formatDuration as B,getCoordinateAtRouteProgress as q,getPosition as $,getProgressAtNearestRoutePoint as G,getRouteProgressBetween as U,getRouteProgressForSection as W,getSectionBBox as V,poiCategories as z,sectionTypes as H,trafficIncidentRequestCategories as Y,withInsertedWaypoints as J}from"@tomtom-org/maps-sdk/core";import*as X from"@turf/turf";import*as K from"h3-js";import{z as Z}from"zod";var Q=(e,t)=>{if(!e)return t;if(!t)return e;const r=new Set(t);return e.filter(e=>r.has(e))},ee=e=>"object"==typeof e&&null!==e&&"reset"in e&&"function"==typeof e.reset,te=(e,t)=>{if(e||t)return{...e,...t}},re=(e,t)=>{const r={...e};if(!t)return r;for(const[s,o]of Object.entries(t))o?r[s]=o:delete r[s];return r},se=class{ttMap;_baseMapModule;_hillshadeModule;constructor(e){this.ttMap=e}get mapLibreMap(){return this.ttMap.mapLibreMap}async getBaseMapModule(){return this._baseMapModule??=await o.get(this.ttMap),this._baseMapModule}async getHillshadeModule(){return this._hillshadeModule??=await a.get(this.ttMap),this._hillshadeModule}reset(){this._baseMapModule=void 0,this._hillshadeModule=void 0}},oe=(e,t)=>{const r=new Set(t);if(!r.has(e))return e;for(let s=2;;s++){const t=`${e}-${s}`;if(!r.has(t))return t}},ne=async(e,t,r={})=>{if(0!==e.length)if(r.parallel)await Promise.all(e.map(t));else for(const s of e)await t(s)},ie=async(e,t,r={})=>{if(e.length<=1)return[...e];const s=e[e.length-1];return await ne(e.slice(0,-1),t,r),[s]},ae=class{handlers={};on(e,t){return(this.handlers[e]??(this.handlers[e]=[])).push(t),()=>{const r=this.handlers[e];if(!r)return;const s=r.indexOf(t);-1!==s&&r.splice(s,1)}}off(e){this.handlers[e]&&(this.handlers[e].length=0)}emit(e,t){const r=this.handlers[e];if(r?.length)for(const s of[...r])s(t)}clear(){for(const e of Object.keys(this.handlers))this.handlers[e]=[]}},le=async(e,t)=>{if(0===t.length)return;const r=(e=>{const t=/* @__PURE__ */new Set,r=[];for(const s of e){const e="string"==typeof s.id?s.id:void 0;if(e){if(t.has(e))continue;t.add(e)}r.push(s)}return r})([...e.getShown().geometry.features??[],...t]);await e.show({type:"FeatureCollection",features:r})},ce=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}findById(e){return this._entries.find(t=>t.id===e)}async addEntry(e,t,r,s){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const o=`geometries-${this._entries.length}`,n=s?oe(s,this._entries.map(e=>e.id)):o;return this._entries.push({id:n,timestamp:Date.now(),label:r,features:e,provenance:t}),this.events.emit("entries-change",this._entries),n}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}async getEntryGeometriesModule(e,t="outline"){const r=this._requireEntry(e),s={...b(),theme:t};return r._module?r._shownTheme!==t&&(r._module.applyConfig(s),r._shownTheme=t):(r._module=await i.get(this._ttMap,s),r._shownTheme=t),r._module}async showEntry(e,t="outline",r="replace"){const s=this._requireEntry(e);await this._hideOthersUnderSingleMode(e);const o=await this.getEntryGeometriesModule(e,t),n=s.features;"add"===r?await le(o,n):await(async(e,t)=>{0!==t.length?await e.show({type:"FeatureCollection",features:t}):await e.clear()})(o,n);const i=s._shown;s._shown=!0,i||this.events.emit("shown-change",this.shownEntryIds)}async _hideOthersUnderSingleMode(e){if("single"!==this._entryMode)return;const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const r of t)await this.hideEntry(r.id)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._module&&await t._module.clear(),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`CustomGeometriesState: unknown entry "${e}"`);return t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._module?.clear();this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},de=(e,t)=>e.localeCompare(t),ue=e=>[...e].sort(de),pe=e=>({places:{shownAsPin:ue(e.places.shownAsPinIds),shownAsBaseMap:ue(e.places.shownAsBaseMapIds),entryMode:e.places.entryMode},routes:{shown:ue(e.routing.shownEntryIds),entryMode:e.routing.entryMode},ranges:{shown:ue(e.ranges.shownEntryIds),entryMode:e.ranges.entryMode},traffic:{shown:ue(e.trafficAreaAnalytics.shownEntryIds),entryMode:e.trafficAreaAnalytics.entryMode,entryCount:e.trafficAreaAnalytics.entries.length},incidents:{shown:ue(e.trafficIncidents.shownEntryIds),monitored:ue(e.trafficIncidents.entries.filter(e=>"running"===e._monitor?.status).map(e=>e.id)),entryMode:e.trafficIncidents.entryMode},customGeometries:{shown:ue(e.customGeometries.shownEntryIds),entryMode:e.customGeometries.entryMode,entryCount:e.customGeometries.entries.length},byod:{shown:ue(e.byod.shownEntryIds),entryMode:e.byod.entryMode,entryCount:e.byod.entries.length}}),he=(e,t)=>{if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0},me=(e,t,r)=>{if(!he(t,r))return 0===r.length?`${e} cleared`:`${e} shown: ${r.map(e=>`"${e}"`).join(", ")}`},ye=(e,t)=>{const r=[],s=((e,t)=>{const r=[];return he(e.shownAsPin,t.shownAsPin)||r.push(0===t.shownAsPin.length?"no places shown as pins":`places shown as pins: ${t.shownAsPin.map(e=>`"${e}"`).join(", ")}`),he(e.shownAsBaseMap,t.shownAsBaseMap)||r.push(0===t.shownAsBaseMap.length?"no places shown as base-map POIs":`places shown as base-map POIs: ${t.shownAsBaseMap.map(e=>`"${e}"`).join(", ")}`),r.length?r.join("; "):void 0})(e.places,t.places);s&&r.push(s);const o=me("routes",e.routes.shown,t.routes.shown);o&&r.push(o);const n=me("ranges",e.ranges.shown,t.ranges.shown);n&&r.push(n);const i=me("traffic analytics",e.traffic.shown,t.traffic.shown);i&&r.push(i);const a=me("incidents",e.incidents.shown,t.incidents.shown);a&&r.push(a);const l=me("incidents monitor",e.incidents.monitored,t.incidents.monitored);l&&r.push(l);const c=me("custom geometries",e.customGeometries.shown,t.customGeometries.shown);c&&r.push(c);const d=me("byod",e.byod.shown,t.byod.shown);return d&&r.push(d),e.places.entryMode!==t.places.entryMode&&r.push(`places entryMode → ${t.places.entryMode}`),e.routes.entryMode!==t.routes.entryMode&&r.push(`routes entryMode → ${t.routes.entryMode}`),e.ranges.entryMode!==t.ranges.entryMode&&r.push(`ranges entryMode → ${t.ranges.entryMode}`),e.incidents.entryMode!==t.incidents.entryMode&&r.push(`incidents entryMode → ${t.incidents.entryMode}`),e.customGeometries.entryMode!==t.customGeometries.entryMode&&r.push(`custom geometries entryMode → ${t.customGeometries.entryMode}`),e.byod.entryMode!==t.byod.entryMode&&r.push(`byod entryMode → ${t.byod.entryMode}`),0===r.length?null:`[map state changed since last response: ${r.join("; ")}]`},fe=class{_ttMap;_poisModule;constructor(e){this._ttMap=e}async getPOIsModule(){return this._poisModule??=await l.get(this._ttMap),this._poisModule}get poisModule(){return this._poisModule}reset(){this._poisModule=void 0}},ge=async(e,t)=>{if(0===t.length)return;const r=(e=>{const t=/* @__PURE__ */new Set,r=[];for(const s of e){const e="string"==typeof s.id?s.id:void 0;if(e){if(t.has(e))continue;t.add(e)}r.push(s)}return r})([...e.getShown().geometry.features??[],...t]);await e.show({type:"FeatureCollection",features:r})},be=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this._hideEntry(e)),this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)),this.events.emit("mode-change",e))}async getEntryPlacesModule(e,t){const r=this._requireEntry(e);return r._modules??={},r._modules.places?r._shownAs!==t&&r._modules.places.applyTheme(t):r._modules.places=await c.get(this._ttMap,{theme:t}),r._modules.places}async getEntryGeometriesModule(e,t="outline"){const r=this._requireEntry(e);r._modules??={};const s={...b(),theme:t};return r._modules.geometries?r._modules.geometries.applyConfig(s):r._modules.geometries=await i.get(this._ttMap,s),r._modules.geometries}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`PlacesState: unknown entry "${e}"`);return t}get entries(){return this._entries}get latestPlace(){return this._entries.at(-1)?.places}async addPlaceResult(e,t,r,s,o){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this._hideEntry(e)),this._entries=[]);const n=`places-${this._entries.length}`,i=r?oe(r,this._entries.map(e=>e.id)):n;return this._entries.push({id:i,timestamp:Date.now(),label:t,places:"features"in e?e.features:[e],...s?.length&&{connections:s},...o?.length&&{geometries:o}}),this.events.emit("entries-change",this._entries),o?.length&&this.events.emit("geometries-change",{entryId:i,count:o.length}),i}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}findPlaceById(e){for(let t=this._entries.length-1;t>=0;t--){const r=this._entries[t];if(!r._byId){r._byId={};for(const e of r.places)r._byId[e.id]=e}const s=r._byId[e];if(s)return{place:s,entryId:r.id}}}getGeometryForPlace(e){for(let t=this._entries.length-1;t>=0;t--){const r=this._entries[t];if(!r.geometries?.length)continue;r._geometryByPlaceId||(r._geometryByPlaceId=this._buildGeometryIndex(r));const s=r._geometryByPlaceId[e];if(s)return s}}async fetchPlaceGeometry(e){const t=this.getGeometryForPlace(e);if(t)return t;const r=this.findPlaceById(e);if(!r)return;if(!r.place.properties.dataSources?.geometry?.id)return;const s=(await P({geometries:[r.place]})).features[0];if(!s)return;const o=this._entries.find(e=>e.id===r.entryId);return o&&(o.geometries??=[],o.geometries.push(s),o._geometryByPlaceId??={},o._geometryByPlaceId[e]=s,this.events.emit("geometries-change",{entryId:o.id,count:o.geometries.length})),s}async fetchGeometriesForEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)return[];const r=/* @__PURE__ */new Set;for(const l of t.geometries??[])"string"==typeof l.id&&r.add(l.id);const s=[],o=/* @__PURE__ */new Set;for(const l of t.places){const e=l.properties.dataSources?.geometry?.id;e&&(r.has(e)||o.has(e)||(o.add(e),s.push(l)))}if(0===s.length)return t.geometries??[];const n=[];for(let l=0;l<s.length;l+=5)n.push(s.slice(l,l+5));const i=await Promise.all(n.map(e=>P({geometries:e}))),a=[...t.geometries??[]];for(const l of i)a.push(...l.features);return t.geometries=a,t._geometryByPlaceId=void 0,this.events.emit("geometries-change",{entryId:t.id,count:a.length}),a}async showPlaceGeometries(e,t="outline",r="replace",s){const o=s??this._latestGeometriesEntryId();if(!o)return;const n=await this.getEntryGeometriesModule(o,t);"add"===r?await ge(n,e):await(async(e,t)=>{0!==t.length?await e.show({type:"FeatureCollection",features:t}):await e.clear()})(n,e)}_latestGeometriesEntryId(){for(let e=this._entries.length-1;e>=0;e--)if(this._entries[e].geometries?.length)return this._entries[e].id;return this._entries.at(-1)?.id}_buildGeometryIndex(e){const t=/* @__PURE__ */new Map;for(const s of e.geometries??[])"string"==typeof s.id&&t.set(s.id,s);const r={};for(const s of e.places){const e=s.properties.dataSources?.geometry?.id;if(!e)continue;const o=t.get(e);o&&(r[s.id]=o)}return r}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shownAs&&e.add(t.id);return e}get shownAsPinIds(){return this._idsShownAs("pin")}get shownAsBaseMapIds(){return this._idsShownAs("base-map")}get shownAsPinClusteredIds(){return this._idsShownAs("pin-clustered")}_idsShownAs(e){const t=/* @__PURE__ */new Set;for(const r of this._entries)r._shownAs===e&&t.add(r.id);return t}getShownMarkerType(e){return this._entries.find(t=>t.id===e)?._shownAs}getShownFeatures(){const e=[];for(const t of this._entries)t._shownAs&&e.push(...t.places);return e}async setShownEntries(e,t){const r=new Set(e),s=[],o=[];for(const n of this._entries)r.has(n.id)?s.push(n):n._shownAs===t&&o.push(n);for(const n of o)await this._hideEntry(n);for(const n of s)await this._showEntryAs(n,t);return this.events.emit("shown-change",this.shownEntryIds),this.getShownFeatures()}async addShownEntries(e,t){if("single"===this._entryMode){const r=e.at(-1);return r?this.setShownEntries([r],t):this.getShownFeatures()}const r=e.flatMap(e=>{const t=this._entries.find(t=>t.id===e);return t?[t]:[]}).filter(e=>e._shownAs!==t);for(const s of r)await this._showEntryAs(s,t);return r.length>0&&this.events.emit("shown-change",this.shownEntryIds),this.getShownFeatures()}async removeShownEntries(e){const t=e.flatMap(e=>{const t=this._entries.find(t=>t.id===e);return t?._shownAs?[t]:[]});for(const r of t)await this._hideEntry(r);return t.length>0&&this.events.emit("shown-change",this.shownEntryIds),this.getShownFeatures()}async clearShownEntries(){const e=this._entries.filter(e=>e._shownAs);if(0!==e.length){for(const t of e)await this._hideEntry(t);this.events.emit("shown-change",this.shownEntryIds)}}async clearShown(){await this.clearShownEntries(),await this.clearShownGeometries()}async clearShownGeometries(e){const t=e?e.flatMap(e=>{const t=this._entries.find(t=>t.id===e);return t?._modules?.geometries?[t]:[]}):this._entries.filter(e=>e._modules?.geometries);if(0!==t.length)for(const r of t)await r._modules.geometries.clear(),this.events.emit("geometries-change",{entryId:r.id,count:0})}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);if(-1===t)return;const r=this._entries[t],s=!!r._shownAs;r._modules&&(await(r._modules.places?.clear()),await(r._modules.geometries?.clear())),this._entries.splice(t,1),this.events.emit("entries-change",this._entries),s&&this.events.emit("shown-change",this.shownEntryIds)}async _showEntryAs(e,t){const r=await this.getEntryPlacesModule(e.id,t);await r.show({type:"FeatureCollection",features:e.places}),e.connections?.length&&await r.showConnections(e.connections),e._shownAs=t}async _hideEntry(e){e._shownAs&&(e._modules?.places&&await e._modules.places.clear(),e._shownAs=void 0)}reset(){for(const e of this._entries)e._modules&&(e._modules.places?.clear(),e._modules.geometries?.clear());this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},we=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}async getEntryGeometriesModule(e,t="outline"){const r=this._requireEntry(e);return r._modules??={},r._modules.geometries?r._shownTheme!==t&&(r._modules.geometries.applyConfig(f(void 0,t)),r._shownTheme=t):(r._modules.geometries=await i.get(this._ttMap,f(void 0,t)),r._shownTheme=t),r._modules.geometries}async getEntryPlacesModule(e){const t=this._requireEntry(e);return t._modules??={},t._modules.places||(t._modules.places=await c.get(this._ttMap)),t._modules.places}async markEntryShown(e){const t=this._requireEntry(e);if("single"===this._entryMode){const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const e of t)await this.hideEntry(e.id)}const r=t._shown;t._shown=!0,r||this.events.emit("shown-change",this.shownEntryIds)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._modules?.geometries&&await t._modules.geometries.clear(),t._modules?.places&&await t._modules.places.clear(),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`RangeState: unknown entry "${e}"`);return t}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}async addEntry(e){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const t=`ranges-${this._entries.length}`;return this._entries.push({id:t,timestamp:Date.now(),...e}),this.events.emit("entries-change",this._entries),t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._modules?.geometries?.clear(),e._modules?.places?.clear();this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},ve=class{_ttMap;_entries=[];_planningSlots=[];_params={};_entryMode="multiple";_mainColor;_lastShownEntryId;events=new ae;constructor(e){this._ttMap=e}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}async getEntryRoutingModule(e){const t=this._requireEntry(e);return t._module||(t._module=await d.get(this._ttMap)),t._module}async showEntry(e){const t=this._requireEntry(e);if("single"===this._entryMode){const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const e of t)await this.hideEntry(e.id)}const r=await this.getEntryRoutingModule(e);void 0!==this._mainColor&&r.applyConfig({theme:{mainColor:this._mainColor}}),await r.showRoutes(t.data),t.waypoints.length&&await r.showWaypoints(t.waypoints);const s=t._shown;t._shown=!0,this._lastShownEntryId=e,s||this.events.emit("shown-change",this.shownEntryIds)}get mainColor(){return this._mainColor}setMainColor(e){if(this._mainColor=e,void 0!==e)for(const t of this._entries)t._shown&&t._module&&t._module.applyConfig({theme:{mainColor:e}})}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._module&&(await t._module.clearRoutes(),await t._module.clearWaypoints()),t._shown=!1,this._lastShownEntryId===e&&(this._lastShownEntryId=this._entries.findLast(e=>e._shown)?.id),this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`RoutingState: unknown entry "${e}"`);return t}get currentEntryModule(){if(!this._lastShownEntryId)return;const e=this._entries.find(e=>e.id===this._lastShownEntryId);return e?._shown?e._module:void 0}get entries(){return this._entries}get currentRoutes(){return this._entries.at(-1)?.data}get currentWaypoints(){return this._entries.at(-1)?.waypoints}get planningSlots(){return this._planningSlots}get params(){return this._params}async addRoutes(e,t,r){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const s=`routes-${this._entries.length}`;return this._entries.push({id:s,timestamp:Date.now(),label:r,data:e,waypoints:t,params:{...this._params}}),this._planningSlots=[...t],this.events.emit("entries-change",this._entries),this.events.emit("planning-change",this._planningSlots),s}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}setWaypointAt(e,t){const r=Math.max(2,e+1);for(;this._planningSlots.length<r;)this._planningSlots.push(null);this._planningSlots[e]=t,this.events.emit("planning-change",this._planningSlots)}setParams(e){this._params={...this._params,...e},this.events.emit("params-change",this._params)}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._module?.clearRoutes(),e._module?.clearWaypoints();this._entries=[],this._planningSlots=[],this._params={},this._lastShownEntryId=void 0,this._mainColor=void 0,this.events.emit("entries-change",this._entries),this.events.emit("planning-change",this._planningSlots),this.events.emit("params-change",this._params),this.events.emit("shown-change",this.shownEntryIds)}},Ie={type:"circle",paint:{"circle-radius":4,"circle-color":"#0a3653","circle-stroke-color":"#ffffff","circle-stroke-width":1}},Se={type:"line",paint:{"line-color":"#0a3653","line-width":2}},Ee={type:"fill",paint:{"fill-color":"#0a3653","fill-opacity":.25,"fill-outline-color":"#0a3653"}},Me={Point:"point",MultiPoint:"point",LineString:"line",MultiLineString:"line",Polygon:"polygon",MultiPolygon:"polygon"},xe=e=>{const t=(e=>{const t=/* @__PURE__ */new Set;for(const r of e.features){const e=r.geometry;if(!e)continue;if("GeometryCollection"===e.type){for(const r of e.geometries){const e=Me[r.type];e&&t.add(e)}continue}const s=Me[e.type];s&&t.add(s)}return t})(e),r=[];return t.has("polygon")&&r.push(Ee),t.has("line")&&r.push(Se),t.has("point")&&r.push(Ie),0===r.length&&r.push(Ie),r},Pe=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}findById(e){return this._entries.find(t=>t.id===e)}async addEntry(e,t,r={}){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const s=oe(r.explicitId??`byod-${this._entries.length}`,this._entries.map(e=>e.id));return this._entries.push({id:s,timestamp:Date.now(),label:t,data:e,source:r.source??{kind:"integrator"},layers:r.layers??xe(e)}),this.events.emit("entries-change",this._entries),s}async getEntryModule(e){const t=this._requireEntry(e);return t._module??=await n.get(this._ttMap,{sources:{data:{layers:t.layers}}}),t._module}async showEntry(e){const t=this._requireEntry(e);await this._hideOthersUnderSingleMode(e),await(await this.getEntryModule(e)).show(t.data,"data");const r=t._shown;t._shown=!0,r||this.events.emit("shown-change",this.shownEntryIds)}async _hideOthersUnderSingleMode(e){if("single"!==this._entryMode)return;const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const r of t)await this.hideEntry(r.id)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(await(t._module?.clear()),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`BYODState: unknown entry "${e}"`);return t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._module?.clear().catch(()=>{});this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},De=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}findById(e){return this._entries.find(t=>t.id===e)}async addEntry(e,t,r,s){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const o=`tta-${this._entries.length}`,n=s?oe(s,this._entries.map(e=>e.id)):o;return this._entries.push({id:n,timestamp:Date.now(),label:t,data:e,params:r}),this.events.emit("entries-change",this._entries),n}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}async getEntryModule(e){const t=this._requireEntry(e);return t._module||(t._module=await u.get(this._ttMap),t._configChangeUnsub=t._module.events.on("config-change",r=>{t._config=r,this.events.emit("config-change",{entryId:e,config:r})})),t._module}async showEntry(e){const t=this._requireEntry(e);if("single"===this._entryMode){const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const e of t)await this.hideEntry(e.id)}const r=await this.getEntryModule(e);await r.clear(),await r.show(t.data);const s=t._shown;t._shown=!0,s||this.events.emit("shown-change",this.shownEntryIds)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(await(t._module?.clear()),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);if(-1===t)return;await this.hideEntry(e);const r=this._entries[t];r._configChangeUnsub?.(),r._configChangeUnsub=void 0,r._module=void 0,this._entries.splice(t,1),this.events.emit("entries-change",this._entries)}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`TrafficAreaAnalyticsState: unknown entry "${e}"`);return t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._configChangeUnsub?.(),e._module?.clear();this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},_e=Z.discriminatedUnion("kind",[Z.object({kind:Z.literal("place").describe('Use the exact literal "place".'),id:Z.string().min(1).describe("Id of a single place; resolves to that place’s footprint.")}).describe("A single place footprint."),Z.object({kind:Z.literal("places").describe('Use the exact literal "places".'),id:Z.string().min(1).describe("Id of a places-entry; resolves to every footprint in the entry.")}).describe("Every footprint in a places entry."),Z.object({kind:Z.literal("ranges").describe('Use the exact literal "ranges".'),id:Z.string().min(1).describe("Id of a ranges-entry; resolves to every isochrone polygon in the entry.")}).describe("Every isochrone polygon in a ranges entry."),Z.object({kind:Z.literal("customGeometries").describe('Use the exact literal "customGeometries".'),id:Z.string().min(1).describe("Id of a custom-geometries entry produced by a previous `processData` call.")}).describe("A derived entry produced by a previous `processData` call.")]).describe('Tagged id `{ kind, id }`. `kind` must be exactly one of: "place" | "places" | "ranges" | "customGeometries" (do NOT use variants like "ranges-entry", "place-footprints", etc.). Use `recallGeometries` to list every available `{kind, id}` pair.'),Te=Z.array(_e).min(1).describe('Tagged ids `{ kind, id }` of polygon sources to feed the run. `kind` must be exactly one of `"place"` | `"places"` | `"ranges"` | `"customGeometries"` (no variants). Mix kinds freely (e.g. union place footprints with isochrones). Use `recallGeometries` to list available ids.'),Ae="`geometry.properties` shape varies by source — place footprints: `CommonPlaceProps`; ranges: service response (`range.type`, `range.value`); custom: whatever the producing op set. Treat as opaque unless you know the source. Every feature also carries `properties._source: { kind, id }` (the tagged id it was passed in under) — use it to filter / partition mixed inputs by role inside the sandbox.",Ce="Source ids that could not contribute (unknown id, no geometry data source, fetch failure, or empty entry).",ke="Tagged input ids that contributed at least one polygon to the run, in input order.",Oe='`places` is a FeatureCollection of Point Features. `Place.properties` shape:\n```ts\ntype PlaceProperties = {\n type: "POI" | "Street" | "Geography" | "Point Address" | "Address Range" | "Cross Street";\n address: {\n freeformAddress: string;\n streetNumber?: string; streetName?: string;\n municipality?: string; municipalitySubdivision?: string;\n countrySubdivision?: string; countrySubdivisionName?: string;\n countrySecondarySubdivision?: string; countryTertiarySubdivision?: string;\n postalCode?: string; extendedPostalCode?: string;\n countryCode?: string; countryCodeISO3?: string; country?: string;\n localName?: string;\n };\n poi?: { // when type === "POI"\n name: string; phone?: string; url?: string;\n brands?: string[];\n categories: string[]; // POICategory enum, e.g. "RESTAURANT", "CAFE_PUB", "GAS_STATION", "HOTEL_MOTEL", "ELECTRIC_VEHICLE_STATION"\n localizedCategories: string[]; // language-sensitive\n openingHours?: { mode: string; timeRanges: Array<{ startTime: {date,hour,minute}, endTime: {date,hour,minute} }>; alwaysOpenThisPeriod: boolean };\n timeZone?: { name: string };\n };\n // Search hits also carry:\n score?: number; // relevance (higher = better)\n distance?: number; // meters from geoBias, when provided\n info?: string;\n // Geography hits also carry:\n geographyType?: Array<"Country"|"CountrySubdivision"|"CountrySecondarySubdivision"|"CountryTertiarySubdivision"|"Municipality"|"MunicipalitySubdivision"|"Neighbourhood"|"PostalCodeArea">;\n'+({}.experimentalSearch?' // Experimental — populated only when `experimentalSearch` is on and the place came from\n // the exploration-search backend (DE / NL / FR area-tag pipeline).\n areaId?: string; // id of the surrounding small-area polygon (few km², not a whole municipality)\n areaCountry?: string; // ISO2 country of the area polygon\n areaTags?: string[]; // e.g. "coastal", "walkable", "transit_connected"\n':"")+" // Optional: mapcodes, entryPoints, addressRanges, relatedPois, chargingPark, dataSources.\n};\n```\nAreas (Street, Geography, …) also carry `feature.bbox`. Coordinates are `[lng, lat]`; guard `?` fields with `?.` / `??`.",Re=Z.union([Z.array(Z.number()).refine(e=>4===e.length,{message:"BBox must have 4 elements"})]).describe("[minLng, minLat, maxLng, maxLat] or [W,S,E,N]"),Le=Z.object({type:Z.literal("Polygon"),coordinates:Z.array(Z.array(Z.array(Z.number())))}),Fe=Z.object({type:Z.literal("MultiPolygon"),coordinates:Z.array(Z.array(Z.array(Z.array(Z.number()))))}),Ne=Z.union([Le,Fe]),je=Z.string().regex(/^[a-z0-9][a-z0-9-]{1,48}$/,"Use lowercase letters, digits, and hyphens (2–49 chars).").optional().describe('Compact lowercase kebab-case id derived from the user prompt (e.g. "vegan-cafes-amsterdam", "ev-stations-along-route"). Avoid generic names like "places" / "results". Auto-suffixed on collision — do not track uniqueness yourself. Default: `places-N`.'),Be=Z.enum(["add","replace"]).optional().describe('WITHIN THE TARGETED ENTRY ONLY — controls how the new polygons combine with what is already rendered on this entry\'s GeometriesModule layer. Does NOT affect other entries (use `hidePreviousEntries` for that). "replace" (default) clears this entry\'s layer first; "add" stacks (deduped by feature id).'),qe=Z.object({show:Z.boolean().optional().describe("Also render the polygons on the map. Default: false."),mode:Be,theme:Z.enum(["filled","outline","inverted"]).optional().describe('Visual theme. Default: "outline".')}).optional().describe('Batch-fetch boundary polygons for the resulting places and cache them on the entry. Use when the user wants the *area / shape / boundary* (e.g. "outline this neighbourhood", "show the city footprint"). Set `show: true` to also render. Skips places without a geometry source (most addresses/streets).'),$e=Z.object({theme:Z.enum(["filled","outline","inverted"]).optional().describe('Visual theme. Default: "outline".'),placeIds:Z.array(Z.string()).optional().describe("Subset of place ids within the targeted entries — render boundaries only for these. Ids outside the entries are ignored. Default: all places with a geometry source."),mode:Be}).describe('Render boundary polygons for the targeted entries. Use when the user wants the *area / shape / outline / footprint* (e.g. "outline this neighbourhood", "show the city footprint"). Independent of `markerType` — set both to render pins AND boundaries.'),Ge=Z.enum(["base-map","pin","pin-clustered","none"]).describe('"pin" for few results; "base-map" for 10+; "pin-clustered" aggregates nearby pins under a count badge (best for dense city-scale POI sets — clusters expose aggregated `placeTypes` / `poiCategories`); "none" for towns/cities/areas.'),Ue=e=>Z.union([Z.literal("all"),Z.array(Z.string())]).optional().describe(`Which previously-shown ${e} entries to hide before showing the new ones. \`"all"\` clears every previous entry (use for the typical "show X" — replace existing); an array hides only the listed entry IDs. Omit to stack on top of existing entries (use for "also show", "additionally", "and X"). The entries being shown by this call are never hidden. Only applied when \`entryMode === "multiple"\` (in \`single\` mode every other entry is auto-hidden, so this prop is ignored).`),We=Z.object({markerType:Ge,zoomMode:Z.enum(["auto","none"]).describe('Camera behaviour. "none" keeps the map still.'),hidePreviousEntries:Ue("places")}),Ve=Z.object({markerType:Z.boolean(),zoomMode:Z.boolean()}),ze={mode:Z.literal("within"),viewport:Z.literal(!0).optional().describe("When true, search within the current map viewport bounds. EXCLUSIVE with all other within fields.")},He=Z.enum(["poi","place"]).optional().describe('Drives query resolution. `"poi"` for venues / landmarks / businesses (e.g. "Eiffel Tower"). `"place"` for addresses, cities, neighborhoods, parks, postal codes (e.g. "De Jordaan, Amsterdam", "1600 Pennsylvania Ave"). Default: "place".'),Ye=["boundingBox","viewport","query","placeId"],Je=Z.object({...ze,boundingBox:Re.optional().describe("Single bbox [W,S,E,N]. EXCLUSIVE with viewport / query / placeId."),query:Z.string().optional().describe('Resolve a name/address/POI → its boundary polygon (or bbox) as search area (e.g. "Paris", "De Jordaan, Amsterdam"). Pair with `queryAs` to disambiguate. EXCLUSIVE with boundingBox / viewport / placeId.'),queryAs:He,placeId:Z.string().optional().describe("ID of a session place — its boundary polygon (fetched on demand) is the search area. EXCLUSIVE with boundingBox / viewport / query.")}).refine(e=>1===Ye.filter(t=>void 0!==e[t]).length,{message:"`within` requires EXACTLY ONE of: boundingBox, viewport, query, placeId."}),Xe=["position","viewport","query"],Ke=Z.object({mode:Z.literal("nearby"),position:Z.array(Z.number()).length(2).optional().describe("Bias point [lng, lat]. EXCLUSIVE with viewport / query."),viewport:Z.literal(!0).optional().describe("When true, bias toward the current map viewport centre. EXCLUSIVE with position / query."),query:Z.string().optional().describe("Resolve a name/address/POI → its centroid as bias point. Pair with `queryAs` to disambiguate. EXCLUSIVE with position / viewport."),queryAs:He,radiusMeters:Z.number().positive().optional().describe("Hard radius cap (metres) around the bias point. Independent of bias source.")}).refine(e=>1===Xe.filter(t=>void 0!==e[t]).length,{message:"`nearby` requires EXACTLY ONE of: position, viewport, query (radiusMeters is independent)."}),Ze=Z.object({mode:Z.literal("global")}),Qe=Z.union([Je,Ke,Ze]),et=["places","routes","incidents","customGeometries","trafficAreaAnalytics","byod"],tt=e=>Z.object({kinds:Z.array(Z.enum(e)).min(1)}),rt={places:{fieldName:"placesEntryIDs",fieldLabel:"`placesEntryIDs`",buildField:e=>(({verb:e,extra:t})=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing places entries to ${e} (e.g. ["places-2"], ["places-1", "places-2"]). Use recallPlaces to list IDs. Omit to ${e} the latest entry.`+(t?` ${t}`:"")))({verb:e.verb,extra:"Injected as `places` (merged) and `placesByEntry[id]`. Undefined when omitted. "+e.requiredHint}),sandboxDoc:"• `places` — merged FeatureCollection over `placesEntryIDs`; `placesByEntry[id]` keeps them separate.",schemaDoc:Oe,recallTool:"recallPlaces"},routes:{fieldName:"routesEntryIDs",fieldLabel:"`routesEntryIDs`",buildField:e=>(({verb:e,extra:t})=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing routes entries to ${e} (e.g. ["routes-0"], ["routes-0", "routes-1"]). Use recallRoutes to list IDs. Omit to ${e} the latest route.`+(t?` ${t}`:"")))({verb:e.verb,extra:"Injected as `routes` (merged) and `routesByEntry[id]`. Undefined when omitted. "+e.requiredHint}),sandboxDoc:"• `routes` — merged FeatureCollection over `routesEntryIDs`; `routesByEntry[id]` keeps them separate.",schemaDoc:'`routes` is a FeatureCollection of LineString Features (each route has `feature.bbox`). `Route.properties` shape:\n```ts\ntype RouteProperties = {\n index: number; // 0 = main route, 1+ = alternatives\n summary: {\n lengthInMeters: number;\n travelTimeInSeconds: number; // includes traffic delay\n noTrafficTravelTimeInSeconds?: number;\n trafficDelayInSeconds: number;\n trafficLengthInMeters: number;\n departureTime: Date; arrivalTime: Date;\n // EV-only:\n batteryConsumptionInkWh?: number; batteryConsumptionInPCT?: number;\n remainingChargeAtArrivalInkWh?: number; remainingChargeAtArrivalInPCT?: number;\n totalChargingTimeInSeconds?: number;\n // Combustion-only:\n fuelConsumptionInLiters?: number;\n };\n // Segments grouped by characteristic. Every section has { id, startPointIndex, endPointIndex }\n // unless noted; *Index values reference `geometry.coordinates`.\n sections: {\n leg: Array<{ id; startPointIndex?; endPointIndex?; summary: RouteSummary }>; // ALWAYS present; one per non-circle waypoint pair\n country?: Array<{ id; startPointIndex; endPointIndex; countryCodeISO3: string }>; // ISO 3166-1 alpha-3\n tollVignette?: Array<{ id; startPointIndex; endPointIndex; countryCodeISO3: string }>;\n traffic?: Array<{\n id; startPointIndex; endPointIndex;\n categories: TrafficIncidentCategory[]; // e.g. "Jam","Accident","RoadWorks","Closure"\n magnitudeOfDelay: "Unknown"|"Minor"|"Moderate"|"Major"|"Undefined";\n tec: { effectCode?: number; causes?: Array<{ mainCauseCode: number; subCauseCode?: number }> };\n effectiveSpeedInKmh?: number;\n delayInSeconds?: number;\n }>;\n importantRoadStretch?: Array<{ id; startPointIndex; endPointIndex; index: number; streetName?: string; roadNumbers?: string[] }>;\n lanes?: Array<{\n id; startPointIndex; endPointIndex;\n lanes: Array<{ directions: PossibleLaneDirection[]; follow?: PossibleLaneDirection }>;\n laneSeparators: PossibleLaneSeparator[];\n properties?: string[]; // e.g. "IS_MANEUVER"\n }>;\n speedLimit?: Array<{ id; startPointIndex; endPointIndex; maxSpeedLimitInKmh: number }>;\n roadShields?: Array<{ id; startPointIndex; endPointIndex; roadShieldReferences: RoadShieldReference[] }>;\n // Plain section ranges (id + startPointIndex + endPointIndex only):\n ferry?: SectionProps[]; motorway?: SectionProps[]; toll?: SectionProps[]; tunnel?: SectionProps[];\n unpaved?: SectionProps[]; urban?: SectionProps[]; pedestrian?: SectionProps[];\n carpool?: SectionProps[]; carTrain?: SectionProps[];\n lowEmissionZone?: SectionProps[]; vehicleRestricted?: SectionProps[];\n };\n progress?: Array<{ pointIndex: number; travelTimeInSeconds?: number; distanceInMeters?: number }>; // cumulative milestones\n guidance?: { instructions: Array<{ pointIndex: number; maneuver: string; street?: string; message?: string }> };\n};\n```\n`pointIndex` values reference positions in `geometry.coordinates`. Guard `?` fields with `?.` / `??`. For "how long / how far" questions, use `summary.lengthInMeters` / `summary.travelTimeInSeconds`.',recallTool:"recallRoutes"},incidents:{fieldName:"incidentsEntryIDs",fieldLabel:"`incidentsEntryIDs`",buildField:e=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing incidents entries to ${e.verb} (e.g. ["incidents-0"]). Injected as \`incidents\` (merged) and \`incidentsByEntry[id]\`. Undefined when omitted. Use \`recallState\` to list incident entries. `+e.requiredHint),sandboxDoc:"• `incidents` — flat `TrafficIncident[]` over `incidentsEntryIDs`; `incidentsByEntry[id]` keeps them separate.",schemaDoc:"",recallTool:"recallState"},customGeometries:{fieldName:"geometriesEntryIDs",fieldLabel:"`geometriesEntryIDs`",buildField:e=>Te.optional().describe("Tagged polygon sources `{ kind, id }` (see geometriesEntryIDsSchema). Injected as `geometries`. Undefined when omitted. "+e.requiredHint),sandboxDoc:"• `geometries` — mixed Polygon/MultiPolygon array over `geometriesEntryIDs`; every feature carries `properties._source: { kind, id }` so you can filter by role (see `GEOMETRIES_PROPS_DOC`).",schemaDoc:"`geometries` is an array of Polygon/MultiPolygon Features. Each has `id` (the geometry-data-source id, matches `place.properties.dataSources.geometry.id`) and `bbox`. `properties` varies by source — see `GEOMETRIES_PROPS_DOC`. Use `turf.area`, `turf.union`, `turf.intersect`, `turf.bbox`, `turf.centroid`, `turf.buffer`, … directly on these features. For h3, convert via `h3.polygonToCells(feature.geometry.coordinates, res)`.",recallTool:"recallGeometries"},trafficAreaAnalytics:{fieldName:"trafficAreaAnalyticsEntryIDs",fieldLabel:"`trafficAreaAnalyticsEntryIDs`",buildField:e=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing traffic-area-analytics entries to ${e.verb} (e.g. ["tta-0"]). Injected as \`trafficAreaAnalytics\` (merged FeatureCollection of tile/hex regions with metric properties) and \`trafficAreaAnalyticsByEntry[id]\`. Undefined when omitted. Use \`recallState\` to list available ids. `+e.requiredHint),sandboxDoc:"• `trafficAreaAnalytics` — merged TrafficAreaAnalytics FeatureCollection over `trafficAreaAnalyticsEntryIDs`. Each feature is a tile / hex region with metric properties on `feature.properties` (e.g. `congestionLevel`, `speed`, `freeFlowSpeed`, `travelTime`, `networkLength`). `trafficAreaAnalyticsByEntry[id]` keeps each request's collection separate. The collection's top-level `properties` (metrics list, date range) come from the first entry when ids span multiple requests.",schemaDoc:"",recallTool:"recallState"},byod:{fieldName:"byodEntryIDs",fieldLabel:"`byodEntryIDs`",buildField:e=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing BYOD entries to ${e.verb} (e.g. ["byod-0"]) — customer-authored GeoJSON layers. Injected as \`byod\` (merged FeatureCollection) and \`byodByEntry[id]\`. Undefined when omitted. Use \`recallByod\` to list available ids. `+e.requiredHint),sandboxDoc:"• `byod` — merged customer-authored GeoJSON FeatureCollection over `byodEntryIDs`; mixed Point / LineString / Polygon features are normal (BYOD layers carry whatever the integrator uploaded). `byodByEntry[id]` keeps each entry separate. Treat `feature.properties` as opaque application-specific data.",schemaDoc:"",recallTool:"recallByod"}},st=(e,t)=>et.filter(r=>e.includes(r)&&(!t||t.kinds.includes(r))),ot=e=>e.map(e=>rt[e].fieldLabel).join(" / "),nt=e=>et.filter(t=>e.includes(t)).map(e=>rt[e].sandboxDoc).join("\n"),it=e=>et.filter(t=>e.includes(t)).map(e=>rt[e].schemaDoc).filter(e=>e.length>0).join("\n\n"),at=(e,t)=>{const r={};for(const s of et){if(!e.includes(s))continue;const o=rt[s];r[o.fieldName]=o.buildField(t)}return r},lt=e=>{for(const t of et)if(e[rt[t].fieldName]?.length)return!0;return!1},ct=e=>`At least one of ${ot(e)} must be set. Call ${Array.from(new Set(e.map(e=>rt[e].recallTool))).map(e=>`\`${e}\``).join(" / ")} first to list available ids.`,dt=(Z.union([Z.tuple([Z.number(),Z.number(),Z.number(),Z.number()]),Z.object({bbox:Z.tuple([Z.number(),Z.number(),Z.number(),Z.number()]),padding:Z.number().optional(),animate:Z.boolean().optional()})]).describe("Bounds to fit the camera to: a `[W, S, E, N]` BBox tuple, or `{ bbox: [W,S,E,N], padding?: number, animate?: boolean }`. Default padding: 50px."),Z.object({bbox:Z.tuple([Z.number(),Z.number(),Z.number(),Z.number()]).describe("Bounds the camera was fit to."),padding:Z.number().describe("Padding (px) applied around the bbox.")})),ut=e=>Array.isArray(e)&&4===e.length&&e.every(e=>"number"==typeof e&&Number.isFinite(e)),pt=e=>{if(ut(e))return!0;if(!e||"object"!=typeof e)return!1;const t=e;return!!ut(t.bbox)&&(!!(void 0===t.padding||"number"==typeof t.padding&&Number.isFinite(t.padding))&&!(void 0!==t.animate&&"boolean"!=typeof t.animate))},ht=(e,t)=>{const r=ut(t)?t:t.bbox,s=(ut(t)?void 0:t.padding)??50,o=ut(t)?void 0:t.animate;return e.baseMap.mapLibreMap.fitBounds(r,{padding:s,...void 0!==o&&{animate:o}}),{bbox:r,padding:s}},mt=async(e,t,r)=>{if("multiple"!==e.entryMode||void 0===r)return;const s=new Set(t),o="all"===r?[...e.shownEntryIds].filter(e=>!s.has(e)):r.filter(e=>!s.has(e));for(const n of o)await e.hideEntry(n)},yt=Z.union([Z.object({query:Z.string().describe("Location string to resolve."),queryAs:Z.enum(["poi","place"]).describe('"poi" for venues / landmarks / businesses; "place" for addresses, cities, neighborhoods, parks, postal codes, and other geographies.')}),Z.object({position:Z.object({lng:Z.number(),lat:Z.number()}).describe("Explicit coordinates — use when you already have a position.")}),Z.object({placeId:Z.string().describe("ID of a place stored in session state (any previous places entry). Resolves to the full Place with its POI name, address, and coordinates — prefer this over re-searching by name when routing to a place the user has already seen.")})]),ft=Z.object({source:_e,reason:Z.string()}),gt=e=>`${e.source.kind}:${e.source.id}: ${e.reason}`,bt=(e,t)=>({...e,properties:{...e.properties??{},_source:t}}),wt=async(e,t)=>{const r=t.places.findPlaceById(e);if(!r)return{kind:"skip",reason:"No place with this id in session state. Use `recallGeometries` to list known places ids and entries."};if(!r.place.properties.dataSources?.geometry?.id)return{kind:"skip",reason:"Place has no geometry data source (e.g. addresses and streets often lack one)."};try{const s=await t.places.fetchPlaceGeometry(e);return s?{kind:"success",place:r.place,feature:s,ownerEntryId:r.entryId}:{kind:"skip",reason:"Geometry Data service returned no feature for this place."}}catch(s){return{kind:"skip",reason:`Geometry fetch failed: ${s instanceof Error?s.message:String(s)}`}}},vt=async(e,t)=>{if(0===e.length)return{error:"`geometriesEntryIDs` must contain one or more tagged ids (e.g. `{ kind: 'places', id: <entryId> }`, `{ kind: 'place', id: <placeId> }`, `{ kind: 'ranges', id: <entryId> }`, `{ kind: 'customGeometries', id: <entryId> }`). Use `recallGeometries` to list available ids."};const r=(e=>{const t={place:[],places:[],ranges:[],customGeometries:[]};for(const r of e)t[r.kind].push(r);return t})(e),s=((e,t)=>{const r=[],s=/* @__PURE__ */new Map,o=[],n=/* @__PURE__ */new Set;for(const i of e){const e=t.places.entries.find(e=>e.id===i.id);if(!e){o.push({source:i,reason:"No places entry with this id in session state. Use `recallGeometries` to list available ids."});continue}let a=0;for(const t of e.places)t.properties.dataSources?.geometry?.id&&"string"==typeof t.id&&(a++,s.has(t.id)||s.set(t.id,i.id),n.has(t.id)||(n.add(t.id),r.push(t.id)));0===a&&o.push({source:i,reason:"Places entry has no places with a geometry data source (e.g. addresses and streets often lack one)."})}return{placeIds:r,placeIdToEntry:s,skipped:o}})(r.places,t),o=[...s.skipped],n=/* @__PURE__ */new Set,i=[],a=/* @__PURE__ */new Map;for(const f of r.place)n.has(f.id)||(n.add(f.id),i.push(f.id),a.set(f.id,f));for(const f of s.placeIds)n.has(f)||(n.add(f),i.push(f));const l=[],c=[],d=/* @__PURE__ */new Set,u=/* @__PURE__ */new Set,p=/* @__PURE__ */new Set;for(const f of i){const e=await wt(f,t),r=a.get(f),n=s.placeIdToEntry.get(f);if("skip"===e.kind){const t=r??{kind:"place",id:f};o.push({source:t,reason:e.reason});continue}const i=r??(n?{kind:"places",id:n}:{kind:"place",id:f});l.push(bt(e.feature,i)),c.push(e.place),d.add(e.ownerEntryId),r&&u.add(r.id),n&&p.add(n)}const h=((e,t)=>{const r=[],s=/* @__PURE__ */new Set,o=[];for(const n of e){const e=t.ranges.entries.find(e=>e.id===n.id);if(!e){o.push({source:n,reason:"No ranges entry with this id in session state."});continue}const i=e.ranges.flatMap(e=>e.polygon?.features??[]);if(0!==i.length){for(const e of i)r.push(bt(e,n));s.add(n.id)}else o.push({source:n,reason:"Ranges entry has no polygons — recompute via findReachableAreas first."})}return{features:r,contributingIds:s,skipped:o}})(r.ranges,t);l.push(...h.features),o.push(...h.skipped);const m=((e,t)=>{const r=[],s=/* @__PURE__ */new Set,o=[];for(const n of e){const e=t.customGeometries.findById(n.id);if(e)if(0!==e.features.length){for(const t of e.features)r.push(bt(t,n));s.add(n.id)}else o.push({source:n,reason:"Custom-geometries entry is empty."});else o.push({source:n,reason:"No custom-geometries entry with this id in session state. Use `recallGeometries` to list available ids."})}return{features:r,contributingIds:s,skipped:o}})(r.customGeometries,t);l.push(...m.features),o.push(...m.skipped);const y=m.contributingIds;return 0===l.length?{error:`No input geometries available. ${o.map(gt).join(" | ")}`}:{value:{geometries:l,sourcePlaces:c,affectedEntries:[...[...d].map(e=>({id:e,kind:"places"})),...[...y].map(e=>({id:e,kind:"customGeometries"}))],skipped:o,contributingSourceIds:e.filter(e=>{switch(e.kind){case"place":return u.has(e.id);case"places":return p.has(e.id);case"ranges":return h.contributingIds.has(e.id);case"customGeometries":return y.has(e.id)}})}}},It=["placesEntryIDs","routesEntryIDs","incidentsEntryIDs","geometriesEntryIDs","trafficAreaAnalyticsEntryIDs","byodEntryIDs"],St=async(e,t)=>{if(!(e=>It.some(t=>(e[t]?.length??0)>0))(e))return{error:"At least one of `placesEntryIDs`, `routesEntryIDs`, `incidentsEntryIDs`, `geometriesEntryIDs`, `trafficAreaAnalyticsEntryIDs`, `byodEntryIDs` must be set. Use `recallPlaces` / `recallRoutes` / `recallGeometries` / `recallByod` / `recallState` to list available ids."};const r=((e,t)=>{const r=Mt(e.placesEntryIDs,t.places,{missingId:e=>`No places entry with id "${e}". Use recallPlaces to list available IDs.`});if("error"in r)return r;const s=Mt(e.routesEntryIDs,t.routing,{missingId:e=>`No routes entry with id "${e}". Use recallRoutes to list available IDs.`});if("error"in s)return s;const o=Mt(e.incidentsEntryIDs,t.trafficIncidents,{missingId:e=>`No incidents entry with id "${e}". Call getTrafficIncidents first.`});if("error"in o)return o;const n=Mt(e.trafficAreaAnalyticsEntryIDs,t.trafficAreaAnalytics,{missingId:e=>`No traffic-area-analytics entry with id "${e}". Call getTrafficAreaAnalytics first.`});if("error"in n)return n;const i=Mt(e.byodEntryIDs,t.byod,{missingId:e=>`No BYOD entry with id "${e}". Use recallByod to list available IDs.`});return"error"in i?i:{value:{places:r.value,routes:s.value,incidents:o.value,trafficAreaAnalytics:n.value,byod:i.value}}})(e,t);if("error"in r)return r;const{places:s,routes:o,incidents:n,trafficAreaAnalytics:i,byod:a}=r.value;let l,c={affectedEntries:[],sourceIds:[],skipped:[]};if(e.geometriesEntryIDs?.length){const r=await vt(e.geometriesEntryIDs,t);if("error"in r)return r;l=r.value.geometries,c={affectedEntries:r.value.affectedEntries,sourceIds:r.value.contributingSourceIds,skipped:r.value.skipped}}return{value:{resolved:{places:s,routes:o,incidents:n,trafficAreaAnalytics:i,byod:a},sandbox:{places:s.length?{type:"FeatureCollection",features:s.flatMap(e=>e.places)}:void 0,placesByEntry:s.length?Object.fromEntries(s.map(e=>[e.id,{type:"FeatureCollection",features:[...e.places]}])):void 0,routes:o.length?{type:"FeatureCollection",features:o.flatMap(e=>e.data.features)}:void 0,routesByEntry:o.length?Object.fromEntries(o.map(e=>[e.id,{type:"FeatureCollection",features:[...e.data.features]}])):void 0,incidents:n.length?n.flatMap(e=>e.data):void 0,incidentsByEntry:n.length?Object.fromEntries(n.map(e=>[e.id,[...e.data]])):void 0,geometries:l,trafficAreaAnalytics:i.length?Et(i.map(e=>e.data)):void 0,trafficAreaAnalyticsByEntry:i.length?Object.fromEntries(i.map(e=>[e.id,e.data])):void 0,byod:a.length?{type:"FeatureCollection",features:a.flatMap(e=>e.data.features)}:void 0,byodByEntry:a.length?Object.fromEntries(a.map(e=>[e.id,e.data])):void 0},geometries:c}}},Et=e=>({type:"FeatureCollection",properties:e[0].properties,features:e.flatMap(e=>e.features)}),Mt=(e,t,r)=>{if(!e?.length)return{value:[]};const s=[];for(const o of e){const e=t.entries.find(e=>e.id===o);if(!e)return{error:r.missingId(o)};s.push(e)}return{value:s}},xt='CROSS-KIND OPS — `turf` and `h3` operate on EVERY feature regardless of which input slot it came from. You can freely combine `places` (Points), `routes` (LineStrings), `incidents` (Point or LineString), `geometries` (Polygon / MultiPolygon), and `trafficAreaAnalytics` (Polygon tiles with `properties` like `congestionLevel` / `speed` / `travelTime` / `freeFlowSpeed`) in the same call. Common bridges:\n• Point ↔ LineString — `turf.pointToLineDistance(point, lineFeature, { units: "meters" })`, `turf.nearestPointOnLine(line, point)`.\n• Point ↔ Polygon — `turf.booleanPointInPolygon(point, poly)`, `turf.pointsWithinPolygon(turf.featureCollection(points), poly)`.\n• LineString ↔ Polygon — `turf.lineIntersect(line, poly)`, `turf.booleanCrosses(line, poly)`, `turf.lineSplit(line, poly)`.\n• Polygon ↔ Polygon — `turf.union/intersect/difference(turf.featureCollection([a, b]))` (Turf v7 takes a single collection).\n• Point/LineString ↔ trafficAreaAnalytics tile — `turf.booleanPointInPolygon(p, tile)` for points; `turf.lineIntersect(routeLine, tile)` for routes; iterate `trafficAreaAnalytics.features` to find which tile a place/route segment falls into and read its metric (`tile.properties.congestionLevel`, etc.).\n• Buffer to bridge kinds — `turf.buffer(anyFeature, meters/1000, { units: "kilometers" })` turns a point or line into a polygon you can then filter against.\n• Bbox of anything — `turf.bbox(featureOrCollection)`.\n• Centroid for Point↔shape distance — `turf.centroid(polygonOrLine).geometry.coordinates`.\n• h3 — `h3.latLngToCell(lat, lng, res)` for any Point; `h3.polygonToCells(poly.geometry.coordinates, res)` for any Polygon; for a LineString iterate `geometry.coordinates` and bin each `[lng, lat]`.\nNote: arguments to turf functions are GeoJSON Features — pass the whole feature (`places.features[i]`, `routes.features[0]`, `geometries[i]`, `incidents[i]`, `trafficAreaAnalytics.features[i]`), not the bare `geometry.coordinates` (a few helpers accept coords but most expect Features). Always check the function signature at https://turfjs.org/docs/.',Pt=["places","placesByEntry","routes","routesByEntry","incidents","incidentsByEntry","geometries","trafficAreaAnalytics","trafficAreaAnalyticsByEntry","byod","byodByEntry","h3","turf"],Dt=(e,t)=>[e.places,e.placesByEntry,e.routes,e.routesByEntry,e.incidents,e.incidentsByEntry,e.geometries,e.trafficAreaAnalytics,e.trafficAreaAnalyticsByEntry,e.byod,e.byodByEntry,t.h3,t.turf],_t=new Set(z),Tt=e=>`In scope: ${e.map(e=>`\`${e}\``).join(", ")} — injected as parameters; use directly. No \`require\` / \`import\` / \`await import\` (body is an AsyncFunction, not a module — such lines are stripped silently). \`h3\` = h3-js (hex math). \`turf\` = @turf/turf v7 (verify names at https://turfjs.org/docs/). TURF INPUTS: pass a Feature or FeatureCollection — never a bare Array (throws "Unknown Geometry Type"). FC-shaped inputs (\`places\`, \`routes\`, \`trafficAreaAnalytics\`, \`byod\`) → pass directly: \`turf.bbox(places)\`. Array inputs (\`incidents\`, \`geometries\`, \`places.features\`, …) → iterate per-feature (\`for (const f of places.features) turf.X(f)\`) or wrap with \`turf.featureCollection([...])\`. Never \`.map(f => f.geometry.coordinates)\` / \`.flatMap(f => f.geometry.coordinates)\` to feed turf — that strips the feature wrapper turf reads and throws "coordinates must be an Array" (or worse, mixes Point/LineString shapes silently). Pass the whole feature and let turf read the geometry: \`turf.pointToLineDistance(point, lineFeature, { units: "meters" })\`. \`incidents\` mixes Point + LineString features — guard with \`if (inc.geometry.type === "LineString")\` before line-only ops. TURF v7 SET OPS: \`union\`/\`intersect\`/\`difference\` take ONE FeatureCollection — \`turf.difference(turf.featureCollection([outer, inner]))\`. The v6 two-arg form throws. PERF: for large-N nearest/within queries, pre-bucket points with \`h3.latLngToCell(lat, lng, res)\` + \`h3.gridDisk(cell, k)\` and only run turf on same-/neighbour-cell pairs — turns O(N·M) into ~O(N+M). Pick \`res\` so each cell ≈ your query radius. No spatial search or HTTP — call \`discoverPlaces\` BEFORE this tool for new places. Top-level \`await\`/locals/consts fine. End with \`return …;\`.`,At=new Set(["bar","line","pie","doughnut","radar","polarArea","scatter","bubble"]),Ct=e=>{if(!e||"object"!=typeof e)return!1;const t=e;if("Feature"!==t.type)return!1;const r=t.geometry;return!!r&&("Polygon"===r.type||"MultiPolygon"===r.type)&&Array.isArray(r.coordinates)},kt=(e,t)=>{if(void 0===e)return{error:"Analysis code must return a value (the aggregation result). Hint: `code` is the function BODY, not a function expression. If you wrote `(args) => { ...; return result; }` (or `async (args) => { ... }`) as the whole code, the arrow is created and discarded — drop the wrapper and have the body itself end with `return result;`."};const r=Ot(e);return"error"in r?{error:r.error}:"chart"!==t||(e=>{if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.type&&At.has(t.type)&&"object"==typeof t.data&&null!==t.data})(r.value)?{value:r.value}:{error:'Analysis code with `outputFormat: "chart"` must return a Chart.js ChartConfiguration ({ type: "bar"|"line"|"pie"|"doughnut"|"radar"|"polarArea"|"scatter"|"bubble", data: { labels, datasets }, options? }).'}},Ot=e=>{try{return{value:JSON.parse(JSON.stringify(e))}}catch(t){return{error:`Returned value is not JSON-serializable: ${t instanceof Error?t.message:String(t)}. Return only plain objects, arrays, strings, numbers, booleans, and null — no functions, classes, circular references, or DOM/Map references.`}}},Rt=async(e,t,r,s)=>{try{const s=Object.getPrototypeOf(async()=>{}).constructor,o=Lt(e,t);return{value:await new s(...t,o)(...r)}}catch(o){return{error:Nt(s,o)}}},Lt=(e,t)=>{const r=t.join("|"),s=String.raw`(?:(?:await\s+)?(?:require|import)\s*\(`+String.raw`|arguments\s*\[\s*\d+\s*\]\s*(?:\.\w+|\[\s*['"]\w+['"]\s*\]))`,o=new RegExp(String.raw`^[ \t]*(?:const|let|var)[ \t]+(?:${r})[ \t]*=[ \t]*${s}[^\n]*\n?`,"gm");return e.replace(o,"")},Ft=[{pattern:/Cannot read propert(?:y|ies) of undefined/i,hint:"A property was read on `undefined`. Likely a `reduce` callback that does not `return` the accumulator, or a `reduce` called without an initial value — use `arr.reduce((acc, x) => { ...; return acc; }, {})`. Also check unguarded chains like `feature.properties.poi.categories` — use optional chaining (`?.`) and `??` defaults."},{pattern:/Cannot read propert(?:y|ies) of null/i,hint:"A property was read on `null`. Guard property access with `?.` and provide defaults with `??`."},{pattern:/\bh3\.\w+ is not a function/i,hint:"`h3` is [h3-js](https://github.com/uber/h3-js) — hex-grid math only. NO spatial search / place lookup / HTTP. Common: `latLngToCell`, `cellToLatLng`, `cellToBoundary`, `polygonToCells`, `gridDisk`, `cellArea`. For nearby places call `discoverPlaces` BEFORE this tool — sandbox cannot call other tools."},{pattern:/\bturf\.\w+ is not a function/i,hint:"`turf` is [@turf/turf](https://turfjs.org/) — GeoJSON geometry only. NO spatial search / place lookup / HTTP. Common: `area`, `length`, `bbox`, `centroid`, `union`, `intersect`, `buffer`, `distance`, `booleanPointInPolygon`, `pointsWithinPolygon`, `clustersDbscan`, `convex`. Verify names at https://turfjs.org/docs/. For nearby places call `discoverPlaces` BEFORE this tool."},{pattern:/coordinates must be an Array|Unknown Geometry Type/i,hint:'turf was handed a raw coordinate (or a bare Array of features) instead of a Feature / FeatureCollection. Do NOT extract `.geometry.coordinates` with `.map` / `.flatMap` and feed the result to turf — pass the WHOLE feature: `turf.pointToLineDistance(pointFeature, lineFeature, { units: "meters" })`. `incidents` is an Array of mixed Point + LineString features; guard with `if (inc.geometry.type === "LineString")` before line-only ops. For FC-shaped inputs (`places`, `routes`, `trafficAreaAnalytics`, `byod`), pass the value directly (`turf.bbox(places)`); never `turf.bbox(places.features)`.'},{pattern:/is not a function/i,hint:"A method was called on a value that does not have it (typo, or the value is not the expected type). Verify the type before calling, e.g. `Array.isArray(x) ? x.map(...) : []`."},{pattern:/Illegal return statement/i,hint:"A `return` was placed outside the top-level function body — usually a missing brace or a callback that the parser thinks closed too early. Common cause: a multi-line `const X = arr.map((item) => { ... })` whose closing `})` was lost. Re-emit the code with balanced braces, keep `return` only at the top level of the body, and try again."},{pattern:/Identifier ['"]?\w+['"]? has already been declared/i,hint:"A variable was declared twice — typically because the code prepends `const turf = require(...)` / `const turf = await import(...)` / `const turf = arguments[0].turf` / `const places = ...`. Those names are already in scope as function parameters; drop the redeclaration line and use them directly (`turf.bbox(places)`)."},{pattern:/Unexpected (?:token|identifier|end of input)/i,hint:"The code did not parse as a JavaScript async-function body. Common causes: unbalanced braces/parens/brackets, a stray template literal, or `import`/`export` statements (not allowed in sandbox bodies — `places`/`geometries`/`h3`/`turf` are already in scope). Re-emit a self-contained body that ends with `return ...;`."},{pattern:/\brequire is not defined\b|\bimport (?:is not defined|outside a module)\b/i,hint:"The sandbox is not a Node module — `require` and top-level `import` are not available. The libraries you need (`h3`, `turf`) and inputs (`places`, `geometries`) are already injected as parameters; drop the import lines and use them directly."},{pattern:/\b(?:functions|tools|agent|recallPlaces|recallRoutes|recallRanges|discoverPlaces|locatePlace) is not defined\b/i,hint:"The sandbox cannot call other tools — `functions.recallPlaces(...)`, `tools.discoverPlaces(...)`, etc. are NOT available. To use additional entries, list every needed id in the tool inputs (`placesEntryIDs`, `routesEntryIDs`, `incidentsEntryIDs`, `geometriesEntryIDs`): each is exposed via the injected sandbox args (`places`, `routes`, `incidents`, `geometries`, plus their `*ByEntry` per-entry views). To bring in NEW places call `discoverPlaces` BEFORE this tool and pass the resulting entry id back in."},{pattern:/is not iterable/i,hint:"A value used in `for..of`, spread, or destructuring was not iterable. Default arrays with `?? []`, e.g. `for (const c of feature.properties.poi?.categories ?? []) { ... }`."},{pattern:/Cannot access '.+?' before initialization/i,hint:"A `let`/`const` was used before its declaration (temporal dead zone). Move the declaration above its first use."},{pattern:/Assignment to constant variable/i,hint:"A `const`-declared variable was reassigned. Either use `let`, or mutate the value in place."},{pattern:/structuredClone|could not be cloned|DataCloneError/i,hint:"Return value must be pure JSON — no functions or classes. The result crosses a JSON boundary before rendering. For per-point detail you would put in a Chart.js `tooltip.callbacks` function, use a `string[]` label instead — it renders multi-line in the axis tick and the tooltip title."}],Nt=(e,t)=>{const r=t instanceof Error?t.message:String(t),s=`${e} code execution failed: ${r}`;if(t instanceof Error&&"SyntaxError"===t.name)return`${s} Hint: the provided \`code\` is not valid JavaScript — ensure it parses as an async function body and \`return\`s a value.`;const o=Ft.find(({pattern:e})=>e.test(r));return o?`${s} Hint: ${o.hint}`:s},jt=async(e,t,r)=>{const s=await e.places.fetchGeometriesForEntry(t);return r.show&&s.length>0?(await e.places.showPlaceGeometries(s,r.theme??"outline",r.mode??"replace"),{fetched:s.length,shown:!0}):{fetched:s.length,shown:!1}},Bt=async(e,t,r)=>{const s={markerType:!1,zoomMode:!1};if("none"!==r.markerType){if("multiple"===e.places.entryMode&&"all"!==r.hidePreviousEntries){if(Array.isArray(r.hidePreviousEntries)){const s=new Set(t),o=r.hidePreviousEntries.filter(e=>!s.has(e));o.length>0&&await e.places.removeShownEntries(o)}await e.places.addShownEntries(t,r.markerType)}else await e.places.setShownEntries(t,r.markerType);s.markerType=!0}if("auto"===r.zoomMode){const t=e.places.getShownFeatures();if(t.length>0){const r=F({type:"FeatureCollection",features:t});r&&e.baseMap.mapLibreMap.fitBounds(r,{padding:50})}s.zoomMode=!0}return s},qt=(e,t=6)=>{if(e.mapLibreMap.getZoom()<t)return;const r=e.mapLibreMap.getCenter();return[r.lng,r.lat]},$t=e=>{const t=e.mapLibreMap.getBounds();return[t.getWest(),t.getSouth(),t.getEast(),t.getNorth()]},Gt=async(e,t,r,s)=>{const o=await Rt(e.code,["incidents","h3","turf","now","log","previous"],[t,K,X,new Date(s),()=>{},r],"Analysis");return"error"in o?{error:o.error}:kt(o.value,e.outputFormat)},Ut=class{_specs=[];_historyByName=/* @__PURE__ */new Map;get specs(){return this._specs}get results(){const e=[];for(const t of this._specs){const r=this.getResult(t.name);r&&e.push(r)}return e}history(e){return this._historyByName.get(e)??[]}getResult(e){return this._historyByName.get(e)?.at(-1)}register(e){const t=this._specs.findIndex(t=>t.name===e.name);if(t>=0){const r=this._specs[t],s=r.code!==e.code||r.outputFormat!==e.outputFormat||r.description!==e.description;this._specs[t]=e,s&&this._historyByName.delete(e.name)}else this._specs.push(e)}unregister(e){this._specs=this._specs.filter(t=>t.name!==e)}attach(e){let t=this._historyByName.get(e.name);t||(t=[],this._historyByName.set(e.name,t)),t.push(e),t.length>240&&t.shift()}async replay(e,t){const r=[];for(const s of this._specs){const o=await Gt(s,e,this.getResult(s.name)?.data,t);if("error"in o)continue;const n={name:s.name,timestamp:t,...s.description&&{description:s.description},outputFormat:s.outputFormat,data:o.value};this.attach(n),r.push(n)}return r}},Wt=class{_bbox;events=new ae;_status="idle";_area;_error;_intervalHandle;_clearInterval;_fetchIncidents;constructor(e){this._bbox=e}get status(){return this._status}get monitoredArea(){return this._area}get error(){return this._error}get isRunning(){return"running"===this._status}get isActive(){return"idle"!==this._status}start(e,t,r=6e4){if("running"===this._status)return;this._status="running",this._error=void 0,this._area=e,this._fetchIncidents=t.fetchIncidents,this._clearInterval=t.clearInterval??(e=>clearInterval(e));const s=t.setInterval??setInterval;this._tick(),this._intervalHandle=s(()=>{this._tick()},r)}stop(){"idle"!==this._status&&(this._releaseTimer(),this._status="idle",this._error=void 0,this._area=void 0,this._fetchIncidents=void 0)}async _tick(){const e=this._fetchIncidents;if(e)try{const t=await e(this._bbox),r={takenAt:Date.now(),incidents:t};this.events.emit("tick",r)}catch(t){this._failed(t instanceof Error?t.message:String(t))}}_failed(e){this._releaseTimer(),this._error=e,this._status="stopped-error",this._fetchIncidents=void 0,this.events.emit("error",e)}_releaseTimer(){null!=this._intervalHandle&&(this._clearInterval?.(this._intervalHandle),this._intervalHandle=void 0),this._clearInterval=void 0}},Vt=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this._teardownEntry(e),{parallel:!0}),this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)),this.events.emit("mode-change",e))}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}async addIncidentsEntry(e,t,r,s,o){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this._teardownEntry(e),{parallel:!0}),this._entries=[]);const n=oe(o??`incidents-${this._entries.length}`,this._entries.map(e=>e.id));return this._entries.push({id:n,timestamp:s,label:r,data:e,params:t}),this.events.emit("entries-change",this._entries),n}async replaceEntryData(e,t,r){const s=this._entries.find(t=>t.id===e);if(!s)return!1;const o=(s._tick??0)+1;if(s._tick=o,s.data=t,s.timestamp=r,this.events.emit("entries-change",this._entries),s._module){if(await s._module.show({type:"FeatureCollection",features:t}),s._tick!==o)return!0;if(s._focus){const e=new Set(t.map(e=>e.properties.id)),r=[...s._focus.ids].filter(t=>e.has(t));s._module.setFocus(r.length>0?r:null),0===r.length&&(s._focus=void 0,this.events.emit("focus-change",{entryId:s.id,focus:null}))}}return s._tick!==o||await this._rerunRegisteredSpecs(e,r),!0}async getEntryModule(e){const t=this._requireEntry(e);return t._module??=await h.get(this._ttMap),t._module}async showEntry(e,t=!0){const r=this._requireEntry(e),s=await this.getEntryModule(e);await s.show({type:"FeatureCollection",features:r.data}),s.setVisible(!0),t&&this._ttMap.mapLibreMap.fitBounds(r.params.bbox,{padding:50,maxZoom:14});const o=!!r._shown;r._shown=!0,o||this.events.emit("shown-change",this.shownEntryIds)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._focus&&(t._focus=void 0,this.events.emit("focus-change",{entryId:t.id,focus:null})),t._module?.setVisible(!1),await(t._module?.clear()),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);if(-1===t)return;const r=this._entries[t],s=!!r._shown;await this._teardownEntry(r),this._entries.splice(t,1),this.events.emit("entries-change",this._entries),s&&this.events.emit("shown-change",this.shownEntryIds)}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);return!!r&&((r._analyses??=new Ut).attach(t),this.events.emit("analysis-change",{entryId:r.id,analysis:t}),this.events.emit("entries-change",this._entries),!0)}setAnalysisSpec(e){const t=this._entries.find(t=>t.id===e.source);t&&(t._analyses??=new Ut).register(e)}removeAnalysisSpec(e){for(const t of this._entries)t._analyses?.unregister(e)}getFocus(e){const t=this._entries.find(t=>t.id===e);return t?._focus?{ids:new Set(t._focus.ids),reason:t._focus.reason}:null}setFocus(e,t,r){const s=this._entries.find(t=>t.id===e);if(!s)return{focusedCount:0,droppedIds:[...t]};if(0===t.length)return this.clearFocus(e),{focusedCount:0,droppedIds:[]};const o=new Set(s.data.map(e=>e.properties.id)),n=[],i=[];for(const a of t)o.has(a)?n.push(a):i.push(a);return 0===n.length?{focusedCount:0,droppedIds:i}:(s._focus={ids:new Set(n),reason:r},s._module?.setFocus(n),this.events.emit("focus-change",{entryId:s.id,focus:this.getFocus(e)}),{focusedCount:n.length,droppedIds:i})}clearFocus(e){const t=this._entries.find(t=>t.id===e);t?._focus&&(t._focus=void 0,t._module?.setFocus(null),this.events.emit("focus-change",{entryId:t.id,focus:null}))}focusedFeatures(e){const t=this._entries.find(t=>t.id===e);if(!t?._focus)return[];const r={};for(const o of t.data){const e=o.properties.id;"string"==typeof e&&(r[e]=o)}const s=[];for(const o of t._focus.ids){const e=r[o];e&&s.push(e)}return s}navigateFocus(e,t){const r=this.focusedFeatures(e);if(0===r.length)return;const s=r[(t%r.length+r.length)%r.length],o=this._ttMap.mapLibreMap;if("Point"===s.geometry.type)return void o.flyTo({center:s.geometry.coordinates,zoom:15,duration:600});const n=F(s);n&&o.fitBounds(n,{padding:120,maxZoom:16,duration:600})}findEntryWithIncident(e){for(let t=this._entries.length-1;t>=0;t--){const r=this._entries[t];if(r.data.some(t=>t.properties.id===e))return r}}startMonitoring(e,t,r,s){const o=this._requireEntry(e),n=o._monitor??=this._createMonitorForEntry(o),i=n.isRunning;n.start(t,r,s?.intervalMs),i||this.events.emit("monitor-start",{entryId:e})}stopMonitoring(e){const t=this._entries.find(t=>t.id===e)?._monitor;t?.isActive&&(t.stop(),this.events.emit("monitor-stop",{entryId:e,reason:"manual"}))}isMonitored(e){return this._entries.find(t=>t.id===e)?._monitor?.isRunning??!1}_createMonitorForEntry(e){const t=new Wt(e.params.bbox);return t.events.on("tick",t=>{this.replaceEntryData(e.id,t.incidents,t.takenAt),this.events.emit("monitor-tick",{entryId:e.id,snap:t})}),t.events.on("error",t=>{this.events.emit("monitor-error",{entryId:e.id,error:t}),this.events.emit("monitor-stop",{entryId:e.id,reason:"error"})}),t}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`TrafficIncidentsState: unknown entry "${e}"`);return t}async _rerunRegisteredSpecs(e,t){const r=this._entries.find(t=>t.id===e);if(!r?._analyses?.specs.length)return;const s=await r._analyses.replay(r.data,t);for(const o of s)this.events.emit("analysis-change",{entryId:r.id,analysis:o});s.length>0&&this.events.emit("entries-change",this._entries)}async _teardownEntry(e){this.stopMonitoring(e.id),e._focus&&(e._focus=void 0,this.events.emit("focus-change",{entryId:e.id,focus:null})),e._shown&&(e._shown=!1),e._module&&(e._module.setVisible(!1),await e._module.clear()),e._module=void 0}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const t of this._entries)this._teardownEntry(t);const e=this._entries.length>0;this._entries=[],e&&(this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)),this.events.clear()}},zt=class{_ttMap;_trafficFlowModule;_trafficIncidentsModule;constructor(e){this._ttMap=e}async getTrafficFlowModule(){return this._trafficFlowModule??=await p.get(this._ttMap),this._trafficFlowModule}async getTrafficIncidentsModule(){return this._trafficIncidentsModule??=await m.get(this._ttMap),this._trafficIncidentsModule}get trafficFlowModule(){return this._trafficFlowModule}get trafficIncidentsModule(){return this._trafficIncidentsModule}reset(){this._trafficFlowModule=void 0,this._trafficIncidentsModule=void 0}};R.instance.put({language:"en-GB"});var Ht,Yt={places:"places",routes:"routing",incidents:"trafficIncidents",customGeometries:"customGeometries",trafficAreaAnalytics:"trafficAreaAnalytics",byod:"byod",ranges:"ranges"},Jt={places:["recallPlaces","updatePlacesDisplay","locatePlace","discoverPlaces"],routes:["recallRoutes","updateRoutesDisplay","setRoute","addWaypointsToRoute","removeWaypointsFromRoute","replaceWaypointInRoute","getCurrentWaypoints","updateWaypointsDisplay"],incidents:["getTrafficIncidents","focusIncidents","startTrafficIncidentsMonitor","stopTrafficIncidentsMonitor","getShownTileIncidents"],customGeometries:["recallGeometries"],trafficAreaAnalytics:["getTrafficAreaAnalytics","queryTrafficAnalytics","updateTrafficAreaAnalyticsDisplay"],byod:["recallByod","addByodLayer","updateByodDisplay"],ranges:["findReachableAreas","recallRanges"]},Xt=Object.keys({places:!0,routing:!0,ranges:!0,customGeometries:!0,byod:!0,trafficAreaAnalytics:!0,trafficIncidents:!0}),Kt=Object.keys({places:!0,routing:!0,ranges:!0,customGeometries:!0,byod:!0,trafficAreaAnalytics:!0,trafficIncidents:!0}),Zt='You are a helpful map assistant with access to a TomTom interactive map and location services.\nIf a request is completely unrelated to geospatial topics, gracefully decline and steer the user back to map-related tasks you can help with.\n\nCOORDINATE ORDER:\n- All coordinates are [longitude, latitude] (GeoJSON standard)\n- All bounding boxes are [minLng, minLat, maxLng, maxLat]\n\nTOOL EXECUTION:\n- Call context-independent tools in the same step (parallel execution).\n- Many service tools have a show or showOnMap parameter — use it to display results on the map in one step instead of calling a separate show tool afterward.\n- Use standalone updatePlacesDisplay / updateRoutesDisplay / updateWaypointsDisplay only when displaying results already in context from a previous step.\n\nSESSION STATE & ENTRIES:\n- Seven append-only histories live in the session: `places`, `routes`, `ranges`, `customGeometries`, `trafficAreaAnalytics`, `trafficIncidents`, `byod`. Each tool call that produces results stores a new ENTRY (id like `places-3`, `routes-1`, `byod-0`) — these ids stay stable and are accepted by every show / recall / `processData` / `analyseData` tool via the matching `*EntryIDs` field.\n- Each slice has an `entryMode`: `multiple` (default — overlay several entries on the map) or `single` (only the latest entry stays; switching to it auto-drops every older entry from history). Inspect via `recallState` / `recallPlaces` / `recallRoutes` / `recallRanges` / `recallGeometries` / `recallByod` (traffic slices surface via `recallState`). Flip with `setEntryMode({ slice, mode })` when the user asks to "only show one at a time" or "let me overlay multiple".\n- Mode is sticky across turns — under `single`, calling another `discoverPlaces` automatically replaces the previous places entry.\n- Always check `recallX` (or `recallState`) before guessing entry ids; never invent them.\n\nLOCATION REFERENCE:\n- "near me" / "my location" / "where I am" → getCurrentLocation (user\'s physical GPS position, may prompt permission)\n- "in this area" / "here on the map" / "near the map center" → getViewport (map\'s current view)\n- If getCurrentLocation fails due to permission denial, fall back to getViewport as the reference point\n\nRESPONSE FORMATTING:\n- Use markdown in all responses\n- Show key information in bold\n- Use bullet lists when presenting multiple items or results\n- Keep responses concise — lead with the most relevant information\n\n',Qt=(e,t)=>e||(t?`${Zt}\n\nADDITIONAL INSTRUCTIONS:\n${t}`:Zt),er=Z.object({geoJson:Z.union([Z.record(Z.string(),Z.unknown()),Z.array(Z.record(Z.string(),Z.unknown()))]).describe("GeoJSON object or array of GeoJSON objects")}),tr=Z.object({meters:Z.number().describe("Distance in meters to format"),unitType:Z.enum(["metric","imperial_us","imperial_uk"]).optional().describe("default: metric")}),rr=Z.object({seconds:Z.number().describe("Duration in seconds to format")}),sr=Z.object({error:Z.string()}),or={id:Z.string().describe("Stable place feature ID; pass as `{ placeId }` to setRoute / addWaypointsToRoute / etc."),name:Z.string().optional().describe("POI name if available"),address:Z.string().optional().describe("Human-readable address"),position:Z.array(Z.number()).length(2).describe("[longitude, latitude]")},nr={areaId:Z.string().optional().describe('Id of the small area polygon this place sits in (few km², not a whole municipality). Round-trip into `discoverPlaces.where.areaId` for "what else is in this same area?". Only set on explorationSearch results in DE / NL / FR.'),areaCountry:Z.string().optional().describe("ISO 3166-1 alpha-2 country code of the surrounding area polygon."),areaTags:Z.array(Z.string()).optional().describe('Tokens describing the surrounding small area (few km², not a whole municipality) (e.g. "coastal", "walkable", "transit_connected"). Useful for downstream `areaTags` filters.')},ir=e=>Z.object(e.experimentalSearch?{...or,...nr}:or).describe("Compact place summary"),ar=e=>Z.object({count:Z.number(),features:Z.array(ir(e))}).describe("Compact places summary"),lr=ir({}),cr=(ar({}),Z.object({travelTimeInSeconds:Z.number(),lengthInMeters:Z.number(),trafficDelayInSeconds:Z.number().optional(),trafficLengthInMeters:Z.number().optional(),departureTime:Z.string().optional(),arrivalTime:Z.string().optional(),noTrafficTravelTimeInSeconds:Z.number().optional(),historicTrafficTravelTimeInSeconds:Z.number().optional(),liveTrafficIncidentsTravelTimeInSeconds:Z.number().optional()})),dr=Z.object({index:Z.number().describe("0=best, 1+=alternative"),summary:cr,legSummaries:Z.array(cr).optional().describe("Present when route has multiple legs")}),ur=Z.object({count:Z.number(),routes:Z.array(dr)}),pr=Z.object({}),hr=Z.union([Z.object({position:Z.array(Z.number()).min(2).max(2).describe("[longitude, latitude]"),accuracy:Z.number().describe("Accuracy of the location in meters"),timestamp:Z.number().describe("Timestamp of the location data")}),sr]),mr=Z.number().int().min(0).optional().describe("default: 0"),yr=(Z.object({routeIndex:mr,traveledTimeInSeconds:Z.number().min(0).optional().describe("seconds from start"),traveledDistanceInMeters:Z.number().min(0).optional().describe("meters from start"),clockTime:Z.string().optional().describe("ISO 8601; elapsed time derived from departure time")}).refine(({traveledTimeInSeconds:e,traveledDistanceInMeters:t,clockTime:r})=>1===[e,t,r].filter(e=>void 0!==e).length,{message:"Provide exactly one of: traveledTimeInSeconds, traveledDistanceInMeters, or clockTime."}),Z.union([Z.object({bbox:Z.array(Z.number()).describe("GeoJSON bbox [minLng, minLat, maxLng, maxLat]")}),sr]),Z.object({sectionType:Z.enum(H).describe("e.g. country|traffic|motorway|toll"),id:Z.string().describe("Section ID — obtain from `recallRoutes({ id })` detail mode or from `analyseData` over routes.")}),Z.object({sectionType:Z.enum(H).describe("e.g. country|traffic|motorway|toll"),ids:Z.array(Z.string()).optional().describe("Section IDs to query. If omitted, all sections of the given sectionType are returned.")}),["location","route","waypoint","place","locate","discover","traffic","map style","utilities","search","detour","along-route","reachable-range","isochrone","range","EV","coverage","recall","state","carpool","carTrain","country","ferry","importantRoadStretch","lanes","leg","lowEmissionZone","motorway","pedestrian","roadShields","speedLimit","toll","tollVignette","tunnel","unpaved","urban","vehicleRestricted"]),fr=Z.object({name:Z.string(),description:Z.string(),examplePrompts:Z.array(Z.string()),relatedTools:Z.array(Z.string()).optional()}),gr=Z.union([Z.object({mode:Z.literal("summary"),examplePrompts:Z.array(Z.string())}),Z.object({mode:Z.literal("detail"),query:Z.string().optional(),tag:Z.enum(yr).optional(),matchedTools:Z.array(fr),totalMatched:Z.number()}),sr]),br=Z.object({mode:Z.enum(["summary","detail"]).describe('"summary": overview of all capabilities with example prompts mixed from all tools. "detail": searchable full tool info — use when you need to find a specific tool.'),query:Z.string().optional().describe("Detail mode only. Case-insensitive keyword search across tool names, descriptions, and tags."),tag:Z.enum(yr).optional().describe("Detail mode only. Filter results to tools with a specific tag.")}),wr='List capabilities. `mode: "summary"` returns mixed example prompts; `mode: "detail"` returns searchable per-tool descriptions and examples. Call when the user asks what the agent can do, or to find the right tool.',vr=(e,t,r)=>({description:e.description,inputSchema:e.inputSchema,outputSchema:r?e.outputSchema:void 0,execute:r=>e.execute(r,t)}),Ir=(e,t)=>({name:e,description:t.description,classificationPrompt:t.classificationPrompt,tags:t.tags,examples:t.examples,examplePrompts:t.examplePrompts,relatedTools:t.relatedTools,dependsOn:t.dependsOn,scopePrompt:t.scopePrompt}),Sr=(e,t)=>"function"==typeof e?e(t):e,Er=(e,t,r)=>{const s=!1!==r?.outputSchemas,o={featureFlags:r?.featureFlags,enabledDataKinds:r?.enabledDataKinds},n={},i={},a={};for(const[c,d]of Object.entries(e)){const e=Sr(d,o);if(n[c]=vr(e,t,s),i[c]=Ir(c,e),"function"==typeof d&&e.scopeSchema){const t=d,r=e.scopeSchema;a[c]={applyScope:e=>{const s=r.safeParse(e);if(!s.success)return null;try{const e=t({...o,scope:s.data});return{description:e.description,inputSchema:e.inputSchema}}catch{return null}},defaultDescription:e.description,defaultInputSchema:e.inputSchema}}}var l;if(n.help&&(n.help=(l=()=>i,{description:wr,inputSchema:br,outputSchema:gr,execute:async e=>{const{mode:t,query:r,tag:s}=e;try{const e=Object.values(l());if("summary"===t)return{mode:"summary",examplePrompts:e.flatMap(e=>e.examplePrompts??[])};let o=e;if(s&&(o=o.filter(e=>e.tags?.includes(s)??!1)),r){const e=r.toLowerCase();o=o.filter(t=>t.name.toLowerCase().includes(e)||t.description.toLowerCase().includes(e)||(t.tags??[]).some(t=>t.toLowerCase().includes(e))||(t.examplePrompts??[]).some(t=>t.toLowerCase().includes(e)))}return{mode:"detail",query:r,tag:s,matchedTools:o.map(e=>({name:e.name,description:e.description,examplePrompts:e.examplePrompts??[],relatedTools:e.relatedTools??[]})),totalMatched:o.length}}catch(o){return{error:`Failed to load help: ${o instanceof Error?o.message:String(o)}`}}}})),0===Object.keys(n).length)throw new Error("MapAgent requires at least one tool.");return{tools:n,toolsMetadata:i,scopableTools:a}},Mr=Z.object({added:Z.array(Z.string()).describe("Source IDs added by the executed code."),removed:Z.array(Z.string()).describe("Source IDs removed by the executed code."),updated:Z.array(Z.string()).describe("Source IDs whose definition changed.")}),xr=Z.object({added:Z.array(Z.string()).describe("Layer IDs added by the executed code."),removed:Z.array(Z.string()).describe("Layer IDs removed by the executed code."),updated:Z.array(Z.string()).describe("Layer IDs whose properties changed.")}),Pr=Z.object({code:Z.string().describe('Async JavaScript code executed in an environment where `map` is the live MapLibre Map instance. Full JS is supported: variables, loops, conditionals, helper functions, top-level `await`. Example: await a map event with `await new Promise(r => map.once("idle", r))`. Return a value with `return` to include it in the result.')}),Dr=Z.union([Z.object({success:Z.literal(!0),result:Z.unknown().optional().describe("Return value of the executed code, if any."),sources:Mr,layers:xr}),sr]),_r=e=>{const t=e.getStyle()?.sources??{},r=/* @__PURE__ */new Map;for(const[s,o]of Object.entries(t))r.set(s,JSON.stringify(o));return r},Tr=e=>{const t=e.getStyle()?.layers??[],r=/* @__PURE__ */new Map;for(const s of t)r.set(s.id,JSON.stringify(s));return r},Ar=(e,t)=>{const r=[],s=[],o=[];for(const[n,i]of t){const t=e.get(n);void 0===t?r.push(n):t!==i&&o.push(n)}for(const n of e.keys())t.has(n)||s.push(n);return{added:r,removed:s,updated:o}},Cr=Z.union([Z.object({success:Z.literal(!0)}),sr]),kr=Z.object({where:Z.union([Z.object({boundingBox:Re.describe("BBox [W,S,E,N]."),padding:Z.number().optional().describe("Padding in pixels. Default: 50.")}).describe("Fit the camera to a bounding box."),Z.object({position:Z.array(Z.number()).length(2).describe("[lng, lat] to fly to."),zoom:Z.number().optional().describe("Camera zoom level. Default: 14.")}).describe("Fly the camera to a specific position.")])}),Or=Z.union([Z.object({layers:Z.array(Z.object({id:Z.string(),layout:Z.unknown().optional(),paint:Z.unknown().optional()}))}),sr]),Rr=Z.object({layerIdQuery:Z.string().optional().describe("Case-insensitive partial match on layer IDs. If omitted, all layers are returned."),include:Z.array(Z.enum(["paint","layout"])).describe('Which properties to include in the response. Pass only what you need: "paint" for visual properties like colors and opacity, "layout" for structural properties like sizes, positions, and visibility.')}),Lr=Z.union([Z.object({center:Z.array(Z.number()).describe("[lng, lat]"),zoom:Z.number().describe("0–22"),bbox:Z.array(Z.number()).describe("[W, S, E, N]"),pitch:Z.number().describe("degrees, 0=top-down, 85=near-horizontal"),bearing:Z.number().describe("degrees, 0=north, 90=east")}),sr]),Fr=Z.object({}),Nr=Z.union([Z.object({layerId:Z.string(),propertyName:Z.string(),value:Z.unknown(),success:Z.literal(!0)}),Z.object({layerId:Z.string(),propertyName:Z.string(),error:Z.string()})]),jr=Z.union([Z.object({results:Z.array(Nr)}),sr]),Br=Z.object({changes:Z.array(Z.object({layerId:Z.string().describe("MapLibre layer ID from getMapStyleLayers"),propertyName:Z.string().describe("e.g. visibility|text-field|icon-size"),value:Z.any()}))}).describe("List of layout property changes to apply in one go."),qr=Z.union([Z.object({layerId:Z.string(),propertyName:Z.string(),value:Z.unknown(),success:Z.literal(!0)}),Z.object({layerId:Z.string(),propertyName:Z.string(),error:Z.string()})]),$r=Z.union([Z.object({results:Z.array(qr)}),sr]),Gr=Z.object({changes:Z.array(Z.object({layerId:Z.string().describe("MapLibre layer ID from getMapStyleLayers"),propertyName:Z.string().describe("e.g. fill-color|line-width|text-color"),value:Z.any()})).describe("List of paint property changes to apply in one go.")}),Ur=Z.union([Z.object({success:Z.literal(!0)}),sr]),Wr=Z.object({pitch:Z.number().min(0).max(85).optional().describe("0=top-down, 85=near-horizontal"),bearing:Z.number().min(-180).max(180).optional().describe("0=north, 90=east")}),Vr=Z.union([Z.object({zoom:Z.number().describe("Resulting zoom level after the change")}),sr]),zr=Z.object({delta:Z.number().describe("Amount to zoom. Positive values zoom in, negative values zoom out. Can have decimals. Typical step: 1 or 2.")}),Hr=e=>Object.entries(e).filter(([,e])=>e.scopePrompt).map(([e])=>e),Yr=Z.object({kinds:Z.array(Z.string()).min(1).describe("Entry-data kinds the turn touches (one of the kinds listed in the tool's SCOPE hint).")}),Jr=(e,t)=>{t&&(e.inputTokens+=t.inputTokens??0,e.outputTokens+=t.outputTokens??0,e.totalTokens+=t.totalTokens??0)},Xr=e=>{if(!e||!Array.isArray(e.tools)||!e.tools.every(e=>"string"==typeof e))return;const t=e.toolScopes&&"object"==typeof e.toolScopes?e.toolScopes:void 0;return{tools:e.tools,...t&&{scopes:t}}},Kr=(e,t)=>{const r=e.toolScopes?Object.fromEntries(Object.entries(e.toolScopes).filter(([,e])=>null!=e)):void 0;return{tools:e.tools,...r&&Object.keys(r).length>0&&{toolScopes:r},usage:t}},Zr=async(t,s,o)=>{const n=new Set(Hr(o)),i=(e=>{const t=Z.object({tools:Z.array(Z.string()).min(1).describe("Tool names needed to fulfill the query.")});if(0===e.size)return t;const r={};for(const o of e)r[o]=Yr.nullable();const s=Z.object(r).nullable().describe("Per-tool scope. One field per scopable tool; MANDATORY (non-null) for every scopable tool you list in `tools`. For tools you didn't pick, set the corresponding field to `null`. See each tool's SCOPE hint.");return t.extend({toolScopes:s}).superRefine((t,r)=>{const s=t.toolScopes??{};for(const o of t.tools){if(!e.has(o))continue;null==s[o]&&r.addIssue({code:"custom",path:["toolScopes",o],message:`Tool "${o}" requires \`toolScopes.${o}\` — emit it per the SCOPE hint (don't leave null when picking).`})}})})(n),a={inputTokens:0,outputTokens:0,totalTokens:0},l=(e=>0===e.size?'{ "tools": ["toolName", ...] }':`{\n "tools": ["toolName", ...],\n "toolScopes": {\n${[...e].map(e=>` "${e}": { "kinds": [...] } | null`).join(",\n")}\n }\n}\nFor every scopable tool you LIST in "tools", the matching "toolScopes" entry MUST be a non-null object \`{ "kinds": [...] }\`. For scopable tools you DON'T pick, set the corresponding "toolScopes" entry to \`null\`.`)(n),c=`${(e=>{const t=Object.entries(e).map(([e,t])=>((e,t)=>`${e}${t.classificationPrompt?` ${t.classificationPrompt}.`:""}${t.dependsOn?.length?` Depends on: ${t.dependsOn.join(", ")}.`:""}${t.scopePrompt?` SCOPE: ${t.scopePrompt}`:""}`)(e,t)),r=Hr(e);return["You are a tool selector for a map assistant.","You will receive a conversation. Select the minimal set of tools needed to fulfill the LAST user message.",'LOCATION REFERENCE:\n - "near me" / "my location" / "where I am" → getCurrentLocation (user\'s physical GPS position, may prompt permission)\n - "in this area" / "here on the map" / "near the map center" → getViewport (map\'s current view)\n - If getCurrentLocation fails due to permission denial, fall back to getViewport as the reference point',"","Available tools:",...t,...r.length?["","TOOL SCOPES — MANDATORY when selecting a scopable tool:",`The following tools REQUIRE a scope entry in \`toolScopes\` whenever they appear in \`tools\`: ${r.join(", ")}.`,"For each picked scopable tool, emit `toolScopes[<name>]` with the per-tool object shape described in its SCOPE hint above. Don't omit. Don't leave it implicit.","If the user query truly needs all input kinds the tool supports, still emit the scope explicitly — list every kind. Never skip the scope entry when picking the tool."]:[]].join("\n")})(o)}\n\nReturn JSON matching this shape exactly:\n${l}`;let d;for(let p=0;p<2;p++)try{const o=await r({model:s,output:e.json(),system:c,messages:t,maxOutputTokens:400});Jr(a,o.usage);const n=i.safeParse(o.output);if(n.success)return Kr(n.data,a);d=Xr(o.output)??d}catch(u){const e=u;Jr(a,e.usage),d=Xr(e.value)??d}if(!d)throw new Error("Classifier failed after 2 attempts to produce a valid response.");return{tools:d.tools,...d.scopes&&{toolScopes:d.scopes},usage:a}},Qr=async(e,t)=>{const{chatModel:r,toolsMetadata:s}=t,o=performance.now(),{tools:n,toolScopes:i,usage:a}=await Zr(e,r,s);return{activeToolNames:n,...i&&{toolScopes:i},timeMs:performance.now()-o,usage:a}},es=e=>"text"===e.type,ts=e=>{if("string"==typeof e)return e;const t=e.filter(es);return t.length>0?t.map(e=>e.text).join(""):null},rs=e=>{for(let t=e.length-1;t>=0;t--)if("user"===e[t].role)return ts(e[t].content);return null},ss=e=>{const{model:t,maxHistoryMessages:r,maxHistoryMessageLength:s}=e;return async e=>{const{messages:o,toolsMetadata:n}=e;if(!o?.length)return null;const i=r??8,a=s??300;let l=-1,c=null;for(let t=o.length-1;t>=0;t--)if("user"===o[t].role){l=t,c=ts(o[t].content);break}if(!c)return null;const d=[];for(let t=Math.max(0,l-i);t<l;t++){const e=o[t];if("user"!==e.role&&"assistant"!==e.role)continue;const r=ts(e.content);if(!r)continue;const s=r.length>a?r.slice(0,a)+"…":r;d.push({role:e.role,content:s})}d.push({role:"user",content:c});try{return await Qr(d,{chatModel:t,toolsMetadata:n})}catch{return null}}},os=e=>{if(!Array.isArray(e))return e.properties?.address?.freeformAddress},ns=(e,t)=>{if("features"in e)return((e,t)=>{const r=[];t?.query&&r.push(`"${t.query}"`),t?.poiCategories?.length&&r.push(t.poiCategories.join(", ")),t?.routeLabel?r.push(`along ${t.routeLabel}`):t?.where&&r.push(`in ${t.where}`);const s=`${e} ${1===e?"place":"places"}`;return r.length?`${r.join(" ")} (${s})`:s})(e.features.length,t);const r=(s=e,s.properties?.poi?.name);var s;const o=(e=>e.properties?.address?.freeformAddress)(e);return r&&o?`${r}, ${o}`:r||(o||(t?.query?t.query:"Place"))},is={timeMinutes:"min",distanceKM:"km",remainingChargeCPT:"% remaining",spentChargePCT:"% spent",spentFuelLiters:"L"},as=(e,t)=>`${[...e].sort((e,t)=>e.value-t.value).map(e=>`${e.value} ${is[e.type]??e.type}`).join("/")} from ${1===t.length?t[0]:2===t.length?t.join(", "):`${t.length} origins`}`,ls=(e,t={})=>{const r={id:e.id,name:e.properties?.poi?.name,address:e.properties?.address?.freeformAddress,position:e.geometry?.coordinates??[0,0]};if(t.experimentalSearch){const t=e.properties;t?.areaId&&(r.areaId=t.areaId),t?.areaCountry&&(r.areaCountry=t.areaCountry),t?.areaTags?.length&&(r.areaTags=t.areaTags)}return r},cs=(e,t={})=>{const r=Array.isArray(e)?e:e.features;return{count:r.length,features:r.slice(0,5).map(e=>ls(e,t))}},ds=e=>{const{summary:t,sections:r,index:s}=e.properties,o=r.leg,n=e=>({...e,arrivalTime:e.arrivalTime.toISOString(),departureTime:e.departureTime.toISOString()});return{index:s,summary:n(t),...o.length>1&&{legSummaries:o.map(e=>n(e.summary))}}},us=e=>({count:e.features.length,routes:e.features.map(ds)}),ps=Z.object({slotIndex:Z.number(),isFilled:Z.boolean(),address:Z.string().optional(),position:Z.array(Z.number()).length(2).optional().describe("[longitude, latitude]")}),hs=e=>{if(null==e)return null;const t=$(e);let r;return Array.isArray(e)||(r=e.properties?.address?.freeformAddress),{slotIndex:-1,isFilled:!0,...r&&{address:r},...t&&{position:t}}},ms=async(e,t,r)=>{const[s,o]="poi"===t?[A,C]:[M,x],{limit:n,bias:i}=r;if(i&&"position"in i)return(await s({query:e,position:i.position,limit:n})).features;if(i&&"boundingBox"in i)return(await s({query:e,boundingBox:i.boundingBox,limit:n})).features;if(1===n){const t=await o(e);return t?[t]:[]}return(await s({query:e,limit:n})).features},ys=async(e,t,r)=>{const[s]=await ms(e,t,{limit:1,bias:r});return s??null},fs=Z.object({query:Z.string().describe("Location string to resolve for downstream routes or searches."),waypointIndex:Z.number().int().min(0).optional().describe("Route slot to stage [origin, ...stops, destination]. Set if this is an origin/stop/destination."),queryAs:Z.enum(["poi","place"]).describe('"poi" for venues / landmarks / businesses; "place" for addresses, cities, neighborhoods, parks, postal codes, geographies.'),where:Qe.describe("Geographic scope."),show:We.optional().describe("Map display actions after resolving. Skip for intermediate calculations."),entryId:je,geometry:qe}),gs=Z.union([lr.extend({waypointIndex:Z.number().optional(),shown:Ve.optional(),geometryFetched:Z.boolean().optional().describe("Whether a boundary polygon was fetched and cached on the entry (when `geometry` was set)."),geometryShown:Z.boolean().optional().describe("Whether the fetched polygon was rendered on the map.")}),sr]),bs=async(e,t)=>"global"===e.mode?{bias:void 0}:"within"===e.mode?(async(e,t)=>{if(e.viewport)return{bias:{boundingBox:$t(t.baseMap)}};if(e.boundingBox)return{bias:{boundingBox:e.boundingBox}};if(e.query){const r=qt(t.baseMap),s=await ys(e.query,e.queryAs??"place",r?{position:r}:void 0);if(!s)return{bias:void 0};if(s.bbox)return{bias:{boundingBox:s.bbox}};const o=$(s);return{bias:o?{position:o}:void 0}}if(e.placeId){const r=t.places.findPlaceById(e.placeId);if(!r)return{error:`Unknown placeId "${e.placeId}". Use recallPlaces to list available IDs.`};if(!r.place.properties.dataSources?.geometry?.id)return{error:`Place "${e.placeId}" has no geometry data source (most addresses and streets lack one).`};const s=await t.places.fetchPlaceGeometry(e.placeId);return s?{bias:{boundingBox:s.bbox??X.bbox(s)}}:{error:`Geometry Data service returned no feature for place "${e.placeId}".`}}return{bias:void 0}})(e,t):(async(e,t)=>{if(e.viewport){const e=qt(t.baseMap);return{bias:e?{position:e}:void 0}}if(e.position)return{bias:{position:e.position}};if(e.query){const r=qt(t.baseMap),s=await ys(e.query,e.queryAs??"place",r?{position:r}:void 0);if(!s)return{bias:void 0};const o=$(s);return{bias:o?{position:o}:void 0}}return{bias:void 0}})(e,t),ws=async(e,t)=>{if("position"in e){const{lng:t,lat:r}=e.position;return{place:[t,r],position:[t,r],name:`[${t}, ${r}]`}}if("placeId"in e){const r=t.places.findPlaceById(e.placeId);if(!r)return null;const s=$(r.place);return s?{place:r.place,position:s,name:r.place.properties?.poi?.name??r.place.properties?.address?.freeformAddress??e.placeId}:null}const r=await ys(e.query,e.queryAs);if(!r)return null;const s=$(r);return s?{place:r,position:s,name:r.properties?.address?.freeformAddress??e.query,query:e.query}:null},vs=Z.object({routeType:Z.enum(T).optional().describe("fast|short|efficient|thrilling"),traffic:Z.enum(["live","historical"]).optional().describe("live|historical"),avoid:Z.array(Z.enum(L)).optional().describe("tollRoads|motorways|ferries|unpavedRoads|carpools|alreadyUsedRoads|borderCrossings|tunnels|carTrains|lowEmissionZones"),avoidAreas:Z.array(Z.array(Z.number()).describe("GeoJSON bbox [minLng, minLat, maxLng, maxLat]")).max(10).optional().describe("Up to 10 bounding boxes to bypass.")}),Is=Z.object({option:Z.enum(["departAt","arriveBy"]),date:Z.string().describe("ISO 8601")}),Ss=Z.object({maxAlternatives:Z.number().int().min(0).max(5).optional().describe("0–5, default 0"),costModel:vs.optional(),when:Is.optional().describe("Omit to depart now.")}),Es=Z.union([ur,Z.object({success:Z.literal(!0)}),sr]),Ms=Z.object({locations:Z.array(yt).min(2).optional().describe("Ordered [origin, ...stops, destination]. Minimum 2. Provide to set/replace waypoints; omit to keep the current waypoints."),parameters:Ss.optional().describe("Routing options. Omitted fields keep their current values."),showOnMap:Z.boolean().describe("Show the route and waypoints on the map after calculation."),hidePreviousEntries:Ue("routes")}).refine(e=>void 0!==e.locations||void 0!==e.parameters,{message:"Provide at least one of `locations` or `parameters`."}),xs=e=>{const t=e.filter(e=>null!==e);return t.length>=2?t:null},Ps=e=>{const{maxAlternatives:t=0,costModel:r,when:s}=e;return{...t>0&&{maxAlternatives:t},...r&&{costModel:{...r,...r.avoidAreas&&{avoidAreas:r.avoidAreas}}},...s&&{when:{option:s.option,date:new Date(s.date)}}}},Ds=async(e,t,r,s)=>{const o=await S({locations:t,...Ps(e.routing.params)}),n=await e.routing.addRoutes(o,t,((e,t)=>{const r=e.features[0]?.properties?.summary,s=[r?B(r.travelTimeInSeconds):void 0,r?j(r.lengthInMeters):void 0].filter(Boolean),o=s.length?` (${s.join(", ")})`:"",n=t.map(os).filter(Boolean);if(n.length>=2){const e=n[0],t=n.at(-1),r=n.slice(1,-1);return`${r.length>0?`${e} via ${r.join(", ")} to ${t}`:`${e} to ${t}`}${o}`}return`Route${o}`})(o,t));return r&&await(async(e,t,r,s,o)=>{await mt(e.routing,[t],o),await e.routing.showEntry(t);const n=F(r);n&&e.baseMap.mapLibreMap.fitBounds(n,{padding:50})})(e,n,o,0,s),us(o)},_s=Z.union([ur,sr]),Ts=Z.object({origin:yt.optional().describe("Prepend a new origin in front of the current origin. The previous origin is kept as the first intermediate stop (it is not replaced)."),destination:yt.optional().describe("Append a new destination after the current destination. The previous destination is kept as an intermediate stop (it is not replaced)."),stops:Z.array(yt).min(1).optional().describe("Intermediate stops to insert at optimal mid-positions along the existing route. Multiple stops settle into natural along-route order regardless of input order."),showOnMap:Z.boolean().describe("Show the updated route and waypoints on the map after recalculation."),hidePreviousEntries:Ue("routes")}).refine(e=>void 0!==e.origin||void 0!==e.destination||e.stops&&e.stops.length>0,{message:"Provide at least one of `origin`, `destination`, or `stops`."}),As=e=>"query"in e?`"${e.query}"`:"placeId"in e?`placeId "${e.placeId}"`:JSON.stringify(e.position),Cs=async(e,t,r)=>{const s=await ms(e,t,{limit:5,bias:r?{position:r}:void 0});if(0===s.length)return null;for(const o of s){if(!o.properties.dataSources?.geometry?.id)continue;const e=(await P({geometries:[o]})).features[0];if(e)return{place:o,geometry:e}}return{place:s[0]}},ks=e=>Z.union([ar(e).extend({shown:Ve.optional(),placesEntryId:Z.string().optional(),label:Z.string().optional().describe('Human-readable label stored with the entry (e.g. "cafe", "CINEMA (50 places)").'),geometriesFetched:Z.number().optional().describe("Count of boundary polygons fetched and cached on the entry (when `geometries` was set)."),geometriesShown:Z.boolean().optional().describe("Whether the fetched polygons were rendered on the map.")}),sr]),Os=(ks({}),{...ze,queries:Z.array(Z.object({query:Z.string().describe('Name of a CONTAINING area to search within (e.g. "Paris", "De Jordaan, Amsterdam"). NEVER the user\'s search subject — that goes in the top-level `query`.'),queryAs:He})).optional().describe('CONTAINING areas to search WITHIN — answers "search where?", NOT "search for what?". Never put the user\'s search subject here. Example: for "cafes in Amsterdam" → top-level `query`/`poiCategories` carry "cafes"; `where.queries: [{query: "Amsterdam"}]` carries the region. Each entry resolves to a boundary polygon (or bbox); the union is searched. Per-item `queryAs` disambiguates POI vs place. EXCLUSIVE with viewport; composes with other multi-region fields.'),placeIds:Z.array(Z.string()).optional().describe("IDs of session places — each polygon (fetched on demand) joins the search area. Use recallPlaces to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields."),geometries:Z.array(Ne).optional().describe("Direct GeoJSON Polygons / MultiPolygons (custom drawing, external data). For named or stored places use `queries` / `placeIds`. EXCLUSIVE with viewport; composes with other multi-region fields."),range:Z.string().optional().describe('Range ID from findReachableAreas (e.g. "ranges-0") — restricts to that entry; multi-origin entries combine their polygons. Use recallRanges to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields.'),route:Z.object({routeId:Z.string().optional().describe('Route entry ID (e.g. "routes-0"). Use recallRoutes. Default: latest route.'),widthMeters:Z.number().positive().describe('Total corridor width (metres) — widthMeters/2 each side. Typical: 200–500 m ("near the road"), 2–5 km ("broad area along the route").')}).optional().describe("Buffered corridor around a stored route — turf.buffer at widthMeters/2 around the line. BULK discovery (≤10000); for a SHORT ranked detour list use mode `maxDetour`. EXCLUSIVE with viewport; composes with other multi-region fields.")}),Rs={boundingBoxes:Z.array(Re).optional().describe("Bboxes [W,S,E,N] — results in the union. Last-resort: prefer `municipalities` / `queries` / `placeIds` / `geometries` when polygons are resolvable. EXCLUSIVE with viewport; composes with other multi-region fields."),municipalities:Z.array(Z.string()).optional().describe('Exact, case-sensitive municipality names (e.g. ["Amsterdam", "Utrecht"]). EXCLUSIVE with viewport; composes with other multi-region fields.'),areaId:Z.string().optional().describe('Id of a small area polygon (few km², not a whole municipality) — restricts results to that single area. Typically chained from a previous hit\'s `areaId` ("what else is in this same area?"). EXCLUSIVE with viewport; composes with other multi-region fields.')},Ls=e=>Z.object(e.experimentalSearch?{...Os,...Rs}:Os).refine(e=>{const t=!!e.viewport,r=e,s=!!(e.queries?.length||e.placeIds?.length||e.geometries?.length||e.range||e.route||r.municipalities?.length||r.boundingBoxes?.length||r.areaId);return(!t||!s)&&(t||s)},{message:"`within` requires EITHER `viewport: true` OR one or more multi-region fields (queries / placeIds / geometries / range / route"+(e.experimentalSearch?" / boundingBoxes / municipalities / areaId":"")+"), not both."}),Fs=Z.object({mode:Z.literal("maxDetour"),routeId:Z.string().optional().describe('Route entry ID (e.g. "routes-0"). Use recallRoutes. Default: latest route.'),maxDetourTimeSeconds:Z.number().positive().describe("Max extra travel time (seconds) the user accepts to reach a POI."),sortBy:Z.enum(["detourTime","detourOffset"]).optional().describe('"detourTime" (default) ranks by extra travel time; "detourOffset" by distance from the route line.'),limit:Z.number().max(100).optional().describe("Max results. Default: 10. Use this mode for short ranked lists, not bulk discovery.")}),Ns=e=>{const t=e.experimentalSearch?"boundingBoxes/queries/placeIds/municipalities/areaId/geometries/range/route":"queries/placeIds/geometries/range/route";return Z.union([Ls(e),Ke,Fs,Ze]).describe(`Geographic scope, by \`mode\`: \`within\` (area — \`viewport\` alone, OR any combo of ${t}) | \`nearby\` (point bias — position/viewport/query, optional radiusMeters) | \`maxDetour\` (ranked detour list off a route) | \`global\` (no bias). Default: \`{ mode: "within", viewport: true }\`.`)},js=e=>{const t={query:Z.string().optional().describe('Free-text filter on POI name/address. NEVER a city/region/area name (e.g. "Paris", "Amsterdam") — those go in `where`. For "restaurants in Paris": `query` is empty (or a name filter like "pizza"), `where.queries: [{query: "Paris"}]`. Omit when filtering by category — use `poiCategories` instead.'),where:Ns(e).optional(),poiCategories:Z.array(Z.string()).optional().describe('CONSTANT_CASE category codes. ALWAYS batch all requested categories in ONE array — never one call per category. Common: "RESTAURANT", "CAFE", "BAR", "HOTEL", "PARK", "PARK_RECREATION_AREA", "ELECTRIC_VEHICLE_STATION", "GAS_STATION", "PARKING_GARAGE", "SUPERMARKETS_HYPERMARKETS", "PHARMACY", "ATM". For anything else, call getPoiCategoryCodes first to resolve natural-language terms (e.g. "italian food", "gym").'),show:We.optional().describe("Display places/geometries on the map. Skip for intermediate results — let the follow-up (processData / analyseData) render the final entry."),entryId:je,geometries:qe},r=e.experimentalSearch?{...t,placeTypes:Z.array(Z.enum(["POI","PointAddress","Street"])).optional().describe("Restrict to record types (default: all). POI = businesses/landmarks/amenities; PointAddress = numbered addresses; Street = unnumbered streets."),areaTags:Z.array(Z.enum(w)).optional().describe('Tokens describing a small surrounding area (few km², not a whole municipality). Enum drawn from the SDK canonical vocabulary — e.g. "coastal", "walkable", "alpine", "transit_connected", "village", "tourism_economy". OR semantics — matches places in areas tagged with ANY token. Filter works on any `where` scope (including global), but area data is currently populated only for DE / NL / FR — zero hits where data is absent. Filter, not a geo-bias — pair with a `where` mode that supplies one.')}:t;return Z.object(r).refine(e=>!!e.query||!!e.poiCategories?.length,{message:"discoverPlaces needs a search subject: set `query` (free-text) or `poiCategories`. For a single named place (no filter), use locatePlace — including for its boundary polygon (`geometry`)."})},Bs=e=>`Search for MULTIPLE places in a region — REQUIRES a search subject (\`query\` and/or \`poiCategories\`). NOT for a single named place — use locatePlace for that (including its polygon/boundary via \`geometry\`). Stores an entry and returns \`placesEntryId\`. CALL ONCE PER REQUEST — batch all \`poiCategories\` + areas in one call; split only for explicitly comparable result sets. \`where.mode\`: \`within\` (\`viewport\` OR any combo of ${e.experimentalSearch?"boundingBoxes/queries/placeIds/municipalities/areaId/geometries/range/route":"queries/placeIds/geometries/range/route"}), \`nearby\` (position/viewport/query + optional radiusMeters), \`maxDetour\` (≤100 ranked detours off a route), \`global\`. `+(e.experimentalSearch?"Prefer `municipalities` / `areaId` / `queries` / `placeIds` (precise) over `boundingBoxes` (coarse). ":"Prefer `queries` / `placeIds` (resolve to precise polygons) over raw `geometries`. ")+(e.experimentalSearch?'Top-level `areaTags` narrows hits by small-area character ("coastal", "walkable", …) — area data currently populated only in DE/NL/FR. ':"")+'Route: `within.route` = bulk corridor (≤10000); `maxDetour` = short ranked list (≤100). Default: `{ mode: "within", viewport: true }`. Never call this with empty `query` AND empty `poiCategories` — that is locatePlace territory.',qs={},$s=(js(qs),Bs(qs),e=>{const{geometries:t}=e;return t?.length?{geometries:t}:{}}),Gs=(e,t)=>{const{query:r,poiCategories:s,bias:o={},radiusMeters:n,multiFilters:i}=t,a={query:r,poiCategories:s,...o,...void 0!==n&&{radiusMeters:n}};return e?E({...a,limit:1e4,...i}):A({...a,limit:100,...$s(i)})},Us=async(e,t,r)=>{if(e.viewport)return{bias:{boundingBox:$t(t.baseMap)},multiFilters:r};const s=[],o=[];if(e.queries?.length){const r=await(async(e,t)=>{const r=qt(t.baseMap),s=[],o=[];for(const n of e){const e=await Cs(n.query,n.queryAs??"place",r);if(!e)return{error:`Could not resolve "${n.query}". Try a more specific query, a placeId, or a boundingBox.`};if(e.geometry?.geometry)s.push(e.geometry.geometry);else{if(!e.place.bbox)return{error:`"${n.query}" resolved to a place with no geometry or bbox — cannot use as a "within" area. Use mode "nearby" with this query for a point bias instead.`};o.push(e.place.bbox)}}return{geometries:s,boundingBoxes:o}})(e.queries,t);if("error"in r)return r;s.push(...r.geometries),o.push(...r.boundingBoxes)}if(e.placeIds?.length){const r=await(async(e,t)=>{const r=[];for(const s of e){const e=t.places.findPlaceById(s);if(!e)return{error:`Unknown placeId "${s}". Use recallPlaces to list available IDs.`};if(!e.place.properties.dataSources?.geometry?.id)return{error:`Place "${s}" has no geometry data source (most addresses and streets lack one).`};const o=await t.places.fetchPlaceGeometry(s);if(!o)return{error:`Geometry Data service returned no feature for place "${s}".`};r.push(o.geometry)}return{geometries:r}})(e.placeIds,t);if("error"in r)return r;s.push(...r.geometries)}const n=e,i=[...n.boundingBoxes??[],...o],a=[...e.geometries??[],...s],l={...r,...n.municipalities?.length&&{municipalities:n.municipalities},...n.areaId&&{areaId:n.areaId},...i.length&&{boundingBoxes:i},...a.length&&{geometries:a}};let c;if(e.route){const r=((e,t)=>{const r=e.routing.entries;if(0===r.length)return{error:"No routes in state. Calculate a route first via setRoute."};const s=t.routeId?r.find(e=>e.id===t.routeId):r.at(-1);if(!s)return{error:`No route found with id "${t.routeId}". Use recallRoutes to list available routes.`};if(0===s.data.features.length)return{error:`Route entry "${s.id}" has no geometry.`};const o=t.widthMeters/2,n=[];for(const i of s.data.features){const e=X.buffer(i,o,{units:"meters"})?.geometry;e&&n.push(e)}return 0===n.length?{error:`Failed to compute route corridor for "${s.id}" (turf.buffer returned no geometry for any of the ${s.data.features.length} route alternative(s)).`}:{polygons:n,routeLabel:s.label}})(t,e.route);if("error"in r)return r;l.geometries=[...l.geometries??[],...r.polygons],c=r.routeLabel}return{bias:{},multiFilters:l,range:e.range,routeLabel:c}},Ws=async(e,t,r)=>{if("nearby"===e.mode){const s=await(async(e,t)=>{if(e.viewport){const r=qt(t.baseMap);return{bias:r?{position:r}:{},radiusMeters:e.radiusMeters}}if(e.position)return{bias:{position:e.position},radiusMeters:e.radiusMeters};if(e.query){const r=qt(t.baseMap),s=await ys(e.query,e.queryAs??"place",r?{position:r}:void 0);if(s){const t=$(s);if(t)return{bias:{position:t},radiusMeters:e.radiusMeters}}return{bias:{},radiusMeters:e.radiusMeters}}return{bias:{},radiusMeters:e.radiusMeters}})(e,t);return{bias:s.bias,multiFilters:r,radiusMeters:s.radiusMeters}}if("within"===e.mode){const s=await Us(e,t,r);return"error"in s?s:{bias:s.bias,multiFilters:s.multiFilters,range:s.range,routeLabel:s.routeLabel}}return{bias:{},multiFilters:r}},Vs=e=>{if(e)return"within"===e.mode&&e.queries?.length?e.queries.map(e=>e.query).join(", "):"nearby"===e.mode&&e.query?e.query:void 0},zs=async(e,t,r,s,o,n)=>{const i=s?await Bt(e,[r],s):void 0;let a,l;if(o){const t=await jt(e,r,o);a=t.fetched,t.shown&&(l=!0)}const c=e.places.entries.find(e=>e.id===r)?.label;return{...cs(t,n),placesEntryId:r,...c&&{label:c},...i&&{shown:i},...void 0!==a&&{geometriesFetched:a},...void 0!==l&&{geometriesShown:l}}},Hs=e=>{const t=!0===e.experimentalSearch;return async(r,s)=>{const{query:o,where:n,poiCategories:i,show:a,entryId:l,geometries:c}=r,d=r,u=d.placeTypes,p=d.areaTags,{resolved:h,unresolved:m}=await(async e=>{if(!e)return{resolved:void 0,unresolved:[]};const t=e.filter(e=>_t.has(e)),r=e.filter(e=>!_t.has(e));if(0===r.length)return{resolved:t.length?t:void 0,unresolved:[]};await D();const s=await Promise.all(r.map(async e=>({term:e,codes:await D({filters:[e]})}))),o=[],n=[];for(const{term:a,codes:l}of s)0===l.length?o.push(a):n.push(...l);const i=[...new Set([...t,...n])];return{resolved:i.length?i:void 0,unresolved:o}})(i);if(i?.length&&!h)return{error:`No POI categories matched: ${m.map(e=>`"${e}"`).join(", ")}. POI category codes must be exact CONSTANT_CASE values, or natural-language terms resolvable by the SDK catalog. Call getPoiCategoryCodes to find valid codes for your search subject (e.g. "italian food", "gym").`};const y=n??{mode:"within",viewport:!0},f={...u?.length&&{placeTypes:u},...p?.length&&{areaTags:p}};try{if("maxDetour"===y.mode){const t=await(async(e,t,r,s,o)=>{const n=e.routing.entries;if(0===n.length)return{error:"No routes in state. Calculate a route first via setRoute."};const i=t.routeId?n.find(e=>e.id===t.routeId):n.at(-1);if(!i)return{error:`No route found with id "${t.routeId}". Use recallRoutes to list available routes.`};const a=i.data.features[0];if(!a)return{error:`Route entry "${i.id}" has no geometry.`};const l=await v({route:a,maxDetourTimeSeconds:t.maxDetourTimeSeconds,...r&&{query:r},...s&&{poiCategories:s},...t.sortBy&&{sortBy:t.sortBy},...void 0!==t.limit&&{limit:t.limit}});return{result:l,placesEntryId:await e.places.addPlaceResult(l,ns(l,{query:r,poiCategories:s,routeLabel:i.label}),o)}})(s,y,o,h,l);return"error"in t?t:zs(s,t.result,t.placesEntryId,a,c,e)}const r=await Ws(y,s,f);if("error"in r)return r;const{bias:n,multiFilters:i,radiusMeters:d,range:u,routeLabel:p}=r;if(u){const r=await(async(e,t,r,s,o,n,i,a)=>{const l=e.ranges.entries.find(e=>e.id===t);if(!l||0===l.ranges.length)return{error:`Range "${t}" not found. Use recallRanges to list available ranges.`};const c=l.ranges.map(e=>e.polygon?.features[0]?.geometry).filter(e=>!!e);if(0===c.length)return{error:`Range "${t}" has no polygons. Recompute via findReachableAreas.`};const d=[...c,...o.geometries??[]],u=await Gs(a,{query:r,poiCategories:s,multiFilters:{...o,geometries:d}});return{result:u,placesEntryId:await e.places.addPlaceResult(u,ns(u,{query:r,poiCategories:s,...i&&{routeLabel:i}}),n)}})(s,u,o,h,i,l,p,t);return"error"in r?r:zs(s,r.result,r.placesEntryId,a,c,e)}const m=await(async(e,t,r,s,o,n,i,a,l,c)=>{const d=await Gs(c,{query:t,poiCategories:s,bias:r,radiusMeters:o,multiFilters:n});return{result:d,placesEntryId:await e.places.addPlaceResult(d,ns(d,{query:t,poiCategories:s,where:Vs(a),...l&&{routeLabel:l}}),i)}})(s,o,n,h,d,i,l,y,p,t);return zs(s,m.result,m.placesEntryId,a,c,e)}catch(g){return{error:`Search failed: ${g instanceof Error?g.message:String(g)}`}}}},Ys=(Hs(qs),{classificationPrompt:'Search multiple places by query/category in a region. BATCH categories+areas in one call. `query` filters POI names ONLY — area names go in `where`. `where.mode`: within (area) / nearby (point bias) / maxDetour (route-relative ranked) / global. For "X in [named area]", put X in top-level `query`/`poiCategories` and the area in `where.queries: [{query: "..."}]` — geocoded in one step, no precursor locatePlace. Within-mode: route → corridor. Prefer geometries/municipalities/placeIds over bbox. Skip for already-stored places — use recallPlaces/processData.',tags:["discover","place","location"],examples:['discoverPlaces({ poiCategories: ["RESTAURANT", "CAFE", "BAR"], entryId: "amsterdam-food-spots" }) // batch many categories + semantic id','discoverPlaces({ query: "coffee", where: { mode: "nearby", position: [4.9, 52.4], radiusMeters: 500 } })','discoverPlaces({ query: "parking", where: { mode: "within", queries: [{ query: "De Jordaan, Amsterdam", queryAs: "place" }] } }) // area name goes in `where.queries`, NOT top-level `query`','discoverPlaces({ query: "coffee", where: { mode: "nearby", query: "Schiphol Airport", queryAs: "poi", radiusMeters: 2000 } }) // POI-resolved bias point','discoverPlaces({ poiCategories: ["HOTEL"], where: { mode: "within", municipalities: ["Amsterdam", "Utrecht"] }, entryId: "ams-utrecht-hotels" })','discoverPlaces({ query: "bakery", where: { mode: "within", range: "ranges-0" } })','discoverPlaces({ poiCategories: ["CAFE"], show: { markerType: "pin", zoomMode: "auto" } }) // omit `where` → defaults to viewport','discoverPlaces({ poiCategories: ["RESTAURANT"], where: { mode: "within", queries: [{ query: "Paris" }] } }) // "restaurants in Paris" — subject in `poiCategories`, area in `where.queries`','discoverPlaces({ poiCategories: ["ELECTRIC_VEHICLE_STATION"], where: { mode: "within", route: { routeId: "routes-0", widthMeters: 1000 } } }) // bulk corridor scan along the route','discoverPlaces({ query: "coffee", where: { mode: "maxDetour", maxDetourTimeSeconds: 300, limit: 5 } }) // top 5 coffee detours within 5 min off the latest route','discoverPlaces({ poiCategories: ["ELECTRIC_VEHICLE_STATION"], where: { mode: "maxDetour", routeId: "routes-1", maxDetourTimeSeconds: 600, sortBy: "detourOffset" } }) // ranked EV detours on a specific route','discoverPlaces({ query: "bakery", where: { mode: "within", placeIds: ["G55fc4abe-..."] } }) // search inside stored place polygons (auto-fetched if needed)','discoverPlaces({ query: "vegan", where: { mode: "global" } })'],examplePrompts:["Find coffee shops near me","Italian restaurants in Amsterdam","EV chargers in the current map area","Hotels in Amsterdam and Utrecht","Bakeries within the reachable range","Outline the Amsterdam neighbourhoods","Show the boundaries of these municipalities","Search for new EV stations along my route","Cafes in a 500 m corridor around the planned route","Best 5 coffee stops without losing more than 5 min off my route","EV chargers I can reach with under 10 min detour"],relatedTools:["getPOICategoryCodes","updatePlacesDisplay","updatePlacesDisplay","locatePlace","getViewport","findReachableAreas","processData","analyseData"],dependsOn:["getPOICategoryCodes","setRoute"]}),Js=Z.object({type:Z.enum(["timeMinutes","distanceKM"]).describe("Budget type including its unit."),value:Z.number().describe("Budget value. e.g. 30 for 30 minutes, 50 for 50 km.")}),Xs=Z.object({origins:Z.array(yt).min(1).describe("One or more starting points; one reachable area is computed per origin under the same budgets and bundled into a single ranges entry. Pass a single-element array for the classic one-origin call."),budgets:Z.array(Js).min(1).describe("One or more budget constraints applied to every origin. Pass multiple for nested ranges (e.g. 10, 20, 30 min or km). Order does not matter — sorted internally."),theme:Z.enum(["filled","inverted","outline"]).optional().describe("Visual theme for the polygons. Default: outline (transparent fill with thick colored border). Use inverted for a donut (reachable highlighted, outside dimmed) or filled for solid polygons."),showOnMap:Z.boolean().describe("Whether to render the range polygon(s) on the map."),showOriginPin:Z.boolean().optional().describe("Whether to drop a pin at each origin. Default: true."),hidePreviousEntries:Ue("ranges")}),Ks=Z.object({rangesId:Z.string(),ranges:Z.array(Z.object({originName:Z.string(),budgets:Z.array(Js)})),skipped:Z.array(Z.object({origin:Z.string(),reason:Z.string()})).optional().describe("Origins that could not contribute (unresolved query, empty geometry).")}),Zs=Z.union([Ks,Z.object({status:Z.enum(["no_results"])}),sr]),Qs=async(e,t)=>{const r=await I(e.map(e=>({origin:t,budget:{type:e.type,value:e.value}})));return 0===r.features.length?null:r},eo=e=>e.origin.query??`${e.origin.position[1]}, ${e.origin.position[0]}`,to=Z.union([Z.object({count:Z.number().describe("Number of codes returned"),codes:Z.array(Z.string()).describe('POICategory enum values for discoverPlaces poiCategories. Generic codes (e.g. "RESTAURANT") also accepted by toggleTilesPOIs categories.')}),sr]),ro=Z.object({filters:Z.array(Z.string()).optional().describe("Case-insensitive filter strings matched against category names and synonyms. Omit to return all codes."),language:Z.string().describe('Locale e.g. "en-GB", "fr-FR", "es-ES", "de-DE".')}),so=Z.object({speed:Z.number().optional(),freeFlowSpeed:Z.number().optional(),congestionLevel:Z.number().optional(),travelTime:Z.number().optional(),networkLength:Z.number().optional()}),oo=Z.union([Z.object({entryId:Z.string().describe("Id of the new traffic-area-analytics entry — use with updateTrafficAreaAnalyticsDisplay / analyseData / processData."),name:Z.string().optional(),timezone:Z.string().optional(),dateRange:Z.object({start:Z.string(),end:Z.string()}),baseData:so,metrics:Z.array(Z.string()),tileCount:Z.number(),availableGranularities:Z.array(Z.string())}),sr]),no=Z.object({location:yt.optional().describe("Named location (city, region, country) to fetch the boundary for. Resolved via geocoding + geometryData. Mutually exclusive with bbox and geometry."),bbox:Z.array(Z.number()).length(4).optional().describe("[minLng, minLat, maxLng, maxLat] — use a wide area (at least 5-10km across, city-level) for meaningful results. Mutually exclusive with location."),showOnMap:Z.boolean().optional().describe("Visualize the result immediately with default settings. Use `updateTrafficAreaAnalyticsDisplay` to customize."),startDate:Z.string().optional().describe("Start date 'YYYY-MM-DD'. Use with endDate for a continuous range (max 31 days)."),endDate:Z.string().optional().describe("End date 'YYYY-MM-DD' (inclusive). Defaults to today if omitted."),days:Z.array(Z.string()).optional().describe("Specific dates 'YYYY-MM-DD' for non-consecutive analysis. Mutually exclusive with startDate/endDate."),metrics:Z.union([Z.literal("all"),Z.array(Z.enum(["speed","congestionLevel","freeFlowSpeed","travelTime","networkLength"])).min(1)]).describe("Traffic metrics to analyze. Use 'all' (preferred) or an explicit array like ['speed', 'congestionLevel', 'freeFlowSpeed', 'travelTime', 'networkLength']."),functionalRoadClasses:Z.union([Z.literal("all"),Z.array(Z.enum(["MOTORWAY","MAJOR_ROAD","OTHER_MAJOR_ROAD","SECONDARY_ROAD","LOCAL_CONNECTING_ROAD","LOCAL_ROAD_HIGH_IMPORTANCE","LOCAL_ROAD","LOCAL_ROAD_MINOR_IMPORTANCE","OTHER_ROAD"]))]).optional().describe("Road classes to include. 'all' (default) or array of specific classes."),hours:Z.union([Z.literal("all"),Z.array(Z.number().int().min(0).max(23))]).optional().describe("Hours of the day (0-23) to include. 'all' (default) or array like [7,8,9,17,18] for rush hours.")}),io=e=>`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`,ao=e=>e instanceof Date?Number.isNaN(e.getTime())?void 0:io(e):"string"==typeof e?e.slice(0,10):void 0,lo=e=>e.trafficIncidents.entries.map(e=>({id:e.id,label:e.label,count:e.data.length,timestamp:e.timestamp})),co=Z.union([Z.object({count:Z.number(),entryId:Z.string().optional(),entries:Z.array(Z.object({id:Z.string(),label:Z.string(),count:Z.number(),timestamp:Z.number()}))}),sr]),uo=Z.object({...ze,boundingBox:Re.optional().describe("Single bbox [W,S,E,N]. EXCLUSIVE with viewport; composes with other multi-region fields."),queries:Z.array(Z.object({query:Z.string().describe('Name of a CONTAINING area (e.g. "Paris", "De Jordaan, Amsterdam", "M25"). Resolved to its bbox (or polygon bbox) and merged into the search area.'),queryAs:He})).optional().describe("CONTAINING areas to fetch incidents within. Each entry geocodes to a bbox; the union of all bboxes is used. Pair per-item `queryAs` to disambiguate POI vs place. EXCLUSIVE with viewport; composes with other multi-region fields."),placeIds:Z.array(Z.string()).optional().describe("IDs of session places — each polygon (fetched on demand) contributes its bbox to the area. Use recallPlaces to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields."),geometries:Z.array(Ne).optional().describe("Direct GeoJSON Polygons / MultiPolygons (custom drawing, external data). For named or stored places use `queries` / `placeIds`. EXCLUSIVE with viewport; composes with other multi-region fields."),range:Z.string().optional().describe('Range ID from findReachableAreas (e.g. "ranges-0") — restricts to that entry; multi-origin entries combine their polygons. Use recallRanges to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields.'),route:Z.object({routeId:Z.string().optional().describe('Route entry ID (e.g. "routes-0"). Use recallRoutes. Default: latest route.'),widthMeters:Z.number().positive().describe('Total corridor width (metres) — widthMeters/2 each side. Typical: 200–500 m ("near the road"), 2–5 km ("broad area along the route").')}).optional().describe("Buffered corridor around a stored route — turf.buffer at widthMeters/2 around the line. EXCLUSIVE with viewport; composes with other multi-region fields.")}).refine(e=>yo(e)||fo(e),{message:"`within` requires `viewport: true` OR one or more multi-region fields (boundingBox / queries / placeIds / geometries / range / route) with a meaningful value."}),po=e=>!!e&&4===e.length&&e.some(e=>0!==e),ho=e=>!!e&&e.widthMeters>0&&(void 0===e.routeId||e.routeId.length>0),mo=e=>!!e&&e.length>0,yo=e=>!0===e.viewport,fo=e=>po(e.boundingBox)||!!e.queries?.length||!!e.placeIds?.length||!!e.geometries?.length||mo(e.range)||ho(e.route),go=Z.union([uo]).describe('Geographic scope. Only `within` mode (single area, defaults to `{ mode: "within", viewport: true }`). Use `viewport` alone for "the network / right now / this area", or any combination of boundingBox / queries / placeIds / geometries / range / route — each resolves to a bbox and the union is sent to the SDK (≤10,000 km²).'),bo=Z.object({where:go.optional().describe('Geographic scope. Default: `{ mode: "within", viewport: true }`.'),categoryFilter:Z.array(Z.enum([...Y])).optional().describe("Filter by incident category. If omitted, all categories are returned."),timeValidityFilter:Z.array(Z.enum(["present","future"])).optional().describe("default: ['present']"),label:Z.string().optional().describe('Short human-readable label for this fetch — name the place ("Central London", "Amsterdam ring", "M25 corridor"), not coordinates. Surfaced verbatim in the monitor chip and on the entry chip in the UI; also used by analyseData / focusIncidents to disambiguate which entry to operate on. Always pass one when silent is false; the bbox-coordinate fallback ("bbox a,b → c,d") is for silent intermediate fetches only.'),silent:Z.boolean().optional().describe("Default: false. When true, suppress map rendering (use for intermediate calculations only). Normally, fetched incidents auto-render on the map so the user can visually validate the agent is looking at the right area."),fitBounds:Z.boolean().optional().describe("Default: true. Pass false to skip camera move after rendering.")}),wo=async(e,t)=>{const r=[];let s;if(po(e.boundingBox)&&r.push(e.boundingBox),e.queries?.length){const s=await(async(e,t)=>{const r=qt(t.baseMap),s=[];for(const o of e){const e=await ms(o.query,o.queryAs??"place",{limit:5,bias:r?{position:r}:void 0});if(0===e.length)return{error:`Could not resolve "${o.query}". Try a more specific query, a placeId, or a boundingBox.`};const t=F(e[0]);if(!t)return{error:`"${o.query}" resolved to a place with no bbox — cannot use as a "within" area. Provide a boundingBox or a different query.`};s.push(t)}return{bboxes:s}})(e.queries,t);if("error"in s)return s;r.push(...s.bboxes)}if(e.placeIds?.length){const s=await(async(e,t)=>{const r=[];for(const s of e){const e=t.places.findPlaceById(s);if(!e)return{error:`Unknown placeId "${s}". Use recallPlaces to list available IDs.`};const o=F(e.place);if(o){r.push(o);continue}const n=await t.places.fetchPlaceGeometry(s),i=n?F(n):void 0;if(!i)return{error:`Place "${s}" has no bbox or fetchable boundary polygon. Provide a boundingBox or a different placeId.`};r.push(i)}return{bboxes:r}})(e.placeIds,t);if("error"in s)return s;r.push(...s.bboxes)}if(r.push(...(e=>{if(!e?.length)return[];const t=[];for(const r of e){const e=F(r);e&&t.push(e)}return t})(e.geometries)),mo(e.range)){const s=((e,t)=>{const r=e.ranges.entries.find(e=>e.id===t);if(!r||0===r.ranges.length)return{error:`Range "${t}" not found. Use recallRanges to list available ranges.`};const s=[];for(const o of r.ranges){const e=o.polygon?.features[0],t=e?F(e):void 0;t&&s.push(t)}return 0===s.length?{error:`Range "${t}" has no polygons. Recompute via findReachableAreas.`}:{bboxes:s}})(t,e.range);if("error"in s)return s;r.push(...s.bboxes)}if(ho(e.route)){const o=((e,t)=>{const r=e.routing.entries;if(0===r.length)return{error:"No routes in state. Calculate a route first via setRoute."};const s=t.routeId?r.find(e=>e.id===t.routeId):r.at(-1);if(!s)return{error:`No route found with id "${t.routeId}". Use recallRoutes to list available routes.`};if(0===s.data.features.length)return{error:`Route entry "${s.id}" has no geometry.`};const o=t.widthMeters/2,n=[];for(const i of s.data.features){const e=X.buffer(i,o,{units:"meters"}),t=e?F(e):void 0;t&&n.push(t)}return 0===n.length?{error:`Failed to compute route corridor for "${s.id}" (turf.buffer returned no geometry for any of the ${s.data.features.length} route alternative(s)).`}:{bboxes:n,routeLabel:s.label}})(t,e.route);if("error"in o)return o;r.push(...o.bboxes),s=o.routeLabel}return{bboxes:r,...s&&{routeLabel:s}}},vo=async(e,t)=>{if(fo(e)){const r=await wo(e,t);if("error"in r)return r;const s=(e=>{if(0===e.length)return;let t=Number.POSITIVE_INFINITY,r=Number.POSITIVE_INFINITY,s=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY;for(const[n,i,a,l]of e)n<t&&(t=n),i<r&&(r=i),a>s&&(s=a),l>o&&(o=l);return[t,r,s,o]})(r.bboxes);return s?{bbox:s,...r.routeLabel&&{routeLabel:r.routeLabel}}:{error:"No area could be resolved from `where` — provide at least one within field."}}return yo(e)?(e=>{try{return{bbox:e.baseMap.ttMap.getBBox()}}catch(t){return{error:`Viewport fallback failed: ${t instanceof Error?t.message:String(t)}`}}})(t):{error:"No area could be resolved from `where` — provide at least one within field."}},Io=Z.union([ur,sr]),So=Z.object({waypointIndices:Z.array(Z.number().int()).min(1).describe("Indices of waypoints to remove. 0=origin, last=destination. Min 2 waypoints must remain. Duplicates are not allowed."),showOnMap:Z.boolean().describe("Show the updated route and waypoints on the map after recalculation."),hidePreviousEntries:Ue("routes")}),Eo=Z.union([ur,sr]),Mo=Z.union([Z.literal("origin"),Z.literal("destination"),Z.number().int()]).describe("Which waypoint to overwrite. Use 'origin' or 'destination' for the endpoints, or a 0-based absolute index (0 = origin, last = destination, intermediate stops in between)."),xo=Z.object({waypointIndex:Mo,location:yt.describe("The new waypoint that replaces the targeted one."),showOnMap:Z.boolean().describe("Show the updated route and waypoints on the map after recalculation."),hidePreviousEntries:Ue("routes")}),Po=e=>"query"in e?`"${e.query}"`:"placeId"in e?`placeId "${e.placeId}"`:JSON.stringify(e.position),Do=Z.union([lr,sr]),_o=Z.object({position:Z.array(Z.number()).length(2).describe("[longitude, latitude] GeoJSON position to reverse geocode")}),To=Z.union([Z.object({byodEntryId:Z.string().describe("Id of the new BYOD entry (use with `byodEntryIDs` on analyseData / processData)."),label:Z.string(),featureCount:Z.number(),geometryTypes:Z.array(Z.string()).describe("Geometry types present (Point / LineString / Polygon / …)."),source:Z.union([Z.object({kind:Z.literal("url"),url:Z.string()}),Z.object({kind:Z.literal("inline")})]).describe("Where the data came from."),shown:Z.boolean().describe("Whether the entry was rendered immediately (mirrors the `show` input).")}),sr]),Ao=Z.object({label:Z.string().describe('Human-readable label for the new entry (e.g. "Sales territories", "Customer pins 2026-Q1").'),url:Z.url().optional().describe("URL serving a GeoJSON FeatureCollection. Fetched once and stored on the entry. EXCLUSIVE with `data`."),data:Z.unknown().optional().describe('Inline GeoJSON FeatureCollection (`{ type: "FeatureCollection", features: [...] }`). EXCLUSIVE with `url`. Keep small — large inline payloads bloat the conversation.'),entryId:je,show:Z.boolean().optional().describe("Render the entry immediately after adding. Default: false (entry is stored but hidden — use `updateByodDisplay` to render later).")}).refine(e=>e.url?!e.data:!!e.data,{message:"Pass EXACTLY ONE of `url` or `data`."}),Co=26214400,ko=new Set(["http:","https:"]),Oo=async e=>{const t=new AbortController,r=setTimeout(()=>t.abort(),15e3);try{const r=await fetch(e,{signal:t.signal});if(!r.ok)return{error:`Failed to fetch "${e}": HTTP ${r.status} ${r.statusText}`};const s=((e,t)=>{const r=e.headers.get("content-length");if(!r)return;const s=Number.parseInt(r,10);return Number.isFinite(s)&&s>Co?{error:`Response too large for "${t}": advertised ${s} bytes (max 26214400).`}:void 0})(r,e);if(s)return s;const o=await(async(e,t)=>{const r=e.body?.getReader();if(!r){const r=await e.text();return r.length>Co?{error:`Response too large for "${t}": ${r.length} bytes (max 26214400).`}:{value:r}}const s=[];let o=0;for(;;){const{done:e,value:n}=await r.read();if(e)break;if(o+=n.byteLength,o>Co)return await r.cancel(),{error:`Response exceeded 26214400 bytes while streaming "${t}". Refusing to load.`};s.push(n)}const n=new Uint8Array(o);let i=0;for(const a of s)n.set(a,i),i+=a.byteLength;return{value:(new TextDecoder).decode(n)}})(r,e);return"error"in o?o:{value:JSON.parse(o.value)}}catch(s){return((e,t)=>e instanceof Error&&"AbortError"===e.name?{error:`Fetch timed out after 15000ms for "${t}".`}:{error:`Failed to fetch "${t}": ${e instanceof Error?e.message:String(e)}`})(s,e)}finally{clearTimeout(r)}},Ro=e=>{const t=/* @__PURE__ */new Set;for(const r of e.features){const e=r.geometry?.type;e&&t.add(e)}return[...t]},Lo=(tt(et),Z.union([Z.object({affectedEntries:Z.array(Z.union([Z.object({kind:Z.literal("places"),id:Z.string()}),Z.object({kind:Z.literal("routes"),id:Z.string()}),Z.object({kind:Z.literal("incidents"),id:Z.string()}),Z.object({kind:Z.literal("customGeometries"),id:Z.string()}),Z.object({kind:Z.literal("trafficAreaAnalytics"),id:Z.string()})])).describe("Entries the analysis was attached to: every contributing places / routes / incidents / customGeometries / trafficAreaAnalytics entry. Ranges entries feed the aggregation but do not carry analyses."),sourceIds:Z.array(Z.unknown()).optional().describe(`${ke} (only present when \`geometriesEntryIDs\` was set).`),name:Z.string().describe("Unique name of the analysis within each affected entry."),description:Z.string().optional(),outputFormat:Z.enum(["json","chart"]),analysis:Z.unknown(),skipped:Z.array(ft).optional().describe(Ce),focused:Z.object({incidentsEntryID:Z.string(),focusedCount:Z.number(),droppedIds:Z.array(Z.string()),reason:Z.string().optional()}).optional().describe("Present when the analysis ran with `monitor` AND returned `focusIds: string[]` (+ optional `focusReason`). The tool applied that focus as a side-effect on the source incidents entry.")}),sr])),Fo=(e,t)=>{const r=st(e,t),s={...at(r,{verb:"analyse",requiredHint:`At least one of ${ot(r)} is REQUIRED.`})};return s.name=Z.string().describe('Unique name within each affected entry (e.g. "incidents-per-cluster", "ev-near-route"). Reusing a name replaces the previous analysis on those entries.'),s.description=Z.string().optional().describe("Optional short description of what the analysis computes."),s.outputFormat=Z.enum(["json","chart"]).optional().describe('Shape of the returned value (default: "json"). `json` — compact JSON-serializable object for text display. `chart` — Chart.js `ChartConfiguration` rendered via chart.js in the chat UI. Pick `chart` whenever the user asks for a "chart", "graph", "plot", "histogram", "bar/pie/line chart", etc.'),s.code=Z.string().describe(((e,t)=>{const r=st(e,t),s=nt(r),o=t?it(r):"",n=!!t&&r.length>1,i=!!t&&r.includes("customGeometries"),a=!t||r.includes("incidents")?"When `monitor: { entryId }` is set, the call ALSO registers as a recurring spec on that incidents entry. On every monitor tick the code re-runs against the fresh `incidents` snapshot and these EXTRA sandbox args become available:\n• `previous` — your last result for this `name` on the source entry, or `undefined` on the first run. Use it for id stability (e.g. reuse a prior cluster id when memberIds overlap by ≥50%) and short trend reads ('growing' / 'fading' / 'steady'). Always guard `previous === undefined` — true on every first run.\n• `now: Date` — call-start time; compare against `startTime` / `endTime` on incidents.\n• `log: (...args) => void` — writes into the response logs.\nConstraint: cross-kind reruns are NOT supported — when `monitor` is set, `incidentsEntryIDs: [monitor.entryId]` is the only allowed input. For one-shot cross-kind incidents analysis (no rerun), omit `monitor`.\nFOCUS SIDE-EFFECT: when `applyFocus !== false` AND the result has `focusIds: string[]` (+ optional `focusReason`), `setFocus` is called once on the source entry to dim/highlight the listed incidents. One-shot — the focus side-effect does NOT re-apply on monitor-tick replays.\n\n":"";return`Async JS that aggregates the injected inputs and returns the result.\n\n${Tt(Pt)}\n\nEach input is \`undefined\` when its \`*EntryIDs\` argument was omitted — guard before reading (\`if (places) ...\`, \`routes?.features.length\`, etc.). When defined:\n${s}\n\n`+a+(n?`${xt}\n\n`:"")+`${((e,t="")=>`Return value MUST be pure JSON — plain objects, arrays, strings, numbers, booleans, null. No functions, no classes, no DOM/Map refs (it crosses a JSON boundary). Keep it small for chat display.\n• \`json\` (default) — compact object (${"counts, per-kind breakdowns, hex bins, intersections, distance bins"}, …).\n• \`chart\` — Chart.js config: \`{ type: "bar"|"line"|"pie"|"doughnut"|"radar"|"polarArea"|"scatter"|"bubble", data: { labels: (string | string[])[], datasets }, options? }\`. For per-point detail you would normally put in a \`tooltip.callbacks\` function, use a \`string[]\` label — Chart.js renders it multi-line on the axis tick AND in the tooltip title${t?` (e.g. ${t})`:""}.`)()}\n\n`+(i?`${Ae}\n\n`:"")+o})(e,t)),((e,t,r)=>e.includes(r)&&(!t||t.kinds.includes(r)))(e,t,"incidents")&&(s.monitor=Z.object({entryId:Z.string().describe("Incidents entry id to register the spec on. Must match `incidentsEntryIDs[0]`. The spec re-runs on every monitor-tick of that entry.")}).optional().describe("Opt-in: register this analysis as a recurring spec on the targeted incidents entry. The code re-runs on every monitor-tick with extra sandbox args (`previous`, `now`, `log`). When set, only `incidentsEntryIDs: [monitor.entryId]` is allowed alongside — cross-kind reruns are not supported."),s.applyFocus=Z.boolean().optional().describe("When `monitor` is set AND the result has `focusIds: string[]` (+ optional `focusReason`), call `setFocus` on the source entry to dim/highlight those incidents. Default: true. One-shot — the focus side-effect does NOT re-apply on monitor-tick replays.")),Z.object(s).refine(lt,{message:ct(r)}).refine(e=>{const t=e.monitor;if(!t)return!0;const r=e.incidentsEntryIDs;if(!Array.isArray(r)||1!==r.length||r[0]!==t.entryId)return!1;for(const s of et)if("incidents"!==s&&e[`${"customGeometries"===s?"geometries":s}EntryIDs`]?.length)return!1;return!0},{message:"When `monitor` is set, only `incidentsEntryIDs: [monitor.entryId]` is allowed (cross-kind reruns are not supported in the lean merge). Omit `monitor` for one-shot cross-kind incidents analysis."})},No=(Fo(et,void 0),(e,t)=>{const r=st(e,t),s=r.join(" / "),o=t&&r.includes("incidents")?"Opt-in `monitor: { entryId }` registers the call as a recurring spec on the targeted incidents entry; opt-in `applyFocus` honours `focusIds: string[]` by calling `setFocus`.":"",n="CONTRAST `processData`, which writes NEW renderable map entries (places / custom-geometries / BYOD); `analyseData` never renders or creates entries — only attaches metadata.";if(t)return`Aggregate ${s} entries via dynamic JS — counts, groupings, charts, hex bins, cross-kind correlations. ${n} Each scoped input is exposed as a merged collection and a per-entry record; fields are \`undefined\` when their \`*EntryIDs\` argument is omitted (guard with \`?.\`). Result is attached as \`_analysis[name]\` on every contributing entry. \`outputFormat: "json"\` (default) or \`"chart"\` (Chart.js config). `+o;return`Aggregate ${s} entries via dynamic JS — counts, groupings, charts, hex bins, cross-kind correlations. ${n} Each input is exposed as a merged collection and a per-entry record; fields are \`undefined\` when their \`*EntryIDs\` argument is omitted (guard with \`?.\` / \`if (places) …\`). Result is attached as \`_analysis[name]\` on every contributing entry (ranges feed in but do not carry analyses; BYOD entries are read-only). \`outputFormat: "json"\` (default) or \`"chart"\` (Chart.js config). `+(r.includes("incidents")?"Opt-in `monitor: { entryId }` registers the call as a recurring spec on the targeted incidents entry — the code re-runs on every monitor-tick with `previous` / `now` / `log` available in the sandbox. When `monitor` is set, only `incidentsEntryIDs: [monitor.entryId]` is allowed alongside (no cross-kind reruns). Opt-in `applyFocus` (default true under `monitor`) honours `focusIds: string[]` on the result by calling `setFocus` on the source entry to dim/highlight a subset.":"")}),jo=(No(et,void 0),e=>`Emit \`{ kinds: [${e.map(e=>`"${e}"`).join(" | ")}] }\` listing only the entry kinds the user query touches. Omit when truly cross-kind (covers all).`),Bo=(e,t,r)=>{const s=r.timestamp??Date.now();for(const o of t)e.addAnalysisToEntry(o.id,{name:r.name,timestamp:s,...r.description&&{description:r.description},outputFormat:r.outputFormat,data:r.analysis})},qo=async(e,t)=>{const{placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a,name:l,description:c,code:d,monitor:u,applyFocus:p}=e,h=e.outputFormat??"json";if(u)return $o(t,u.entryId,l,c,d,h,!1!==p);const m=await St({placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a},t);if("error"in m)return{error:m.error};const{resolved:y,sandbox:f,geometries:g}=m.value,b=await Rt(d,Pt,Dt(f,{h3:K,turf:X}),"Analysis");if("error"in b)return{error:b.error};const w=kt(b.value,h);if("error"in w)return{error:w.error};const v=w.value,I={name:l,description:c,outputFormat:h,analysis:v,timestamp:Date.now()};Bo(t.places,y.places,I),Bo(t.routing,y.routes,I),Bo(t.trafficIncidents,y.incidents,I),Bo(t.trafficAreaAnalytics,y.trafficAreaAnalytics,I),Bo(t.places,g.affectedEntries.filter(e=>"places"===e.kind).map(e=>({id:e.id})),I),Bo(t.customGeometries,g.affectedEntries.filter(e=>"customGeometries"===e.kind).map(e=>({id:e.id})),I);const S=[...y.places.map(e=>({kind:"places",id:e.id})),...y.routes.map(e=>({kind:"routes",id:e.id})),...y.incidents.map(e=>({kind:"incidents",id:e.id})),...y.trafficAreaAnalytics.map(e=>({kind:"trafficAreaAnalytics",id:e.id})),...g.affectedEntries.filter(e=>"places"===e.kind&&!y.places.some(t=>t.id===e.id)).map(e=>({kind:"places",id:e.id})),...g.affectedEntries.filter(e=>"customGeometries"===e.kind).map(e=>({kind:"customGeometries",id:e.id}))],E=n?g.sourceIds:void 0;return{affectedEntries:S,...E&&{sourceIds:E},name:l,...c&&{description:c},outputFormat:h,analysis:v,...g.skipped.length&&{skipped:g.skipped}}},$o=async(e,t,r,s,o,n,i)=>{const a=e.trafficIncidents.entries.find(e=>e.id===t);if(!a)return{error:`No incidents entry with id "${t}". Call getTrafficIncidents first.`};const l={name:r,...s&&{description:s},outputFormat:n,code:o,source:a.id},c=a._analyses?.getResult(r)?.data;e.trafficIncidents.setAnalysisSpec(l);const d=a.timestamp,u=await Gt(l,a.data,c,d);if("error"in u)return{error:u.error};const{value:p}=u;Bo(e.trafficIncidents,[a],{name:r,description:s,outputFormat:n,analysis:p,timestamp:d});const h=i?Go(p,a.id,e):void 0;return{affectedEntries:[{kind:"incidents",id:a.id}],name:r,...s&&{description:s},outputFormat:n,analysis:p,...h&&{focused:h}}},Go=(e,t,r)=>{if(!e||"object"!=typeof e)return;const s=e.focusIds;if(!Array.isArray(s)||0===s.length)return;const o=s.filter(e=>"string"==typeof e);if(0===o.length)return;const n=e.focusReason,i="string"==typeof n?n:void 0,a=r.trafficIncidents.setFocus(t,o,i);return{incidentsEntryID:t,focusedCount:a.focusedCount,droppedIds:a.droppedIds,...i&&{reason:i}}},Uo=Z.union([Z.object({incidentsEntryID:Z.string().describe("The entry the focus was applied to."),focusedCount:Z.number().describe("How many of the requested ids ended up in the focus subset."),droppedIds:Z.array(Z.string()).describe("Ids that were not present on the entry and were dropped from the focus."),reason:Z.string().optional().describe("Echo of the focus reason for downstream display.")}),sr]),Wo=Z.object({incidentsEntryID:Z.string().describe("Id of the incidents entry to focus on (from `getTrafficIncidents` or any registered analysis source)."),incidentIds:Z.array(Z.string()).min(1).describe("Ids of incidents within the entry to highlight. Unknown ids are dropped, not errored."),reason:Z.string().optional().describe('Short label shown in the UI FocusChip — what this subset represents (e.g. "top 3 by delay").')}),Vo=Z.object({slotIndex:Z.number().int().min(0).optional().describe("Exact waypoint slot to retrieve. Use when you need one known origin/stop/destination slot."),includeEmptySlots:Z.boolean().optional().describe("Include unfilled slots in the result set. Defaults to false to avoid null-heavy responses."),offset:Z.number().int().min(0).optional().describe("Pagination offset within the filtered slot list. Ignored when slotIndex is provided."),limit:Z.number().int().min(1).max(25).optional().describe("Maximum slots to return. Defaults to 5. Ignored when slotIndex is provided.")}),zo=Z.object({totalSlots:Z.number().describe("Total waypoint slots currently staged"),filledSlots:Z.number().describe("Number of staged slots that already contain coordinates"),matchedCount:Z.number().describe("Total slots matching the request before pagination"),returnedCount:Z.number().describe("Number of slots returned in this page"),offset:Z.number().describe("Offset applied to the filtered slot list"),hasMore:Z.boolean().describe("True when more slots can be fetched with a higher offset"),nextOffset:Z.number().optional().describe("Offset to request the next page when hasMore=true"),waypoints:Z.array(ps)}),Ho=(tt(et),{bboxFromGeoJSON:F,calculateProgressAtRoutePoint:N,getCoordinateAtRouteProgress:q,getProgressAtNearestRoutePoint:G,getRouteProgressBetween:U,getRouteProgressForSection:W,getSectionBBox:V}),Yo=[...Pt,"routeUtils"],Jo=Z.object({theme:Z.enum(["filled","outline","inverted"]).optional().describe('Visual theme. Default: "outline". Use "inverted" for difference/inverse results.'),mode:Be,hidePreviousEntries:Ue("custom-geometries").optional()}),Xo={sourceIds:Z.array(_e).optional().describe(`${ke} (only when \`geometriesEntryIDs\` was set).`),skipped:Z.array(ft).optional().describe(Ce),fitted:dt.optional()},Ko=Z.union([ar({}).extend({placesEntryId:Z.string(),label:Z.string().optional(),connectionsRendered:Z.number().optional(),placesGeometryCount:Z.number().optional().describe("Polygons attached to the new places entry (when `geometries` was returned alongside)."),shownPlaces:Z.boolean().optional(),...Xo}),Z.object({customGeometriesEntryId:_e,customGeometriesCount:Z.number(),label:Z.string(),operation:Z.string().optional(),shownCustomGeometries:Z.boolean().optional(),...Xo}),Z.object({byodEntryId:Z.string(),byodFeatureCount:Z.number(),label:Z.string(),fitted:dt.optional()}),Z.object({fitted:dt}),sr]),Zo=(e,t)=>{const r=st(e,t),s={...at(r,{verb:"process",requiredHint:`At least one of ${ot(r)} is REQUIRED.`})};return s.code=Z.string().describe(((e,t)=>{const r=st(e,t),s=t?it(r):"",o=!!t&&r.length>1,n=t&&r.includes("trafficAreaAnalytics")?"`trafficAreaAnalytics` is a FeatureCollection of tile/hex regions whose `feature.properties` carry metric fields (`congestionLevel`, `speed`, `freeFlowSpeed`, `travelTime`, `networkLength`); use `turf.booleanPointInPolygon` / `turf.lineIntersect` to bridge with places/routes.\n\n":"",i=t&&r.includes("routes")?"`routeUtils` (from @tomtom-org/maps-sdk/core, only useful when `routesEntryIDs` is set):\n• `getSectionBBox(route, section)` → `[W,S,E,N]`. Sections at `route.properties.sections.<type>`.\n• `getRouteProgressForSection(route, section)` / `getRouteProgressBetween(route, startIdx, endIdx)`.\n• `calculateProgressAtRoutePoint(route, pathIndex)`.\n• `getCoordinateAtRouteProgress(route, query)` — `query`: `{ traveledDistanceInMeters }` | `{ traveledTimeInSeconds }` | `{ clockTime: Date }`.\n• `getProgressAtNearestRoutePoint(route, { lng, lat })` — snap a point to the line.\n• `bboxFromGeoJSON(featureOrCollection)`.\n\n":"",a=t&&r.includes("customGeometries")?`${Ae}\n\n`:"",l=r.includes("byod")?"• `byod: FeatureCollection` — writes a NEW BYOD entry (any GeoJSON: Point/Line/Polygon/mixed). Use for derived BYOD layers (e.g. cleaned/filtered customer data).\n":"",c=t?`${nt(r)}\n`:"";return"Async JS returning `{ places?, placeConnections?, geometries?, fitOnMap?, byod? }`. At least one of `places` / `geometries` / `fitOnMap` / `byod` must be present. `places` (with optional attached `geometries`), standalone `geometries`, and `byod` are MUTUALLY EXCLUSIVE — only one is written per call, others are dropped. `fitOnMap` composes with any of them.\n\n• `places: Places` — writes a NEW places entry. Filters/merges/subsets of `places.features`, or computed point features (must conform to the Place schema).\n• `placeConnections: { from, to, id? }[]` — lines between output places (endpoints: Place or place id). Requires `places`. Drawn on the new entry's PlacesModule.\n• `geometries: PolygonFeature[]` — polygons (Polygon/MultiPolygon, stable `id` recommended). When `places` is also present, attached to the new places entry; when `places` is absent, written as a NEW custom-geometries entry.\n• `fitOnMap` — bbox or `{ bbox, padding?, animate? }`. Camera move; when alone, no entry written.\n"+l+`\n${Tt(Yo)}\n\n`+c+"Each input is `undefined` when its `*EntryIDs` argument was omitted — guard before reading. "+n+(o?`${xt}\n\n`:"")+i+"`fitOnMap` (opt): `[W,S,E,N]` BBox or `{ bbox: [W,S,E,N], padding?: number, animate?: boolean }` (default padding 50). Camera fits these bounds after the tool runs. Returning ONLY `fitOnMap` skips state writes (pure camera focus); combine with `places` / `geometries` to also write+focus. Derive bboxes via `turf.bbox(feature | FeatureCollection)`.\n\n"+a+s})(e,t)),s.label=Z.string().optional().describe("Short label for the new entry (used by both places and custom-geometries paths)."),s.entryId=je,s.operation=Z.string().optional().describe("Short operation label (`union`, `buffer`, `difference`, `h3-coverage`, …) stored as provenance on a new custom-geometries entry. Ignored on the places path."),s.show=Z.object({places:We.optional().describe("Render the new places entry (when `places` was returned)."),placesGeometries:Z.object({theme:Z.enum(["filled","outline","inverted"]).optional(),mode:Be}).optional().describe("Render the polygons attached to the new places entry (when both `places` and `geometries` were returned)."),customGeometries:Jo.optional().describe("Render the new custom-geometries entry (when `geometries` was returned WITHOUT `places`).")}).optional().describe("Optional per-output render knobs. The executor only consults the field matching what the code returned (so leaving e.g. `customGeometries` set when `places` was returned is a silent no-op)."),s.connectMarkerType=Z.enum(["pin","base-map"]).optional().describe('Which PlacesModule renders `placeConnections`. Default: "pin". Match the module holding the endpoints.'),Z.object(s).refine(lt,{message:ct(r)})},Qo=(Zo(et,void 0),(e,t)=>{const r=st(e,t).join(" / "),s="CONTRAST `analyseData`, which attaches JSON/chart metadata to existing entries and never renders.";return t?`Run dynamic JS over ${r} entries to produce NEW renderable map data — a places, custom-geometries, or BYOD entry (with optional \`placeConnections\` / attached polygon footprints), and/or a \`fitOnMap\` camera move. ${s} Returns \`{ places?, placeConnections?, geometries?, fitOnMap?, byod? }\`: when \`places\` is returned, inline \`geometries\` attach to it; otherwise they become a standalone custom-geometries entry. Inputs are \`undefined\` when their \`*EntryIDs\` argument is omitted — guard before reading.`:`Run dynamic JS over any combination of ${r} entries to produce NEW renderable map data — a places, custom-geometries, or BYOD entry (with optional \`placeConnections\` / attached polygon footprints), and/or a \`fitOnMap\` camera move. ${s} Returns \`{ places?, placeConnections?, geometries?, fitOnMap?, byod? }\`: when \`places\` is returned, inline \`geometries\` attach to it as place footprints; otherwise they become a standalone custom-geometries entry. \`routeUtils\` is exposed alongside \`h3\` / \`turf\` for route section / progress work. Inputs are \`undefined\` when their \`*EntryIDs\` argument is omitted — guard before reading.`}),en=(Qo(et,void 0),e=>`Emit \`{ kinds: [${e.map(e=>`"${e}"`).join(" | ")}] }\` listing only the entry kinds the user query touches. Omit when truly cross-kind.`),tn=e=>{if(!e||"object"!=typeof e)return!1;const t=e;return"FeatureCollection"===t.type&&Array.isArray(t.features)},rn=e=>{if(!Array.isArray(e))return!1;const t=e=>"string"==typeof e||"object"==typeof e&&null!==e&&"geometry"in e;return e.every(e=>{if(!e||"object"!=typeof e)return!1;const r=e;return t(r.from)&&t(r.to)})},sn=(e,t,r)=>void 0===e?{value:void 0}:t(e)?{value:e}:{error:r},on=e=>e&&e.features.length>0?e:void 0,nn=e=>{if(!e||"object"!=typeof e)return"Process code must return `{ places?, placeConnections?, geometries?, fitOnMap?, byod? }`.";const t=e,r={},s=sn(t.places,tn,"`places` must be a `Places` FeatureCollection of Point features.");if("error"in s)return s.error;r.places=on(s.value);const o=sn(t.placeConnections,rn,"`placeConnections` must be an array of `{ from, to, id? }` (endpoints: Place or place id).");if("error"in o)return o.error;if(r.placeConnections=o.value,void 0!==t.geometries){const e=(e=>{if(!Array.isArray(e))return{error:"`geometries` must be an array of GeoJSON Polygon/MultiPolygon Features."};const t=[];for(let r=0;r<e.length;r++){const s=e[r];if(!Ct(s))return{error:`\`geometries[${r}]\` must be a GeoJSON Feature with Polygon or MultiPolygon geometry.`};t.push(s)}return{value:t}})(t.geometries);if("error"in e)return e.error;r.geometries=e.value.length>0?e.value:void 0}const n=sn(t.fitOnMap,pt,"`fitOnMap` must be `[W,S,E,N]` or `{ bbox: [W,S,E,N], padding?, animate? }`.");if("error"in n)return n.error;r.fitOnMap=n.value;const i=sn(t.byod,tn,"`byod` must be a GeoJSON FeatureCollection (any geometry types).");return"error"in i?i.error:(r.byod=on(i.value),(e=>void 0===e.places&&void 0===e.geometries&&void 0===e.fitOnMap&&void 0===e.byod?"At least one of `places`, `geometries`, `fitOnMap`, `byod` must produce content (empty arrays / FeatureCollections do not count).":void 0===e.places&&void 0!==e.placeConnections?"`placeConnections` requires `places` to be returned alongside.":void 0)(r)??r)},an=async(e,t,r,s)=>{const{label:o,entryId:n,show:i,connectMarkerType:a,geometriesEntryIDs:l}=t,{places:c,placeConnections:d,geometries:u,fitOnMap:p}=r,h=c.features.length,m=`${o?`processed: ${o}`:"processed"} (${h} ${1===h?"place":"places"})`,y=await e.places.addPlaceResult(c,m,n,d,u),f=i?.places?await Bt(e,[y],i.places):void 0;let g,b;if(u?.length&&(g=u.length,i?.placesGeometries&&await e.places.showPlaceGeometries(u,i.placesGeometries.theme??"outline",i.placesGeometries.mode??"replace",y)),d){const t=await(async(e,t,r,s)=>{try{return await(await e.places.getEntryPlacesModule(t,s??"pin")).showConnections(r),{count:r.length}}catch(o){return{error:`Failed to render connections: ${o instanceof Error?o.message:String(o)}`}}})(e,y,d,a);if("error"in t)return t;b=t.count}const w=p?ht(e,p):void 0,v=e.places.entries.find(e=>e.id===y)?.label;return{...cs(c),placesEntryId:y,...v&&{label:v},...f&&{shownPlaces:!0},...void 0!==b&&{connectionsRendered:b},...void 0!==g&&{placesGeometryCount:g},...l&&{sourceIds:s.sourceIds},...s.skipped.length&&{skipped:s.skipped},...w&&{fitted:w}}},ln=async(e,t)=>{const{placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a,code:l}=e,c=await St({placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a},t);if("error"in c)return{error:c.error};const{sandbox:d,geometries:u}=c.value,p=await Rt(l,Yo,[...Dt(d,{h3:K,turf:X}),Ho],"Process");if("error"in p)return{error:p.error};const h=nn(p.value);if("string"==typeof h)return{error:h};const{places:m,geometries:y,fitOnMap:f,byod:g}=h;return m?an(t,e,{...h,places:m},u):y?.length?(async(e,t,r,s,o)=>{const{label:n,entryId:i,operation:a,show:l,geometriesEntryIDs:c}=t,d=n?`processed: ${n}`:`processed geometries (${r.length})`,u=await e.customGeometries.addEntry(r,{sourceIds:o.sourceIds,...a&&{operation:a}},d,i);let p=!1;l?.customGeometries&&(await mt(e.customGeometries,[u],l.customGeometries.hidePreviousEntries),await e.customGeometries.showEntry(u,l.customGeometries.theme??"outline",l.customGeometries.mode??"replace"),p=!0);const h=s?ht(e,s):void 0;return{customGeometriesEntryId:{kind:"customGeometries",id:u},customGeometriesCount:r.length,label:d,...a&&{operation:a},...c&&{sourceIds:o.sourceIds},...o.skipped.length&&{skipped:o.skipped},...p&&{shownCustomGeometries:!0},...h&&{fitted:h}}})(t,e,y,f,u):g?(async(e,t,r,s)=>{const{label:o,entryId:n}=t,i=o?`processed: ${o}`:`processed byod (${r.features.length})`,a=await e.byod.addEntry(r,i,{...n&&{explicitId:n}}),l=s?ht(e,s):void 0;return{byodEntryId:a,byodFeatureCount:r.features.length,label:i,...l&&{fitted:l}}})(t,e,g,f):f?{fitted:ht(t,f)}:{error:"Process code returned no `places`, `geometries`, `fitOnMap`, or `byod`."}},cn=so.extend({date:Z.string().optional(),hour:Z.number().optional(),year:Z.number().optional(),month:Z.number().optional(),week:Z.number().optional(),day:Z.number().optional()}),dn=Z.object({entryId:Z.string().optional().describe('Traffic-area-analytics entry id to query (e.g. "tta-0"). Omit to query the most recently added entry. Use `recallState` to list available ids.'),granularity:Z.enum(["daily","hourly","weekly","monthly","yearly","average"]).optional().describe("Time granularity. Default: 'daily'. Use 'average' for day-of-week × hour patterns."),metric:Z.enum(["speed","congestionLevel","freeFlowSpeed","travelTime","networkLength"]).optional().describe("Filter to a single metric. Default: all requested metrics."),startDate:Z.string().optional().describe("Filter entries from this date (YYYY-MM-DD). Works with 'daily' and 'hourly'."),endDate:Z.string().optional().describe("Filter entries up to this date (YYYY-MM-DD). Works with 'daily' and 'hourly'."),hourStart:Z.number().int().min(0).max(23).optional().describe("Filter hourly/average entries from this hour (0-23)."),hourEnd:Z.number().int().min(0).max(23).optional().describe("Filter hourly/average entries up to this hour (0-23)."),dayOfWeek:Z.array(Z.number().int().min(1).max(7)).optional().describe("Filter 'average' entries by day of week (1=Mon…7=Sun).")}),un=Z.union([Z.object({granularity:Z.string(),count:Z.number(),entries:Z.array(cn),currentVisualization:Z.object({activeMetric:Z.string().optional(),displayMode:Z.string().optional()}).optional()}),sr]),pn=Z.union([Z.object({kind:Z.literal("integrator"),description:Z.string().optional()}),Z.object({kind:Z.literal("url"),url:Z.string()}),Z.object({kind:Z.literal("inline"),description:Z.string().optional()})]),hn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),featureCount:Z.number(),geometryTypes:Z.array(Z.string()),source:pn,shown:Z.boolean()}),mn=hn.extend({features:Z.unknown().describe("GeoJSON FeatureCollection of the entry's features.")}),yn=Z.union([Z.object({entries:Z.array(hn),entryMode:Z.enum(["single","multiple"])}),mn,sr]),fn=Z.object({id:Z.string().optional().describe("BYOD entry id to inspect in detail. Omit to list every BYOD entry currently in session state.")}),gn=e=>{const t=/* @__PURE__ */new Set;for(const r of e.features){const e=r.geometry?.type;e&&t.add(e)}return[...t]},bn=Z.object({id:_e.optional().describe('Tagged id to inspect in detail (e.g. `{ kind: "places", id: "ev-stations-oost" }`, `{ kind: "ranges", id: "ranges-0" }`, `{ kind: "customGeometries", id: "bakery-candidates-gap-oost" }`). Omit to list every available id across all slices.')}),wn=Z.object({name:Z.string(),outputFormat:Z.enum(["json","chart"]),timestamp:Z.number(),description:Z.string().optional()}),vn=Z.object({id:_e.describe("Tagged id of this entry."),label:Z.string(),timestamp:Z.number(),featureCount:Z.number(),operation:Z.string().optional().describe("Operation label (custom-geometries entries only)."),sourceIds:Z.array(_e).optional().describe("Provenance: tagged ids of inputs that fed the producing op (custom only)."),analyses:Z.array(wn).optional().describe("Analyses already attached to this entry (places and custom only).")}),In=vn.extend({features:Z.unknown().describe("GeoJSON FeatureCollection of the entry's polygon features.")}),Sn=Z.union([Z.object({entries:Z.array(vn)}),In,sr]),En=e=>e?.length?e.map(({name:e,outputFormat:t,timestamp:r,description:s})=>({name:e,outputFormat:t,timestamp:r,...s&&{description:s}})):void 0,Mn=e=>{const t=[];for(const r of e.places.entries){if(!r.geometries?.length)continue;const e=En(r._analysis);t.push({id:{kind:"places",id:r.id},label:r.label,timestamp:r.timestamp,featureCount:r.geometries.length,...e&&{analyses:e}})}for(const r of e.ranges.entries){const e=r.ranges.reduce((e,t)=>e+(t.polygon?.features.length??0),0);0!==e&&t.push({id:{kind:"ranges",id:r.id},label:r.label,timestamp:r.timestamp,featureCount:e})}for(const r of e.customGeometries.entries){const e=En(r._analysis);t.push({id:{kind:"customGeometries",id:r.id},label:r.label,timestamp:r.timestamp,featureCount:r.features.length,...r.provenance.operation&&{operation:r.provenance.operation},...r.provenance.sourceIds.length&&{sourceIds:[...r.provenance.sourceIds]},...e&&{analyses:e}})}return t.sort((e,t)=>t.timestamp-e.timestamp)},xn=(e,t)=>{switch(e.kind){case"places":return((e,t)=>{const r=t.places.entries.find(t=>t.id===e.id);if(!r)return{error:`No places entry with id "${e.id}"`};if(!r.geometries?.length)return{error:`Places entry "${e.id}" has no cached footprints. Call discoverPlaces / locatePlace with \`withGeometries: true\` first.`};const s=En(r._analysis);return{id:e,label:r.label,timestamp:r.timestamp,featureCount:r.geometries.length,features:{type:"FeatureCollection",features:r.geometries},...s&&{analyses:s}}})(e,t);case"ranges":return((e,t)=>{const r=t.ranges.entries.find(t=>t.id===e.id);if(!r)return{error:`No ranges entry with id "${e.id}"`};const s=r.ranges.flatMap(e=>e.polygon?.features??[]);return 0===s.length?{error:`Ranges entry "${e.id}" has no polygons.`}:{id:e,label:r.label,timestamp:r.timestamp,featureCount:s.length,features:{type:"FeatureCollection",features:s}}})(e,t);case"customGeometries":return((e,t)=>{const r=t.customGeometries.findById(e.id);if(!r)return{error:`No custom-geometries entry with id "${e.id}"`};const s=En(r._analysis);return{id:e,label:r.label,timestamp:r.timestamp,featureCount:r.features.length,features:{type:"FeatureCollection",features:r.features},...r.provenance.operation&&{operation:r.provenance.operation},...r.provenance.sourceIds.length&&{sourceIds:[...r.provenance.sourceIds]},...s&&{analyses:s}}})(e,t);default:return{error:'`{ kind: "place" }` is not a recallable detail target. Pass a places-entry id (`{ kind: "places", id }`) or use `recallPlaces` to inspect a single place.'}}},Pn=Z.object({id:Z.string().optional().describe('Entry ID to retrieve (e.g. "places-3"). Omit to list all entries.')}),Dn=Z.object({name:Z.string(),outputFormat:Z.enum(["json","chart"]),timestamp:Z.number(),description:Z.string().optional()}),_n=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),featureCount:Z.number().describe("Number of Place features stored in this entry."),shown:Z.boolean().describe("Whether this entry is currently rendered on the map."),markerType:Z.enum(["pin","base-map","pin-clustered"]).optional().describe("Which marker theme is rendering this entry (undefined when `shown` is false)."),analyses:Z.array(Dn).optional().describe("Analyses already attached to this entry via analyseData (name + output format).")}),Tn=Z.enum(["single","multiple"]).describe('Display policy: `multiple` (default) lets several entries render simultaneously; `single` enforces "at most one entry on the map" — switching to it auto-clears non-latest entries.'),An=e=>{const t=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),places:ar(e)});return Z.union([Z.object({entries:Z.array(_n),entryMode:Tn}),t.extend({entryMode:Tn}),sr])},Cn=(An({}),e=>async(t,r)=>{const{id:s}=t,o=r.places.entries,n=r.places.entryMode,i=r.places.shownEntryIds;if(!s)return{entries:[...o].sort((e,t)=>t.timestamp-e.timestamp).map(({id:e,label:t,timestamp:s,places:o,_analysis:n})=>{const a=i.has(e),l=a?r.places.getShownMarkerType(e):void 0;return{id:e,label:t,timestamp:s,featureCount:o.length,shown:a,...l&&{markerType:l},...n?.length&&{analyses:n.map(({name:e,outputFormat:t,timestamp:r,description:s})=>({name:e,outputFormat:t,timestamp:r,...s&&{description:s}}))}}}),entryMode:n};const a=o.find(e=>e.id===s);if(!a)return{error:`No entry found with id "${s}"`};const l=cs(a.places,e);return{id:a.id,label:a.label,timestamp:a.timestamp,places:l,entryMode:n}}),kn=(Cn({}),{classificationPrompt:"List/inspect places entries in state — pick valid IDs before any `placesEntryIDs` tool (processData, analyseData, updatePlacesDisplay).",tags:["place","location"],examples:["recallPlaces()",'recallPlaces({ id: "places-0" })'],examplePrompts:["What places did I search for?","List every places entry with its ID","Which IDs can I pass to processData or updatePlacesDisplay?","Recall the bakeries I discovered earlier","What is in entry places-2?"],relatedTools:["discoverPlaces","locatePlace","reverseGeocode","updatePlacesDisplay"],dependsOn:["locatePlace","discoverPlaces"]}),On=Z.object({id:Z.string().optional().describe('Entry ID to retrieve (e.g. "ranges-0"). Omit to list all entries.')}),Rn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),originCount:Z.number()}),Ln=Z.object({origin:Z.object({query:Z.string().optional(),position:Z.tuple([Z.number(),Z.number()])}),budgets:Z.array(Z.object({type:Z.string(),value:Z.number()}))}),Fn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),ranges:Z.array(Ln)}),Nn=Z.enum(["single","multiple"]).describe('Display policy: `multiple` (default) lets several reachable-range entries render at once; `single` enforces "at most one entry on the map" — switching to it auto-clears non-latest entries.'),jn=Z.union([Z.object({entries:Z.array(Rn),entryMode:Nn}),Fn.extend({entryMode:Nn}),sr]),Bn=Z.object({id:Z.string().optional().describe('Entry ID to retrieve (e.g. "routes-1"). Omit to list all entries.')}),qn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),shown:Z.boolean().describe("Whether this entry is currently rendered on the map.")}),$n=Z.object({maxAlternatives:Z.number().optional(),costModel:vs.optional(),when:Is.optional()}),Gn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),routes:ur,waypoints:Z.array(Z.object({position:Z.array(Z.number()).length(2).describe("[longitude, latitude]")})),params:$n}),Un=Z.enum(["single","multiple"]).describe('Display policy: `multiple` (default) lets several routes render at once; `single` enforces "at most one route on the map" — switching to it auto-clears non-latest entries.'),Wn=Z.union([Z.object({entries:Z.array(qn),entryMode:Un}),Gn.extend({entryMode:Un}),sr]),Vn=Z.object({id:Z.string(),label:Z.string()}),zn=Z.enum(["single","multiple"]),Hn=Z.union([Z.object({places:Z.array(Vn).describe("Places history entries — newest first."),routes:Z.array(Vn).describe("Routes history entries — newest first."),ranges:Z.array(Vn).describe("Reachable-range history entries — newest first."),entryModes:Z.object({places:zn,routes:zn,ranges:zn}).describe("Per-slice display policy. `multiple` (default) lets several entries render at once; `single` keeps only the latest entry — set via setEntryMode tools."),mapStyle:Z.object({id:Z.string().optional().describe('Standard style id (e.g. "standardLight").')}).nullable().describe("Current map style. `null` when the map has no style assigned yet."),mapPOIs:Z.object({visible:Z.boolean().optional(),filterMode:Z.enum(["only","all_except"]).optional(),categories:Z.array(Z.string()).optional()}).nullable().describe("Built-in POI overlay config. `null` when the POIsModule has not been initialised in this session."),traffic:Z.object({flow:Z.object({visible:Z.boolean().optional()}).nullable().describe("Traffic-flow layer config. `null` when the module is uninitialised."),incidents:Z.object({visible:Z.boolean().optional()}).nullable().describe("Traffic-incidents layer config. `null` when the module is uninitialised.")}).describe("Real-time traffic visualization configs.")}),sr]),Yn=Z.object({}),Jn=["places","routes","ranges","customGeometries","trafficAreaAnalytics","trafficIncidents","byod","mapPOIs","trafficFlow","mapStyle"],Xn=Z.union([Z.object({success:Z.literal(!0),cleared:Z.array(Z.enum(Jn)).describe("Slices that were wiped / reset to defaults.")}),sr]),Kn=Z.object({}),Zn=Z.enum(Xt),Qn=Z.enum(["single","multiple"]),ei=Z.union([Z.object({success:Z.literal(!0),slice:Zn,mode:Qn,droppedEntryIds:Z.array(Z.string()).describe("Entry IDs removed from history because the new mode is `single` and they were not the latest.")}),sr]),ti=Z.object({slice:Zn.describe("Which entry-owning slice to reconfigure. Names match the keys on the agent state exactly — `routing` (not `routes`), `customGeometries` (not `geometries`), and so on."),mode:Qn.describe("`multiple` (default) lets several entries render at once; `single` keeps only the latest entry — switching to it auto-clears every older entry.")}),ri=`Switch the display policy of one entry-owning slice between \`multiple\` and \`single\`. Supported slices: ${Xt.join(", ")}. \`single\` ⇒ at most one entry on the map, and any older entries get dropped from history immediately. Use when the user asks for a "clean view" / "only show one at a time" or wants to compare overlays again.`,si=Z.union([Z.object({incidentsEntryID:Z.string(),alreadyRunning:Z.boolean().describe("True when the entry already had a running monitor — start is idempotent. When true, any new intervalMs / filter context in this call was IGNORED; the monitor keeps running with whatever it was started with. Call stopTrafficIncidentsMonitor first if you need different settings.")}),sr]),oi=Z.object({incidentsEntryID:Z.string().describe("Id of the incidents entry to monitor (returned by `getTrafficIncidents`)."),intervalMs:Z.number().int().positive().optional().describe("Polling interval in milliseconds. Defaults to 60000 (60s).")}),ni=Z.union([Z.object({incidentsEntryID:Z.string(),wasRunning:Z.boolean().describe("True when a monitor was actually stopped; false when it was already idle.")}),sr]),ii=Z.object({incidentsEntryID:Z.string().describe("Id of the incidents entry whose monitor should stop.")}),ai=Z.enum(Kt),li=Z.union([Z.object({success:Z.literal(!0),cleared:Z.array(ai)}),sr]),ci=Z.object({layers:Z.array(ai).optional().describe("Slice keys to clear (default: all). One per `ToolState` slice that renders on the map: "+Kt.map(e=>`\`${e}\``).join(", ")+". Map-only — slice histories (`entries`) are kept.")}),di=(Z.union([Z.object({success:Z.literal(!0)}),sr]),Z.object({sectionType:Z.enum(H).describe("e.g. country|traffic|motorway|toll"),id:Z.string().describe("From a shown route section"),padding:Z.number().optional().describe("pixels, default: 50")}),Z.union([Z.object({count:Z.number(),trafficIncidents:Z.array(Z.object({id:Z.string(),category:Z.string(),magnitudeOfDelay:Z.string().describe("unknown|minor|moderate|major|indefinite"),description:Z.string().optional(),delayInSeconds:Z.number().optional(),roadCategory:Z.string().optional(),averageSpeedKmph:Z.number().optional()}))}),sr])),ui=Z.object({}),pi=Z.union([Z.object({styles:Z.array(Z.string()).readonly()}),sr]),hi=Z.object({}),mi=Z.union([Z.object({success:Z.literal(!0),language:Z.string()}),sr]),yi=Z.object({language:Z.string().describe('e.g. "en-US", "fr-FR"')}),fi=Z.union([Z.object({success:Z.literal(!0),style:Z.string()}),sr]),gi=Z.object({style:Z.enum(g).describe("Map visual theme")}),bi=Z.union([Z.object({success:Z.literal(!0),visible:Z.boolean(),layerGroups:Z.array(Z.string())}),sr]),wi=Z.object({visible:Z.boolean(),layerGroups:Z.array(Z.enum(y)).describe("Vector-tile base-map layer groups to show or hide.")}),vi=Z.union([Z.object({success:Z.literal(!0),allMapPOIsVisible:Z.boolean().optional(),filterCategories:Z.object({show:Z.enum(["only","all_except"]),values:Z.array(Z.string())}).optional(),reset:Z.boolean().optional()}),sr]),Ii=Z.object({reset:Z.boolean().optional().describe("Reset all tile-POI configuration to defaults. Other params ignored when true."),allMapPOIsVisible:Z.boolean().optional().describe("Overall visibility of the vector-tile POI icons baked into the base map style."),filterCategories:Z.object({show:Z.enum(["only","all_except"]).describe('"only": keep only these categories. "all_except": hide these categories.'),values:Z.array(Z.string()).describe('MapStylePOICategory / POICategoryGroup values (e.g. "RESTAURANT", "FOOD_DRINKS_GROUP"). Fine-grained codes like "ITALIAN_RESTAURANT" not supported here.')}).optional().describe("Restrict which built-in vector-tile POI categories are shown.")}),Si=Z.object({visible:Z.boolean()}),Ei=Z.union([Z.object({success:Z.literal(!0),trafficFlowVisible:Z.boolean()}),sr]),Mi=Z.object({visible:Z.boolean()}),xi=Z.union([Z.object({success:Z.literal(!0),trafficIncidentsVisible:Z.boolean()}),sr]),Pi=Z.enum(["show","hide","remove"]).describe("`show` renders the listed entries (lazy-init their CustomGeoJSONModule); `hide` removes them from the map but keeps them in session state; `remove` hides AND drops them from history."),Di=Z.object({entryIds:Z.array(Z.string()).min(1).optional().describe('BYOD entry ids to act on (e.g. ["byod-0"]). Use `recallByod` to list. Omit when `action: "hide"` and `clearAll: true` to hide every shown BYOD entry in one call.'),action:Pi,clearAll:Z.boolean().optional().describe('Only valid with `action: "hide"`. When true, hides every currently-shown BYOD entry (ignores `entryIds`). Default: false.'),hideOthers:Z.boolean().optional().describe('Only valid with `action: "show"`. When true, hides every other BYOD entry before showing the listed ones. Default: false (stack).')}).refine(e=>!("hide"!==e.action||!e.clearAll)||!!e.entryIds?.length,{message:'`entryIds` is required unless `action: "hide"` with `clearAll: true`.'}).refine(e=>!e.clearAll||"hide"===e.action,{message:'`clearAll: true` is only valid with `action: "hide"`.'}).refine(e=>!e.hideOthers||"show"===e.action,{message:'`hideOthers: true` is only valid with `action: "show"`.'}),_i=Z.union([Z.object({action:Pi,affectedIds:Z.array(Z.string()).describe("Entry ids that were actually shown / hidden / removed."),shown:Z.array(Z.string()).describe("BYOD entry ids currently rendered on the map after this call.")}),sr]),Ti=Z.object({id:Z.string(),label:Z.string(),featureCount:Z.number(),markerType:Z.enum(["pin","base-map","pin-clustered"]).describe("How the entry is rendered on the map.")}),Ai=Z.union([Z.object({success:Z.literal(!0),totalShown:Z.number(),shown:Z.array(Ti).describe("Entries currently on the map, with their labels."),actuallyAdded:Z.array(Z.string()).describe("Ids from `add` / `addMatching` that were not already shown. Empty if it was a no-op."),actuallyRemoved:Z.array(Z.string()).describe("Ids from `remove` / `removeMatching` that were actually on the map. Empty if no-op."),geometriesRendered:Z.number().optional().describe("Number of boundary polygons rendered when `geometry` was set."),geometriesSkipped:Z.array(Z.object({placeId:Z.string(),reason:Z.string()})).optional().describe("Places whose boundary could not be rendered (no geometry data source, fetch failure).")}),sr]),Ci=Z.object({add:Z.array(Z.string()).optional().describe('Entry ids to show (e.g. ["places-1"]). Omit while still setting `markerType` or `geometry` to target the most recently stored entry.'),remove:Z.array(Z.string()).optional().describe("Entry ids to hide."),addMatching:Z.array(Z.string()).optional().describe("Category labels to show."),removeMatching:Z.array(Z.string()).optional().describe("Category labels to hide."),clear:Z.boolean().optional().describe("Hide every displayed entry."),markerType:Z.enum(["pin","base-map","pin-clustered"]).optional().describe('How newly-added entries are rendered as markers: "pin", "base-map", or "pin-clustered" (pins with a count badge where nearby ones aggregate). Defaults to "pin" unless `geometry` is set without an explicit `markerType`, in which case markers are skipped. Entries previously shown with another marker type are moved to this one.'),geometry:$e.optional(),fitBounds:Z.boolean().optional().describe("Pan/zoom to fit the resulting display. Defaults to true when entries are being added, false otherwise.")}).refine(e=>void 0!==e.add||void 0!==e.remove||void 0!==e.addMatching||void 0!==e.removeMatching||!0===e.clear||void 0!==e.markerType||void 0!==e.geometry,{message:"updatePlacesDisplay requires at least one of: add, remove, addMatching, removeMatching, clear, markerType, geometry."}),ki=(e,t)=>{if(0===t.length)return[];const r=t.map(e=>e.toLowerCase()),s=/* @__PURE__ */new Set;for(const o of e.places.entries){const e=o.label.toLowerCase();r.some(t=>e.includes(t))&&s.add(o.id)}return[...s]},Oi=Z.object({id:Z.string(),label:Z.string(),routeCount:Z.number().describe("Number of route alternatives in the entry."),selectedIndex:Z.number().describe("Highlighted alternative index (0 unless overridden by this call).")}),Ri=Z.union([Z.object({success:Z.literal(!0),totalShown:Z.number(),shown:Z.array(Oi).describe("Routes entries currently on the map, with their labels."),actuallyAdded:Z.array(Z.string()).describe("Ids from `add` / `addMatching` that were not already shown. Empty if it was a no-op."),actuallyRemoved:Z.array(Z.string()).describe("Ids from `remove` / `removeMatching` that were actually on the map. Empty if no-op."),mainColor:Z.string().optional().describe("Sticky main colour now applied to every shown route, when set by this or a previous call.")}),sr]),Li=Z.object({add:Z.array(Z.string()).optional().describe('Entry ids to show (e.g. ["routes-1"]). Omit while still setting `selectedIndex` to target the most recently stored entry.'),remove:Z.array(Z.string()).optional().describe("Entry ids to hide."),addMatching:Z.array(Z.string()).optional().describe("Label substrings to show."),removeMatching:Z.array(Z.string()).optional().describe("Label substrings to hide."),clear:Z.boolean().optional().describe("Hide every displayed entry."),selectedIndex:Z.number().int().min(0).optional().describe("Which alternative variant to highlight on newly-added entries (default: 0). Use to switch the selected alternative on a multi-alternative result. Applied after the entry is shown — entries previously rendered with another index are re-rendered."),fitBounds:Z.boolean().optional().describe("Pan/zoom to fit the resulting display. Defaults to true when entries are being added, false otherwise."),mainColor:Z.string().optional().describe('Recolor the route line + waypoint icons. CSS named colour ("coral", "steelblue") or hex ("#FF0000"). Sticky — applied to every currently-shown entry AND remembered so subsequent shows pick it up automatically. Can be set by itself (no add/remove/clear) to recolor without changing visibility.')}).refine(e=>void 0!==e.add||void 0!==e.remove||void 0!==e.addMatching||void 0!==e.removeMatching||!0===e.clear||void 0!==e.selectedIndex||void 0!==e.mainColor,{message:"updateRoutesDisplay requires at least one of: add, remove, addMatching, removeMatching, clear, selectedIndex, mainColor."}),Fi=(e,t)=>{if(0===t.length)return[];const r=t.map(e=>e.toLowerCase()),s=/* @__PURE__ */new Set;for(const o of e.routing.entries){const e=o.label.toLowerCase();r.some(t=>e.includes(t))&&s.add(o.id)}return[...s]},Ni={mode:Z.enum(["hexgrid-3d","hexgrid-2d","square-3d","square-2d","heatmap"]).optional().describe("Visualization mode. 'hexgrid-3d' (default): 3D hexagons. 'hexgrid-2d': flat hexagons. 'square-3d': 3D squares. 'square-2d': flat squares. 'heatmap': density layer."),metric:Z.enum(["congestionLevel","speed","travelTime"]).optional().describe("Metric to visualize. Defaults to first fetched metric on the targeted entries."),colorTheme:Z.enum(["trafficLight","heat","monochrome","viridis","plasma"]).optional().describe("Preset color theme. 'trafficLight' (green→amber→red, default), 'heat' (blue→orange→red), 'monochrome' (grey), 'viridis' (purple→green→yellow), 'plasma' (purple→magenta→yellow)."),heightScale:Z.number().optional().describe("Extrusion height multiplier / visual maximum in meters. Default: metric-dependent."),scaleMode:Z.enum(["raw","predefinedRange","currentRange"]).optional().describe("Height scaling mode. 'raw' (default), 'predefinedRange' (normalize to SDK range), 'currentRange' (normalize to live data range)."),filter:Z.object({min:Z.number().optional(),max:Z.number().optional()}).refine(e=>void 0!==e.min||void 0!==e.max,{message:"Filter must have at least one of min or max"}).optional().describe("Filter visible tiles for the active metric by value range.")},ji=Z.object({id:Z.string(),label:Z.string(),activeMetric:Z.string().optional(),displayMode:Z.string().optional()}),Bi=Z.object({add:Z.array(Z.string()).optional().describe('Entry ids to render (e.g. ["tta-0"]). Use `recallState` to list available ids.'),remove:Z.array(Z.string()).optional().describe("Entry ids to hide."),addMatching:Z.array(Z.string()).optional().describe("Label substrings to show."),removeMatching:Z.array(Z.string()).optional().describe("Label substrings to hide."),clear:Z.boolean().optional().describe("Hide every displayed entry."),fitBounds:Z.boolean().optional().describe("Pan/zoom to fit the union of currently-shown entry bboxes. Defaults to true when entries are added."),...Ni}).refine(e=>void 0!==e.add||void 0!==e.remove||void 0!==e.addMatching||void 0!==e.removeMatching||!0===e.clear||void 0!==e.mode||void 0!==e.metric||void 0!==e.colorTheme||void 0!==e.heightScale||void 0!==e.scaleMode||void 0!==e.filter,{message:"updateTrafficAreaAnalyticsDisplay requires at least one of: add, remove, addMatching, removeMatching, clear, mode, metric, colorTheme, heightScale, scaleMode, filter."}),qi=Z.union([Z.object({success:Z.literal(!0),totalShown:Z.number(),shown:Z.array(ji).describe("Traffic-area-analytics entries currently on the map."),actuallyAdded:Z.array(Z.string()),actuallyRemoved:Z.array(Z.string())}),sr]),$i=(e,t)=>{if(0===t.length)return[];const r=t.map(e=>e.toLowerCase()),s=/* @__PURE__ */new Set;for(const o of e.trafficAreaAnalytics.entries)r.some(e=>o.label.toLowerCase().includes(e))&&s.add(o.id);return[...s]},Gi=async(e,t,r)=>{const s=e.trafficAreaAnalytics.findById(t);if(!s)return;const o=s.data.properties?.metrics?.[0]??"congestionLevel",n=r.mode??"hexgrid-3d",i=r.metric??o,a=r.colorTheme??"trafficLight",l=await e.trafficAreaAnalytics.getEntryModule(t);l.setMode(n),l.setMetric(i),l.setColor(a),void 0===r.heightScale&&void 0===r.scaleMode||l.setHeight({...void 0!==r.heightScale&&{maxHeightMeters:r.heightScale},...void 0!==r.scaleMode&&{scaleMode:r.scaleMode}}),r.filter?l.filter({min:r.filter.min,max:r.filter.max}):l.clearFilter()},Ui=e=>({id:e.id,label:e.label,...e._config?.activeMetric&&{activeMetric:e._config.activeMetric},...e._config?.displayMode&&{displayMode:e._config.displayMode}}),Wi=Z.union([Z.object({success:Z.literal(!0),visible:Z.boolean(),count:Z.number()}),sr]),Vi=Z.object({visible:Z.boolean().optional().describe("Render the staged waypoints (true, default) or clear them (false). No route line is drawn either way.")}),zi="Dynamic-JS sandbox over any combination of places / routes / incidents / geometries / trafficAreaAnalytics / byod entries. Each input arg is `undefined` when its `*EntryIDs` was omitted.",Hi={locatePlace:{description:"Resolve ONE named location (landmark, venue, city, neighborhood, address, POI) to a normalized place summary. Use this for any SINGLE named place — including requests for its boundary / polygon / outline / footprint (via `geometry`), pin or zoom display (via `show`), or staging as a routing waypoint. For multi-place or category searches, use discoverPlaces.",classificationPrompt:"Resolve a single location string (landmark, city, address) to a place without showing it; optionally stages a waypoint slot. Do NOT call as a precursor to discoverPlaces — discoverPlaces resolves named areas itself via `where.query` (or `where.municipalities` / `where.placeId`).",inputSchema:fs,outputSchema:gs,execute:async(e,t)=>{const{query:r,queryAs:s,waypointIndex:o,where:n,show:i,entryId:a,geometry:l}=e;try{const e=await bs(n,t);if("error"in e)return e;const c=await ys(r,s,e.bias);if(!c)return{error:`No result found for "${r}"`};const d=await t.places.addPlaceResult(c,ns(c,{query:r}),a);void 0!==o&&t.routing.setWaypointAt(o,c);const u=i?await(async(e,t,r,s)=>{const o=await Bt(e,[r],{...s,zoomMode:"none"});if("auto"===s.zoomMode){if(t.bbox)e.baseMap.mapLibreMap.fitBounds(t.bbox);else{const r=$(t);r&&e.baseMap.mapLibreMap.flyTo({center:r,zoom:13})}o.zoomMode=!0}return o})(t,c,d,i):void 0;let p,h;if(l){const e=await jt(t,d,l);p=e.fetched>0,e.shown&&(h=!0)}return{...ls(c),...void 0!==o&&{waypointIndex:o},...u&&{shown:u},...void 0!==p&&{geometryFetched:p},...void 0!==h&&{geometryShown:h}}}catch(c){return{error:`Location resolution failed: ${c instanceof Error?c.message:String(c)}`}}},tags:["locate","place","location","waypoint"],examples:['locatePlace("Buckingham Palace", queryAs: "poi", where: { mode: "within", viewport: true }, waypointIndex: 0)','locatePlace("Tower Bridge", queryAs: "poi", where: { mode: "nearby", viewport: true }, waypointIndex: 1)','locatePlace("Westminster Abbey", queryAs: "poi", where: { mode: "global" })','locatePlace("10 Downing Street", queryAs: "place", where: { mode: "nearby", position: [4.9, 52.37] })','locatePlace("Amsterdam", queryAs: "place", where: { mode: "global" }, entryId: "amsterdam-city", geometry: { show: true }) // outline the city','locatePlace("Utrecht", queryAs: "place", where: { mode: "global" }, entryId: "utrecht-city", geometry: { show: true, mode: "add" }) // also outline a second city'],examplePrompts:["Where is the Louvre?","Pin Central Station on the map","Show Times Square on the map","Pin Schiphol Airport on the map","Outline Amsterdam on the map","Show the boundary of Berlin"],relatedTools:["setRoute","addWaypointsToRoute","getCurrentWaypoints","recallPlaces"]},reverseGeocode:{description:"Convert [lng, lat] to an address and place summary. Use after a click / getViewport / getCurrentLocation. Coordinates only — for text queries use locatePlace or discoverPlaces.",classificationPrompt:"Convert [lng, lat] coordinates to an address; use after map clicks or any tool that returns a position.",inputSchema:_o,outputSchema:Do,execute:async(e,t)=>{const{position:r}=e,s=r;try{const e=await _({position:s});return e?(await t.places.addPlaceResult(e,ns(e)),ls(e)):{error:"No result found for the given coordinates"}}catch(o){return{error:`Reverse geocoding failed: ${o instanceof Error?o.message:String(o)}`}}},tags:["location","place"],examples:["reverseGeocode({ position: [4.9, 52.4] })"],examplePrompts:["What's the address at this point?","Which city is [4.9, 52.4] in?","Name the place where I clicked"],relatedTools:["getViewport","getCurrentLocation","updatePlacesDisplay","locatePlace"]},discoverPlaces:e=>((e,t)=>({...t,description:Bs(e),inputSchema:js(e),outputSchema:ks(e),execute:Hs(e)}))(e.featureFlags??{},Ys),processData:(Ht={classificationPrompt:`${zi} WRITES NEW renderable map entries — returns \`{ places?, placeConnections?, geometries?, fitOnMap?, byod? }\` to write a places, custom-geometries, or BYOD entry, move the camera, or any combination. NOT for digests/charts/counts — those are \`analyseData\` (metadata only, never renders). Use for: filter/merge places (set ops), connect places (hub-spoke), compute polygons (union/buffer/h3-coverage/difference), focus camera on route sections via \`routeUtils.getSectionBBox\`, cross-reference (POIs near route, places-within-reachable, places-inside-high-congestion-tile).`,tags:["turf","h3","geometry","place","route","aggregation","connections"],examples:['processData({ placesEntryIDs: ["places-2"], code: "return { places: { type: \'FeatureCollection\', features: places.features.filter(p => (p.properties.poi?.name ?? \'\').toLowerCase().includes(\'vegan\')) } };", label: "vegan only", show: { places: { markerType: "pin", zoomMode: "auto", hidePreviousEntries: "all" } } })','processData({ placesEntryIDs: ["places-1"], code: "const [hub, ...rest] = places.features; return { places, placeConnections: rest.map(p => ({ from: hub, to: p })) };" })','processData({ placesEntryIDs: ["places-1"], code: "const cells = new Set(); for (const p of places.features) cells.add(h3.latLngToCell(p.geometry.coordinates[1], p.geometry.coordinates[0], 8)); return { places, geometries: [...cells].map(c => turf.polygon([h3.cellToBoundary(c, true)], { cell: c })) };", show: { places: { markerType: "pin", zoomMode: "auto" }, placesGeometries: { theme: "outline" } } })','processData({ routesEntryIDs: ["routes-0"], code: "const r = routes.features[0]; const sec = (r.properties.sections.traffic ?? []).slice().sort((a,b) => (b.delayInSeconds ?? 0) - (a.delayInSeconds ?? 0))[0]; const bbox = sec ? routeUtils.getSectionBBox(r, sec) : turf.bbox(r); return { fitOnMap: { bbox, padding: 80 } };" })','processData({ routesEntryIDs: ["routes-0"], code: "const r = routes.features[0]; const sec = r.properties.sections.toll?.[0]; if (!sec) return { fitOnMap: turf.bbox(r) }; return { fitOnMap: routeUtils.getSectionBBox(r, sec) };" })','processData({ placesEntryIDs: ["ev-stations"], routesEntryIDs: ["routes-0"], code: "const r = routes.features[0]; const buf = turf.buffer(r, 0.5, { units: \'kilometers\' }); const near = places.features.filter(p => turf.booleanPointInPolygon(p, buf)); return { places: { type: \'FeatureCollection\', features: near }, fitOnMap: turf.bbox(turf.featureCollection([...near, r])) };", label: "ev near route", entryId: "ev-near-routes-0", show: { places: { markerType: "pin", zoomMode: "none", hidePreviousEntries: "all" } } })','processData({ geometriesEntryIDs: [{ kind: "place", id: "amsterdam-id" }, { kind: "place", id: "rotterdam-id" }], code: "return { geometries: [turf.union(turf.featureCollection(geometries))].filter(Boolean) };", label: "ams ∪ rdam", operation: "union" })','processData({ geometriesEntryIDs: [{ kind: "place", id: "paris-id" }], code: "return { geometries: geometries.map(f => ({ ...turf.buffer(f, 5, { units: \'kilometers\' }), id: `${f.id}-buf` })) };", label: "paris +5km", operation: "buffer" })','processData({ geometriesEntryIDs: [{ kind: "place", id: "amsterdam-id" }, { kind: "ranges", id: "ranges-0" }], code: "const outer = geometries.filter(g => g.properties._source.kind === \'place\'); const inner = geometries.filter(g => g.properties._source.kind === \'ranges\'); const env = turf.union(turf.featureCollection(outer)); const u = turf.union(turf.featureCollection(inner)); const inv = (env && u) ? turf.difference(turf.featureCollection([env, u])) : env; return { geometries: inv ? [{ ...inv, id: \'unreachable\' }] : [] };", label: "amsterdam minus reachable", operation: "difference", show: { customGeometries: { theme: "inverted", hidePreviousEntries: "all" } } })','processData({ geometriesEntryIDs: [{ kind: "ranges", id: "ranges-0" }, { kind: "places", id: "ev-stations" }], code: "const ranges = geometries.filter(g => g.properties._source.kind === \'ranges\'); const env = ranges.length ? turf.union(turf.featureCollection(ranges)) : null; const ev = (placesByEntry?.[\'ev-stations\']?.features ?? []); const kept = env ? ev.filter(p => turf.booleanPointInPolygon(p, env)) : ev; return { places: { type: \'FeatureCollection\', features: kept } };", label: "ev within reachable" })','processData({ placesEntryIDs: ["ev-stations"], trafficAreaAnalyticsEntryIDs: ["tta-0"], code: "const hot = trafficAreaAnalytics.features.filter(t => (t.properties.congestionLevel ?? 0) >= 75); const kept = places.features.filter(p => hot.some(t => turf.booleanPointInPolygon(p, t))); return { places: { type: \'FeatureCollection\', features: kept } };", label: "ev in high-congestion tiles" })','processData({ trafficAreaAnalyticsEntryIDs: ["tta-0"], code: "const hot = trafficAreaAnalytics.features.filter(t => (t.properties.congestionLevel ?? 0) >= 75).map((t, i) => ({ ...t, id: `hot-${i}` })); const u = hot.length ? turf.union(turf.featureCollection(hot)) : null; return { geometries: u ? [{ ...u, id: \'congestion-hot-zone\' }] : [] };", label: "congestion hot zone", operation: "union", show: { customGeometries: { theme: "filled" } } })'],examplePrompts:["Keep only the vegan restaurants","Draw lines from this hub to every nearby cafe","Show the h3 hex coverage of these places","Fit the map to the worst traffic section of the route","Fit the map to the first toll section of the route","Filter my loaded EV stations to those within 500 m of the route","Create a custom polygon by unioning these city boundaries","Compute a 500 m buffer around this neighbourhood","Areas of Amsterdam not reachable from the hospitals","Filter loaded places to those inside the reachable area","Keep only the EV stations that fall inside high-congestion tiles","Create a custom polygon layer outlining the congestion hot zone"],relatedTools:["analyseData","recallPlaces","recallRoutes","recallGeometries","recallState"],dependsOn:["discoverPlaces","locatePlace","setRoute","getTrafficIncidents","getTrafficAreaAnalytics","findReachableAreas","recallPlaces","recallRoutes","recallGeometries"]},e=>((e,t)=>{const r=e.enabledDataKinds??et;return{description:Qo(r,e.scope),inputSchema:Zo(r,e.scope),outputSchema:Ko,execute:ln,scopeSchema:tt(r),scopePrompt:en(r),...t}})(e,Ht)),analyseData:(e=>t=>((e,t)=>{const r=e.enabledDataKinds??et;return{description:No(r,e.scope),inputSchema:Fo(r,e.scope),outputSchema:Lo,execute:qo,scopeSchema:tt(r),scopePrompt:jo(r),...t}})(t,e))({classificationPrompt:`${zi} WRITES metadata only — returns JSON or a Chart.js config (\`outputFormat: "chart"\`), attached as \`_analysis[name]\` on every contributing entry. Never renders or creates new map entries (that is \`processData\`). Use for: counts / groupings / charts (by-category, hex density); route summaries (per-leg / per-country / alternative compare); route TRAFFIC analysis from \`route.properties.sections.traffic[]\` — pick THIS for "distribution / count / chart of <something> along the route", NOT \`getTrafficIncidents\`; geometry stats (areas, h3 coverage); historical traffic-area-analytics breakdowns (congestion / speed / travel-time per tile); cross-kind correlations (incidents-per-cluster, places-near-route, distance-to-route bins, route-segments-by-tile-metric). For recurring monitor-tick reruns with \`previous\` / \`now\` / \`log\` and optional \`focusIds\` side-effect, set \`monitor: { entryId }\` (only \`incidentsEntryIDs: [monitor.entryId]\` allowed alongside).`,tags:["analysis","turf","h3","place","route","geometry"],examples:['analyseData({ placesEntryIDs: ["places-2"], name: "by-category", code: "const counts = {}; for (const p of places.features) for (const c of (p.properties.poi?.categories ?? [])) counts[c] = (counts[c] ?? 0) + 1; return { total: places.features.length, byCategory: counts };" })','analyseData({ placesEntryIDs: ["places-1"], name: "hex-density-8", code: "const bins = {}; for (const p of places.features) { const [lng,lat] = p.geometry.coordinates; const cell = h3.latLngToCell(lat, lng, 8); bins[cell] = (bins[cell] ?? 0) + 1; } return { resolution: 8, bins };" })','analyseData({ placesEntryIDs: ["places-2"], name: "top-categories-bar", outputFormat: "chart", code: "const counts = {}; for (const p of places.features) for (const c of (p.properties.poi?.categories ?? [])) counts[c] = (counts[c] ?? 0) + 1; const entries = Object.entries(counts).sort((a,b) => b[1]-a[1]).slice(0,10); return { type: \'bar\', data: { labels: entries.map(e => e[0]), datasets: [{ label: \'Places\', data: entries.map(e => e[1]) }] } };" })','analyseData({ routesEntryIDs: ["routes-0"], name: "summary", code: "return routes.features.map(r => ({ index: r.properties.index, distanceKm: r.properties.summary.lengthInMeters / 1000, durationMin: r.properties.summary.travelTimeInSeconds / 60, trafficDelaySec: r.properties.summary.trafficDelayInSeconds }));" })','analyseData({ routesEntryIDs: ["routes-0"], name: "alternative-comparison", outputFormat: "chart", code: "const r = routes.features; return { type: \'bar\', data: { labels: r.map(x => `Route ${x.properties.index}`), datasets: [{ label: \'Travel time (min)\', data: r.map(x => Math.round(x.properties.summary.travelTimeInSeconds / 60)) }, { label: \'Distance (km)\', data: r.map(x => Math.round(x.properties.summary.lengthInMeters / 1000)) }] } };" })','analyseData({ routesEntryIDs: ["routes-0"], name: "on-route-incidents-by-category", outputFormat: "chart", code: "const sections = (routes.features[0].properties.sections.traffic ?? []); const counts = {}; for (const s of sections) for (const c of (s.categories ?? [])) counts[c] = (counts[c] ?? 0) + 1; const entries = Object.entries(counts).sort((a,b) => b[1]-a[1]); return { type: \'bar\', data: { labels: entries.map(e => e[0]), datasets: [{ label: \'Incidents on route\', data: entries.map(e => e[1]) }] }, options: { plugins: { title: { display: true, text: \'Traffic incidents along the route\' } } } };" })','analyseData({ routesEntryIDs: ["routes-0"], name: "delay-by-country", code: "const r = routes.features[0]; const byCountry = {}; for (const c of (r.properties.sections.country ?? [])) byCountry[c.countryCodeISO3] = (byCountry[c.countryCodeISO3] ?? 0) + (r.properties.sections.traffic ?? []).filter(t => t.startPointIndex >= c.startPointIndex && t.endPointIndex <= c.endPointIndex).reduce((s, t) => s + (t.delayInSeconds ?? 0), 0); return byCountry;" })','analyseData({ routesEntryIDs: ["routes-0"], placesEntryIDs: ["ev-stations"], name: "ev-near-route-500m", code: "const route = routes.features[0]; const out = {}; for (const [id, fc] of Object.entries(placesByEntry)) out[id] = fc.features.filter(p => turf.pointToLineDistance(p, route, { units: \'meters\' }) <= 500).length; return { withinMeters: 500, byEntry: out };" })','analyseData({ routesEntryIDs: ["routes-0"], placesEntryIDs: ["cafes"], name: "cafe-distance-bins-km", outputFormat: "chart", code: "const route = routes.features[0]; const distances = places.features.map(p => turf.pointToLineDistance(p, route, { units: \'kilometers\' })); const max = Math.max(0, ...distances); const binWidth = 1; const binCount = Math.max(1, Math.ceil(max / binWidth)); const bins = new Array(binCount).fill(0); for (const d of distances) bins[Math.min(binCount - 1, Math.floor(d / binWidth))]++; const labels = bins.map((_, i) => `${i}-${i + binWidth} km`); return { type: \'bar\', data: { labels, datasets: [{ label: \'cafes\', data: bins }] }, options: { plugins: { title: { display: true, text: \'Cafe distance to route\' } } } };" })','analyseData({ geometriesEntryIDs: [{ kind: "places", id: "cities" }], name: "areas", code: "return geometries.map(f => ({ id: f.id, km2: +(turf.area(f)/1e6).toFixed(2) }));" })','analyseData({ geometriesEntryIDs: [{ kind: "places", id: "cities" }], name: "area-bar", outputFormat: "chart", code: "return { type: \'bar\', data: { labels: geometries.map(f => f.id), datasets: [{ label: \'km²\', data: geometries.map(f => +(turf.area(f)/1e6).toFixed(2)) }] } };" })','analyseData({ placesEntryIDs: ["clusters"], incidentsEntryIDs: ["incidents-0"], name: "incidents-per-cluster", code: "const out = {}; for (const c of places.features) { const inCluster = incidents.filter(i => { const coord = i.geometry.type === \'Point\' ? i.geometry.coordinates : turf.centroid(i).geometry.coordinates; return turf.distance(coord, c.geometry.coordinates, { units: \'kilometers\' }) <= 0.5; }); out[c.id] = inCluster.length; } return out;" })','analyseData({ geometriesEntryIDs: [{ kind: "customGeometries", id: "buffered-routes" }], incidentsEntryIDs: ["incidents-0"], name: "incidents-in-corridors", code: "const out = []; for (const g of geometries) { const hits = incidents.filter(i => { const coord = i.geometry.type === \'Point\' ? i.geometry.coordinates : turf.centroid(i).geometry.coordinates; return turf.booleanPointInPolygon(coord, g); }); out.push({ id: g.id, count: hits.length, totalDelaySec: hits.reduce((s, i) => s + (i.properties.delayInSeconds ?? 0), 0) }); } return out;" })',"analyseData({ trafficAreaAnalyticsEntryIDs: [\"tta-0\"], name: \"congestion-distribution\", outputFormat: \"chart\", code: \"const bins = [0, 25, 50, 75, 100]; const counts = new Array(bins.length).fill(0); for (const t of trafficAreaAnalytics.features) { const c = t.properties.congestionLevel ?? 0; const idx = Math.min(bins.length - 1, Math.floor(c / 25)); counts[idx]++; } return { type: 'bar', data: { labels: ['0–24', '25–49', '50–74', '75+'], datasets: [{ label: 'Tiles', data: counts.slice(0, 4) }] } };\" })",'analyseData({ placesEntryIDs: ["ev-stations"], trafficAreaAnalyticsEntryIDs: ["tta-0"], name: "ev-stations-by-congestion", code: "const buckets = { low: 0, mid: 0, high: 0 }; for (const p of places.features) { const tile = trafficAreaAnalytics.features.find(t => turf.booleanPointInPolygon(p, t)); if (!tile) continue; const c = tile.properties.congestionLevel ?? 0; if (c < 25) buckets.low++; else if (c < 75) buckets.mid++; else buckets.high++; } return buckets;" })','analyseData({ routesEntryIDs: ["routes-0"], trafficAreaAnalyticsEntryIDs: ["tta-0"], name: "route-tile-coverage", code: "const r = routes.features[0]; const hits = trafficAreaAnalytics.features.filter(t => turf.lineIntersect(r, t).features.length > 0); return { tilesCrossed: hits.length, avgCongestion: hits.length ? +(hits.reduce((s, t) => s + (t.properties.congestionLevel ?? 0), 0) / hits.length).toFixed(1) : null };" })','analyseData({ incidentsEntryIDs: ["incidents-0"], name: "by-category", code: "const c = {}; for (const i of incidents) c[i.properties.category] = (c[i.properties.category] ?? 0) + 1; return { total: incidents.length, byCategory: c };" })','analyseData({ incidentsEntryIDs: ["incidents-0"], name: "top-delays", code: "return [...incidents].sort((a,b) => (b.properties.delayInSeconds ?? 0) - (a.properties.delayInSeconds ?? 0)).slice(0,5).map(i => ({ id: i.properties.id, delay: i.properties.delayInSeconds, road: i.properties.roadNumbers?.[0] }));" })',"analyseData({ incidentsEntryIDs: [\"incidents-0\"], monitor: { entryId: \"incidents-0\" }, name: \"rolling-count\", code: \"const trend = previous ? (incidents.length > previous.count ? 'growing' : incidents.length < previous.count ? 'fading' : 'steady') : 'new'; return { count: incidents.length, trend, at: now.toISOString() };\" })",'analyseData({ incidentsEntryIDs: ["incidents-0"], monitor: { entryId: "incidents-0" }, name: "top-3-focus", code: "const top = [...incidents].sort((a,b) => (b.properties.delayInSeconds ?? 0) - (a.properties.delayInSeconds ?? 0)).slice(0,3); return { count: top.length, focusIds: top.map(i => i.properties.id), focusReason: \'top 3 by delay\' };" })'],examplePrompts:["How many of each POI category are in these results?","h3 hex density of these places","Bar chart of the top POI categories","How long is each route alternative?","Compare travel times across alternatives","How many EV stations are within 500 m of the route?","Chart the distance from each cafe to the route, binned in km","Histogram of place-to-route distances","Distribution of traffic incidents along the drive from Paris to Brussels","Chart the incidents on the route by category","Total delay per country on this route","How big are these areas?","Bar chart of city areas","Incidents per place cluster","Total delay per traffic corridor","Congestion distribution across the analytics tiles","Average historical speed per tile, as a chart","Group these EV stations by the congestion level of the tile they fall in","For the route, which tiles does it cross and what is their average congestion?","How many incidents of each category?","Top 5 incidents by delay","Total delay grouped by road","Keep counting the incidents and tell me when it grows or fades","Highlight the 3 worst incidents by delay on the map","Bar chart of incidents per category","Which jams are growing vs fading on this monitored area?"],relatedTools:["processData","focusIncidents","recallPlaces","recallRoutes","recallGeometries","recallState"],dependsOn:["discoverPlaces","setRoute","getTrafficIncidents","getTrafficAreaAnalytics","findReachableAreas","recallPlaces","recallRoutes","recallGeometries"]}),setRoute:{description:"Calculate or recalculate a route. Provide `locations` to set/replace origin/stops/destination, `parameters` to update cost model / alternatives / when, or both in a single call. At least one of `locations` or `parameters` MUST be provided. If only `parameters` is given and waypoints already exist, recalculates with the new options; if no waypoints exist yet, parameters are stored and no recalculation runs. Car/driving only. Do NOT use to show, query, or modify an EXISTING displayed route — use updateRoutesDisplay / recallRoutes / addWaypointsToRoute / removeWaypointsFromRoute / replaceWaypointInRoute / discoverPlaces (detour|withinRoute) / getRouteProgress instead.",classificationPrompt:"Use to define a new route or change route waypoints/options. Pass `locations` for a new origin/stops/destination, `parameters` to update cost model / alternatives / when, or both together. Do not use to show, query, or modify an existing displayed route.",inputSchema:Ms,outputSchema:Es,execute:async(e,t)=>{const{locations:r,parameters:s,showOnMap:o,hidePreviousEntries:n}=e;try{let e;if(s&&t.routing.setParams(s),r){const s=await Promise.all(r.map(e=>ws(e,t))),o=[];for(let e=0;e<r.length;e++){if(null!==s[e])continue;const t=r[e];"query"in t?o.push(`"${t.query}"`):"placeId"in t&&o.push(`placeId "${t.placeId}"`)}if(o.length>0)return{error:`Could not resolve: ${o.join(", ")}`};if(e=xs(s.map(e=>e?.place??null)),!e)return{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}else if(e=xs(t.routing.planningSlots),!e)return{success:!0};return Ds(t,e,o,n)}catch(i){return{error:`Route calculation failed: ${i instanceof Error?i.message:String(i)}`}}},tags:["route","waypoint","location"],examples:['setRoute({ locations: [{ query: "Amsterdam" }, { query: "Brussels" }] })','setRoute({ locations: [{ query: "Eiffel Tower" }, { query: "Louvre Museum" }, { query: "Notre Dame" }] })',"setRoute({ parameters: { maxAlternatives: 2 } })",'setRoute({ parameters: { costModel: { routeType: "fast", avoid: ["tollRoads"] } } })','setRoute({ parameters: { when: { option: "departAt", date: "2025-06-01T09:00:00Z" } } })','setRoute({ locations: [{ query: "Paris" }, { query: "Lyon" }], parameters: { costModel: { routeType: "short" } } })'],examplePrompts:["Get directions from Paris to Lyon","Route me from Amsterdam to Brussels","Plan a new trip from Amsterdam to Schiphol airport","Recalculate my route to Brussels from Schiphol airport","Recalculate the route from Utrecht to Brussels","Avoid toll roads on my current route","Find the fastest route from Berlin to Munich","Show 2 alternative routes for my current trip","Depart at 9am on my current route"],relatedTools:["updateRoutesDisplay","addWaypointsToRoute","removeWaypointsFromRoute","replaceWaypointInRoute","recallRoutes"]},addWaypointsToRoute:{description:"Extend the current route — prepend a new origin in front of the existing one, append a new destination after the existing one, and/or insert intermediate stops at optimal positions along the existing route geometry. Existing origin/destination are preserved as intermediate stops, never replaced. Always recalculates. At least one of `origin`, `destination`, or `stops` MUST be provided. Requires an existing route. To outright replace waypoints, use setRoute instead.",classificationPrompt:"Extend the route by prepending a new origin and/or appending a new destination (preserving the existing endpoints as stops), and/or inserting intermediate stops at optimal positions, then recalculate. Use setRoute when the user wants to fully replace waypoints.",inputSchema:Ts,outputSchema:_s,execute:async(e,t)=>{const{origin:r,destination:s,stops:o,showOnMap:n,hidePreviousEntries:i}=e;try{const e=await(async e=>{const t=e.routing.currentEntryModule;if(!t)return"No existing route on the map. Use calculate-route or updateRoutesDisplay first.";const r=t.getShown(),s=r?.mainLines,o=r?.waypoints;return s&&s.features.length?!o||o.features.length<2?"No existing waypoints available. Use calculate-route first to create a route with waypoints.":{shownRouteLines:s,shownWaypoints:o}:"No existing route available. Use calculate-route first."})(t);if("string"==typeof e)return{error:e};const{shownRouteLines:a,shownWaypoints:l}=e,c=await(async(e,t)=>{const{origin:r,destination:s,stops:o=[]}=e,n=[...r?[{kind:"origin",location:r}]:[],...s?[{kind:"destination",location:s}]:[],...o.map(e=>({kind:"stop",location:e}))],i=await Promise.all(n.map(({location:e})=>ws(e,t))),a=[];for(let c=0;c<n.length;c++)null===i[c]&&a.push(As(n[c].location));if(a.length>0)return{error:`Could not resolve: ${a.join(", ")}`};const l={stops:[]};for(let c=0;c<n.length;c++){const e=i[c]?.place;"origin"===n[c].kind?l.origin=e:"destination"===n[c].kind?l.destination=e:l.stops.push(e)}return l})({origin:r,destination:s,stops:o},t);if("error"in c)return c;const d=[...c.origin?[c.origin]:[],...l.features,...c.destination?[c.destination]:[]],u=xs(c.stops.length>0?J(a.features[0],d,c.stops):d);return u?Ds(t,u,n,i):{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}catch(a){return{error:`Failed to add waypoints to route: ${a instanceof Error?a.message:String(a)}`}}},tags:["route","waypoint"],examples:['addWaypointsToRoute({ stops: [{ query: "Westminster Abbey" }] }) // single intermediate stop','addWaypointsToRoute({ stops: [{ query: "Reims" }, { query: "Metz" }] }) // multiple stops, settle into along-route order','addWaypointsToRoute({ origin: { query: "my house" } }) // prepend new origin; previous origin becomes a stop','addWaypointsToRoute({ destination: { query: "Heathrow Airport" } }) // append new destination; previous destination becomes a stop','addWaypointsToRoute({ origin: { query: "Schiphol" }, destination: { query: "Brussels" } }) // extend on both ends','addWaypointsToRoute({ origin: { query: "X" }, stops: [{ query: "Y" }] }) // combine endpoint extension and stops'],examplePrompts:["Add a stop at Westminster Abbey","Add stops at Reims and Metz on the way","Pick me up from my house first, then continue the trip","Continue past the destination to Heathrow"],relatedTools:["locatePlace","setRoute","updateRoutesDisplay","removeWaypointsFromRoute","replaceWaypointInRoute"],dependsOn:["setRoute"]},removeWaypointsFromRoute:{description:"Remove one or more waypoints from the current route by index; always recalculates. Index 0 = origin, last = destination (both protected when removing them would leave fewer than 2). Requires an existing route.",classificationPrompt:"Remove one or more waypoints by index from the current route and recalculate.",inputSchema:So,outputSchema:Io,execute:async(e,t)=>{const{waypointIndices:r,showOnMap:s,hidePreviousEntries:o}=e;try{const e=((e,t)=>{if(!e||e.length<2)return"No existing waypoints available. Use calculate-route first to create a route with waypoints.";const r=new Set(t);if(r.size!==t.length)return"Duplicate waypoint indices are not allowed.";for(const s of t)if(s<0||s>=e.length)return`Invalid waypoint index ${s}. Valid range is 0 to ${e.length-1}.`;return e.length-t.length<2?"Cannot remove waypoints: at least 2 waypoints (origin and destination) must remain.":e.filter((e,t)=>!r.has(t))})(t.routing.currentWaypoints,r);if("string"==typeof e)return{error:e};const n=xs(e);return n?Ds(t,n,s,o):{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}catch(n){return{error:`Failed to remove waypoints from route: ${n instanceof Error?n.message:String(n)}`}}},tags:["route","waypoint"],examples:["removeWaypointsFromRoute({ waypointIndices: [1] }) // remove a single stop","removeWaypointsFromRoute({ waypointIndices: [1, 2] }) // remove multiple stops in one call"],examplePrompts:["Remove the second stop from my route","Delete the Utrecht stop","Drop the last two intermediate stops"],relatedTools:["addWaypointsToRoute","replaceWaypointInRoute","setRoute","updateRoutesDisplay"],dependsOn:["setRoute"]},replaceWaypointInRoute:{description:"Replace a single waypoint in the current route in place — origin, destination, or any intermediate stop by 0-based index. Always recalculates. Other waypoints are preserved. Requires an existing route. Use addWaypointsToRoute to extend the route, removeWaypointsFromRoute to drop a waypoint, or setRoute to fully replace all waypoints.",classificationPrompt:"Overwrite a single waypoint of the current route in place — by 'origin', 'destination', or numeric index — and recalculate. Other waypoints are kept.",inputSchema:xo,outputSchema:Eo,execute:async(e,t)=>{const{waypointIndex:r,location:s,showOnMap:o,hidePreviousEntries:n}=e;try{const e=t.routing.currentWaypoints;if(!e||e.length<2)return{error:"No existing waypoints available. Use calculate-route first to create a route with waypoints."};const l=(i=r,a=e.length,"origin"===i?0:"destination"===i?a-1:i<0||i>=a?`Invalid waypoint index ${i}. Valid range is 0 to ${a-1}.`:i);if("string"==typeof l)return{error:l};const c=await ws(s,t);if(!c)return{error:`Could not resolve location: ${Po(s)}`};const d=[...e];d[l]=c.place;const u=xs(d);return u?Ds(t,u,o,n):{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}catch(l){return{error:`Failed to replace waypoint in route: ${l instanceof Error?l.message:String(l)}`}}var i,a},tags:["route","waypoint"],examples:['replaceWaypointInRoute({ waypointIndex: "origin", location: { query: "Schiphol" } })','replaceWaypointInRoute({ waypointIndex: "destination", location: { query: "Heathrow" } })','replaceWaypointInRoute({ waypointIndex: 1, location: { query: "Utrecht" } }) // overwrite the first intermediate stop'],examplePrompts:["Change the origin to Schiphol","Make Heathrow my destination","Replace the second stop with Utrecht"],relatedTools:["addWaypointsToRoute","removeWaypointsFromRoute","setRoute","locatePlace"],dependsOn:["setRoute"]},getPOICategoryCodes:{description:'Resolve natural language (e.g. "gym", "italian food", "bookstore") into POICategory codes for use in the discoverPlaces poiCategories parameter. Returns all codes when no filter is given.',classificationPrompt:'Resolve natural-language category names (e.g. "gym", "italian food") to POICategory codes for discoverPlaces.',inputSchema:ro,outputSchema:to,execute:async(e,t)=>{const{filters:r,language:s}=e;try{const e=await D({filters:r,language:s});return{count:e.length,codes:e}}catch(o){return{error:`Failed to fetch POI category codes: ${o instanceof Error?o.message:String(o)}`}}},tags:["discover","place"],examples:["getPOICategoryCodes()",'getPOICategoryCodes(filters: ["gym"])','getPOICategoryCodes(filters: ["italian restaurant"]) // multi-word phrase, spaces removed before matching','getPOICategoryCodes(filters: ["restaurant"], language: "fr-FR")'],examplePrompts:["What POI categories are available?","What's the code for gym?"],relatedTools:["discoverPlaces","toggleTilesPOIs"]},getTrafficIncidents:{description:'LOADER. Fetches traffic incidents (present or future) into a cached entry, renders them on the map, returns `{ count, entryId, entries }` (`entries` lists every cached entry so the agent can pick a follow-up target). No per-incident details, no aggregation — those belong to analyseData/focusIncidents. \n\nAREA via `where` (within mode only). Omit `where` to use the current map viewport ("the network / right now / this area / here"). For named areas use `where.queries`; for stored places use `where.placeIds`; for an isochrone use `where.range`; for a corridor along a route use `where.route`; for raw bounds use `where.boundingBox`; for custom shapes use `where.geometries`. Multi-region fields compose into one union bbox (≤10,000 km²). \n\nCALL ONCE PER AREA. Each call writes a new entry; re-calling burns steps. Pick the right follow-up instead: analyseData (counts, top-N, charts, clusters, trend via `previous`); focusIncidents (highlight a subset); startTrafficIncidentsMonitor (keep the entry fresh in the background). \n\n`silent: true` suppresses rendering (intermediate calcs only); `label` names the snapshot in the UI.',classificationPrompt:'AREA-BOUND incident loader — bbox / viewport / "incidents around here" / "accidents in <city>". DO NOT pick for route-bound queries ("incidents ALONG / ON the route", "delays on the drive from X to Y"): a calculated route already carries its incidents in `route.properties.sections.traffic[]` (category, magnitudeOfDelay, delayInSeconds, effectiveSpeedInKmh) — call `analyseData` with `routesEntryIDs` for those. When this tool IS the right pick: MANDATORY co-pick startTrafficIncidentsMonitor (they are a pair — data goes stale without it). Also co-pick analyseData (one-shot aggregations / charts / clusters; pass `monitor: { entryId }` for the recurring spec + focus side-effect) and focusIncidents (highlight a subset) when the query implies them.',inputSchema:bo,outputSchema:co,execute:async(e,t)=>{const{where:r,categoryFilter:s,timeValidityFilter:o,label:n,silent:i=!1,fitBounds:a=!0}=e,l=r??{mode:"within",viewport:!0},c=await vo(l,t);if("error"in c)return c;const{bbox:d,routeLabel:u}=c,p={...s&&{categoryFilter:s},...o&&{timeValidityFilter:o}};try{const e=await O({...p,bbox:d});if(!e.features.length)return{count:0,entries:lo(t)};const r={bbox:d,...p},s=await t.trafficIncidents.addIncidentsEntry(e.features,r,n??((e,t,r)=>{const s=[];let o;if(e.categoryFilter?.length&&s.push(`filtered: ${e.categoryFilter.join(",")}`),e.timeValidityFilter?.length&&s.push(`when: ${e.timeValidityFilter.join(",")}`),t&&!t.viewport&&(t.queries?.length?o=t.queries.map(e=>e.query).join(", "):r?o=`along ${r}`:t.range&&(o=t.range)),!o){const[t,r,s,n]=e.bbox;o=`bbox ${t.toFixed(2)},${r.toFixed(2)} → ${s.toFixed(2)},${n.toFixed(2)}`}return s.length?`${o} — ${s.join("; ")}`:o})(r,l,u),Date.now());return i||await t.trafficIncidents.showEntry(s,a),{count:e.features.length,entryId:s,entries:lo(t)}}catch(h){return{error:`Failed to get traffic incidents: ${h instanceof Error?h.message:String(h)}`}}},tags:["traffic","location"],examples:["getTrafficIncidents({}) // omit `where` → current viewport",'getTrafficIncidents({ where: { mode: "within", boundingBox: [4.728, 52.278, 5.080, 52.479] } })','getTrafficIncidents({ where: { mode: "within", queries: [{ query: "Amsterdam" }] } }) // named area','getTrafficIncidents({ where: { mode: "within", queries: [{ query: "Amsterdam" }] }, categoryFilter: ["accident", "jam"] })','getTrafficIncidents({ where: { mode: "within", route: { widthMeters: 1000 } }, categoryFilter: ["roadworks"], timeValidityFilter: ["future"] }) // corridor around latest route','getTrafficIncidents({ where: { mode: "within", range: "ranges-0" } }) // inside a reachable area'],examplePrompts:["Fetch traffic incidents in Amsterdam","Any accidents in this area?","Show roadworks across the city"],relatedTools:["calculateBBox","getViewport","analyseData","focusIncidents","startTrafficIncidentsMonitor"]},focusIncidents:{description:"Highlight a subset of incidents on an existing entry. Drives the dim/highlight on the map and the FocusChip in the UI. Compose with `analyseData` to compute the ids first (e.g. top-N by delay, members of a cluster). Unknown ids are dropped silently and returned in `droppedIds`. Errors only when the entry is unknown or NONE of the ids resolve.",classificationPrompt:'Highlight a subset of incidents on an existing entry (drives map dim/highlight + FocusChip). Pick for "focus on…", "show only…", "highlight the…". Compose with analyseData to compute the ids.',inputSchema:Wo,outputSchema:Uo,execute:async(e,t)=>{const{incidentsEntryID:r,incidentIds:s,reason:o}=e;if(!t.trafficIncidents.entries.find(e=>e.id===r))return{error:`No incidents entry with id "${r}". Call getTrafficIncidents first.`};const n=t.trafficIncidents.setFocus(r,s,o);return 0===n.focusedCount?{error:`None of the ${s.length} requested ids exist on entry "${r}". The snapshot may have rolled — call getTrafficIncidents and recompute the ids before retrying.`}:{incidentsEntryID:r,focusedCount:n.focusedCount,droppedIds:n.droppedIds,...o&&{reason:o}}},tags:["traffic","incident","state","ui"],examples:["focusIncidents({ incidentsEntryID: 'incidents-0', incidentIds: ['TTI-...', 'TTI-...'], reason: 'top 3 by delay' })"],examplePrompts:["Focus on the 3 worst incidents by delay","Highlight the closures","Show only the incidents on the A4"],relatedTools:["analyseData","getTrafficIncidents"],dependsOn:["getTrafficIncidents"]},startTrafficIncidentsMonitor:{description:"Start a background monitor for an existing incidents entry. Re-fetches the entry's bbox + filters every interval (default 60s) and replaces its data; any registered analyses re-run on each tick. The first tick fires immediately, so this duplicates the most recent getTrafficIncidents fetch — only call it when the user wants the entry to stay current. \n\nIdempotent on a running monitor: a second call is a no-op and returns alreadyRunning: true. A new intervalMs is NOT applied to a running monitor — stop first, then start again to change settings.",classificationPrompt:'Polls an existing incidents entry on a fixed interval (default 60s); registered analyses re-run on each tick. ALWAYS pick this whenever getTrafficIncidents is picked — they are a mandatory pair, no exceptions. Without it the loaded data is a frozen snapshot. Also pick on its own when the user says "live"/"watch"/"keep updated"/"refresh every N seconds" against an already-loaded entry.',inputSchema:oi,outputSchema:si,execute:async(e,t)=>{const{incidentsEntryID:r,intervalMs:s}=e,o=t.trafficIncidents.entries.find(e=>e.id===r);if(!o)return{error:`No incidents entry with id "${r}". Call getTrafficIncidents first.`};const n=t.trafficIncidents.isMonitored(r),{bbox:i,categoryFilter:a,timeValidityFilter:l}=o.params,c={...a&&{categoryFilter:a},...l&&{timeValidityFilter:l}};return t.trafficIncidents.startMonitoring(r,{bbox:i,capturedAt:o.timestamp,label:o.label},{fetchIncidents:async e=>(await O({...c,bbox:e})).features},s?{intervalMs:s}:void 0),{incidentsEntryID:r,alreadyRunning:n}},tags:["traffic","incident","state"],examples:["startTrafficIncidentsMonitor({ incidentsEntryID: 'incidents-0' })","startTrafficIncidentsMonitor({ incidentsEntryID: 'incidents-0', intervalMs: 30000 })"],examplePrompts:["Keep this updated","Refresh the incidents every 30 seconds","Watch this area for new accidents"],relatedTools:["getTrafficIncidents","stopTrafficIncidentsMonitor","analyseData"],dependsOn:["getTrafficIncidents"]},stopTrafficIncidentsMonitor:{description:"Stop the background monitor for an incidents entry. The entry itself stays in state with its last data — use removeEntry if you also want to drop the entry. No-op when no monitor is running for the entry.",classificationPrompt:'Stop polling an incidents entry; the entry stays in state with its last data. Pick for "stop refreshing"/"pause updates" or when ending a watch.',inputSchema:ii,outputSchema:ni,execute:async(e,t)=>{const{incidentsEntryID:r}=e;if(!t.trafficIncidents.entries.find(e=>e.id===r))return{error:`No incidents entry with id "${r}".`};const s=t.trafficIncidents.isMonitored(r);return t.trafficIncidents.stopMonitoring(r),{incidentsEntryID:r,wasRunning:s}},tags:["traffic","incident","state"],examples:["stopTrafficIncidentsMonitor({ incidentsEntryID: 'incidents-0' })"],examplePrompts:["Stop refreshing","Pause updates for this area"],relatedTools:["startTrafficIncidentsMonitor","getTrafficIncidents"],dependsOn:["startTrafficIncidentsMonitor"]},getTrafficAreaAnalytics:{description:"Fetch historical traffic analytics for an area (≤31 days) via location (named city/region) or bbox. Fetch ALL metrics in ONE call (visualization needs them together); use city-scale areas (~10 km+) for meaningful data. Returns period averages — `showOnMap` visualizes immediately; queryTrafficAnalytics for breakdowns.",classificationPrompt:"Fetch historical traffic analytics (speed, congestion, travel time) for an area. queryTrafficAnalytics drills down without re-fetching.",inputSchema:no,outputSchema:oo,execute:async(e,t)=>{const{location:r,bbox:s,showOnMap:o,startDate:n,endDate:i,days:a,metrics:l,functionalRoadClasses:c,hours:d}=e;if(!r&&!s)return{error:"Provide a location or bbox to define the analytics area."};const u=process.env.MOVE_PORTAL_KEY;if(!u)return{error:"MOVE_PORTAL_KEY environment variable is not set."};try{const e=await(async(e,t,r)=>{if(e){const t=await ws(e,r);if(!t)return{error:"Could not resolve the provided location."};if(Array.isArray(t.place))return{error:"A bare position cannot define an analytics area. Provide a named location query instead."};const s=(await P({geometries:[t.place]}))?.features?.[0]?.geometry;return s||{error:`No boundary polygon found for "${t.name}". Try a bbox instead.`}}return t?(e=>{const[t,r,s,o]=e;return{type:"Polygon",coordinates:[[[t,r],[s,r],[s,o],[t,o],[t,r]]]}})(t):{error:"No area provided."}})(r,s,t);if("error"in e)return e;const p=((e,t,r)=>e&&e.length>0?{days:e}:t?r?{startDate:t,endDate:r}:{startDate:t}:{})(a,n,i),h=await k({apiKey:u,...p,metrics:l,functionalRoadClasses:c??"all",hours:d??"all",geometry:e}),m=h.features[0]?.properties?.name,y=m?`${m} (${p.startDate??"…"} → ${p.endDate??"…"})`:`analytics (${p.startDate??"…"} → ${p.endDate??"…"})`,f=await t.trafficAreaAnalytics.addEntry(h,y,{...p,metrics:l,functionalRoadClasses:c??"all",hours:d??"all"});return o&&await(async(e,t)=>{await e.trafficAreaAnalytics.showEntry(t);const r=e.trafficAreaAnalytics.findById(t);if(!r)return;const s=await e.trafficAreaAnalytics.getEntryModule(t),o=r.data.properties?.metrics?.[0]??"congestionLevel";s.setMetric(o);const n=F(r.data.features);n&&e.baseMap.mapLibreMap.fitBounds(n,{padding:50,pitch:45})})(t,f),((e,t,r,s)=>{const o=t.features[0]?.properties;if(!o)return{entryId:e,dateRange:{start:"",end:""},baseData:{},metrics:[],tileCount:0,availableGranularities:[]};const n=r??ao(t.properties?.startDate)??(()=>{const e=/* @__PURE__ */new Date;return e.setDate(e.getDate()-3),io(e)})(),i=s??ao(t.properties?.endDate)??(()=>{const e=/* @__PURE__ */new Date;return e.setDate(e.getDate()-2),io(e)})(),a=[];return o.timedData?.yearly?.length&&a.push("yearly"),o.timedData?.monthly?.length&&a.push("monthly"),o.timedData?.weekly?.length&&a.push("weekly"),o.timedData?.daily?.length&&a.push("daily"),o.timedData?.hourly?.length&&a.push("hourly"),o.timedData?.average?.length&&a.push("average"),{entryId:e,...o.name&&{name:o.name},...o.timezone&&{timezone:o.timezone},dateRange:{start:n,end:i},baseData:o.baseData,metrics:t.properties?.metrics??[],tileCount:o.tiledData?.tiles?.length??0,availableGranularities:a}})(f,h,p.startDate,p.endDate)}catch(p){return{error:`Failed to get traffic area analytics: ${p instanceof Error?p.message:String(p)}`}}},tags:["traffic","location"],examples:["getTrafficAreaAnalytics({ bbox: [4.728, 52.278, 5.080, 52.479], metrics: ['speed', 'congestionLevel'], startDate: '2025-03-01', endDate: '2025-03-07' })","getTrafficAreaAnalytics({ bbox: [...], metrics: ['congestionLevel'], hours: [7,8,9,17,18] })"],examplePrompts:["What was the average congestion in Amsterdam last week?","Analyze traffic patterns during rush hour in this area","Show me historical speed data for the city center"],relatedTools:["queryTrafficAnalytics","updateTrafficAreaAnalyticsDisplay","calculateBBox","getViewport"],dependsOn:["locatePlace"]},queryTrafficAnalytics:{description:'Query cached traffic analytics (from getTrafficAreaAnalytics) without re-fetching — filter by granularity, date/hour range, day, metric. Returns filtered entries plus the current map visualization state (metric, mode). Use for breakdowns and "what is shown" follow-ups.',classificationPrompt:"Query cached traffic analytics data or check what metric/mode is currently displayed on the map.",inputSchema:dn,outputSchema:un,execute:async(e,t)=>{const{entryId:r,granularity:s="daily",metric:o,startDate:n,endDate:i,hourStart:a,hourEnd:l,dayOfWeek:c}=e,d=r?t.trafficAreaAnalytics.findById(r):t.trafficAreaAnalytics.latestEntry;if(!d)return{error:r?`No traffic-area-analytics entry with id "${r}". Use recallState to list available ids.`:"No analytics entries available. Call getTrafficAreaAnalytics first."};const u=d.data,p=u.features[0]?.properties;if(!p)return{error:"Analytics result has no region data."};const h=p.timedData,m=h?.[s];if(!m||!Array.isArray(m)||0===m.length)return{error:`No '${s}' data available. Available: ${Object.keys(h??{}).join(", ")}`};const y=ao(u.properties?.startDate);let f=m.map((e,t)=>{const r={};if("daily"===s||"hourly"===s){const o=((e,t,r,s)=>{const o=ao(e);if(o)return o;if(s&&t>=0){const e=new Date(s);if(!Number.isNaN(e.getTime())){const s="hourly"===r?Math.floor(t/24):t;return e.setDate(e.getDate()+s),io(e)}}})(e.date,t,s,y);o&&(r.date=o)}if(void 0!==e.hour&&(r.hour=e.hour),void 0!==e.year&&(r.year=e.year),void 0!==e.month&&(r.month=e.month),void 0!==e.week&&(r.week=e.week),void 0!==e.day&&(r.day=e.day),o){const t=e[o];void 0!==t&&(r[o]=t)}else Object.assign(r,(e=>({...void 0!==e.speed&&{speed:e.speed},...void 0!==e.freeFlowSpeed&&{freeFlowSpeed:e.freeFlowSpeed},...void 0!==e.congestionLevel&&{congestionLevel:e.congestionLevel},...void 0!==e.travelTime&&{travelTime:e.travelTime},...void 0!==e.networkLength&&{networkLength:e.networkLength}}))(e));return r});n&&(f=f.filter(e=>!e.date||e.date>=n)),i&&(f=f.filter(e=>!e.date||e.date<=i)),void 0!==a&&(f=f.filter(e=>void 0===e.hour||e.hour>=a)),void 0!==l&&(f=f.filter(e=>void 0===e.hour||e.hour<=l)),c&&c.length>0&&(f=f.filter(e=>void 0===e.day||c.includes(e.day)));const g=d._config,b=g?{activeMetric:g.activeMetric,displayMode:g.displayMode}:void 0;return{granularity:s,count:f.length,entries:f,...b&&{currentVisualization:b}}},tags:["traffic","location"],examples:["queryTrafficAnalytics({ granularity: 'daily' })","queryTrafficAnalytics({ granularity: 'hourly', hourStart: 7, hourEnd: 9, metric: 'congestionLevel' })","queryTrafficAnalytics({ granularity: 'average', dayOfWeek: [6, 7] })"],examplePrompts:["Show me the daily breakdown","What was rush hour congestion?","Which day had the worst traffic?","What metric am I looking at?","What is currently shown on the map?"],relatedTools:["getTrafficAreaAnalytics","updateTrafficAreaAnalyticsDisplay"],dependsOn:["getTrafficAreaAnalytics"]},updatePlacesDisplay:{description:'Show, hide, and swap which places entries are displayed on the map in one atomic call. Target entries by id (`add` / `remove`) or by category label (`addMatching` / `removeMatching`). `clear` hides every currently-shown entry; combine with `add` to swap the display ("hide everything, show these"). Markers (`markerType`) and boundary polygons (`geometry`) are independent display channels — set either or both for the targeted entries. Omit `add`/`addMatching` while still setting `markerType` or `geometry` to target the most recently stored entry. Does not delete from history.',classificationPrompt:"Show, hide, and swap places entries on the map — add some (as pins and/or boundary polygons), remove others, swap atomically, or render boundaries.",inputSchema:Ci,outputSchema:Ai,execute:async(e,t)=>{const{clear:r,markerType:s,geometry:o,fitBounds:n}=e;try{const i=((e,t)=>{const{add:r,remove:s,addMatching:o,removeMatching:n,markerType:i,geometry:a}=t;let l=[...r??[],...ki(e,o??[])];const c=[...s??[],...ki(e,n??[])];if(void 0===r&&void 0===o&&(void 0!==i||void 0!==a)){const t=e.places.entries.at(-1)?.id;if(!t)return{error:"No places available to display. Resolve a place or run a search first (e.g. discoverPlaces)."};l=[t]}const d=new Set(e.places.entries.map(e=>e.id)),u=l.filter(e=>!d.has(e));return u.length>0?{error:`Unknown places entry ids: ${u.join(", ")}. Use recallPlaces to list available entries.`}:{add:l,remove:c}})(t,e);if("error"in i)return i;const{add:a,remove:l}=i,c=new Set(t.places.shownEntryIds);r&&(await t.places.clearShownEntries(),await t.places.clearShownGeometries()),l.length>0&&(await t.places.removeShownEntries(l),await t.places.clearShownGeometries(l));const d=void 0!==s||!o;let u;a.length>0&&d&&await(async(e,t,r,s)=>{const o=void 0!==r?t:t.filter(e=>!s.has(e));o.length>0&&await e.places.addShownEntries(o,r??"pin")})(t,a,s,c),o&&a.length>0&&(u=await(async(e,t,r)=>{const s=r.theme??"outline",o=r.mode??"replace",n=r.placeIds?new Set(r.placeIds):void 0,i=[];let a=0;for(const c of t){const t=e.places.entries.find(e=>e.id===c);if(!t)continue;const r=n?t.places.filter(e=>n.has(e.id)):t.places,d=[];for(const s of r)if(s.properties.dataSources?.geometry?.id)try{const t=await e.places.fetchPlaceGeometry(s.id);if(!t){i.push({placeId:s.id,reason:"Geometry Data service returned no feature for this place."});continue}d.push(t)}catch(l){i.push({placeId:s.id,reason:`Geometry fetch failed: ${l instanceof Error?l.message:String(l)}`})}else i.push({placeId:s.id,reason:"Place has no geometry data source (e.g. addresses and streets often lack one)."});d.length>0&&(await e.places.showPlaceGeometries(d,s,o,c),a+=d.length)}return{rendered:a,skipped:i}})(t,a,o));const p=t.places.shownEntryIds,h=a.filter(e=>!c.has(e)&&p.has(e)),m=l.filter(e=>c.has(e)&&!p.has(e)),y=n??a.length>0,f=t.places.getShownFeatures();if(y&&f.length>0){const e=F(f);e&&t.baseMap.mapLibreMap.fitBounds(e,{padding:50})}const g=t.places.entries.filter(e=>p.has(e.id)).map(e=>({id:e.id,label:e.label,featureCount:e.places.length,markerType:t.places.getShownMarkerType(e.id)??"pin"}));return{success:!0,totalShown:f.length,shown:g,actuallyAdded:h,actuallyRemoved:m,...u&&{geometriesRendered:u.rendered},...u?.skipped.length&&{geometriesSkipped:u.skipped}}}catch(i){return{error:`updatePlacesDisplay failed: ${i instanceof Error?i.message:String(i)}`}}},tags:["place","location","map style","boundary","outline"],examples:['updatePlacesDisplay({ add: ["places-2"] })','updatePlacesDisplay({ add: ["places-1", "places-3"], markerType: "base-map" })','updatePlacesDisplay({ remove: ["places-1"] })','updatePlacesDisplay({ removeMatching: ["cinema"] }) // hide every entry labelled "cinema"','updatePlacesDisplay({ addMatching: ["cafe"] })',"updatePlacesDisplay({ clear: true })",'updatePlacesDisplay({ clear: true, add: ["places-2"] }) // atomic swap: hide everything, show this','updatePlacesDisplay({ markerType: "pin" }) // re-pin the most recently stored entry',"updatePlacesDisplay({ geometry: {} }) // outline the most recently stored entry",'updatePlacesDisplay({ add: ["places-2"], geometry: { mode: "add" } }) // also outline on top of existing boundaries','updatePlacesDisplay({ add: ["places-2"], markerType: "pin", geometry: { theme: "outline" } }) // pin AND outline','updatePlacesDisplay({ add: ["places-2"], geometry: { placeIds: ["G55fc4abe-..."], theme: "outline" } }) // outline a subset'],examplePrompts:["Show the results on the map","Pin the search results","Hide the banks","Also show the cafes on the map","Additionally pin the pubs","Remove the banks from the map",'Drop every entry labelled "parking" from the map',"Toggle off the hotels without clearing the rest","Replace the pins with the new set","Show entry places-3 as base-map POIs","Show the boundary of Amsterdam on the map","Outline these neighbourhoods","Highlight the shape of this place","Pin the cities and outline them too","Also outline Utrecht while keeping Amsterdam drawn","Add the neighbourhood boundaries on top of the pins"],relatedTools:["discoverPlaces","locatePlace","reverseGeocode","recallPlaces","clearMap","flyTo"],dependsOn:["discoverPlaces","locatePlace","processData"]},updateRoutesDisplay:{description:'Show, hide, swap, and recolor which routes entries are displayed on the map in one atomic call. Target entries by id (`add` / `remove`) or by label substring (`addMatching` / `removeMatching`). `clear` hides every currently-shown entry; combine with `add` to swap the display ("hide everything, show this"). `selectedIndex` re-highlights an alternative variant on the newly-added entries; with no `add`/`addMatching` it implicitly targets the most recently stored entry. `mainColor` recolors the route line + waypoint icons (sticky across entry swaps — applied to current shows AND remembered); it does NOT auto-show a hidden entry, so set `add`/`addMatching` too if nothing is on the map yet. Does not delete from history. Does not call any service.',classificationPrompt:"Show, hide, swap, and recolor routes entries on the map — add some, remove others, swap atomically, switch the highlighted alternative, or set `mainColor` (CSS color / hex; sticky across entry swaps).",inputSchema:Li,outputSchema:Ri,execute:async(e,t)=>{const{clear:r,selectedIndex:s,fitBounds:o,mainColor:n}=e;try{const i=((e,t)=>{const{add:r,remove:s,addMatching:o,removeMatching:n,selectedIndex:i}=t;let a=[...r??[],...Fi(e,o??[])];const l=[...s??[],...Fi(e,n??[])];if(void 0===r&&void 0===o&&void 0!==i){const t=e.routing.entries.at(-1)?.id;if(!t)return{error:"No routes available to display. Calculate a route first via setRoute."};a=[t]}const c=new Set(e.routing.entries.map(e=>e.id)),d=a.filter(e=>!c.has(e));return d.length>0?{error:`Unknown routes entry ids: ${d.join(", ")}. Use recallRoutes to list available entries.`}:{add:a,remove:l}})(t,e);if("error"in i)return i;const{add:a,remove:l}=i;void 0!==n&&t.routing.setMainColor(n);const{beforeShown:c,afterShown:d}=await(async(e,t)=>{const r=new Set(e.routing.shownEntryIds);if(t.clear)for(const s of e.routing.shownEntryIds)await e.routing.hideEntry(s);for(const s of t.remove)await e.routing.hideEntry(s);for(const s of t.add)await e.routing.showEntry(s);return void 0!==t.selectedIndex&&await(async(e,t,r)=>{if(0!==r)for(const s of t){const t=e.routing.entries.find(e=>e.id===s);if(!t)continue;const o=await e.routing.getEntryRoutingModule(s);await o.showRoutes(t.data,{selectedIndex:r}),t.waypoints.length&&await o.showWaypoints(t.waypoints)}})(e,t.add,t.selectedIndex),{beforeShown:r,afterShown:e.routing.shownEntryIds}})(t,{add:a,remove:l,clear:!!r,selectedIndex:s}),u=a.filter(e=>!c.has(e)&&d.has(e)),p=l.filter(e=>c.has(e)&&!d.has(e));(o??a.length>0)&&((e,t)=>{const r=[];for(const o of e.routing.entries)t.has(o.id)&&r.push(...o.data.features);if(0===r.length)return;const s=F({type:"FeatureCollection",features:r});s&&e.baseMap.mapLibreMap.fitBounds(s,{padding:50})})(t,d);const h=t.routing.entries.filter(e=>d.has(e.id)).map(e=>({id:e.id,label:e.label,routeCount:e.data.features.length,selectedIndex:u.includes(e.id)?s??0:0})),m=t.routing.mainColor;return{success:!0,totalShown:d.size,shown:h,actuallyAdded:u,actuallyRemoved:p,...void 0!==m&&{mainColor:m}}}catch(i){return{error:`updateRoutesDisplay failed: ${i instanceof Error?i.message:String(i)}`}}},tags:["route","waypoint","map style"],examples:['updateRoutesDisplay({ add: ["routes-1"] })','updateRoutesDisplay({ add: ["routes-0", "routes-2"] })','updateRoutesDisplay({ remove: ["routes-1"] })','updateRoutesDisplay({ removeMatching: ["Brussels"] }) // hide every entry whose label mentions "Brussels"','updateRoutesDisplay({ addMatching: ["Edinburgh"] })',"updateRoutesDisplay({ clear: true })",'updateRoutesDisplay({ clear: true, add: ["routes-2"] }) // atomic swap: hide everything, show this',"updateRoutesDisplay({ selectedIndex: 1 }) // re-highlight alternative #1 on the most recent route",'updateRoutesDisplay({ add: ["routes-0"], selectedIndex: 2, fitBounds: false })','updateRoutesDisplay({ mainColor: "coral" }) // recolor every currently-shown route (and future shows)','updateRoutesDisplay({ add: ["routes-1"], mainColor: "#FF0000" }) // show in red'],examplePrompts:["Draw the route on the map","Show the directions","Display the calculated route","Show the Edinburgh route from earlier","Also show the Brussels route","Hide the Lyon route","Switch to the second alternative","Highlight alternative 2","Replace what is shown with this route","Make the route line red","Change the route color to coral","Style the route in steelblue"],relatedTools:["setRoute","processData","recallRoutes","clearMap"],dependsOn:["setRoute"]},updateTrafficAreaAnalyticsDisplay:{description:"Show, hide, swap, and restyle HISTORICAL traffic-area-analytics entries on the map in one atomic call. Target entries by id (`add` / `remove`) or by label substring (`addMatching` / `removeMatching`). `clear` hides every currently-shown entry; combine with `add` to swap. Render knobs (`mode`, `metric`, `colorTheme`, `heightScale`, `scaleMode`, `filter`) apply to every entry being added AND every entry that stays shown after this call. When only render knobs are set with no add/remove/clear, implicitly targets the most recently added entry. Not real-time — use `toggleTilesTrafficFlow` for the live vector-tile flow overlay.",classificationPrompt:"Show / hide / swap / restyle historical traffic-area-analytics entries on the map. `add` / `remove` / `addMatching` / `removeMatching` / `clear` for visibility; `mode` / `metric` / `colorTheme` / `heightScale` / `scaleMode` / `filter` for the rendering. Render knobs alone implicitly target the most recent entry.",inputSchema:Bi,outputSchema:qi,execute:async(e,t)=>{try{const r=((e,t)=>{const{add:r,remove:s,addMatching:o,removeMatching:n,clear:i}=t;let a=[...r??[],...$i(e,o??[])];const l=[...s??[],...$i(e,n??[])],c=void 0===r&&void 0===o&&void 0===s&&void 0===n&&!0!==i,d=void 0!==t.mode||void 0!==t.metric||void 0!==t.colorTheme||void 0!==t.heightScale||void 0!==t.scaleMode||void 0!==t.filter;if(c&&d){const t=e.trafficAreaAnalytics.entries.at(-1)?.id;if(!t)return{error:"No traffic-area-analytics entries available. Call getTrafficAreaAnalytics first."};a=[t]}const u=new Set(e.trafficAreaAnalytics.entries.map(e=>e.id)),p=a.filter(e=>!u.has(e));return p.length>0?{error:`Unknown traffic-area-analytics entry ids: ${p.join(", ")}. Use recallState to list available ids.`}:{add:a,remove:l}})(t,e);if("error"in r)return r;const{add:s,remove:o}=r,n=new Set(t.trafficAreaAnalytics.shownEntryIds);if(e.clear)for(const e of t.trafficAreaAnalytics.shownEntryIds)await t.trafficAreaAnalytics.hideEntry(e);for(const e of o)await t.trafficAreaAnalytics.hideEntry(e);for(const e of s)await t.trafficAreaAnalytics.showEntry(e);const i=t.trafficAreaAnalytics.shownEntryIds,a=s.filter(e=>!n.has(e)&&i.has(e)),l=o.filter(e=>n.has(e)&&!i.has(e)),c=new Set([...a,...i]);for(const u of c)await Gi(t,u,e);if((e.fitBounds??a.length>0)&&i.size>0){const e=F({type:"FeatureCollection",features:t.trafficAreaAnalytics.entries.filter(e=>i.has(e.id)).flatMap(e=>e.data.features)});e&&t.baseMap.mapLibreMap.fitBounds(e,{padding:50,pitch:45})}const d=t.trafficAreaAnalytics.entries.filter(e=>i.has(e.id)).map(Ui);return{success:!0,totalShown:i.size,shown:d,actuallyAdded:a,actuallyRemoved:l}}catch(r){return{error:`Failed to update traffic-area-analytics display: ${r instanceof Error?r.message:String(r)}`}}},tags:["traffic","map style"],examples:['updateTrafficAreaAnalyticsDisplay({ add: ["tta-0"] })',"updateTrafficAreaAnalyticsDisplay({ mode: 'hexgrid-2d', metric: 'speed', colorTheme: 'heat' }) // re-style the latest (implicit)","updateTrafficAreaAnalyticsDisplay({ filter: { min: 50 } })","updateTrafficAreaAnalyticsDisplay({ scaleMode: 'currentRange', heightScale: 200 })","updateTrafficAreaAnalyticsDisplay({ clear: true })",'updateTrafficAreaAnalyticsDisplay({ clear: true, add: ["tta-1"] }) // atomic swap','updateTrafficAreaAnalyticsDisplay({ remove: ["tta-0"] })','updateTrafficAreaAnalyticsDisplay({ addMatching: ["Amsterdam"] })'],examplePrompts:["Show the analytics on the map","Visualize the congestion","Only show areas with congestion above 50%","Normalize height to the current data range","Switch to heatmap view","Also show the Amsterdam analytics","Hide the Rotterdam analytics","Replace what is on the map with this analytics"],relatedTools:["getTrafficAreaAnalytics","queryTrafficAnalytics","toggleTilesTrafficFlow","clearMap","analyseData","processData"],dependsOn:["getTrafficAreaAnalytics"]},updateWaypointsDisplay:{description:"Show or hide the staged origin/stop/destination markers held in `state.routing.planningSlots` (the slots being built up for the next `setRoute` — NOT the waypoints of a calculated route entry). No route line is drawn; for that use `updateRoutesDisplay`. Requires at least one routes entry to host the module.",classificationPrompt:"Show or hide the STAGED origin/stop/destination markers (`state.routing.planningSlots` — what is queued for the NEXT `setRoute`). No route line is drawn; for that use `updateRoutesDisplay`. Single-collection toggle: `visible: true` (default) renders the staged set, `visible: false` clears it.",inputSchema:Vi,outputSchema:Wi,execute:async(e,t)=>{const r=e.visible??!0;try{const e=[...t.routing.shownEntryIds][0],s=t.routing.entries.at(-1),o=e??s?.id;if(!o)return{error:"No route history yet — calculate a route via setRoute first."};const n=await t.routing.getEntryRoutingModule(o);if(!r)return await n.clearWaypoints(),{success:!0,visible:!1,count:0};const i=t.routing.planningSlots;return await n.showWaypoints(i),{success:!0,visible:!0,count:i.length}}catch(s){return{error:`Failed to update waypoints display: ${s instanceof Error?s.message:String(s)}`}}},tags:["waypoint","route"],examples:["updateWaypointsDisplay()","updateWaypointsDisplay({ visible: true })","updateWaypointsDisplay({ visible: false })"],examplePrompts:["Show the staged waypoints on the map","Mark my pending route stops","Hide the staged waypoints"],relatedTools:["setRoute","updateRoutesDisplay","getCurrentWaypoints","clearMap"],dependsOn:["setRoute","locatePlace"]},getShownTileIncidents:{description:'List the traffic incidents currently rendered by the TomTom traffic-tile overlay (the rasterised live tiles, not the GeoJSON `getTrafficIncidents` service). Reads `state.trafficTiles.getTrafficIncidentsModule().getShown()` — so it only reflects what the tile layer is showing in the current viewport. For full TrafficIncident objects (with start/end times, road numbers, lanes, …) call `getTrafficIncidents` instead; for "incidents along this route" read `route.properties.sections.traffic[]` via `analyseData`.',classificationPrompt:'Read traffic incidents currently rendered by the TomTom TRAFFIC-TILE OVERLAY (the rasterised live tiles toggled via `toggleTilesTrafficIncidents`). DO NOT confuse with `getTrafficIncidents` — that\'s the GeoJSON service that returns full structured incidents with start/end times, road numbers, lanes, etc. Pick this ONLY for "what\'s on the tile overlay right now"; for richer per-incident data use `getTrafficIncidents`, and for "incidents along the route" use `analyseData` over routes (`route.properties.sections.traffic[]`).',inputSchema:ui,outputSchema:di,execute:async(e,t)=>{try{const e=(await t.trafficTiles.getTrafficIncidentsModule()).getShown();return 0===e.trafficIncidents.length?{error:"No traffic incidents currently rendered by the traffic-tile overlay."}:{count:e.trafficIncidents.length,trafficIncidents:e.trafficIncidents.map(e=>({id:e.properties.id,category:e.properties.category,magnitudeOfDelay:e.properties.magnitudeOfDelay,description:e.properties.description,delayInSeconds:e.properties.delayInSeconds,roadCategory:e.properties.roadCategory,averageSpeedKmph:e.properties.averageSpeedKmph}))}}catch(r){return{error:`Failed to read traffic-tile overlay incidents: ${r instanceof Error?r.message:String(r)}`}}},tags:["traffic","location","map style"],examples:["getShownTileIncidents()"],examplePrompts:["What incidents does the traffic tile overlay show right now?","What's rendered on the traffic incidents layer?"],relatedTools:["toggleTilesTrafficIncidents","getTrafficIncidents","flyTo","getViewport"],dependsOn:["toggleTilesTrafficIncidents"]},clearMap:{description:"Hide everything a chosen set of state slices is rendering on the map (places, routing, ranges, customGeometries, byod, trafficAreaAnalytics, trafficIncidents). Map-only — slice `entries` history is untouched. Use to clean up before showing new results.",classificationPrompt:"Hide what the chosen state slices are rendering on the map. `layers` are SLICE KEYS: `places`, `routing`, `ranges`, `customGeometries`, `byod`, `trafficAreaAnalytics`, `trafficIncidents`. Omit to clear every slice. Map-only — slice history is kept.",inputSchema:ci,outputSchema:li,execute:async(e,t)=>{const{layers:r}=e,s=r&&r.length>0?r:Kt;try{for(const e of s)await t[e].clearShown();return{success:!0,cleared:[...s]}}catch(o){return{error:`Failed to clear map: ${o instanceof Error?o.message:String(o)}`}}},tags:["place","route","map style"],examples:["clearMap()",'clearMap({ layers: ["places"] })','clearMap({ layers: ["places", "routing"] })','clearMap({ layers: ["trafficAreaAnalytics", "trafficIncidents"] })','clearMap({ layers: ["byod"] })'],examplePrompts:["Clear the map","Remove all markers","Start fresh","Hide the analytics overlay"],relatedTools:["updatePlacesDisplay","updateRoutesDisplay","updateTrafficAreaAnalyticsDisplay"]},flyTo:{description:"Move the map camera to a bounding box or a specific position. Use with boundingBox to frame a region or set of results. Use with position to center on a point.",classificationPrompt:"Move the camera to a bounding box or specific position; use after locating places or computing bounds.",inputSchema:kr,outputSchema:Cr,execute:async(e,t)=>{const{where:r}=e;try{return"boundingBox"in r?t.baseMap.mapLibreMap.fitBounds(r.boundingBox,{padding:r.padding??50}):t.baseMap.mapLibreMap.flyTo({center:r.position,zoom:r.zoom??14}),{success:!0}}catch(s){return{error:`flyTo failed: ${s instanceof Error?s.message:String(s)}`}}},tags:["location","map style"],examples:["flyTo({ where: { boundingBox: [4.8, 52.3, 5.0, 52.5] } })","flyTo({ where: { boundingBox: [4.8, 52.3, 5.0, 52.5], padding: 100 } })","flyTo({ where: { position: [4.9, 52.4], zoom: 14 } })"],examplePrompts:["Go to Berlin","Center the map on Rome","Fly to these coordinates"],relatedTools:["getViewport","locatePlace","updatePlacesDisplay","updateRoutesDisplay","calculateBBox"]},getViewport:{description:'Get the current map viewport: center coordinates [longitude, latitude], zoom level, and bounding box [minLng, minLat, maxLng, maxLat]. Use for queries referencing the map view: "in this area", "what I can see", "near the map center". Does NOT return the user\'s physical location — use getCurrentLocation for "near me" queries. Reads map state. Does not call any service.',classificationPrompt:'Return the map center, zoom, and bounding box; use for area-relative queries like "in this area" or "near the map center".',inputSchema:Fr,outputSchema:Lr,execute:async(e,t)=>{try{const e=t.baseMap.mapLibreMap;return{center:e.getCenter().toArray(),zoom:e.getZoom(),bbox:t.baseMap.ttMap.getBBox(),pitch:e.getPitch(),bearing:e.getBearing()}}catch(r){return{error:`Failed to get viewport: ${r instanceof Error?r.message:String(r)}`}}},tags:["location","map style"],examples:["getViewport()"],examplePrompts:["Where is the map centered?","What's the current map area?","What are the current map bounds?"],relatedTools:["locatePlace","flyTo","discoverPlaces","reverseGeocode","getTrafficIncidents"]},recallGeometries:{description:"List or inspect every polygon source in session state — place footprints (cached on places entries via `withGeometries: true`), reachable-area isochrones, and custom-geometries entries produced by `processData`. ALWAYS call this before passing ids to `processData` or `analyseData` (`geometriesEntryIDs`); never guess. No args = index across all three slices; pass `id` to retrieve features and provenance for one entry. No service call.",classificationPrompt:"List/inspect every polygon source in state across places footprints, ranges isochrones, and custom-geometries entries. Returns tagged ids (`{ kind, id }`) the geometry tools accept.",inputSchema:bn,outputSchema:Sn,execute:async(e,t)=>{const{id:r}=e;return r?xn(r,t):{entries:Mn(t)}},tags:["geometry","boundary"],examples:["recallGeometries()",'recallGeometries({ id: { kind: "places", id: "ev-stations-oost" } })','recallGeometries({ id: { kind: "customGeometries", id: "bakery-candidates-gap-oost" } })'],examplePrompts:["What polygons are in state right now?","Which IDs can I pass to processData (geometriesEntryIDs)?","Recall the custom zone we just produced","What does the bakery-candidates entry contain?"],relatedTools:["processData","analyseData","recallPlaces","recallRanges"]},recallPlaces:e=>((e,t)=>({...t,description:"List / inspect places entries already in session state (from discoverPlaces / locatePlace / processData) — the same entries every `placesEntryIDs` parameter accepts. ALWAYS call this before referencing past places; never guess IDs. No args = index (id, label, timestamp, featureCount, analyses) + `entryMode`; pass `id` for full Place coordinates. No service call.",inputSchema:Pn,outputSchema:An(e),execute:Cn(e)}))(e.featureFlags??{},kn),recallByod:{description:"List or inspect BYOD (bring-your-own-data) GeoJSON layers in session state. These come from either programmatic seeding by the embedding app OR from `addByodLayer` calls. Call this BEFORE passing ids to `byodEntryIDs` on `analyseData` / `processData` / `updateByodDisplay` — never guess. No args → index (id, label, featureCount, geometryTypes, source, shown); pass `id` → full FeatureCollection.",classificationPrompt:"List/inspect BYOD (bring-your-own-data) layers in session state. Returns ids the model can pass to `byodEntryIDs` (analyseData / processData) or to `updateByodDisplay` for visibility control.",inputSchema:fn,outputSchema:yn,execute:async(e,t)=>{if(!e.id)return{entries:t.byod.entries.map(e=>({id:e.id,label:e.label,timestamp:e.timestamp,featureCount:e.data.features.length,geometryTypes:gn(e.data),source:e.source,shown:!!e._shown})),entryMode:t.byod.entryMode};const r=t.byod.findById(e.id);return r?{id:r.id,label:r.label,timestamp:r.timestamp,featureCount:r.data.features.length,geometryTypes:gn(r.data),source:r.source,shown:!!r._shown,features:r.data}:{error:`No BYOD entry with id "${e.id}". Call recallByod with no args to list available IDs.`}},tags:["recall","byod","state"],examples:["recallByod()",'recallByod({ id: "byod-0" })'],examplePrompts:["What customer-uploaded layers do we have?","List BYOD entries","Show details of the sales-territories BYOD layer"],relatedTools:["addByodLayer","updateByodDisplay","analyseData","processData"]},addByodLayer:{description:"Register a customer-owned GeoJSON layer as a new BYOD entry. Accepts either a URL (fetched once) or inline GeoJSON. The slice renders the entry with sensible defaults based on geometry type (Point → circle, Line → line, Polygon → fill). The new entry id is usable with `byodEntryIDs` on `analyseData` / `processData` and with `updateByodDisplay` to toggle visibility. For very large datasets, prefer programmatic seeding from the embedding app via `state.byod.addEntry(...)`.",classificationPrompt:"Add a customer-owned GeoJSON layer (URL fetch or inline) as a new BYOD entry. Use when the user wants to import / load / overlay a customer-supplied dataset. Returns the new entry id (use with `byodEntryIDs` on analyseData / processData).",inputSchema:Ao,outputSchema:To,execute:async(e,t)=>{let r,s;if(e.url){const t=(e=>{let t;try{t=new URL(e)}catch{return{error:`Invalid URL: "${e}".`}}return ko.has(t.protocol)?{url:t}:{error:`URL protocol "${t.protocol}" is not allowed. Only http: and https: are supported.`}})(e.url);if("error"in t)return t;const o=await Oo(t.url);if("error"in o)return o;r=o.value,s={kind:"url",url:e.url}}else r=e.data,s={kind:"inline"};if(!(e=>{if(!e||"object"!=typeof e)return!1;const t=e;return"FeatureCollection"===t.type&&Array.isArray(t.features)})(r))return{error:'Payload is not a GeoJSON FeatureCollection (`{ type: "FeatureCollection", features: [...] }`).'};const o=await t.byod.addEntry(r,e.label,{source:s,...e.entryId&&{explicitId:e.entryId}});let n=!1;if(e.show)try{await t.byod.showEntry(o),n=!0}catch(i){return{error:`addByodLayer added entry "${o}" but rendering failed: ${i instanceof Error?i.message:String(i)}`}}return{byodEntryId:o,label:e.label,featureCount:r.features.length,geometryTypes:Ro(r),source:s,shown:n}},tags:["byod","state","import"],examples:['addByodLayer({ label: "Sales territories", url: "https://example.com/territories.geojson", show: true })','addByodLayer({ label: "Inline pins", data: { type: "FeatureCollection", features: [/* … */] } })'],examplePrompts:["Load my territories layer from https://...","Import this GeoJSON as a BYOD layer","Add a customer-pins layer from this URL"],relatedTools:["recallByod","updateByodDisplay","analyseData","processData"]},updateByodDisplay:{description:'Toggle visibility (or drop) for one or more BYOD entries. Use after `addByodLayer` or after seeing entries via `recallByod`. Three actions: `show` renders listed entries, `hide` removes them from the map but keeps them in state, `remove` hides AND drops them from history. Pair `action: "hide"` with `clearAll: true` to hide every shown BYOD entry in one call. Pair `action: "show"` with `hideOthers: true` to swap to a single set of entries (replaces what\'s already on the map).',classificationPrompt:"Toggle visibility (or drop) of BYOD entries already in session state. Use after `addByodLayer` or after `recallByod` exposes the ids. Three actions: show / hide / remove. Pair with `clearAll` or `hideOthers` for bulk swaps.",inputSchema:Di,outputSchema:_i,execute:async(e,t)=>{const r=t.byod,{action:s,clearAll:o,hideOthers:n}=e,i=e.entryIds??[],a=((e,t)=>{for(const r of t)if(!e.findById(r))return{error:`No BYOD entry with id "${r}". Call recallByod to list available IDs.`}})(r,i);if(a)return a;let l;return l="show"===s?await(async(e,t,r)=>{const s=[];if(r){const r=new Set(t);for(const t of e.shownEntryIds)r.has(t)||(await e.hideEntry(t),s.push(t))}for(const o of t)await e.showEntry(o),s.push(o);return s})(r,i,!!n):"hide"===s?await(async(e,t,r)=>{const s=[],o=r?e.shownEntryIds:t;for(const n of o)await e.hideEntry(n),s.push(n);return s})(r,i,!!o):await(async(e,t)=>{const r=[];for(const s of t)await e.removeEntry(s),r.push(s);return r})(r,i),{action:s,affectedIds:l,shown:[...r.shownEntryIds]}},tags:["byod","display","visibility"],examples:['updateByodDisplay({ entryIds: ["byod-0"], action: "show" })','updateByodDisplay({ entryIds: ["byod-0", "byod-1"], action: "show", hideOthers: true })','updateByodDisplay({ entryIds: ["byod-0"], action: "hide" })','updateByodDisplay({ action: "hide", clearAll: true })','updateByodDisplay({ entryIds: ["byod-0"], action: "remove" })'],examplePrompts:["Hide the customer pins layer","Show only the territories BYOD layer","Hide all BYOD layers","Drop the imported file from the session"],relatedTools:["recallByod","addByodLayer"],dependsOn:["addByodLayer"]},recallRoutes:{description:"List calculated routes from session history, or retrieve one by `id` (route details + params). IDs are stable and accepted by updateRoutesDisplay. Never guess IDs — list first. No service call.",classificationPrompt:"Return previously calculated routes from this session.",inputSchema:Bn,outputSchema:Wn,execute:async(e,t)=>{const{id:r}=e,s=t.routing.entryMode,o=t.routing.shownEntryIds;if(!r)return{entries:[...t.routing.entries].reverse().map(({id:e,label:t,timestamp:r})=>({id:e,label:t,timestamp:r,shown:o.has(e)})),entryMode:s};const n=t.routing.entries.find(e=>e.id===r);if(!n)return{error:`No entry found with id "${r}"`};const i=us(n.data),a=n.waypoints.map(e=>hs(e)).filter(e=>null!==e).map(e=>({position:e.position??[0,0]}));return{id:n.id,label:n.label,timestamp:n.timestamp,routes:i,waypoints:a,params:n.params,entryMode:s}},tags:["route"],examples:["recallRoutes()",'recallRoutes({ id: "routes-0" })'],examplePrompts:["What routes have I calculated?","Show the first route we planned","What was the route to Schiphol?"],relatedTools:["setRoute","updateRoutesDisplay","getCurrentWaypoints"],dependsOn:["setRoute"]},getCurrentWaypoints:{description:"Read STAGED waypoint slots queued for the NEXT setRoute call (origin / stops / destination not yet committed to a route). Distinct from `recallRoutes({ id })`, which returns the waypoints of an existing calculated route entry. `slotIndex` for one slot; otherwise paged (default 5), empty slots omitted unless `includeEmptySlots: true`. No service call.",classificationPrompt:"STAGED waypoint slots only — the origin/stops/destination being built up for the NEXT `setRoute` call (read off `state.routing.planningSlots`, NOT off any calculated route entry). For waypoints of an EXISTING calculated route, use `recallRoutes({ id })` — its detail mode returns the entry's own waypoints.",inputSchema:Vo,outputSchema:zo,execute:async(e,t)=>{const{slotIndex:r,includeEmptySlots:s=!1,offset:o=0,limit:n=5}=e,i=t.routing.planningSlots.map((e,t)=>{const r=hs(e);return r?{...r,slotIndex:t}:{slotIndex:t,isFilled:!1}}),a=s?i:i.filter(e=>e.isFilled),l=void 0!==r,c=l?a.filter(e=>e.slotIndex===r):a.slice(o,o+n),d=o+c.length;return{totalSlots:i.length,filledSlots:i.filter(e=>e.isFilled).length,matchedCount:l?c.length:a.length,returnedCount:c.length,offset:l?0:o,hasMore:!l&&d<a.length,...!l&&d<a.length&&{nextOffset:d},waypoints:c}},tags:["waypoint","route"],examples:["getCurrentWaypoints()","getCurrentWaypoints({ slotIndex: 1 })","getCurrentWaypoints({ includeEmptySlots: true })","getCurrentWaypoints({ offset: 2, limit: 4 })"],examplePrompts:["What's currently staged for the next route?","What's my pending destination before I hit go?","Show the stops I have queued up"],relatedTools:["locatePlace","setRoute","updateWaypointsDisplay","recallRoutes"]},getStandardMapStyles:{description:"Get the list of available standard map style IDs. If you need more fine styling control, refer to getMapStyleLayers.",classificationPrompt:"List available standard map style IDs for use with setMapStandardStyle.",inputSchema:hi,outputSchema:pi,execute:async(e,t)=>{try{return{styles:g}}catch(r){return{error:`Failed to get standard map styles: ${r instanceof Error?r.message:String(r)}`}}},tags:["map style"],examples:["getStandardMapStyles()"],examplePrompts:["What map styles are available?","List the map themes"],relatedTools:["setMapStandardStyle"]},setMapStandardStyle:{description:"Set the map style to a standard preset (light, dark, mono, driving, satellite, etc). For detailed or incremental style tweaking use getMapStyleLayers, setLayoutProperties, and setPaintProperties.",classificationPrompt:"Switch the map to a standard style preset (light, dark, satellite, driving, etc.).",inputSchema:gi,outputSchema:fi,execute:async(e,t)=>{const{style:r}=e;try{return t.baseMap.ttMap.setStyle(r),{success:!0,style:r}}catch(s){return{error:`Failed to set map style: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setMapStandardStyle("standardDark")','setMapStandardStyle("satellite")'],examplePrompts:["Switch to dark mode","Use the satellite view","Change to the light theme"],relatedTools:["getStandardMapStyles","setLanguage","toggleTilesBaseMapLayerGroups","getMapStyleLayers"]},setLanguage:{description:"Set the language for map labels and all subsequent service API responses (search, geocoding, routing instructions).",classificationPrompt:"Change the language for map labels and all subsequent service API responses.",inputSchema:yi,outputSchema:mi,execute:async(e,t)=>{const{language:r}=e;try{return R.instance.put({language:r}),t.baseMap.ttMap.setLanguage(r),{success:!0,language:r}}catch(s){return{error:`Failed to set language: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setLanguage("fr-FR")','setLanguage("de-DE")'],examplePrompts:["Show map labels in French","Switch the map to German","Use Spanish for place names"],relatedTools:["setMapStandardStyle"]},toggleTilesBaseMapLayerGroups:{description:"Show / hide groups of vector-tile base-map style layers (water, buildings3D, roadLines, placeLabels, capitalLabels, …). Operates on the underlying MapLibre style — orthogonal to anything rendered through PlacesModule / RoutingModule / CustomGeoJSONModule (those layers are unaffected).",classificationPrompt:"Show / hide vector-tile base-map layer groups (buildings3D, roadLabels, water, placeLabels, …). Style-layer only — does NOT touch places / routes / custom-geometries rendered by the agent.",inputSchema:wi,outputSchema:bi,execute:async(e,t)=>{const{visible:r,layerGroups:s}=e;try{return(await t.baseMap.getBaseMapModule()).setVisible(r,{layerGroups:{mode:"include",names:s}}),{success:!0,visible:r,layerGroups:s}}catch(o){return{error:`Failed to toggle tile base-map layer groups: ${o instanceof Error?o.message:String(o)}`}}},tags:["map style"],examples:['toggleTilesBaseMapLayerGroups({ visible: false, layerGroups: ["buildings3D"] })','toggleTilesBaseMapLayerGroups({ visible: true, layerGroups: ["roadLabels"] })'],examplePrompts:["Hide 3D buildings","Show road labels","Toggle the building layer"],relatedTools:["toggleTilesPOIs","getMapStyleLayers","setMapStandardStyle"]},toggleTilesPOIs:{description:"Show/hide the built-in vector-tile POI icons baked into the base map style (restaurants, hotels, gas stations, …). NOT the same as places shown via `discoverPlaces` / `updatePlacesDisplay` — those are session-state pins on the PlacesModule, untouched by this tool. `allMapPOIsVisible` toggles overall visibility; `filterCategories` restricts which categories show; `reset: true` restores defaults.",classificationPrompt:"Show / hide vector-tile POI icons baked into the base-map style (restaurants/hotels/gas stations etc. shipped with the map). NOT places shown via `discoverPlaces` / `updatePlacesDisplay` (those are PlacesModule pins, untouched).",inputSchema:Ii,outputSchema:vi,execute:async(e,t)=>{try{const r=await t.mapPOIs.getPOIsModule();if(e.reset)return r.resetConfig(),{success:!0,reset:!0};const{allMapPOIsVisible:s,filterCategories:o}=e;return void 0!==s&&r.setVisible(s),o&&r.filterCategories({show:o.show,values:o.values}),{success:!0,allMapPOIsVisible:s,filterCategories:o}}catch(r){return{error:`Failed to toggle tile POIs: ${r instanceof Error?r.message:String(r)}`}}},tags:["place","map style"],examples:["toggleTilesPOIs({ allMapPOIsVisible: false })",'toggleTilesPOIs({ allMapPOIsVisible: true, filterCategories: { show: "only", values: ["RESTAURANT", "CAFE"] } })','toggleTilesPOIs({ filterCategories: { show: "all_except", values: ["PARKING_GARAGE"] } })',"toggleTilesPOIs({ reset: true })"],examplePrompts:["Hide all map POI icons","Show only restaurant icons on the map style","Turn off parking icons in the base map"],relatedTools:["toggleTilesBaseMapLayerGroups","discoverPlaces","setMapStandardStyle"]},toggleTilesTrafficFlow:{description:"Toggle the vector-tile traffic-flow overlay (live coloured road segments). NOT the same as `updateTrafficAreaAnalyticsDisplay` (historical analytics) or `route.properties.sections.traffic[]` (per-route incidents). Just a base-map style layer on/off.",classificationPrompt:"Toggle the vector-tile traffic-flow overlay (live coloured road segments). NOT the same as `updateTrafficAreaAnalyticsDisplay` (historical) or route-section traffic (read via `analyseData`).",inputSchema:Si,outputSchema:Ei,execute:async(e,t)=>{const{visible:r}=e;try{return(await t.trafficTiles.getTrafficFlowModule()).setVisible(r),{success:!0,trafficFlowVisible:r}}catch(s){return{error:`Failed to toggle tile traffic flow: ${s instanceof Error?s.message:String(s)}`}}},tags:["traffic","map style"],examples:["toggleTilesTrafficFlow({ visible: true })","toggleTilesTrafficFlow({ visible: false })"],examplePrompts:["Show traffic flow","Turn off traffic colors","Enable the congestion overlay"],relatedTools:["toggleTilesTrafficIncidents","setRoute","getTrafficIncidents"]},toggleTilesTrafficIncidents:{description:"Toggle the vector-tile traffic-incidents overlay (live jam/accident/closure markers). NOT the same as `getTrafficIncidents` (GeoJSON service → structured entries) or `analyseData` (sandbox over fetched entries). Just a base-map style layer on/off. Use `getShownTileIncidents` to read what this overlay is currently rendering.",classificationPrompt:"Toggle the vector-tile traffic-incidents overlay (live jam/accident/closure markers). NOT the GeoJSON `getTrafficIncidents` service and NOT `analyseData` (which aggregates fetched entries). Use `getShownTileIncidents` to read what this overlay currently renders.",inputSchema:Mi,outputSchema:xi,execute:async(e,t)=>{const{visible:r}=e;try{return(await t.trafficTiles.getTrafficIncidentsModule()).setVisible(r),{success:!0,trafficIncidentsVisible:r}}catch(s){return{error:`Failed to toggle tile traffic incidents: ${s instanceof Error?s.message:String(s)}`}}},tags:["traffic","map style"],examples:["toggleTilesTrafficIncidents({ visible: true })","toggleTilesTrafficIncidents({ visible: false })"],examplePrompts:["Show traffic incidents overlay","Hide accident icons","Enable incident markers on the map"],relatedTools:["toggleTilesTrafficFlow","getTrafficIncidents","getShownTileIncidents","setRoute"]},getMapStyleLayers:{description:"Get map layer IDs with their paint/layout properties from MapLibre style for advanced runtime manipulation. Use before setLayoutProperty/setPaintProperty for custom styling.",classificationPrompt:"List MapLibre layer IDs with their paint/layout properties for custom styling.",inputSchema:Rr,outputSchema:Or,execute:async(e,t)=>{const{layerIdQuery:r,include:s}=e;try{let e=t.baseMap.mapLibreMap.getStyle().layers??[];return r&&(e=e.filter(e=>e.id.toLowerCase().includes(r.toLowerCase())),0===e.length)?{error:`No layers found matching query: "${r}"`}:{layers:e.map(e=>({id:e.id,...s.includes("paint")&&{paint:e.paint},...s.includes("layout")&&{layout:e.layout}}))}}catch(o){return{error:`Failed to get style: ${o instanceof Error?o.message:String(o)}`}}},tags:["map style"],examples:["getMapStyleLayers()",'getMapStyleLayers("road")','getMapStyleLayers("water")'],examplePrompts:["What are the paint properties of the water layer?","Show me the road layer layout"],relatedTools:["setLayoutProperties","setPaintProperties"]},setLayoutProperties:{description:"Set MapLibre layout properties (visibility, text-field, icon-size) on named layers — use getMapStyleLayers for valid IDs. For colors/widths use setPaintProperties.",classificationPrompt:"Set MapLibre layout properties (visibility, text-field, icon-size) on named layers.",inputSchema:Br,outputSchema:jr,execute:async(e,t)=>{const{changes:r}=e;try{return{results:r.map(({layerId:e,propertyName:r,value:s})=>{try{return t.baseMap.mapLibreMap.setLayoutProperty(e,r,s),{layerId:e,propertyName:r,value:s,success:!0}}catch(o){return{layerId:e,propertyName:r,error:`Failed to set layout property: ${o instanceof Error?o.message:String(o)}`}}})}}catch(s){return{error:`Failed to set layout properties: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setLayoutProperties([{ layerId: "road-label", propertyName: "visibility", value: "none" }])','setLayoutProperties([{ layerId: "poi-label", propertyName: "text-field", value: "{name}" }, { layerId: "poi-label", propertyName: "icon-size", value: 1.2 }])'],examplePrompts:["Hide the road-label layer","Change the text size on building labels","Make the city labels bigger"],relatedTools:["getMapStyleLayers","setPaintProperties"],dependsOn:["getMapStyleLayers"]},setPaintProperties:{description:"Set MapLibre paint properties (colors, widths, opacity) on named layers — use getMapStyleLayers for valid IDs. For visibility/structure use setLayoutProperties; for route styling prefer `updateRoutesDisplay({ mainColor })`.",classificationPrompt:"Set MapLibre paint properties (colors, widths, opacity) on named layers.",inputSchema:Gr,outputSchema:$r,execute:async(e,t)=>{const{changes:r}=e;try{return{results:r.map(({layerId:e,propertyName:r,value:s})=>{try{return t.baseMap.mapLibreMap.setPaintProperty(e,r,s),{layerId:e,propertyName:r,value:s,success:!0}}catch(o){return{layerId:e,propertyName:r,error:`Failed to set paint property: ${o instanceof Error?o.message:String(o)}`}}})}}catch(s){return{error:`Failed to set paint properties: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setPaintProperties([{ layerId: "water", propertyName: "fill-color", value: "#0000ff" }])','setPaintProperties([{ layerId: "road", propertyName: "line-color", value: "#ff0000" }, { layerId: "road", propertyName: "line-width", value: 3 }])'],examplePrompts:["Make the water bright blue","Change the road color to red","Make buildings semi-transparent","Make the country borders pink"],relatedTools:["getMapStyleLayers","setLayoutProperties","updateRoutesDisplay"],dependsOn:["getMapStyleLayers"]},setPitchBearing:{description:"Tilt and/or rotate the map camera. Pitch 0 = top-down view, 85 = near-horizontal (3D effect). Bearing 0 = north up, 90 = east up. Provide at least one of pitch or bearing.",classificationPrompt:"Tilt (pitch) and/or rotate (bearing) the map camera for a 3D perspective.",inputSchema:Wr,outputSchema:Ur,execute:async(e,t)=>{const{pitch:r,bearing:s}=e;if(void 0===r&&void 0===s)return{error:"Provide at least one of pitch or bearing"};try{return t.baseMap.mapLibreMap.easeTo({...void 0!==r&&{pitch:r},...void 0!==s&&{bearing:s}}),{success:!0}}catch(o){return{error:`Failed to set pitch/bearing: ${o instanceof Error?o.message:String(o)}`}}},tags:["map style"],examples:["setPitchBearing({ pitch: 45 })","setPitchBearing({ bearing: 90 })","setPitchBearing({ pitch: 60, bearing: -45 })"],examplePrompts:["Tilt the map to 45 degrees","Rotate the map to face north","Set a 3D perspective view"],relatedTools:["getViewport","locatePlace","flyTo"]},zoomInOrOut:{description:"Zoom the map in or out by a given amount relative to the current zoom level. Positive delta zooms in (closer), negative delta zooms out (further away). Returns the resulting zoom level.",classificationPrompt:"Adjust the map zoom level by a delta; positive zooms in, negative zooms out.",inputSchema:zr,outputSchema:Vr,execute:async(e,t)=>{const{delta:r}=e;try{const e=t.baseMap.mapLibreMap,s=e.getZoom()+r;return e.zoomTo(s),{zoom:s}}catch(s){return{error:`Zoom failed: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:["zoomInOrOut({ delta: 2 })","zoomInOrOut({ delta: -1 })"],examplePrompts:["Zoom in","Zoom out 2 levels","Get a wider view"],relatedTools:["getViewport","flyTo"]},executeMaplibreCode:{description:"Run arbitrary MapLibre JS against the live `map` for anything no other tool covers — custom sources/layers, camera, style mutations, animations, event sequences. Full JS (loops, helpers, top-level `await`). Returns added/removed/updated sources & layers.",classificationPrompt:"Execute any MapLibre JS code against the live map for maximum flexibility — use when no other tool covers the required operation.",inputSchema:Pr,outputSchema:Dr,execute:async(e,t)=>{const{code:r}=e;try{const e=t.baseMap.mapLibreMap,s=_r(e),o=Tr(e),n=Object.getPrototypeOf(async()=>{}).constructor,i=await new n("map",r)(e),a=Ar(s,_r(e));return{success:!0,result:i??void 0,sources:a,layers:Ar(o,Tr(e))}}catch(s){return{error:Nt("Code",s)}}},tags:["map style","maplibre","layers","sources","animation"],examples:["executeMaplibreCode({ code: \"map.addSource('pts', { type: 'geojson', data: fc }); map.addLayer({ id: 'pts', type: 'circle', source: 'pts', paint: { 'circle-color': '#f00' } })\" })",'executeMaplibreCode({ code: "map.easeTo({ center: [4.9, 52.37], zoom: 14, duration: 2000 })" })',"executeMaplibreCode({ code: \"for (let i = 0; i <= 20; i++) { map.setPaintProperty('fill', 'fill-opacity', i / 20); await new Promise(r => setTimeout(r, 50)); }\" })","executeMaplibreCode({ code: \"map.setFog({ color: 'white', 'horizon-blend': 0.1 })\" })"],examplePrompts:["Add a GeoJSON layer to the map","Animate the map camera","Fade in a layer gradually","Set map fog or atmosphere","Add a raster tile overlay"],relatedTools:["getMapStyleLayers","setLayoutProperties","setPaintProperties","flyTo"],dependsOn:["getMapStyleLayers"]},calculateBBox:{description:"Compute a `[minLng, minLat, maxLng, maxLat]` bbox from GeoJSON (longitude before latitude).",classificationPrompt:"Compute a [minLng, minLat, maxLng, maxLat] bounding box from GeoJSON features or tool results.",inputSchema:er,execute:async(e,t)=>{const{geoJson:r}=e;try{const e=F(r);return e?{bbox:e,minLng:e[0],minLat:e[1],maxLng:e[2],maxLat:e[3]}:{error:"Could not calculate bounding box - input may be empty or invalid GeoJSON"}}catch(s){return{error:`Failed to calculate bbox: ${s instanceof Error?s.message:String(s)}`}}},tags:["utilities","location"],examples:["calculateBBox(place)","calculateBBox(routes)","calculateBBox([place1, place2])"],examplePrompts:["What's the bounding box of these places?","Calculate the extent of this route"],relatedTools:["flyTo","getViewport","updatePlacesDisplay","updateRoutesDisplay"]},formatDistance:{description:'Format a distance in meters into a human-readable string (e.g. "2.5 km", "1.5 mi"). Use after getSectionProgress, recallRoutes, or any tool that returns distances in meters.',classificationPrompt:'Format meters into a human-readable string (e.g. "2.5 km"); use after tools that return distances.',inputSchema:tr,execute:async(e,t)=>{const{meters:r,unitType:s}=e;try{return{formatted:j(r,s?{type:s}:void 0),input:{meters:r,unitType:s??"metric"}}}catch(o){return{error:`Failed to format distance: ${o instanceof Error?o.message:String(o)}`}}},tags:["utilities","route"],examples:["formatDistance(2500)",'formatDistance(5000, "imperial_us")'],examplePrompts:["Format 5000 meters as a distance","Convert meters to a readable string"],relatedTools:["formatDuration","setRoute"]},formatDuration:{description:'Format a duration in seconds into a human-readable time string (e.g. "1 h 30 min"). Use after getSectionProgress, recallRoutes, or any tool that returns durations in seconds.',classificationPrompt:'Format seconds into a human-readable string (e.g. "1 h 30 min"); use after tools that return durations.',inputSchema:rr,execute:async(e,t)=>{const{seconds:r}=e;try{const e=B(r);return void 0===e?{formatted:void 0,message:"< 1 min",input:{seconds:r}}:{formatted:e,input:{seconds:r}}}catch(s){return{error:`Failed to format duration: ${s instanceof Error?s.message:String(s)}`}}},tags:["utilities","route"],examples:["formatDuration(3600)","formatDuration(9000)"],examplePrompts:["Format 3600 seconds as a duration","Convert seconds to hours and minutes"],relatedTools:["formatDistance","setRoute"]},getCurrentLocation:{description:'Browser geolocation — the user\'s physical position [lng, lat] for "near me" / "where I am" queries (may prompt for permission). For map-based reference use getViewport.',classificationPrompt:'Get the user\'s physical GPS location from the browser; use for "near me" queries, not map center.',inputSchema:pr,outputSchema:hr,execute:async(e,t)=>{if(void 0===globalThis.window||void 0===globalThis.navigator)return{error:"Current location is only available in a browser environment"};if(!("geolocation"in globalThis.navigator))return{error:"Geolocation is not supported by this browser"};try{const e=await new Promise((e,t)=>{globalThis.navigator.geolocation.getCurrentPosition(e,t,{enableHighAccuracy:!0,timeout:15e3,maximumAge:0})});return{position:[e.coords.longitude,e.coords.latitude],accuracy:e.coords.accuracy,timestamp:e.timestamp}}catch(r){const e=(e=>{if("object"!=typeof e||null===e||!("code"in e))return;const t=e.code;return 1===t?"Location permission denied by the user":2===t?"Current location is unavailable":3===t?"Timed out while trying to get current location":void 0})(r);return e?{error:e}:{error:`Failed to get current location: ${r instanceof Error?r.message:String(r)}`}}},tags:["utilities","location"],examples:["getCurrentLocation()"],examplePrompts:["Where am I?","Get my GPS location","Use my current position"],relatedTools:["discoverPlaces","reverseGeocode","locatePlace"]},help:{description:wr,classificationPrompt:"Return available capabilities in summary or searchable detail mode; call when the user asks what you can do.",inputSchema:br,outputSchema:gr,execute:async()=>{throw new Error("help tool requires setupTools() — it needs resolved metadata from all tools.")},tags:["utilities"],examples:['help({ mode: "summary" })','help({ mode: "detail", query: "traffic" })','help({ mode: "detail", tag: "route" })','help({ mode: "detail", query: "route", tag: "utilities" })'],examplePrompts:["What can you do?","Show me available tools","How do I search for places?"]},findReachableAreas:{description:"Compute reachable-area polygons (isochrone/isodistance) from one or more origins under shared time/distance budgets. Multiple origins → compared in one call (sequential to respect QPS); multiple budgets → nested ranges. Stored in state (recallRanges); `showOnMap: true` renders. Per-origin failures land under `skipped` instead of aborting the whole call.",classificationPrompt:"Calculate isochrone/isodistance polygons showing how far you can travel from one or more origins.",inputSchema:Xs,outputSchema:Zs,execute:async(e,t)=>{const{origins:r,budgets:s,theme:o,showOnMap:n,showOriginPin:i=!0,hidePreviousEntries:a}=e;try{const e=[...s].sort((e,t)=>t.value-e.value),l=[],c=[];for(const s of r){const r="query"in s?s.query:"position"in s?`${s.position.lat}, ${s.position.lng}`:"placeId"in s?s.placeId:"unnamed origin",o=await ws(s,t);if(!o){c.push({origin:r,reason:"origin not found"});continue}const n=await Qs(e,o.position);n?l.push({origin:o.query?{query:o.query,position:o.position}:{position:o.position},budgets:e,polygon:n}):c.push({origin:o.name??r,reason:"no reachable area returned"})}if(0===l.length)return{status:"no_results"};const d=o??"outline",u=l.map(eo),p=await t.ranges.addEntry({label:as(e,u),ranges:l});return(n||i)&&await mt(t.ranges,[p],a),await(async(e,t,r,s,o,n)=>{if(o){const o=(e=>{const t=e.flatMap(e=>e.polygon?.features??[]);return 0===t.length?null:{type:"FeatureCollection",features:t}})(r);o&&(await(await e.ranges.getEntryGeometriesModule(t,s)).show(o),o.bbox&&e.baseMap.mapLibreMap.fitBounds(o.bbox,{padding:50}))}if(n){const s=await e.ranges.getEntryPlacesModule(t),o=r.map((e,r)=>({type:"Feature",id:`range-origin-${t}-${r}`,geometry:{type:"Point",coordinates:e.origin.position},properties:{type:"Point Address",address:{freeformAddress:eo(e)}}}));await s.show(o)}})(t,p,l,d,n,i),(n||i)&&await t.ranges.markEntryShown(p),{rangesId:p,ranges:l.map(t=>({originName:eo(t),budgets:e})),...c.length&&{skipped:c}}}catch(l){return{error:`Reachable area calculation failed: ${l instanceof Error?l.message:String(l)}`}}},tags:["reachable-range","isochrone","range","EV","coverage","multi-origin"],examples:['findReachableAreas({ origins: [{ query: "Amsterdam", queryAs: "place" }], budgets: [{ type: "timeMinutes", value: 30 }], showOnMap: true })','findReachableAreas({ origins: [{ query: "Amsterdam", queryAs: "place" }, { query: "Rotterdam", queryAs: "place" }], budgets: [{ type: "timeMinutes", value: 30 }], showOnMap: true })','findReachableAreas({ origins: [{ position: { lng: 4.9, lat: 52.3 } }], budgets: [{ type: "timeMinutes", value: 10 }, { type: "timeMinutes", value: 30 }], showOnMap: true, theme: "inverted" })','findReachableAreas({ origins: [{ query: "my location", queryAs: "place" }], budgets: [{ type: "distanceKM", value: 50 }], showOnMap: false })'],examplePrompts:["Show me where I can reach in 30 minutes from Amsterdam","Compare 30-minute reach from Amsterdam, Rotterdam and Utrecht","What area is within 50 km of Berlin?","Show 10, 20, 30 minute drive zones from here"],relatedTools:["recallRanges","locatePlace"]},recallRanges:{description:"List stored reachable-range results, or retrieve one by `id` (returns each origin and its budgets). Multi-origin entries surface every origin under `ranges`. Never guess IDs — list first. No service call.",classificationPrompt:"Retrieve stored reachable range results from session history.",inputSchema:On,outputSchema:jn,execute:async(e,t)=>{const{id:r}=e,s=t.ranges.entryMode;if(!r)return{entries:[...t.ranges.entries].reverse().map(({id:e,label:t,timestamp:r,ranges:s})=>({id:e,label:t,timestamp:r,originCount:s.length})),entryMode:s};const o=t.ranges.entries.find(e=>e.id===r);return o?{id:o.id,label:o.label,timestamp:o.timestamp,ranges:o.ranges.map(e=>({origin:{...e.origin,position:e.origin.position},budgets:e.budgets})),entryMode:s}:{error:`No entry found with id "${r}"`}},tags:["recall","reachable-range","state"],examples:["recallRanges()",'recallRanges({ id: "ranges-0" })'],examplePrompts:["What range did I calculate earlier?","Recall the isochrone from Amsterdam"],relatedTools:["findReachableAreas"]},recallState:{description:"Single-shot session snapshot: places / routes / ranges entries (id+label) + per-slice `entryMode` + active map style + POIs / traffic visualization configs. Read-only. Use as orientation before making decisions that depend on what is already loaded.",classificationPrompt:"Inventory the agent's session: list every places / routes / ranges entry by id+label, plus current map style and POIs/traffic visualization configs.",inputSchema:Yn,outputSchema:Hn,execute:async(e,t)=>{const r=e=>[...e].sort((e,t)=>t.timestamp-e.timestamp),s=r(t.places.entries).map(({id:e,label:t})=>({id:e,label:t})),o=r(t.routing.entries).map(({id:e,label:t})=>({id:e,label:t})),n=r(t.ranges.entries).map(({id:e,label:t})=>({id:e,label:t})),i="string"==typeof(a=t.baseMap.ttMap.getStyle())?a:a&&"object"==typeof a&&"id"in a?a.id:void 0;var a;const l=void 0===i?null:{id:i},c=t.mapPOIs.poisModule?.getConfig(),d=c?{..."boolean"==typeof c.visible&&{visible:c.visible},...c.filters?.categories?.show&&{filterMode:c.filters.categories.show},...c.filters?.categories?.values&&{categories:[...c.filters.categories.values]}}:null,u=t.trafficTiles.trafficFlowModule?.getConfig(),p=t.trafficTiles.trafficIncidentsModule?.getConfig(),h={flow:u?{..."boolean"==typeof u.visible&&{visible:u.visible}}:null,incidents:p?{..."boolean"==typeof p.visible&&{visible:p.visible}}:null};return{places:s,routes:o,ranges:n,entryModes:{places:t.places.entryMode,routes:t.routing.entryMode,ranges:t.ranges.entryMode},mapStyle:l,mapPOIs:d,traffic:h}},tags:["recall","state","overview"],examples:["recallState()"],examplePrompts:["What do you have so far?","List everything you remember from this session","Show me the current map / data overview"],relatedTools:["recallPlaces","recallRoutes","recallRanges","resetState"]},resetState:{description:'Wipe the session: drops every places/routes/ranges entry + their map renders, resets POIs + traffic configs to defaults, reverts the map style to "standardLight". Destructive — use only when the user explicitly asks for a clean slate ("start over", "reset", "clear everything").',classificationPrompt:"Wipe the session: clear every places/routes/ranges entry, reset POIs/traffic configs, revert to standard style. Use only when the user asks for a clean slate.",inputSchema:Kn,outputSchema:Xn,execute:async(e,t)=>{try{await t.places.clearShownEntries();for(const e of t.places.entries)await(e._modules?.geometries?.clear());for(const e of t.routing.entries)e._module&&(await e._module.clearRoutes(),await e._module.clearWaypoints());for(const e of t.ranges.entries)await(e._modules?.geometries?.clear()),await(e._modules?.places?.clear());for(const e of t.trafficAreaAnalytics.entries)await(e._module?.clear());return t.mapPOIs.poisModule?.resetConfig(),t.trafficTiles.trafficFlowModule?.resetConfig(),t.trafficTiles.trafficIncidentsModule?.resetConfig(),t.places.reset(),t.routing.reset(),t.ranges.reset(),t.customGeometries.reset(),t.trafficAreaAnalytics.reset(),t.trafficIncidents.reset(),t.byod.reset(),t.baseMap.ttMap.setStyle("standardLight",{keepState:!1}),{success:!0,cleared:[...Jn]}}catch(r){return{error:`Failed to reset state: ${r instanceof Error?r.message:String(r)}`}}},tags:["state","reset","destructive"],examples:["resetState()"],examplePrompts:["Start over","Reset everything","Clear all my data and the map"],relatedTools:["recallState","clearMap"]},setEntryMode:{description:ri,classificationPrompt:"Switch any entry-owning slice (places, routing, ranges, customGeometries, byod, trafficAreaAnalytics, trafficIncidents) between `multiple` (overlay several entries) and `single` (latest only). `single` drops older entries.",inputSchema:ti,outputSchema:ei,execute:async(e,t)=>{const{slice:r,mode:s}=e;try{const e=t[r],o=e.entries.map(e=>e.id);await e.setEntryMode(s);const n=new Set(e.entries.map(e=>e.id));return{success:!0,slice:r,mode:s,droppedEntryIds:o.filter(e=>!n.has(e))}}catch(o){return{error:`Failed to set entry mode: ${o instanceof Error?o.message:String(o)}`}}},tags:["state","mode","display"],examples:['setEntryMode({ slice: "places", mode: "single" })','setEntryMode({ slice: "routing", mode: "multiple" })','setEntryMode({ slice: "trafficIncidents", mode: "single" })'],examplePrompts:["Only show one route at a time","Let me overlay multiple places searches","Switch ranges back to single mode","Only keep the latest traffic incidents snapshot"],relatedTools:["recallState","recallPlaces","recallRoutes","recallRanges"]}},Yi=Hi,Ji=Object.keys(Yi),Xi=()=>{const e={};for(const t of Ji){const r=Yi[t];e[t]=("function"==typeof r?r({}):r).examplePrompts??[]}return e},Ki=async(e,t)=>{((e,t)=>{for(const[r,s]of Object.entries(t)){const t=e[r];t&&(t.description=s.defaultDescription,t.inputSchema=s.defaultInputSchema)}})(e.tools,e.scopableTools);let r=null;if(e.classifier)try{r=await e.classifier({messages:t,toolsMetadata:e.toolsMetadata})}catch{r=null}return e.onClassify?.(r),((e,t,r)=>{if(r)for(const[s,o]of Object.entries(r)){const r=t[s];if(!r||null==o)continue;const n=e[s];if(!n)continue;const i=r.applyScope(o);i&&(n.description=i.description,n.inputSchema=i.inputSchema)}})(e.tools,e.scopableTools,r?.toolScopes),r},Zi=(e,r)=>{if(!r.model)throw new Error("MapAgent requires a model option. Please provide an AI SDK LanguageModel instance.");const o=((e,t)=>{const r={places:new be(e),mapPOIs:new fe(e),routing:new ve(e),baseMap:new se(e),trafficTiles:new zt(e),trafficAreaAnalytics:new De(e),trafficIncidents:new Vt(e),ranges:new we(e),customGeometries:new ce(e),byod:new Pe(e)};return t&&Object.assign(r,t),r})(e,r.state);if(r.dataEntries)for(const[t,s]of Object.entries(r.dataEntries)){const e=t;s?.entryMode&&o[Yt[e]].setEntryMode(s.entryMode)}const n=e=>!1!==r.dataEntries?.[e]?.enabled,i=Object.keys(Yt).filter(e=>!n(e)),a=et.filter(e=>n(e)),l=new Set(i.flatMap(e=>Jt[e])),c=!1===r.includeDefaultTools?{}:Yi,{tools:d,toolsMetadata:u,scopableTools:p}=Er(re(Object.fromEntries(Object.entries(c).filter(([e])=>!l.has(e))),r.tools),o,{outputSchemas:r.outputSchemas,featureFlags:r.featureFlags,enabledDataKinds:a}),h=!1===r.classifier?null:r.classifier??ss({model:r.model}),m=Qt(r.systemPrompt,r.systemPromptSuffix);let y=null;const f=(()=>{let e=Promise.resolve();return async t=>{const r=e;let s=()=>{};e=new Promise(e=>{s=e});try{return await r,await t()}finally{s()}}})(),g={tools:d,scopableTools:p,toolsMetadata:u,classifier:h,onClassify:r.onClassify},b=new t({model:r.model,tools:d,instructions:m,stopWhen:s(r.maxSteps??10),prepareStep:e=>f(async()=>{0===e.stepNumber&&(y=await Ki(g,e.messages));const t=y?.activeToolNames,s=await(r.prepareStep?.(e)),o=r.stepProviderOptions?.({activeTools:t,stepNumber:e.stepNumber});return((e,t,r)=>r?{...r,activeTools:Q(e,r.activeTools),providerOptions:te(t,r.providerOptions)}:{...e?{activeTools:e}:{},...t?{providerOptions:t}:{}})(t,o,s)}),providerOptions:r.providerOptions});return Object.assign(b,{state:o,destroy:()=>(e=>{for(const t of Object.values(e))ee(t)&&t.reset()})(o)})};export{Zt as BASE_SYSTEM_PROMPT,Yt as DATA_ENTRY_KIND_TO_SLICE,Yi as DEFAULT_TOOLS,Xt as ENTRY_MODE_SLICE_NAMES,Jt as TOOLS_BY_DATA_ENTRY_KIND,Ji as TOOL_NAMES,Qt as buildSystemPrompt,Qr as classifyUserIntent,ss as createDefaultClassifier,Zi as createMapAgent,rs as extractLastUserText,ye as formatStateDigestDiff,_e as geometriesIdSchema,Xi as getDefaultToolPrompts,pe as getStateDigest,re as resolveTools};
1
+ import{Output as e,ToolLoopAgent as t,generateText as r,stepCountIs as s}from"ai";import{BaseMapModule as o,CustomGeoJSONModule as n,GeometriesModule as i,HillshadeModule as a,POIsModule as l,PlacesModule as c,RoutingModule as d,TrafficAreaAnalyticsModule as u,TrafficFlowModule as p,TrafficIncidentOverlayModule as h,TrafficIncidentsModule as y,baseMapLayerGroupNames as m,reachableRangeGeometryConfig as f,standardStyleIDs as g,themedGeometryConfig as b}from"@tomtom-org/maps-sdk/map";import{POPULATED_AREA_TAGS as w,alongRouteSearch as v,calculateReachableRanges as I,calculateRoute as S,explorationSearch as E,geocode as x,geocodeOne as M,geometryData as D,getPOICategoryCodes as P,reverseGeocode as T,routeTypes as _,search as A,searchOne as C,trafficAreaAnalytics as k,trafficIncidentDetails as O}from"@tomtom-org/maps-sdk/services";import{TomTomConfig as R,avoidableTypes as N,bboxFromGeoJSON as L,calculateProgressAtRoutePoint as F,formatDistance as j,formatDuration as B,getCoordinateAtRouteProgress as q,getPosition as $,getProgressAtNearestRoutePoint as G,getRouteProgressBetween as U,getRouteProgressForSection as W,getSectionBBox as z,poiCategories as V,sectionTypes as Y,trafficIncidentRequestCategories as H,withInsertedWaypoints as J}from"@tomtom-org/maps-sdk/core";import*as X from"@turf/turf";import*as K from"h3-js";import{z as Z}from"zod";var Q=(e,t)=>{if(!e)return t;if(!t)return e;const r=new Set(t);return e.filter(e=>r.has(e))},ee=e=>"object"==typeof e&&null!==e&&"reset"in e&&"function"==typeof e.reset,te=(e,t)=>{if(e||t)return{...e,...t}},re=(e,t)=>{const r={...e};if(!t)return r;for(const[s,o]of Object.entries(t))o?r[s]=o:delete r[s];return r},se=class{ttMap;_baseMapModule;_hillshadeModule;constructor(e){this.ttMap=e}get mapLibreMap(){return this.ttMap.mapLibreMap}async getBaseMapModule(){return this._baseMapModule??=await o.get(this.ttMap),this._baseMapModule}async getHillshadeModule(){return this._hillshadeModule??=await a.get(this.ttMap),this._hillshadeModule}reset(){this._baseMapModule=void 0,this._hillshadeModule=void 0}},oe=(e,t)=>{const r=new Set(t);if(!r.has(e))return e;for(let s=2;;s++){const t=`${e}-${s}`;if(!r.has(t))return t}},ne=async(e,t,r={})=>{if(0!==e.length)if(r.parallel)await Promise.all(e.map(t));else for(const s of e)await t(s)},ie=async(e,t,r={})=>{if(e.length<=1)return[...e];const s=e[e.length-1];return await ne(e.slice(0,-1),t,r),[s]},ae=class{handlers={};on(e,t){return(this.handlers[e]??(this.handlers[e]=[])).push(t),()=>{const r=this.handlers[e];if(!r)return;const s=r.indexOf(t);-1!==s&&r.splice(s,1)}}off(e){this.handlers[e]&&(this.handlers[e].length=0)}emit(e,t){const r=this.handlers[e];if(r?.length)for(const s of[...r])s(t)}clear(){for(const e of Object.keys(this.handlers))this.handlers[e]=[]}},le=async(e,t)=>{if(0===t.length)return;const r=(e=>{const t=/* @__PURE__ */new Set,r=[];for(const s of e){const e="string"==typeof s.id?s.id:void 0;if(e){if(t.has(e))continue;t.add(e)}r.push(s)}return r})([...e.getShown().geometry.features??[],...t]);await e.show({type:"FeatureCollection",features:r})},ce=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}findById(e){return this._entries.find(t=>t.id===e)}async addEntry(e,t,r,s){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const o=`geometries-${this._entries.length}`,n=s?oe(s,this._entries.map(e=>e.id)):o;return this._entries.push({id:n,timestamp:Date.now(),label:r,features:e,provenance:t}),this.events.emit("entries-change",this._entries),n}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}async getEntryGeometriesModule(e,t="outline"){const r=this._requireEntry(e),s={...b(),theme:t};return r._module?r._shownTheme!==t&&(r._module.applyConfig(s),r._shownTheme=t):(r._module=await i.get(this._ttMap,s),r._shownTheme=t),r._module}async showEntry(e,t="outline",r="replace"){const s=this._requireEntry(e);await this._hideOthersUnderSingleMode(e);const o=await this.getEntryGeometriesModule(e,t),n=s.features;"add"===r?await le(o,n):await(async(e,t)=>{0!==t.length?await e.show({type:"FeatureCollection",features:t}):await e.clear()})(o,n);const i=s._shown;s._shown=!0,i||this.events.emit("shown-change",this.shownEntryIds)}async _hideOthersUnderSingleMode(e){if("single"!==this._entryMode)return;const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const r of t)await this.hideEntry(r.id)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._module&&await t._module.clear(),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`CustomGeometriesState: unknown entry "${e}"`);return t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._module?.clear();this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},de=(e,t)=>e.localeCompare(t),ue=e=>[...e].sort(de),pe=e=>({places:{shownAsPin:ue(e.places.shownAsPinIds),shownAsBaseMap:ue(e.places.shownAsBaseMapIds),entryMode:e.places.entryMode},routes:{shown:ue(e.routing.shownEntryIds),entryMode:e.routing.entryMode},ranges:{shown:ue(e.ranges.shownEntryIds),entryMode:e.ranges.entryMode},traffic:{shown:ue(e.trafficAreaAnalytics.shownEntryIds),entryMode:e.trafficAreaAnalytics.entryMode,entryCount:e.trafficAreaAnalytics.entries.length},incidents:{shown:ue(e.trafficIncidents.shownEntryIds),monitored:ue(e.trafficIncidents.entries.filter(e=>"running"===e._monitor?.status).map(e=>e.id)),entryMode:e.trafficIncidents.entryMode},customGeometries:{shown:ue(e.customGeometries.shownEntryIds),entryMode:e.customGeometries.entryMode,entryCount:e.customGeometries.entries.length},byod:{shown:ue(e.byod.shownEntryIds),entryMode:e.byod.entryMode,entryCount:e.byod.entries.length}}),he=(e,t)=>{if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0},ye=(e,t,r)=>{if(!he(t,r))return 0===r.length?`${e} cleared`:`${e} shown: ${r.map(e=>`"${e}"`).join(", ")}`},me=(e,t)=>{const r=[],s=((e,t)=>{const r=[];return he(e.shownAsPin,t.shownAsPin)||r.push(0===t.shownAsPin.length?"no places shown as pins":`places shown as pins: ${t.shownAsPin.map(e=>`"${e}"`).join(", ")}`),he(e.shownAsBaseMap,t.shownAsBaseMap)||r.push(0===t.shownAsBaseMap.length?"no places shown as base-map POIs":`places shown as base-map POIs: ${t.shownAsBaseMap.map(e=>`"${e}"`).join(", ")}`),r.length?r.join("; "):void 0})(e.places,t.places);s&&r.push(s);const o=ye("routes",e.routes.shown,t.routes.shown);o&&r.push(o);const n=ye("ranges",e.ranges.shown,t.ranges.shown);n&&r.push(n);const i=ye("traffic analytics",e.traffic.shown,t.traffic.shown);i&&r.push(i);const a=ye("incidents",e.incidents.shown,t.incidents.shown);a&&r.push(a);const l=ye("incidents monitor",e.incidents.monitored,t.incidents.monitored);l&&r.push(l);const c=ye("custom geometries",e.customGeometries.shown,t.customGeometries.shown);c&&r.push(c);const d=ye("byod",e.byod.shown,t.byod.shown);return d&&r.push(d),e.places.entryMode!==t.places.entryMode&&r.push(`places entryMode → ${t.places.entryMode}`),e.routes.entryMode!==t.routes.entryMode&&r.push(`routes entryMode → ${t.routes.entryMode}`),e.ranges.entryMode!==t.ranges.entryMode&&r.push(`ranges entryMode → ${t.ranges.entryMode}`),e.incidents.entryMode!==t.incidents.entryMode&&r.push(`incidents entryMode → ${t.incidents.entryMode}`),e.customGeometries.entryMode!==t.customGeometries.entryMode&&r.push(`custom geometries entryMode → ${t.customGeometries.entryMode}`),e.byod.entryMode!==t.byod.entryMode&&r.push(`byod entryMode → ${t.byod.entryMode}`),0===r.length?null:`[map state changed since last response: ${r.join("; ")}]`},fe=class{_ttMap;_poisModule;constructor(e){this._ttMap=e}async getPOIsModule(){return this._poisModule??=await l.get(this._ttMap),this._poisModule}get poisModule(){return this._poisModule}reset(){this._poisModule=void 0}},ge=async(e,t)=>{if(0===t.length)return;const r=(e=>{const t=/* @__PURE__ */new Set,r=[];for(const s of e){const e="string"==typeof s.id?s.id:void 0;if(e){if(t.has(e))continue;t.add(e)}r.push(s)}return r})([...e.getShown().geometry.features??[],...t]);await e.show({type:"FeatureCollection",features:r})},be=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this._hideEntry(e)),this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)),this.events.emit("mode-change",e))}async getEntryPlacesModule(e,t){const r=this._requireEntry(e);return r._modules??={},r._modules.places?r._shownAs!==t&&r._modules.places.applyTheme(t):r._modules.places=await c.get(this._ttMap,{theme:t}),r._modules.places}async getEntryGeometriesModule(e,t="outline"){const r=this._requireEntry(e);r._modules??={};const s={...b(),theme:t};return r._modules.geometries?r._modules.geometries.applyConfig(s):r._modules.geometries=await i.get(this._ttMap,s),r._modules.geometries}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`PlacesState: unknown entry "${e}"`);return t}get entries(){return this._entries}get latestPlace(){return this._entries.at(-1)?.places}async addPlaceResult(e,t,r,s,o){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this._hideEntry(e)),this._entries=[]);const n=`places-${this._entries.length}`,i=r?oe(r,this._entries.map(e=>e.id)):n;return this._entries.push({id:i,timestamp:Date.now(),label:t,places:"features"in e?e.features:[e],...s?.length&&{connections:s},...o?.length&&{geometries:o}}),this.events.emit("entries-change",this._entries),o?.length&&this.events.emit("geometries-change",{entryId:i,count:o.length}),i}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}findPlaceById(e){for(let t=this._entries.length-1;t>=0;t--){const r=this._entries[t];if(!r._byId){r._byId={};for(const e of r.places)r._byId[e.id]=e}const s=r._byId[e];if(s)return{place:s,entryId:r.id}}}getGeometryForPlace(e){for(let t=this._entries.length-1;t>=0;t--){const r=this._entries[t];if(!r.geometries?.length)continue;r._geometryByPlaceId||(r._geometryByPlaceId=this._buildGeometryIndex(r));const s=r._geometryByPlaceId[e];if(s)return s}}async fetchPlaceGeometry(e){const t=this.getGeometryForPlace(e);if(t)return t;const r=this.findPlaceById(e);if(!r)return;if(!r.place.properties.dataSources?.geometry?.id)return;const s=(await D({geometries:[r.place]})).features[0];if(!s)return;const o=this._entries.find(e=>e.id===r.entryId);return o&&(o.geometries??=[],o.geometries.push(s),o._geometryByPlaceId??={},o._geometryByPlaceId[e]=s,this.events.emit("geometries-change",{entryId:o.id,count:o.geometries.length})),s}async fetchGeometriesForEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)return[];const r=/* @__PURE__ */new Set;for(const l of t.geometries??[])"string"==typeof l.id&&r.add(l.id);const s=[],o=/* @__PURE__ */new Set;for(const l of t.places){const e=l.properties.dataSources?.geometry?.id;e&&(r.has(e)||o.has(e)||(o.add(e),s.push(l)))}if(0===s.length)return t.geometries??[];const n=[];for(let l=0;l<s.length;l+=5)n.push(s.slice(l,l+5));const i=await Promise.all(n.map(e=>D({geometries:e}))),a=[...t.geometries??[]];for(const l of i)a.push(...l.features);return t.geometries=a,t._geometryByPlaceId=void 0,this.events.emit("geometries-change",{entryId:t.id,count:a.length}),a}async showPlaceGeometries(e,t="outline",r="replace",s){const o=s??this._latestGeometriesEntryId();if(!o)return;const n=await this.getEntryGeometriesModule(o,t);"add"===r?await ge(n,e):await(async(e,t)=>{0!==t.length?await e.show({type:"FeatureCollection",features:t}):await e.clear()})(n,e)}_latestGeometriesEntryId(){for(let e=this._entries.length-1;e>=0;e--)if(this._entries[e].geometries?.length)return this._entries[e].id;return this._entries.at(-1)?.id}_buildGeometryIndex(e){const t=/* @__PURE__ */new Map;for(const s of e.geometries??[])"string"==typeof s.id&&t.set(s.id,s);const r={};for(const s of e.places){const e=s.properties.dataSources?.geometry?.id;if(!e)continue;const o=t.get(e);o&&(r[s.id]=o)}return r}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shownAs&&e.add(t.id);return e}get shownAsPinIds(){return this._idsShownAs("pin")}get shownAsBaseMapIds(){return this._idsShownAs("base-map")}get shownAsPinClusteredIds(){return this._idsShownAs("pin-clustered")}_idsShownAs(e){const t=/* @__PURE__ */new Set;for(const r of this._entries)r._shownAs===e&&t.add(r.id);return t}getShownMarkerType(e){return this._entries.find(t=>t.id===e)?._shownAs}getShownFeatures(){const e=[];for(const t of this._entries)t._shownAs&&e.push(...t.places);return e}async setShownEntries(e,t){const r=new Set(e),s=[],o=[];for(const n of this._entries)r.has(n.id)?s.push(n):n._shownAs===t&&o.push(n);for(const n of o)await this._hideEntry(n);for(const n of s)await this._showEntryAs(n,t);return this.events.emit("shown-change",this.shownEntryIds),this.getShownFeatures()}async addShownEntries(e,t){if("single"===this._entryMode){const r=e.at(-1);return r?this.setShownEntries([r],t):this.getShownFeatures()}const r=e.flatMap(e=>{const t=this._entries.find(t=>t.id===e);return t?[t]:[]}).filter(e=>e._shownAs!==t);for(const s of r)await this._showEntryAs(s,t);return r.length>0&&this.events.emit("shown-change",this.shownEntryIds),this.getShownFeatures()}async removeShownEntries(e){const t=e.flatMap(e=>{const t=this._entries.find(t=>t.id===e);return t?._shownAs?[t]:[]});for(const r of t)await this._hideEntry(r);return t.length>0&&this.events.emit("shown-change",this.shownEntryIds),this.getShownFeatures()}async clearShownEntries(){const e=this._entries.filter(e=>e._shownAs);if(0!==e.length){for(const t of e)await this._hideEntry(t);this.events.emit("shown-change",this.shownEntryIds)}}async clearShown(){await this.clearShownEntries(),await this.clearShownGeometries()}async clearShownGeometries(e){const t=e?e.flatMap(e=>{const t=this._entries.find(t=>t.id===e);return t?._modules?.geometries?[t]:[]}):this._entries.filter(e=>e._modules?.geometries);if(0!==t.length)for(const r of t)await r._modules.geometries.clear(),this.events.emit("geometries-change",{entryId:r.id,count:0})}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);if(-1===t)return;const r=this._entries[t],s=!!r._shownAs;r._modules&&(await(r._modules.places?.clear()),await(r._modules.geometries?.clear())),this._entries.splice(t,1),this.events.emit("entries-change",this._entries),s&&this.events.emit("shown-change",this.shownEntryIds)}async _showEntryAs(e,t){const r=await this.getEntryPlacesModule(e.id,t);await r.show({type:"FeatureCollection",features:e.places}),e.connections?.length&&await r.showConnections(e.connections),e._shownAs=t}async _hideEntry(e){e._shownAs&&(e._modules?.places&&await e._modules.places.clear(),e._shownAs=void 0)}reset(){for(const e of this._entries)e._modules&&(e._modules.places?.clear(),e._modules.geometries?.clear());this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},we=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}async getEntryGeometriesModule(e,t="outline"){const r=this._requireEntry(e);return r._modules??={},r._modules.geometries?r._shownTheme!==t&&(r._modules.geometries.applyConfig(f(void 0,t)),r._shownTheme=t):(r._modules.geometries=await i.get(this._ttMap,f(void 0,t)),r._shownTheme=t),r._modules.geometries}async getEntryPlacesModule(e){const t=this._requireEntry(e);return t._modules??={},t._modules.places||(t._modules.places=await c.get(this._ttMap)),t._modules.places}async markEntryShown(e){const t=this._requireEntry(e);if("single"===this._entryMode){const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const e of t)await this.hideEntry(e.id)}const r=t._shown;t._shown=!0,r||this.events.emit("shown-change",this.shownEntryIds)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._modules?.geometries&&await t._modules.geometries.clear(),t._modules?.places&&await t._modules.places.clear(),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`RangeState: unknown entry "${e}"`);return t}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}async addEntry(e){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const t=`ranges-${this._entries.length}`;return this._entries.push({id:t,timestamp:Date.now(),...e}),this.events.emit("entries-change",this._entries),t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._modules?.geometries?.clear(),e._modules?.places?.clear();this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},ve=class{_ttMap;_entries=[];_planningSlots=[];_params={};_entryMode="multiple";_mainColor;_lastShownEntryId;events=new ae;constructor(e){this._ttMap=e}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}async getEntryRoutingModule(e){const t=this._requireEntry(e);return t._module||(t._module=await d.get(this._ttMap)),t._module}async showEntry(e){const t=this._requireEntry(e);if("single"===this._entryMode){const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const e of t)await this.hideEntry(e.id)}const r=await this.getEntryRoutingModule(e);void 0!==this._mainColor&&r.applyConfig({theme:{mainColor:this._mainColor}}),await r.showRoutes(t.data),t.waypoints.length&&await r.showWaypoints(t.waypoints);const s=t._shown;t._shown=!0,this._lastShownEntryId=e,s||this.events.emit("shown-change",this.shownEntryIds)}get mainColor(){return this._mainColor}setMainColor(e){if(this._mainColor=e,void 0!==e)for(const t of this._entries)t._shown&&t._module&&t._module.applyConfig({theme:{mainColor:e}})}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._module&&(await t._module.clearRoutes(),await t._module.clearWaypoints()),t._shown=!1,this._lastShownEntryId===e&&(this._lastShownEntryId=this._entries.findLast(e=>e._shown)?.id),this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`RoutingState: unknown entry "${e}"`);return t}get currentEntryModule(){if(!this._lastShownEntryId)return;const e=this._entries.find(e=>e.id===this._lastShownEntryId);return e?._shown?e._module:void 0}get entries(){return this._entries}get currentRoutes(){return this._entries.at(-1)?.data}get currentWaypoints(){return this._entries.at(-1)?.waypoints}get planningSlots(){return this._planningSlots}get params(){return this._params}async addRoutes(e,t,r){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const s=`routes-${this._entries.length}`;return this._entries.push({id:s,timestamp:Date.now(),label:r,data:e,waypoints:t,params:{...this._params}}),this._planningSlots=[...t],this.events.emit("entries-change",this._entries),this.events.emit("planning-change",this._planningSlots),s}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}setWaypointAt(e,t){const r=Math.max(2,e+1);for(;this._planningSlots.length<r;)this._planningSlots.push(null);this._planningSlots[e]=t,this.events.emit("planning-change",this._planningSlots)}setParams(e){this._params={...this._params,...e},this.events.emit("params-change",this._params)}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._module?.clearRoutes(),e._module?.clearWaypoints();this._entries=[],this._planningSlots=[],this._params={},this._lastShownEntryId=void 0,this._mainColor=void 0,this.events.emit("entries-change",this._entries),this.events.emit("planning-change",this._planningSlots),this.events.emit("params-change",this._params),this.events.emit("shown-change",this.shownEntryIds)}},Ie=e=>null===e?"null":Array.isArray(e)?"array":typeof e,Se=(e,t)=>{if(!(e.examples.length>=3)){if("string"==typeof t){const r=t.length>60?`${t.slice(0,60)}…`:t;return void(e.examples.includes(r)||e.examples.push(r))}"number"!=typeof t&&"boolean"!=typeof t||e.examples.includes(t)||e.examples.push(t)}},Ee=e=>{const t=/* @__PURE__ */new Set;for(const r of e.features){const e=r.geometry?.type;e&&t.add(e)}return[...t]},xe=e=>{const t=/* @__PURE__ */new Map;for(const r of e.features){const e=r.properties;if(e&&"object"==typeof e)for(const[r,s]of Object.entries(e)){let e=t.get(r);e||(e={count:0,types:/* @__PURE__ */new Set,examples:[]},t.set(r,e)),e.count+=1,e.types.add(Ie(s)),Se(e,s)}}return t},Me=e=>{const t=e.features.length,r=[...xe(e).entries()].map(([e,r])=>({name:e,types:[...r.types],coverage:t>0?Math.round(r.count/t*100)/100:0,examples:r.examples})).sort((e,t)=>t.coverage-e.coverage||e.name.localeCompare(t.name)),s=r.slice(0,40),o=r.length-s.length;return{featureCount:t,geometryTypes:Ee(e),properties:s,...o>0&&{propertiesOmitted:o}}},De=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}findById(e){return this._entries.find(t=>t.id===e)}async addEntry(e,t,r={}){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const s=oe(r.explicitId??`byod-${this._entries.length}`,this._entries.map(e=>e.id));return this._entries.push({id:s,timestamp:Date.now(),label:t,data:e,source:r.source??{kind:"integrator"},layers:r.layers??[],profile:Me(e)}),this.events.emit("entries-change",this._entries),s}setEntryLayers(e,t){const r=this._requireEntry(e);r.layers=t,r._module?.applyConfig({sources:{data:{layers:t}}}),this.events.emit("entries-change",this._entries)}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}async getEntryModule(e){const t=this._requireEntry(e);return t._module??=await n.get(this._ttMap,{sources:{data:{layers:t.layers}}}),t._module}async showEntry(e){const t=this._requireEntry(e);if(0===t.layers.length)return;await this._hideOthersUnderSingleMode(e),await(await this.getEntryModule(e)).show(t.data,"data");const r=t._shown;t._shown=!0,r||this.events.emit("shown-change",this.shownEntryIds)}async _hideOthersUnderSingleMode(e){if("single"!==this._entryMode)return;const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const r of t)await this.hideEntry(r.id)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(await(t._module?.clear()),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);-1!==t&&(await this.hideEntry(e),this._entries.splice(t,1),this.events.emit("entries-change",this._entries))}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`BYODState: unknown entry "${e}"`);return t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._module?.clear().catch(()=>{});this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},Pe=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get latestEntry(){return this._entries.at(-1)}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this.hideEntry(e.id)),this.events.emit("entries-change",this._entries)),this.events.emit("mode-change",e))}findById(e){return this._entries.find(t=>t.id===e)}async addEntry(e,t,r,s){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this.hideEntry(e.id)),this._entries=[]);const o=`tta-${this._entries.length}`,n=s?oe(s,this._entries.map(e=>e.id)):o;return this._entries.push({id:n,timestamp:Date.now(),label:t,data:e,params:r}),this.events.emit("entries-change",this._entries),n}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);if(!r)return!1;r._analysis??=[];const s=r._analysis.findIndex(e=>e.name===t.name);return s>=0?r._analysis[s]=t:r._analysis.push(t),this.events.emit("analysis-added",{entryId:e,analysis:t}),this.events.emit("entries-change",this._entries),!0}async getEntryModule(e){const t=this._requireEntry(e);return t._module||(t._module=await u.get(this._ttMap),t._configChangeUnsub=t._module.events.on("config-change",r=>{t._config=r,this.events.emit("config-change",{entryId:e,config:r})})),t._module}async showEntry(e){const t=this._requireEntry(e);if("single"===this._entryMode){const t=this._entries.filter(t=>t._shown&&t.id!==e);for(const e of t)await this.hideEntry(e.id)}const r=await this.getEntryModule(e);await r.clear(),await r.show(t.data);const s=t._shown;t._shown=!0,s||this.events.emit("shown-change",this.shownEntryIds)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(await(t._module?.clear()),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);if(-1===t)return;await this.hideEntry(e);const r=this._entries[t];r._configChangeUnsub?.(),r._configChangeUnsub=void 0,r._module=void 0,this._entries.splice(t,1),this.events.emit("entries-change",this._entries)}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`TrafficAreaAnalyticsState: unknown entry "${e}"`);return t}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const e of this._entries)e._configChangeUnsub?.(),e._module?.clear();this._entries=[],this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)}},Te=Z.discriminatedUnion("kind",[Z.object({kind:Z.literal("place").describe('Use the exact literal "place".'),id:Z.string().min(1).describe("Id of a single place; resolves to that place’s footprint.")}).describe("A single place footprint."),Z.object({kind:Z.literal("places").describe('Use the exact literal "places".'),id:Z.string().min(1).describe("Id of a places-entry; resolves to every footprint in the entry.")}).describe("Every footprint in a places entry."),Z.object({kind:Z.literal("ranges").describe('Use the exact literal "ranges".'),id:Z.string().min(1).describe("Id of a ranges-entry; resolves to every isochrone polygon in the entry.")}).describe("Every isochrone polygon in a ranges entry."),Z.object({kind:Z.literal("customGeometries").describe('Use the exact literal "customGeometries".'),id:Z.string().min(1).describe("Id of a custom-geometries entry produced by a previous `processData` call.")}).describe("A derived entry produced by a previous `processData` call.")]).describe('Tagged id `{ kind, id }`. `kind` must be exactly one of: "place" | "places" | "ranges" | "customGeometries" (do NOT use variants like "ranges-entry", "place-footprints", etc.). Use `recallGeometries` to list every available `{kind, id}` pair.'),_e=Z.array(Te).min(1).describe('Tagged ids `{ kind, id }` of polygon sources to feed the run. `kind` must be exactly one of `"place"` | `"places"` | `"ranges"` | `"customGeometries"` (no variants). Mix kinds freely (e.g. union place footprints with isochrones). Use `recallGeometries` to list available ids.'),Ae="`geometry.properties` shape varies by source — place footprints: `CommonPlaceProps`; ranges: service response (`range.type`, `range.value`); custom: whatever the producing op set. Treat as opaque unless you know the source. Every feature also carries `properties._source: { kind, id }` (the tagged id it was passed in under) — use it to filter / partition mixed inputs by role inside the sandbox.",Ce="Source ids that could not contribute (unknown id, no geometry data source, fetch failure, or empty entry).",ke="Tagged input ids that contributed at least one polygon to the run, in input order.",Oe='`places` is a FeatureCollection of Point Features. `Place.properties` shape:\n```ts\ntype PlaceProperties = {\n type: "POI" | "Street" | "Geography" | "Point Address" | "Address Range" | "Cross Street";\n address: {\n freeformAddress: string;\n streetNumber?: string; streetName?: string;\n municipality?: string; municipalitySubdivision?: string;\n countrySubdivision?: string; countrySubdivisionName?: string;\n countrySecondarySubdivision?: string; countryTertiarySubdivision?: string;\n postalCode?: string; extendedPostalCode?: string;\n countryCode?: string; countryCodeISO3?: string; country?: string;\n localName?: string;\n };\n poi?: { // when type === "POI"\n name: string; phone?: string; url?: string;\n brands?: string[];\n categories: string[]; // POICategory enum, e.g. "RESTAURANT", "CAFE_PUB", "GAS_STATION", "HOTEL_MOTEL", "ELECTRIC_VEHICLE_STATION"\n localizedCategories: string[]; // language-sensitive\n openingHours?: { mode: string; timeRanges: Array<{ startTime: {date,hour,minute}, endTime: {date,hour,minute} }>; alwaysOpenThisPeriod: boolean };\n timeZone?: { name: string };\n };\n // Search hits also carry:\n score?: number; // relevance (higher = better)\n distance?: number; // meters from geoBias, when provided\n info?: string;\n // Geography hits also carry:\n geographyType?: Array<"Country"|"CountrySubdivision"|"CountrySecondarySubdivision"|"CountryTertiarySubdivision"|"Municipality"|"MunicipalitySubdivision"|"Neighbourhood"|"PostalCodeArea">;\n'+({}.experimentalSearch?' // Experimental — populated only when `experimentalSearch` is on and the place came from\n // the exploration-search backend (DE / NL / FR area-tag pipeline).\n areaId?: string; // id of the surrounding small-area polygon (few km², not a whole municipality)\n areaCountry?: string; // ISO2 country of the area polygon\n areaTags?: string[]; // e.g. "coastal", "walkable", "transit_connected"\n':"")+" // Optional: mapcodes, entryPoints, addressRanges, relatedPois, chargingPark, dataSources.\n};\n```\nAreas (Street, Geography, …) also carry `feature.bbox`. Coordinates are `[lng, lat]`; guard `?` fields with `?.` / `??`.",Re=Z.union([Z.array(Z.number()).refine(e=>4===e.length,{message:"BBox must have 4 elements"})]).describe("[minLng, minLat, maxLng, maxLat] or [W,S,E,N]"),Ne=Z.object({type:Z.literal("Polygon"),coordinates:Z.array(Z.array(Z.array(Z.number())))}),Le=Z.object({type:Z.literal("MultiPolygon"),coordinates:Z.array(Z.array(Z.array(Z.array(Z.number()))))}),Fe=Z.union([Ne,Le]),je=Z.string().regex(/^[a-z0-9][a-z0-9-]{1,48}$/,"Use lowercase letters, digits, and hyphens (2–49 chars).").optional().describe('Compact lowercase kebab-case id derived from the user prompt (e.g. "vegan-cafes-amsterdam", "ev-stations-along-route"). Avoid generic names like "places" / "results". Auto-suffixed on collision — do not track uniqueness yourself. Default: `places-N`.'),Be=Z.enum(["add","replace"]).optional().describe('WITHIN THE TARGETED ENTRY ONLY — controls how the new polygons combine with what is already rendered on this entry\'s GeometriesModule layer. Does NOT affect other entries (use `hidePreviousEntries` for that). "replace" (default) clears this entry\'s layer first; "add" stacks (deduped by feature id).'),qe=Z.object({show:Z.boolean().optional().describe("Also render the polygons on the map. Default: false."),mode:Be,theme:Z.enum(["filled","outline","inverted"]).optional().describe('Visual theme. Default: "outline".')}).optional().describe('Batch-fetch boundary polygons for the resulting places and cache them on the entry. Use when the user wants the *area / shape / boundary* (e.g. "outline this neighbourhood", "show the city footprint"). Set `show: true` to also render. Skips places without a geometry source (most addresses/streets).'),$e=Z.object({theme:Z.enum(["filled","outline","inverted"]).optional().describe('Visual theme. Default: "outline".'),placeIds:Z.array(Z.string()).optional().describe("Subset of place ids within the targeted entries — render boundaries only for these. Ids outside the entries are ignored. Default: all places with a geometry source."),mode:Be}).describe('Render boundary polygons for the targeted entries. Use when the user wants the *area / shape / outline / footprint* (e.g. "outline this neighbourhood", "show the city footprint"). Independent of `markerType` — set both to render pins AND boundaries.'),Ge=Z.enum(["base-map","pin","pin-clustered","none"]).describe('"pin" for few results; "base-map" for 10+; "pin-clustered" aggregates nearby pins under a count badge (best for dense city-scale POI sets — clusters expose aggregated `placeTypes` / `poiCategories`); "none" for towns/cities/areas.'),Ue=e=>Z.union([Z.literal("all"),Z.array(Z.string())]).optional().describe(`Which previously-shown ${e} entries to hide before showing the new ones. \`"all"\` clears every previous entry (use for the typical "show X" — replace existing); an array hides only the listed entry IDs. Omit to stack on top of existing entries (use for "also show", "additionally", "and X"). The entries being shown by this call are never hidden. Only applied when \`entryMode === "multiple"\` (in \`single\` mode every other entry is auto-hidden, so this prop is ignored).`),We=Z.object({markerType:Ge,zoomMode:Z.enum(["auto","none"]).describe('Camera behaviour. "none" keeps the map still.'),hidePreviousEntries:Ue("places")}),ze=Z.object({markerType:Z.boolean(),zoomMode:Z.boolean()}),Ve=Z.object({zoomMode:Z.enum(["auto","none"]).describe('Camera behaviour. "auto" fits the map to the shown BYOD entries; "none" keeps the map still.'),hidePreviousEntries:Ue("BYOD")}),Ye=Z.object({zoomMode:Z.boolean().describe("Whether the camera was moved to fit the shown entries.")}),He={mode:Z.literal("within"),viewport:Z.literal(!0).optional().describe("When true, search within the current map viewport bounds. EXCLUSIVE with all other within fields.")},Je=Z.enum(["poi","place"]).optional().describe('Drives query resolution. `"poi"` for venues / landmarks / businesses (e.g. "Eiffel Tower"). `"place"` for addresses, cities, neighborhoods, parks, postal codes (e.g. "De Jordaan, Amsterdam", "1600 Pennsylvania Ave"). Default: "place".'),Xe=["boundingBox","viewport","query","placeId"],Ke=Z.object({...He,boundingBox:Re.optional().describe("Single bbox [W,S,E,N]. EXCLUSIVE with viewport / query / placeId."),query:Z.string().optional().describe('Resolve a name/address/POI → its boundary polygon (or bbox) as search area (e.g. "Paris", "De Jordaan, Amsterdam"). Pair with `queryAs` to disambiguate. EXCLUSIVE with boundingBox / viewport / placeId.'),queryAs:Je,placeId:Z.string().optional().describe("ID of a session place — its boundary polygon (fetched on demand) is the search area. EXCLUSIVE with boundingBox / viewport / query.")}).refine(e=>1===Xe.filter(t=>void 0!==e[t]).length,{message:"`within` requires EXACTLY ONE of: boundingBox, viewport, query, placeId."}),Ze=["position","viewport","query"],Qe=Z.object({mode:Z.literal("nearby"),position:Z.array(Z.number()).length(2).optional().describe("Bias point [lng, lat]. EXCLUSIVE with viewport / query."),viewport:Z.literal(!0).optional().describe("When true, bias toward the current map viewport centre. EXCLUSIVE with position / query."),query:Z.string().optional().describe("Resolve a name/address/POI → its centroid as bias point. Pair with `queryAs` to disambiguate. EXCLUSIVE with position / viewport."),queryAs:Je,radiusMeters:Z.number().positive().optional().describe("Hard radius cap (metres) around the bias point. Independent of bias source.")}).refine(e=>1===Ze.filter(t=>void 0!==e[t]).length,{message:"`nearby` requires EXACTLY ONE of: position, viewport, query (radiusMeters is independent)."}),et=Z.object({mode:Z.literal("global")}),tt=Z.union([Ke,Qe,et]),rt=["places","routes","incidents","customGeometries","trafficAreaAnalytics","byod"],st=e=>Z.object({kinds:Z.array(Z.enum(e)).min(1)}),ot={places:{fieldName:"placesEntryIDs",fieldLabel:"`placesEntryIDs`",buildField:e=>(({verb:e,extra:t})=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing places entries to ${e} (e.g. ["places-2"], ["places-1", "places-2"]). Use recallPlaces to list IDs. Omit to ${e} the latest entry.`+(t?` ${t}`:"")))({verb:e.verb,extra:"Injected as `places` (merged) and `placesByEntry[id]`. Undefined when omitted. "+e.requiredHint}),sandboxDoc:"• `places` — merged FeatureCollection over `placesEntryIDs`; `placesByEntry[id]` keeps them separate.",schemaDoc:Oe,recallTool:"recallPlaces"},routes:{fieldName:"routesEntryIDs",fieldLabel:"`routesEntryIDs`",buildField:e=>(({verb:e,extra:t})=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing routes entries to ${e} (e.g. ["routes-0"], ["routes-0", "routes-1"]). Use recallRoutes to list IDs. Omit to ${e} the latest route.`+(t?` ${t}`:"")))({verb:e.verb,extra:"Injected as `routes` (merged) and `routesByEntry[id]`. Undefined when omitted. "+e.requiredHint}),sandboxDoc:"• `routes` — merged FeatureCollection over `routesEntryIDs`; `routesByEntry[id]` keeps them separate.",schemaDoc:'`routes` is a FeatureCollection of LineString Features (each route has `feature.bbox`). `Route.properties` shape:\n```ts\ntype RouteProperties = {\n index: number; // 0 = main route, 1+ = alternatives\n summary: {\n lengthInMeters: number;\n travelTimeInSeconds: number; // includes traffic delay\n noTrafficTravelTimeInSeconds?: number;\n trafficDelayInSeconds: number;\n trafficLengthInMeters: number;\n departureTime: Date; arrivalTime: Date;\n // EV-only:\n batteryConsumptionInkWh?: number; batteryConsumptionInPCT?: number;\n remainingChargeAtArrivalInkWh?: number; remainingChargeAtArrivalInPCT?: number;\n totalChargingTimeInSeconds?: number;\n // Combustion-only:\n fuelConsumptionInLiters?: number;\n };\n // Segments grouped by characteristic. Every section has { id, startPointIndex, endPointIndex }\n // unless noted; *Index values reference `geometry.coordinates`.\n sections: {\n leg: Array<{ id; startPointIndex?; endPointIndex?; summary: RouteSummary }>; // ALWAYS present; one per non-circle waypoint pair\n country?: Array<{ id; startPointIndex; endPointIndex; countryCodeISO3: string }>; // ISO 3166-1 alpha-3\n tollVignette?: Array<{ id; startPointIndex; endPointIndex; countryCodeISO3: string }>;\n traffic?: Array<{\n id; startPointIndex; endPointIndex;\n categories: TrafficIncidentCategory[]; // e.g. "Jam","Accident","RoadWorks","Closure"\n magnitudeOfDelay: "Unknown"|"Minor"|"Moderate"|"Major"|"Undefined";\n tec: { effectCode?: number; causes?: Array<{ mainCauseCode: number; subCauseCode?: number }> };\n effectiveSpeedInKmh?: number;\n delayInSeconds?: number;\n }>;\n importantRoadStretch?: Array<{ id; startPointIndex; endPointIndex; index: number; streetName?: string; roadNumbers?: string[] }>;\n lanes?: Array<{\n id; startPointIndex; endPointIndex;\n lanes: Array<{ directions: PossibleLaneDirection[]; follow?: PossibleLaneDirection }>;\n laneSeparators: PossibleLaneSeparator[];\n properties?: string[]; // e.g. "IS_MANEUVER"\n }>;\n speedLimit?: Array<{ id; startPointIndex; endPointIndex; maxSpeedLimitInKmh: number }>;\n roadShields?: Array<{ id; startPointIndex; endPointIndex; roadShieldReferences: RoadShieldReference[] }>;\n // Plain section ranges (id + startPointIndex + endPointIndex only):\n ferry?: SectionProps[]; motorway?: SectionProps[]; toll?: SectionProps[]; tunnel?: SectionProps[];\n unpaved?: SectionProps[]; urban?: SectionProps[]; pedestrian?: SectionProps[];\n carpool?: SectionProps[]; carTrain?: SectionProps[];\n lowEmissionZone?: SectionProps[]; vehicleRestricted?: SectionProps[];\n };\n progress?: Array<{ pointIndex: number; travelTimeInSeconds?: number; distanceInMeters?: number }>; // cumulative milestones\n guidance?: { instructions: Array<{ pointIndex: number; maneuver: string; street?: string; message?: string }> };\n};\n```\n`pointIndex` values reference positions in `geometry.coordinates`. Guard `?` fields with `?.` / `??`. For "how long / how far" questions, use `summary.lengthInMeters` / `summary.travelTimeInSeconds`.',recallTool:"recallRoutes"},incidents:{fieldName:"incidentsEntryIDs",fieldLabel:"`incidentsEntryIDs`",buildField:e=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing incidents entries to ${e.verb} (e.g. ["incidents-0"]). Injected as \`incidents\` (merged) and \`incidentsByEntry[id]\`. Undefined when omitted. Use \`recallState\` to list incident entries. `+e.requiredHint),sandboxDoc:"• `incidents` — flat `TrafficIncident[]` over `incidentsEntryIDs`; `incidentsByEntry[id]` keeps them separate.",schemaDoc:"",recallTool:"recallState"},customGeometries:{fieldName:"geometriesEntryIDs",fieldLabel:"`geometriesEntryIDs`",buildField:e=>_e.optional().describe("Tagged polygon sources `{ kind, id }` (see geometriesEntryIDsSchema). Injected as `geometries`. Undefined when omitted. "+e.requiredHint),sandboxDoc:"• `geometries` — mixed Polygon/MultiPolygon array over `geometriesEntryIDs`; every feature carries `properties._source: { kind, id }` so you can filter by role (see `GEOMETRIES_PROPS_DOC`).",schemaDoc:"`geometries` is an array of Polygon/MultiPolygon Features. Each has `id` (the geometry-data-source id, matches `place.properties.dataSources.geometry.id`) and `bbox`. `properties` varies by source — see `GEOMETRIES_PROPS_DOC`. Use `turf.area`, `turf.union`, `turf.intersect`, `turf.bbox`, `turf.centroid`, `turf.buffer`, … directly on these features. For h3, convert via `h3.polygonToCells(feature.geometry.coordinates, res)`.",recallTool:"recallGeometries"},trafficAreaAnalytics:{fieldName:"trafficAreaAnalyticsEntryIDs",fieldLabel:"`trafficAreaAnalyticsEntryIDs`",buildField:e=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing traffic-area-analytics entries to ${e.verb} (e.g. ["tta-0"]). Injected as \`trafficAreaAnalytics\` (merged FeatureCollection of tile/hex regions with metric properties) and \`trafficAreaAnalyticsByEntry[id]\`. Undefined when omitted. Use \`recallState\` to list available ids. `+e.requiredHint),sandboxDoc:"• `trafficAreaAnalytics` — merged TrafficAreaAnalytics FeatureCollection over `trafficAreaAnalyticsEntryIDs`. Each feature is a tile / hex region with metric properties on `feature.properties` (e.g. `congestionLevel`, `speed`, `freeFlowSpeed`, `travelTime`, `networkLength`). `trafficAreaAnalyticsByEntry[id]` keeps each request's collection separate. The collection's top-level `properties` (metrics list, date range) come from the first entry when ids span multiple requests.",schemaDoc:"",recallTool:"recallState"},byod:{fieldName:"byodEntryIDs",fieldLabel:"`byodEntryIDs`",buildField:e=>Z.array(Z.string()).min(1).optional().describe(`IDs of existing BYOD entries to ${e.verb} (e.g. ["byod-0"]) — customer-authored GeoJSON layers. Injected as \`byod\` (merged FeatureCollection) and \`byodByEntry[id]\`. Undefined when omitted. Use \`recallByod\` to list available ids. `+e.requiredHint),sandboxDoc:"• `byod` — merged customer-authored GeoJSON FeatureCollection over `byodEntryIDs`; mixed Point / LineString / Polygon features are normal (BYOD layers carry whatever the integrator uploaded). `byodByEntry[id]` keeps each entry separate. Treat `feature.properties` as opaque application-specific data.",schemaDoc:"",recallTool:"recallByod"}},nt=(e,t)=>rt.filter(r=>e.includes(r)&&(!t||t.kinds.includes(r))),it=e=>e.map(e=>ot[e].fieldLabel).join(" / "),at=e=>rt.filter(t=>e.includes(t)).map(e=>ot[e].sandboxDoc).join("\n"),lt=e=>rt.filter(t=>e.includes(t)).map(e=>ot[e].schemaDoc).filter(e=>e.length>0).join("\n\n"),ct=(e,t)=>{const r={};for(const s of rt){if(!e.includes(s))continue;const o=ot[s];r[o.fieldName]=o.buildField(t)}return r},dt=e=>{for(const t of rt)if(e[ot[t].fieldName]?.length)return!0;return!1},ut=e=>`At least one of ${it(e)} must be set. Call ${Array.from(new Set(e.map(e=>ot[e].recallTool))).map(e=>`\`${e}\``).join(" / ")} first to list available ids.`,pt=(Z.union([Z.tuple([Z.number(),Z.number(),Z.number(),Z.number()]),Z.object({bbox:Z.tuple([Z.number(),Z.number(),Z.number(),Z.number()]),padding:Z.number().optional(),animate:Z.boolean().optional()})]).describe("Bounds to fit the camera to: a `[W, S, E, N]` BBox tuple, or `{ bbox: [W,S,E,N], padding?: number, animate?: boolean }`. Default padding: 50px."),Z.object({bbox:Z.tuple([Z.number(),Z.number(),Z.number(),Z.number()]).describe("Bounds the camera was fit to."),padding:Z.number().describe("Padding (px) applied around the bbox.")})),ht=e=>Array.isArray(e)&&4===e.length&&e.every(e=>"number"==typeof e&&Number.isFinite(e)),yt=e=>{if(ht(e))return!0;if(!e||"object"!=typeof e)return!1;const t=e;return!!ht(t.bbox)&&(!!(void 0===t.padding||"number"==typeof t.padding&&Number.isFinite(t.padding))&&!(void 0!==t.animate&&"boolean"!=typeof t.animate))},mt=(e,t)=>{const r=ht(t)?t:t.bbox,s=(ht(t)?void 0:t.padding)??50,o=ht(t)?void 0:t.animate;return e.baseMap.mapLibreMap.fitBounds(r,{padding:s,...void 0!==o&&{animate:o}}),{bbox:r,padding:s}},ft=async(e,t,r)=>{if("multiple"!==e.entryMode||void 0===r)return;const s=new Set(t),o="all"===r?[...e.shownEntryIds].filter(e=>!s.has(e)):r.filter(e=>!s.has(e));for(const n of o)await e.hideEntry(n)},gt=Z.union([Z.object({query:Z.string().describe("Location string to resolve."),queryAs:Z.enum(["poi","place"]).describe('"poi" for venues / landmarks / businesses; "place" for addresses, cities, neighborhoods, parks, postal codes, and other geographies.')}),Z.object({position:Z.object({lng:Z.number(),lat:Z.number()}).describe("Explicit coordinates — use when you already have a position.")}),Z.object({placeId:Z.string().describe("ID of a place stored in session state (any previous places entry). Resolves to the full Place with its POI name, address, and coordinates — prefer this over re-searching by name when routing to a place the user has already seen.")})]),bt=Z.object({source:Te,reason:Z.string()}),wt=e=>`${e.source.kind}:${e.source.id}: ${e.reason}`,vt=(e,t)=>({...e,properties:{...e.properties??{},_source:t}}),It=async(e,t)=>{const r=t.places.findPlaceById(e);if(!r)return{kind:"skip",reason:"No place with this id in session state. Use `recallGeometries` to list known places ids and entries."};if(!r.place.properties.dataSources?.geometry?.id)return{kind:"skip",reason:"Place has no geometry data source (e.g. addresses and streets often lack one)."};try{const s=await t.places.fetchPlaceGeometry(e);return s?{kind:"success",place:r.place,feature:s,ownerEntryId:r.entryId}:{kind:"skip",reason:"Geometry Data service returned no feature for this place."}}catch(s){return{kind:"skip",reason:`Geometry fetch failed: ${s instanceof Error?s.message:String(s)}`}}},St=async(e,t)=>{if(0===e.length)return{error:"`geometriesEntryIDs` must contain one or more tagged ids (e.g. `{ kind: 'places', id: <entryId> }`, `{ kind: 'place', id: <placeId> }`, `{ kind: 'ranges', id: <entryId> }`, `{ kind: 'customGeometries', id: <entryId> }`). Use `recallGeometries` to list available ids."};const r=(e=>{const t={place:[],places:[],ranges:[],customGeometries:[]};for(const r of e)t[r.kind].push(r);return t})(e),s=((e,t)=>{const r=[],s=/* @__PURE__ */new Map,o=[],n=/* @__PURE__ */new Set;for(const i of e){const e=t.places.entries.find(e=>e.id===i.id);if(!e){o.push({source:i,reason:"No places entry with this id in session state. Use `recallGeometries` to list available ids."});continue}let a=0;for(const t of e.places)t.properties.dataSources?.geometry?.id&&"string"==typeof t.id&&(a++,s.has(t.id)||s.set(t.id,i.id),n.has(t.id)||(n.add(t.id),r.push(t.id)));0===a&&o.push({source:i,reason:"Places entry has no places with a geometry data source (e.g. addresses and streets often lack one)."})}return{placeIds:r,placeIdToEntry:s,skipped:o}})(r.places,t),o=[...s.skipped],n=/* @__PURE__ */new Set,i=[],a=/* @__PURE__ */new Map;for(const f of r.place)n.has(f.id)||(n.add(f.id),i.push(f.id),a.set(f.id,f));for(const f of s.placeIds)n.has(f)||(n.add(f),i.push(f));const l=[],c=[],d=/* @__PURE__ */new Set,u=/* @__PURE__ */new Set,p=/* @__PURE__ */new Set;for(const f of i){const e=await It(f,t),r=a.get(f),n=s.placeIdToEntry.get(f);if("skip"===e.kind){const t=r??{kind:"place",id:f};o.push({source:t,reason:e.reason});continue}const i=r??(n?{kind:"places",id:n}:{kind:"place",id:f});l.push(vt(e.feature,i)),c.push(e.place),d.add(e.ownerEntryId),r&&u.add(r.id),n&&p.add(n)}const h=((e,t)=>{const r=[],s=/* @__PURE__ */new Set,o=[];for(const n of e){const e=t.ranges.entries.find(e=>e.id===n.id);if(!e){o.push({source:n,reason:"No ranges entry with this id in session state."});continue}const i=e.ranges.flatMap(e=>e.polygon?.features??[]);if(0!==i.length){for(const e of i)r.push(vt(e,n));s.add(n.id)}else o.push({source:n,reason:"Ranges entry has no polygons — recompute via findReachableAreas first."})}return{features:r,contributingIds:s,skipped:o}})(r.ranges,t);l.push(...h.features),o.push(...h.skipped);const y=((e,t)=>{const r=[],s=/* @__PURE__ */new Set,o=[];for(const n of e){const e=t.customGeometries.findById(n.id);if(e)if(0!==e.features.length){for(const t of e.features)r.push(vt(t,n));s.add(n.id)}else o.push({source:n,reason:"Custom-geometries entry is empty."});else o.push({source:n,reason:"No custom-geometries entry with this id in session state. Use `recallGeometries` to list available ids."})}return{features:r,contributingIds:s,skipped:o}})(r.customGeometries,t);l.push(...y.features),o.push(...y.skipped);const m=y.contributingIds;return 0===l.length?{error:`No input geometries available. ${o.map(wt).join(" | ")}`}:{value:{geometries:l,sourcePlaces:c,affectedEntries:[...[...d].map(e=>({id:e,kind:"places"})),...[...m].map(e=>({id:e,kind:"customGeometries"}))],skipped:o,contributingSourceIds:e.filter(e=>{switch(e.kind){case"place":return u.has(e.id);case"places":return p.has(e.id);case"ranges":return h.contributingIds.has(e.id);case"customGeometries":return m.has(e.id)}})}}},Et=["placesEntryIDs","routesEntryIDs","incidentsEntryIDs","geometriesEntryIDs","trafficAreaAnalyticsEntryIDs","byodEntryIDs"],xt=async(e,t)=>{if(!(e=>Et.some(t=>(e[t]?.length??0)>0))(e))return{error:"At least one of `placesEntryIDs`, `routesEntryIDs`, `incidentsEntryIDs`, `geometriesEntryIDs`, `trafficAreaAnalyticsEntryIDs`, `byodEntryIDs` must be set. Use `recallPlaces` / `recallRoutes` / `recallGeometries` / `recallByod` / `recallState` to list available ids."};const r=((e,t)=>{const r=Dt(e.placesEntryIDs,t.places,{missingId:e=>`No places entry with id "${e}". Use recallPlaces to list available IDs.`});if("error"in r)return r;const s=Dt(e.routesEntryIDs,t.routing,{missingId:e=>`No routes entry with id "${e}". Use recallRoutes to list available IDs.`});if("error"in s)return s;const o=Dt(e.incidentsEntryIDs,t.trafficIncidents,{missingId:e=>`No incidents entry with id "${e}". Call getTrafficIncidents first.`});if("error"in o)return o;const n=Dt(e.trafficAreaAnalyticsEntryIDs,t.trafficAreaAnalytics,{missingId:e=>`No traffic-area-analytics entry with id "${e}". Call getTrafficAreaAnalytics first.`});if("error"in n)return n;const i=Dt(e.byodEntryIDs,t.byod,{missingId:e=>`No BYOD entry with id "${e}". Use recallByod to list available IDs.`});return"error"in i?i:{value:{places:r.value,routes:s.value,incidents:o.value,trafficAreaAnalytics:n.value,byod:i.value}}})(e,t);if("error"in r)return r;const{places:s,routes:o,incidents:n,trafficAreaAnalytics:i,byod:a}=r.value;let l,c={affectedEntries:[],sourceIds:[],skipped:[]};if(e.geometriesEntryIDs?.length){const r=await St(e.geometriesEntryIDs,t);if("error"in r)return r;l=r.value.geometries,c={affectedEntries:r.value.affectedEntries,sourceIds:r.value.contributingSourceIds,skipped:r.value.skipped}}return{value:{resolved:{places:s,routes:o,incidents:n,trafficAreaAnalytics:i,byod:a},sandbox:{places:s.length?{type:"FeatureCollection",features:s.flatMap(e=>e.places)}:void 0,placesByEntry:s.length?Object.fromEntries(s.map(e=>[e.id,{type:"FeatureCollection",features:[...e.places]}])):void 0,routes:o.length?{type:"FeatureCollection",features:o.flatMap(e=>e.data.features)}:void 0,routesByEntry:o.length?Object.fromEntries(o.map(e=>[e.id,{type:"FeatureCollection",features:[...e.data.features]}])):void 0,incidents:n.length?n.flatMap(e=>e.data):void 0,incidentsByEntry:n.length?Object.fromEntries(n.map(e=>[e.id,[...e.data]])):void 0,geometries:l,trafficAreaAnalytics:i.length?Mt(i.map(e=>e.data)):void 0,trafficAreaAnalyticsByEntry:i.length?Object.fromEntries(i.map(e=>[e.id,e.data])):void 0,byod:a.length?{type:"FeatureCollection",features:a.flatMap(e=>e.data.features)}:void 0,byodByEntry:a.length?Object.fromEntries(a.map(e=>[e.id,e.data])):void 0},geometries:c}}},Mt=e=>({type:"FeatureCollection",properties:e[0].properties,features:e.flatMap(e=>e.features)}),Dt=(e,t,r)=>{if(!e?.length)return{value:[]};const s=[];for(const o of e){const e=t.entries.find(e=>e.id===o);if(!e)return{error:r.missingId(o)};s.push(e)}return{value:s}},Pt='CROSS-KIND OPS — `turf` and `h3` operate on EVERY feature regardless of which input slot it came from. You can freely combine `places` (Points), `routes` (LineStrings), `incidents` (Point or LineString), `geometries` (Polygon / MultiPolygon), and `trafficAreaAnalytics` (Polygon tiles with `properties` like `congestionLevel` / `speed` / `travelTime` / `freeFlowSpeed`) in the same call. Common bridges:\n• Point ↔ LineString — `turf.pointToLineDistance(point, lineFeature, { units: "meters" })`, `turf.nearestPointOnLine(line, point)`.\n• Point ↔ Polygon — `turf.booleanPointInPolygon(point, poly)`, `turf.pointsWithinPolygon(turf.featureCollection(points), poly)`.\n• LineString ↔ Polygon — `turf.lineIntersect(line, poly)`, `turf.booleanCrosses(line, poly)`, `turf.lineSplit(line, poly)`.\n• Polygon ↔ Polygon — `turf.union/intersect/difference(turf.featureCollection([a, b]))` (Turf v7 takes a single collection).\n• Point/LineString ↔ trafficAreaAnalytics tile — `turf.booleanPointInPolygon(p, tile)` for points; `turf.lineIntersect(routeLine, tile)` for routes; iterate `trafficAreaAnalytics.features` to find which tile a place/route segment falls into and read its metric (`tile.properties.congestionLevel`, etc.).\n• Buffer to bridge kinds — `turf.buffer(anyFeature, meters/1000, { units: "kilometers" })` turns a point or line into a polygon you can then filter against.\n• Bbox of anything — `turf.bbox(featureOrCollection)`.\n• Centroid for Point↔shape distance — `turf.centroid(polygonOrLine).geometry.coordinates`.\n• h3 — `h3.latLngToCell(lat, lng, res)` for any Point; `h3.polygonToCells(poly.geometry.coordinates, res)` for any Polygon; for a LineString iterate `geometry.coordinates` and bin each `[lng, lat]`.\nNote: arguments to turf functions are GeoJSON Features — pass the whole feature (`places.features[i]`, `routes.features[0]`, `geometries[i]`, `incidents[i]`, `trafficAreaAnalytics.features[i]`), not the bare `geometry.coordinates` (a few helpers accept coords but most expect Features). Always check the function signature at https://turfjs.org/docs/.',Tt=["places","placesByEntry","routes","routesByEntry","incidents","incidentsByEntry","geometries","trafficAreaAnalytics","trafficAreaAnalyticsByEntry","byod","byodByEntry","h3","turf"],_t=(e,t)=>[e.places,e.placesByEntry,e.routes,e.routesByEntry,e.incidents,e.incidentsByEntry,e.geometries,e.trafficAreaAnalytics,e.trafficAreaAnalyticsByEntry,e.byod,e.byodByEntry,t.h3,t.turf],At=new Set(V),Ct=e=>`In scope: ${e.map(e=>`\`${e}\``).join(", ")} — injected as named parameters; use them directly. Do NOT redeclare them or read them off \`arguments\`: lines like \`const turf = arguments.turf\` / \`const turf = arguments[0].turf\` / \`const places = arguments.places\` throw "Identifier '…' has already been declared". The body has no \`arguments\`-based access — reference each name as-is. No \`require\` / \`import\` / \`await import\` (body is an AsyncFunction, not a module — such lines are stripped silently). \`h3\` = h3-js (hex math). \`turf\` = @turf/turf v7 (verify names at https://turfjs.org/docs/). TURF INPUTS: pass a Feature or FeatureCollection — never a bare Array (throws "Unknown Geometry Type"). FC-shaped inputs (\`places\`, \`routes\`, \`trafficAreaAnalytics\`, \`byod\`) → pass directly: \`turf.bbox(places)\`. Array inputs (\`incidents\`, \`geometries\`, \`places.features\`, …) → iterate per-feature (\`for (const f of places.features) turf.X(f)\`) or wrap with \`turf.featureCollection([...])\`. Never \`.map(f => f.geometry.coordinates)\` / \`.flatMap(f => f.geometry.coordinates)\` to feed turf — that strips the feature wrapper turf reads and throws "coordinates must be an Array" (or worse, mixes Point/LineString shapes silently). Pass the whole feature and let turf read the geometry: \`turf.pointToLineDistance(point, lineFeature, { units: "meters" })\`. \`incidents\` mixes Point + LineString features — guard with \`if (inc.geometry.type === "LineString")\` before line-only ops. TURF v7 SET OPS: \`union\`/\`intersect\`/\`difference\` take ONE FeatureCollection — \`turf.difference(turf.featureCollection([outer, inner]))\`. The v6 two-arg form throws. PERF: for large-N nearest/within queries, pre-bucket points with \`h3.latLngToCell(lat, lng, res)\` + \`h3.gridDisk(cell, k)\` and only run turf on same-/neighbour-cell pairs — turns O(N·M) into ~O(N+M). Pick \`res\` so each cell ≈ your query radius. No spatial search or HTTP — call \`discoverPlaces\` BEFORE this tool for new places. Top-level \`await\`/locals/consts fine. End with \`return …;\`.`,kt=new Set(["bar","line","pie","doughnut","radar","polarArea","scatter","bubble"]),Ot=e=>{if(!e||"object"!=typeof e)return!1;const t=e;if("Feature"!==t.type)return!1;const r=t.geometry;return!!r&&("Polygon"===r.type||"MultiPolygon"===r.type)&&Array.isArray(r.coordinates)},Rt=(e,t)=>{if(void 0===e)return{error:"Analysis code must return a value (the aggregation result). Hint: `code` is the function BODY, not a function expression. If you wrote `(args) => { ...; return result; }` (or `async (args) => { ... }`) as the whole code, the arrow is created and discarded — drop the wrapper and have the body itself end with `return result;`."};const r=Nt(e);return"error"in r?{error:r.error}:"chart"!==t||(e=>{if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.type&&kt.has(t.type)&&"object"==typeof t.data&&null!==t.data})(r.value)?{value:r.value}:{error:'Analysis code with `outputFormat: "chart"` must return a Chart.js ChartConfiguration ({ type: "bar"|"line"|"pie"|"doughnut"|"radar"|"polarArea"|"scatter"|"bubble", data: { labels, datasets }, options? }).'}},Nt=e=>{try{return{value:JSON.parse(JSON.stringify(e))}}catch(t){return{error:`Returned value is not JSON-serializable: ${t instanceof Error?t.message:String(t)}. Return only plain objects, arrays, strings, numbers, booleans, and null — no functions, classes, circular references, or DOM/Map references.`}}},Lt=async(e,t,r,s)=>{try{const s=Object.getPrototypeOf(async()=>{}).constructor,o=Ft(e,t);return{value:await new s(...t,o)(...r)}}catch(o){return{error:Bt(s,o)}}},Ft=(e,t)=>{const r=t.join("|"),s=String.raw`(?:(?:await\s+)?(?:require|import)\s*\(`+String.raw`|arguments\s*(?:\[\s*\d+\s*\])?\s*(?:\.\w+|\[\s*['"]\w+['"]\s*\]))`,o=new RegExp(String.raw`^[ \t]*(?:const|let|var)[ \t]+(?:${r})[ \t]*=[ \t]*${s}[^\n]*\n?`,"gm");return e.replace(o,"")},jt=[{pattern:/Cannot read propert(?:y|ies) of undefined/i,hint:"A property was read on `undefined`. Likely a `reduce` callback that does not `return` the accumulator, or a `reduce` called without an initial value — use `arr.reduce((acc, x) => { ...; return acc; }, {})`. Also check unguarded chains like `feature.properties.poi.categories` — use optional chaining (`?.`) and `??` defaults."},{pattern:/Cannot read propert(?:y|ies) of null/i,hint:"A property was read on `null`. Guard property access with `?.` and provide defaults with `??`."},{pattern:/\bh3\.\w+ is not a function/i,hint:"`h3` is [h3-js](https://github.com/uber/h3-js) — hex-grid math only. NO spatial search / place lookup / HTTP. Common: `latLngToCell`, `cellToLatLng`, `cellToBoundary`, `polygonToCells`, `gridDisk`, `cellArea`. For nearby places call `discoverPlaces` BEFORE this tool — sandbox cannot call other tools."},{pattern:/\bturf\.\w+ is not a function/i,hint:"`turf` is [@turf/turf](https://turfjs.org/) — GeoJSON geometry only. NO spatial search / place lookup / HTTP. Common: `area`, `length`, `bbox`, `centroid`, `union`, `intersect`, `buffer`, `distance`, `booleanPointInPolygon`, `pointsWithinPolygon`, `clustersDbscan`, `convex`. Verify names at https://turfjs.org/docs/. For nearby places call `discoverPlaces` BEFORE this tool."},{pattern:/coordinates must be an Array|Unknown Geometry Type/i,hint:'turf was handed a raw coordinate (or a bare Array of features) instead of a Feature / FeatureCollection. Do NOT extract `.geometry.coordinates` with `.map` / `.flatMap` and feed the result to turf — pass the WHOLE feature: `turf.pointToLineDistance(pointFeature, lineFeature, { units: "meters" })`. `incidents` is an Array of mixed Point + LineString features; guard with `if (inc.geometry.type === "LineString")` before line-only ops. For FC-shaped inputs (`places`, `routes`, `trafficAreaAnalytics`, `byod`), pass the value directly (`turf.bbox(places)`); never `turf.bbox(places.features)`.'},{pattern:/is not a function/i,hint:"A method was called on a value that does not have it (typo, or the value is not the expected type). Verify the type before calling, e.g. `Array.isArray(x) ? x.map(...) : []`."},{pattern:/Illegal return statement/i,hint:"A `return` was placed outside the top-level function body — usually a missing brace or a callback that the parser thinks closed too early. Common cause: a multi-line `const X = arr.map((item) => { ... })` whose closing `})` was lost. Re-emit the code with balanced braces, keep `return` only at the top level of the body, and try again."},{pattern:/Identifier ['"]?\w+['"]? has already been declared/i,hint:"A variable was declared twice — typically because the code prepends `const turf = require(...)` / `const turf = await import(...)` / `const turf = arguments.turf` / `const turf = arguments[0].turf` / `const places = ...`. Those names are already in scope as function parameters; drop the redeclaration line and use them directly (`turf.bbox(places)`)."},{pattern:/Unexpected (?:token|identifier|end of input)/i,hint:"The code did not parse as a JavaScript async-function body. Common causes: unbalanced braces/parens/brackets, a stray template literal, or `import`/`export` statements (not allowed in sandbox bodies — `places`/`geometries`/`h3`/`turf` are already in scope). Re-emit a self-contained body that ends with `return ...;`."},{pattern:/\brequire is not defined\b|\bimport (?:is not defined|outside a module)\b/i,hint:"The sandbox is not a Node module — `require` and top-level `import` are not available. The libraries you need (`h3`, `turf`) and inputs (`places`, `geometries`) are already injected as parameters; drop the import lines and use them directly."},{pattern:/\b(?:functions|tools|agent|recallPlaces|recallRoutes|recallRanges|discoverPlaces|locatePlace) is not defined\b/i,hint:"The sandbox cannot call other tools — `functions.recallPlaces(...)`, `tools.discoverPlaces(...)`, etc. are NOT available. To use additional entries, list every needed id in the tool inputs (`placesEntryIDs`, `routesEntryIDs`, `incidentsEntryIDs`, `geometriesEntryIDs`): each is exposed via the injected sandbox args (`places`, `routes`, `incidents`, `geometries`, plus their `*ByEntry` per-entry views). To bring in NEW places call `discoverPlaces` BEFORE this tool and pass the resulting entry id back in."},{pattern:/is not iterable/i,hint:"A value used in `for..of`, spread, or destructuring was not iterable. Default arrays with `?? []`, e.g. `for (const c of feature.properties.poi?.categories ?? []) { ... }`."},{pattern:/Cannot access '.+?' before initialization/i,hint:"A `let`/`const` was used before its declaration (temporal dead zone). Move the declaration above its first use."},{pattern:/Assignment to constant variable/i,hint:"A `const`-declared variable was reassigned. Either use `let`, or mutate the value in place."},{pattern:/structuredClone|could not be cloned|DataCloneError/i,hint:"Return value must be pure JSON — no functions or classes. The result crosses a JSON boundary before rendering. For per-point detail you would put in a Chart.js `tooltip.callbacks` function, use a `string[]` label instead — it renders multi-line in the axis tick and the tooltip title."}],Bt=(e,t)=>{const r=t instanceof Error?t.message:String(t),s=`${e} code execution failed: ${r}`,o=jt.find(({pattern:e})=>e.test(r));return o?`${s} Hint: ${o.hint}`:t instanceof Error&&"SyntaxError"===t.name?`${s} Hint: the provided \`code\` is not valid JavaScript — ensure it parses as an async function body and \`return\`s a value.`:s},qt=async(e,t,r)=>{const s=await e.places.fetchGeometriesForEntry(t);return r.show&&s.length>0?(await e.places.showPlaceGeometries(s,r.theme??"outline",r.mode??"replace"),{fetched:s.length,shown:!0}):{fetched:s.length,shown:!1}},$t=async(e,t,r)=>{const s={markerType:!1,zoomMode:!1};if("none"!==r.markerType){if("multiple"===e.places.entryMode&&"all"!==r.hidePreviousEntries){if(Array.isArray(r.hidePreviousEntries)){const s=new Set(t),o=r.hidePreviousEntries.filter(e=>!s.has(e));o.length>0&&await e.places.removeShownEntries(o)}await e.places.addShownEntries(t,r.markerType)}else await e.places.setShownEntries(t,r.markerType);s.markerType=!0}if("auto"===r.zoomMode){const t=e.places.getShownFeatures();if(t.length>0){const r=L({type:"FeatureCollection",features:t});r&&e.baseMap.mapLibreMap.fitBounds(r,{padding:50})}s.zoomMode=!0}return s},Gt=(e,t=6)=>{if(e.mapLibreMap.getZoom()<t)return;const r=e.mapLibreMap.getCenter();return[r.lng,r.lat]},Ut=e=>{const t=e.mapLibreMap.getBounds();return[t.getWest(),t.getSouth(),t.getEast(),t.getNorth()]},Wt=async(e,t,r,s)=>{const o=await Lt(e.code,["incidents","h3","turf","now","log","previous"],[t,K,X,new Date(s),()=>{},r],"Analysis");return"error"in o?{error:o.error}:Rt(o.value,e.outputFormat)},zt=class{_specs=[];_historyByName=/* @__PURE__ */new Map;get specs(){return this._specs}get results(){const e=[];for(const t of this._specs){const r=this.getResult(t.name);r&&e.push(r)}return e}history(e){return this._historyByName.get(e)??[]}getResult(e){return this._historyByName.get(e)?.at(-1)}register(e){const t=this._specs.findIndex(t=>t.name===e.name);if(t>=0){const r=this._specs[t],s=!!("run"in r)||r.code!==e.code||r.outputFormat!==e.outputFormat||r.description!==e.description;this._specs[t]=e,s&&this._historyByName.delete(e.name)}else this._specs.push(e)}registerDeterministic(e){const t=this._specs.findIndex(t=>t.name===e.name);if(t>=0){const r=this._specs[t],s=!("run"in r)||null==e.signature||r.signature!==e.signature;this._specs[t]=e,s&&this._historyByName.delete(e.name)}else this._specs.push(e)}unregister(e){this._specs=this._specs.filter(t=>t.name!==e),this._historyByName.delete(e)}attach(e){let t=this._historyByName.get(e.name);t||(t=[],this._historyByName.set(e.name,t));const r=t[t.length-1];r&&r.timestamp===e.timestamp?t[t.length-1]=e:(t.push(e),t.length>240&&t.shift())}async replay(e,t){const r=[];for(const s of this._specs)if("run"in s)try{const o=this.getResult(s.name),n=await s.run(e,{previous:o?.data,sampledAt:t,previousSampledAt:o?.timestamp}),i={name:s.name,timestamp:t,outputFormat:"json",data:n};this.attach(i),r.push(i)}catch{}else{const o=s,n=await Wt(o,e,this.getResult(o.name)?.data,t);if("error"in n)continue;const i={name:o.name,timestamp:t,...o.description&&{description:o.description},outputFormat:o.outputFormat,data:n.value};this.attach(i),r.push(i)}return r}},Vt=["roadworks","road-closed"];function Yt(e){const t=e.properties.id;if("Point"===e.geometry.type)return X.point(e.geometry.coordinates,{_incidentId:t});const r=X.centroid(e);return r.properties={_incidentId:t},r}function Ht(e){const t=/* @__PURE__ */new Map;for(const r of e)for(const e of r.properties.roadNumbers??[])t.set(e,(t.get(e)??0)+1);return[...t.entries()].sort((e,t)=>t[1]-e[1]).map(([e])=>e)}function Jt(e){const t=/* @__PURE__ */new Map;for(const r of e){const e=r.properties.category;e&&t.set(e,(t.get(e)??0)+1)}return[...t.entries()].sort((e,t)=>t[1]-e[1])[0]?.[0]??"incident"}function Xt(e,t,r,s,o){if(0===e.length)return{groups:[]};const n=function(e,t,r){if(0===r)return e;const s=new Set(t);return e.filter(e=>{const t=e.properties.category,o=e.properties.delayInSeconds??0;return!(s.has(t)&&o<r)})}(e,t.preFilter?.excludeCategories??Vt,t.preFilter?.minDelaySeconds??1);if(0===n.length)return{groups:[]};const i=/* @__PURE__ */new Map;for(const w of n)i.set(w.properties.id,w);const a=n.map(Yt),l=X.featureCollection(a),c=t.eps??.5,d=t.minMembers??3,u=X.clustersDbscan(l,c,{minPoints:d}),p=/* @__PURE__ */new Map;for(const w of u.features){const e=w.properties.cluster;if(void 0===e||e<0)continue;let t=p.get(e);t||(t=[],p.set(e,t)),t.push(w)}const h=t.maxClusters??6,y=[];for(const[,w]of p){const e=w.map(e=>e.properties._incidentId),t=e.map(e=>i.get(e)).filter(e=>void 0!==e),r=t.reduce((e,t)=>e+(t.properties.delayInSeconds??0),0),s=t.reduce((e,t)=>Math.max(e,t.properties.delayInSeconds??0),0),o=X.featureCollection(w),n=X.centroid(o).geometry.coordinates;let a=0;if(w.length>=2){const e=X.bbox(o);a=X.distance([e[0],e[1]],[e[2],e[3]])}const l=Ht(t);y.push({centroid:n,memberIds:e,size:t.length,totalDelaySeconds:r,peakDelaySeconds:s,diameterKm:a,primaryRoads:l,primaryCategory:Jt(t)})}y.sort((e,t)=>t.totalDelaySeconds-e.totalDelaySeconds);const m=y.slice(0,h),f=r?function(e,t){const r=t.map(e=>new Set(e.memberIds)),s=[];for(let a=0;a<e.length;a++){const o=new Set(e[a].memberIds);for(let e=0;e<t.length;e++){const t=r[e];let n=0;for(const e of o)t.has(e)&&n++;const i=o.size+t.size-n,l=0===i?0:n/i;l>=.5&&s.push({freshIdx:a,prevIdx:e,overlap:l})}}s.sort((e,t)=>t.overlap-e.overlap);const o=/* @__PURE__ */new Map,n=/* @__PURE__ */new Set,i=/* @__PURE__ */new Set;for(const{freshIdx:a,prevIdx:l,overlap:c}of s)n.has(a)||i.has(l)||(o.set(a,t[l].id),n.add(a),i.add(l));return o}(m,r):/* @__PURE__ */new Map,g=/* @__PURE__ */new Map;if(r)for(const w of r)g.set(w.id,w);const b=void 0===s||void 0===o||s>o;return{groups:m.map((e,t)=>{const r=f.get(t),s=r??`cluster-${e.centroid[0].toFixed(3)}-${e.centroid[1].toFixed(3)}-${[...e.memberIds].sort()[0]??t}`,o=r?g.get(r):void 0,n=o?.delaySamples??[],i=o?(b?[...n,e.totalDelaySeconds]:[...n.slice(0,-1),e.totalDelaySeconds]).slice(-3):[e.totalDelaySeconds],a=1===i.length?"new":function(e){if(e.length<3)return"steady";const[t,r,s]=e.slice(-3),o=(s-t)/Math.max(t,1);return r>t&&s>r&&o>.1?"growing":r<t&&s<r&&o<-.1?"fading":"steady"}(i);return{id:s,centroid:e.centroid,memberIds:e.memberIds,size:e.size,totalDelaySeconds:e.totalDelaySeconds,peakDelaySeconds:e.peakDelaySeconds,diameterKm:e.diameterKm,primaryRoads:e.primaryRoads,primaryCategory:e.primaryCategory,trend:a,delaySamples:i}})}}var Kt=class{_bbox;events=new ae;_status="idle";_area;_error;_intervalHandle;_clearInterval;_fetchIncidents;constructor(e){this._bbox=e}get status(){return this._status}get monitoredArea(){return this._area}get error(){return this._error}get isRunning(){return"running"===this._status}get isActive(){return"idle"!==this._status}start(e,t,r=6e4){if("running"===this._status)return;this._status="running",this._error=void 0,this._area=e,this._fetchIncidents=t.fetchIncidents,this._clearInterval=t.clearInterval??(e=>clearInterval(e));const s=t.setInterval??setInterval;this._tick(),this._intervalHandle=s(()=>{this._tick()},r)}stop(){"idle"!==this._status&&(this._releaseTimer(),this._status="idle",this._error=void 0,this._area=void 0,this._fetchIncidents=void 0)}async _tick(){const e=this._fetchIncidents;if(e)try{const t=await e(this._bbox),r={takenAt:Date.now(),incidents:t};this.events.emit("tick",r)}catch(t){this._failed(t instanceof Error?t.message:String(t))}}_failed(e){this._releaseTimer(),this._error=e,this._status="stopped-error",this._fetchIncidents=void 0,this.events.emit("error",e)}_releaseTimer(){null!=this._intervalHandle&&(this._clearInterval?.(this._intervalHandle),this._intervalHandle=void 0),this._clearInterval=void 0}},Zt=class{_ttMap;_entries=[];_entryMode="multiple";events=new ae;constructor(e){this._ttMap=e}get entries(){return this._entries}get entryMode(){return this._entryMode}async setEntryMode(e){this._entryMode!==e&&(this._entryMode=e,"single"===e&&this._entries.length>1&&(this._entries=await ie(this._entries,e=>this._teardownEntry(e),{parallel:!0}),this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)),this.events.emit("mode-change",e))}get shownEntryIds(){const e=/* @__PURE__ */new Set;for(const t of this._entries)t._shown&&e.add(t.id);return e}async addIncidentsEntry(e,t,r,s,o){"single"===this._entryMode&&this._entries.length>0&&(await ne(this._entries,e=>this._teardownEntry(e),{parallel:!0}),this._entries=[]);const n=oe(o??`incidents-${this._entries.length}`,this._entries.map(e=>e.id));return this._entries.push({id:n,timestamp:s,label:r,data:e,params:t}),this.events.emit("entries-change",this._entries),n}async replaceEntryData(e,t,r){const s=this._entries.find(t=>t.id===e);if(!s)return!1;const o=(s._tick??0)+1;if(s._tick=o,s.data=t,s.timestamp=r,this.events.emit("entries-change",this._entries),s._module){if(await s._module.show({type:"FeatureCollection",features:t}),s._tick!==o)return!0;if(s._focus){const e=new Set(t.map(e=>e.properties.id)),r=[...s._focus.ids].filter(t=>e.has(t));s._module.setFocus(r.length>0?r:null),0===r.length&&(s._focus=void 0,this.events.emit("focus-change",{entryId:s.id,focus:null}))}}return s._tick!==o||await this._rerunRegisteredSpecs(e,r),!0}async getEntryModule(e){const t=this._requireEntry(e);return t._module??=await h.get(this._ttMap),t._module}async showEntry(e,t=!0){const r=this._requireEntry(e),s=await this.getEntryModule(e);await s.show({type:"FeatureCollection",features:r.data}),s.setVisible(!0),t&&this._ttMap.mapLibreMap.fitBounds(r.params.bbox,{padding:50,maxZoom:14});const o=!!r._shown;r._shown=!0,o||this.events.emit("shown-change",this.shownEntryIds)}async hideEntry(e){const t=this._entries.find(t=>t.id===e);t?._shown&&(t._focus&&(t._focus=void 0,this.events.emit("focus-change",{entryId:t.id,focus:null})),t._module?.setVisible(!1),await(t._module?.clear()),t._shown=!1,this.events.emit("shown-change",this.shownEntryIds))}async removeEntry(e){const t=this._entries.findIndex(t=>t.id===e);if(-1===t)return;const r=this._entries[t],s=!!r._shown;await this._teardownEntry(r),this._entries.splice(t,1),this.events.emit("entries-change",this._entries),s&&this.events.emit("shown-change",this.shownEntryIds)}addAnalysisToEntry(e,t){const r=this._entries.find(t=>t.id===e);return!!r&&((r._analyses??=new zt).attach(t),this.events.emit("analysis-change",{entryId:r.id,analysis:t}),this.events.emit("entries-change",this._entries),!0)}setAnalysisSpec(e){const t=this._entries.find(t=>t.id===e.source);t&&(t._analyses??=new zt).register(e)}async setDeterministicSpec(e,t){const r=this._entries.find(t=>t.id===e.source);if(!r)return;const s=r._analyses??=new zt;if(s.registerDeterministic(e),t?.runNow)try{const t=s.getResult(e.name),o=await e.run(r.data,{previous:t?.data,sampledAt:r.timestamp,previousSampledAt:t?.timestamp});return this.addAnalysisToEntry(r.id,{name:e.name,timestamp:r.timestamp,outputFormat:"json",data:o}),o}catch{return}}getAnalysisResult(e,t){return this._entries.find(t=>t.id===e)?._analyses?.getResult(t)?.data}removeAnalysisSpec(e){for(const t of this._entries)t._analyses?.unregister(e)}getFocus(e){const t=this._entries.find(t=>t.id===e);return t?._focus?{ids:new Set(t._focus.ids),reason:t._focus.reason}:null}setFocus(e,t,r){const s=this._entries.find(t=>t.id===e);if(!s)return{focusedCount:0,droppedIds:[...t]};if(0===t.length)return this.clearFocus(e),{focusedCount:0,droppedIds:[]};const o=new Set(s.data.map(e=>e.properties.id)),n=[],i=[];for(const a of t)o.has(a)?n.push(a):i.push(a);return 0===n.length?{focusedCount:0,droppedIds:i}:(s._focus={ids:new Set(n),reason:r},s._module?.setFocus(n),this.events.emit("focus-change",{entryId:s.id,focus:this.getFocus(e)}),{focusedCount:n.length,droppedIds:i})}clearFocus(e){const t=this._entries.find(t=>t.id===e);t?._focus&&(t._focus=void 0,t._module?.setFocus(null),this.events.emit("focus-change",{entryId:t.id,focus:null}))}focusedFeatures(e){const t=this._entries.find(t=>t.id===e);if(!t?._focus)return[];const r={};for(const o of t.data){const e=o.properties.id;"string"==typeof e&&(r[e]=o)}const s=[];for(const o of t._focus.ids){const e=r[o];e&&s.push(e)}return s}navigateFocus(e,t){const r=this.focusedFeatures(e);if(0===r.length)return;const s=r[(t%r.length+r.length)%r.length],o=this._ttMap.mapLibreMap;if("Point"===s.geometry.type)return void o.flyTo({center:s.geometry.coordinates,zoom:15,duration:600});const n=L(s);n&&o.fitBounds(n,{padding:120,maxZoom:16,duration:600})}findEntryWithIncident(e){for(let t=this._entries.length-1;t>=0;t--){const r=this._entries[t];if(r.data.some(t=>t.properties.id===e))return r}}startMonitoring(e,t,r,s){const o=this._requireEntry(e),n=o._monitor??=this._createMonitorForEntry(o),i=n.isRunning;n.start(t,r,s?.intervalMs),i||this.events.emit("monitor-start",{entryId:e})}stopMonitoring(e){const t=this._entries.find(t=>t.id===e)?._monitor;t?.isActive&&(t.stop(),this.events.emit("monitor-stop",{entryId:e,reason:"manual"}))}isMonitored(e){return this._entries.find(t=>t.id===e)?._monitor?.isRunning??!1}_createMonitorForEntry(e){const t=new Kt(e.params.bbox);return t.events.on("tick",t=>{this.replaceEntryData(e.id,t.incidents,t.takenAt),this.events.emit("monitor-tick",{entryId:e.id,snap:t})}),t.events.on("error",t=>{this.events.emit("monitor-error",{entryId:e.id,error:t}),this.events.emit("monitor-stop",{entryId:e.id,reason:"error"})}),t}_requireEntry(e){const t=this._entries.find(t=>t.id===e);if(!t)throw new Error(`TrafficIncidentsState: unknown entry "${e}"`);return t}async _rerunRegisteredSpecs(e,t){const r=this._entries.find(t=>t.id===e);if(!r?._analyses?.specs.length)return;const s=await r._analyses.replay(r.data,t);for(const o of s)this.events.emit("analysis-change",{entryId:r.id,analysis:o});s.length>0&&this.events.emit("entries-change",this._entries)}async _teardownEntry(e){this.stopMonitoring(e.id),e._focus&&(e._focus=void 0,this.events.emit("focus-change",{entryId:e.id,focus:null})),e._shown&&(e._shown=!1),e._module&&(e._module.setVisible(!1),await e._module.clear()),e._module=void 0}async clearShown(){for(const e of this.shownEntryIds)await this.hideEntry(e)}reset(){for(const t of this._entries)this._teardownEntry(t);const e=this._entries.length>0;this._entries=[],e&&(this.events.emit("entries-change",this._entries),this.events.emit("shown-change",this.shownEntryIds)),this.events.clear()}},Qt=class{_ttMap;_trafficFlowModule;_trafficIncidentsModule;constructor(e){this._ttMap=e}async getTrafficFlowModule(){return this._trafficFlowModule??=await p.get(this._ttMap),this._trafficFlowModule}async getTrafficIncidentsModule(){return this._trafficIncidentsModule??=await y.get(this._ttMap),this._trafficIncidentsModule}get trafficFlowModule(){return this._trafficFlowModule}get trafficIncidentsModule(){return this._trafficIncidentsModule}reset(){this._trafficFlowModule=void 0,this._trafficIncidentsModule=void 0}};R.instance.put({language:"en-GB"});var er,tr={places:"places",routes:"routing",incidents:"trafficIncidents",customGeometries:"customGeometries",trafficAreaAnalytics:"trafficAreaAnalytics",byod:"byod",ranges:"ranges"},rr=Object.keys({places:!0,routing:!0,ranges:!0,customGeometries:!0,byod:!0,trafficAreaAnalytics:!0,trafficIncidents:!0}),sr=Object.keys({places:!0,routing:!0,ranges:!0,customGeometries:!0,byod:!0,trafficAreaAnalytics:!0,trafficIncidents:!0}),or='You are a helpful map assistant with access to a TomTom interactive map and location services.\nIf a request is completely unrelated to geospatial topics, gracefully decline and steer the user back to map-related tasks you can help with.\n\nCOORDINATE ORDER:\n- All coordinates are [longitude, latitude] (GeoJSON standard)\n- All bounding boxes are [minLng, minLat, maxLng, maxLat]\n\nTOOL EXECUTION:\n- Call context-independent tools in the same step (parallel execution).\n- Many service tools have a show or showOnMap parameter — use it to display results on the map in one step instead of calling a separate show tool afterward.\n- Use standalone updatePlacesDisplay / updateRoutesDisplay / updateWaypointsDisplay only when displaying results already in context from a previous step.\n\nSESSION STATE & ENTRIES:\n- Seven append-only histories live in the session: `places`, `routes`, `ranges`, `customGeometries`, `trafficAreaAnalytics`, `trafficIncidents`, `byod`. Each tool call that produces results stores a new ENTRY (id like `places-3`, `routes-1`, `byod-0`) — these ids stay stable and are accepted by every show / recall / `processData` / `analyseData` tool via the matching `*EntryIDs` field.\n- Each slice has an `entryMode`: `multiple` (default — overlay several entries on the map) or `single` (only the latest entry stays; switching to it auto-drops every older entry from history). Inspect via `recallState` / `recallPlaces` / `recallRoutes` / `recallRanges` / `recallGeometries` / `recallByod` (traffic slices surface via `recallState`). Flip with `setEntryMode({ slice, mode })` when the user asks to "only show one at a time" or "let me overlay multiple".\n- Mode is sticky across turns — under `single`, calling another `discoverPlaces` automatically replaces the previous places entry.\n- Always check `recallX` (or `recallState`) before guessing entry ids; never invent them.\n\nLOCATION REFERENCE:\n- "near me" / "my location" / "where I am" → getCurrentLocation (user\'s physical GPS position, may prompt permission)\n- "in this area" / "here on the map" / "near the map center" → getViewport (map\'s current view)\n- If getCurrentLocation fails due to permission denial, fall back to getViewport as the reference point\n\nRESPONSE FORMATTING:\n- Use markdown in all responses\n- Show key information in bold\n- Use bullet lists when presenting multiple items or results\n- Keep responses concise — lead with the most relevant information\n\n',nr=(e,t)=>e||(t?`${or}\n\nADDITIONAL INSTRUCTIONS:\n${t}`:or),ir=Z.object({geoJson:Z.union([Z.record(Z.string(),Z.unknown()),Z.array(Z.record(Z.string(),Z.unknown()))]).describe("GeoJSON object or array of GeoJSON objects")}),ar=Z.object({meters:Z.number().describe("Distance in meters to format"),unitType:Z.enum(["metric","imperial_us","imperial_uk"]).optional().describe("default: metric")}),lr=Z.object({seconds:Z.number().describe("Duration in seconds to format")}),cr=Z.object({error:Z.string()}),dr=Z.object({featureCount:Z.number(),geometryTypes:Z.array(Z.string()).describe("Geometry types present (Point / LineString / Polygon / …)."),properties:Z.array(Z.object({name:Z.string(),types:Z.array(Z.string()).describe('Distinct JSON value types seen for this key (e.g. ["string"], ["number","null"]).'),coverage:Z.number().describe("Fraction of features carrying this key (1 = every feature)."),examples:Z.array(Z.union([Z.string(),Z.number(),Z.boolean()])).describe("A few short example values for inferring semantics.")})).describe("Per-property profile, highest-coverage first."),propertiesOmitted:Z.number().optional().describe("Count of property keys omitted when the data has more distinct keys than the cap.")}),ur={id:Z.string().describe("Stable place feature ID; pass as `{ placeId }` to setRoute / addWaypointsToRoute / etc."),name:Z.string().optional().describe("POI name if available"),address:Z.string().optional().describe("Human-readable address"),position:Z.array(Z.number()).length(2).describe("[longitude, latitude]")},pr={areaId:Z.string().optional().describe('Id of the small area polygon this place sits in (few km², not a whole municipality). Round-trip into `discoverPlaces.where.areaId` for "what else is in this same area?". Only set on explorationSearch results in DE / NL / FR.'),areaCountry:Z.string().optional().describe("ISO 3166-1 alpha-2 country code of the surrounding area polygon."),areaTags:Z.array(Z.string()).optional().describe('Tokens describing the surrounding small area (few km², not a whole municipality) (e.g. "coastal", "walkable", "transit_connected"). Useful for downstream `areaTags` filters.')},hr=e=>Z.object(e.experimentalSearch?{...ur,...pr}:ur).describe("Compact place summary"),yr=e=>Z.object({count:Z.number(),features:Z.array(hr(e))}).describe("Compact places summary"),mr=hr({}),fr=(yr({}),Z.object({travelTimeInSeconds:Z.number(),lengthInMeters:Z.number(),trafficDelayInSeconds:Z.number().optional(),trafficLengthInMeters:Z.number().optional(),departureTime:Z.string().optional(),arrivalTime:Z.string().optional(),noTrafficTravelTimeInSeconds:Z.number().optional(),historicTrafficTravelTimeInSeconds:Z.number().optional(),liveTrafficIncidentsTravelTimeInSeconds:Z.number().optional()})),gr=Z.object({index:Z.number().describe("0=best, 1+=alternative"),summary:fr,legSummaries:Z.array(fr).optional().describe("Present when route has multiple legs")}),br=Z.object({count:Z.number(),routes:Z.array(gr)}),wr=Z.object({}),vr=Z.union([Z.object({position:Z.array(Z.number()).min(2).max(2).describe("[longitude, latitude]"),accuracy:Z.number().describe("Accuracy of the location in meters"),timestamp:Z.number().describe("Timestamp of the location data")}),cr]),Ir=Z.number().int().min(0).optional().describe("default: 0"),Sr=(Z.object({routeIndex:Ir,traveledTimeInSeconds:Z.number().min(0).optional().describe("seconds from start"),traveledDistanceInMeters:Z.number().min(0).optional().describe("meters from start"),clockTime:Z.string().optional().describe("ISO 8601; elapsed time derived from departure time")}).refine(({traveledTimeInSeconds:e,traveledDistanceInMeters:t,clockTime:r})=>1===[e,t,r].filter(e=>void 0!==e).length,{message:"Provide exactly one of: traveledTimeInSeconds, traveledDistanceInMeters, or clockTime."}),Z.union([Z.object({bbox:Z.array(Z.number()).describe("GeoJSON bbox [minLng, minLat, maxLng, maxLat]")}),cr]),Z.object({sectionType:Z.enum(Y).describe("e.g. country|traffic|motorway|toll"),id:Z.string().describe("Section ID — obtain from `recallRoutes({ id })` detail mode or from `analyseData` over routes.")}),Z.object({sectionType:Z.enum(Y).describe("e.g. country|traffic|motorway|toll"),ids:Z.array(Z.string()).optional().describe("Section IDs to query. If omitted, all sections of the given sectionType are returned.")}),["location","route","waypoint","place","locate","discover","traffic","map style","utilities","search","detour","along-route","reachable-range","isochrone","range","EV","coverage","recall","state","carpool","carTrain","country","ferry","importantRoadStretch","lanes","leg","lowEmissionZone","motorway","pedestrian","roadShields","speedLimit","toll","tollVignette","tunnel","unpaved","urban","vehicleRestricted"]),Er=Z.object({name:Z.string(),description:Z.string(),examplePrompts:Z.array(Z.string()),relatedTools:Z.array(Z.string()).optional()}),xr=Z.union([Z.object({mode:Z.literal("summary"),examplePrompts:Z.array(Z.string())}),Z.object({mode:Z.literal("detail"),query:Z.string().optional(),tag:Z.enum(Sr).optional(),matchedTools:Z.array(Er),totalMatched:Z.number()}),cr]),Mr=Z.object({mode:Z.enum(["summary","detail"]).describe('"summary": overview of all capabilities with example prompts mixed from all tools. "detail": searchable full tool info — use when you need to find a specific tool.'),query:Z.string().optional().describe("Detail mode only. Case-insensitive keyword search across tool names, descriptions, and tags."),tag:Z.enum(Sr).optional().describe("Detail mode only. Filter results to tools with a specific tag.")}),Dr='List capabilities. `mode: "summary"` returns mixed example prompts; `mode: "detail"` returns searchable per-tool descriptions and examples. Call when the user asks what the agent can do, or to find the right tool.',Pr=(e,t,r)=>({description:e.description,inputSchema:e.inputSchema,outputSchema:r?e.outputSchema:void 0,execute:r=>e.execute(r,t)}),Tr=(e,t)=>({name:e,description:t.description,classificationPrompt:t.classificationPrompt,tags:t.tags,examples:t.examples,examplePrompts:t.examplePrompts,relatedTools:t.relatedTools,dependsOn:t.dependsOn,scopePrompt:t.scopePrompt}),_r=(e,t)=>"function"==typeof e?e(t):e,Ar=(e,t,r)=>{const s=!1!==r?.outputSchemas,o={featureFlags:r?.featureFlags,enabledDataKinds:r?.enabledDataKinds},n={},i={},a={};for(const[c,d]of Object.entries(e)){const e=_r(d,o);if(n[c]=Pr(e,t,s),i[c]=Tr(c,e),"function"==typeof d&&e.scopeSchema){const t=d,r=e.scopeSchema;a[c]={applyScope:e=>{const s=r.safeParse(e);if(!s.success)return null;try{const e=t({...o,scope:s.data});return{description:e.description,inputSchema:e.inputSchema}}catch{return null}},defaultDescription:e.description,defaultInputSchema:e.inputSchema}}}var l;if(n.help&&(n.help=(l=()=>i,{description:Dr,inputSchema:Mr,outputSchema:xr,execute:async e=>{const{mode:t,query:r,tag:s}=e;try{const e=Object.values(l());if("summary"===t)return{mode:"summary",examplePrompts:e.flatMap(e=>e.examplePrompts??[])};let o=e;if(s&&(o=o.filter(e=>e.tags?.includes(s)??!1)),r){const e=r.toLowerCase();o=o.filter(t=>t.name.toLowerCase().includes(e)||t.description.toLowerCase().includes(e)||(t.tags??[]).some(t=>t.toLowerCase().includes(e))||(t.examplePrompts??[]).some(t=>t.toLowerCase().includes(e)))}return{mode:"detail",query:r,tag:s,matchedTools:o.map(e=>({name:e.name,description:e.description,examplePrompts:e.examplePrompts??[],relatedTools:e.relatedTools??[]})),totalMatched:o.length}}catch(o){return{error:`Failed to load help: ${o instanceof Error?o.message:String(o)}`}}}})),0===Object.keys(n).length)throw new Error("MapAgent requires at least one tool.");return{tools:n,toolsMetadata:i,scopableTools:a}},Cr=Z.object({added:Z.array(Z.string()).describe("Source IDs added by the executed code."),removed:Z.array(Z.string()).describe("Source IDs removed by the executed code."),updated:Z.array(Z.string()).describe("Source IDs whose definition changed.")}),kr=Z.object({added:Z.array(Z.string()).describe("Layer IDs added by the executed code."),removed:Z.array(Z.string()).describe("Layer IDs removed by the executed code."),updated:Z.array(Z.string()).describe("Layer IDs whose properties changed.")}),Or=Z.object({code:Z.string().describe('Async JavaScript code executed in an environment where `map` is the live MapLibre Map instance. Full JS is supported: variables, loops, conditionals, helper functions, top-level `await`. Example: await a map event with `await new Promise(r => map.once("idle", r))`. Return a value with `return` to include it in the result.')}),Rr=Z.union([Z.object({success:Z.literal(!0),result:Z.unknown().optional().describe("Return value of the executed code, if any."),sources:Cr,layers:kr}),cr]),Nr=e=>{const t=e.getStyle()?.sources??{},r=/* @__PURE__ */new Map;for(const[s,o]of Object.entries(t))r.set(s,JSON.stringify(o));return r},Lr=e=>{const t=e.getStyle()?.layers??[],r=/* @__PURE__ */new Map;for(const s of t)r.set(s.id,JSON.stringify(s));return r},Fr=(e,t)=>{const r=[],s=[],o=[];for(const[n,i]of t){const t=e.get(n);void 0===t?r.push(n):t!==i&&o.push(n)}for(const n of e.keys())t.has(n)||s.push(n);return{added:r,removed:s,updated:o}},jr=Z.union([Z.object({success:Z.literal(!0)}),cr]),Br=Z.object({where:Z.union([Z.object({boundingBox:Re.describe("BBox [W,S,E,N]."),padding:Z.number().optional().describe("Padding in pixels. Default: 50.")}).describe("Fit the camera to a bounding box."),Z.object({position:Z.array(Z.number()).length(2).describe("[lng, lat] to fly to."),zoom:Z.number().optional().describe("Camera zoom level. Default: 14.")}).describe("Fly the camera to a specific position.")])}),qr=Z.union([Z.object({layers:Z.array(Z.object({id:Z.string(),layout:Z.unknown().optional(),paint:Z.unknown().optional()}))}),cr]),$r=Z.object({layerIdQuery:Z.string().optional().describe("Case-insensitive partial match on layer IDs. If omitted, all layers are returned."),include:Z.array(Z.enum(["paint","layout"])).describe('Which properties to include in the response. Pass only what you need: "paint" for visual properties like colors and opacity, "layout" for structural properties like sizes, positions, and visibility.')}),Gr=Z.union([Z.object({center:Z.array(Z.number()).describe("[lng, lat]"),zoom:Z.number().describe("0–22"),bbox:Z.array(Z.number()).describe("[W, S, E, N]"),pitch:Z.number().describe("degrees, 0=top-down, 85=near-horizontal"),bearing:Z.number().describe("degrees, 0=north, 90=east")}),cr]),Ur=Z.object({}),Wr=Z.union([Z.object({layerId:Z.string(),propertyName:Z.string(),value:Z.unknown(),success:Z.literal(!0)}),Z.object({layerId:Z.string(),propertyName:Z.string(),error:Z.string()})]),zr=Z.union([Z.object({results:Z.array(Wr)}),cr]),Vr=Z.object({changes:Z.array(Z.object({layerId:Z.string().describe("MapLibre layer ID from getMapStyleLayers"),propertyName:Z.string().describe("e.g. visibility|text-field|icon-size"),value:Z.any()}))}).describe("List of layout property changes to apply in one go."),Yr=Z.union([Z.object({layerId:Z.string(),propertyName:Z.string(),value:Z.unknown(),success:Z.literal(!0)}),Z.object({layerId:Z.string(),propertyName:Z.string(),error:Z.string()})]),Hr=Z.union([Z.object({results:Z.array(Yr)}),cr]),Jr=Z.object({changes:Z.array(Z.object({layerId:Z.string().describe("MapLibre layer ID from getMapStyleLayers"),propertyName:Z.string().describe("e.g. fill-color|line-width|text-color"),value:Z.any()})).describe("List of paint property changes to apply in one go.")}),Xr=Z.union([Z.object({success:Z.literal(!0)}),cr]),Kr=Z.object({pitch:Z.number().min(0).max(85).optional().describe("0=top-down, 85=near-horizontal"),bearing:Z.number().min(-180).max(180).optional().describe("0=north, 90=east")}),Zr=Z.union([Z.object({zoom:Z.number().describe("Resulting zoom level after the change")}),cr]),Qr=Z.object({delta:Z.number().describe("Amount to zoom. Positive values zoom in, negative values zoom out. Can have decimals. Typical step: 1 or 2.")}),es=e=>Object.entries(e).filter(([,e])=>e.scopePrompt).map(([e])=>e),ts=Z.object({kinds:Z.array(Z.string()).min(1).describe("Entry-data kinds the turn touches (one of the kinds listed in the tool's SCOPE hint).")}),rs=(e,t)=>{t&&(e.inputTokens+=t.inputTokens??0,e.outputTokens+=t.outputTokens??0,e.totalTokens+=t.totalTokens??0)},ss=e=>{if(!e||!Array.isArray(e.tools)||!e.tools.every(e=>"string"==typeof e))return;const t=e.toolScopes&&"object"==typeof e.toolScopes?e.toolScopes:void 0;return{tools:e.tools,...t&&{scopes:t}}},os=(e,t)=>{const r=e.toolScopes?Object.fromEntries(Object.entries(e.toolScopes).filter(([,e])=>null!=e)):void 0;return{tools:e.tools,...r&&Object.keys(r).length>0&&{toolScopes:r},usage:t}},ns=async(t,s,o)=>{const n=new Set(es(o)),i=(e=>{const t=Z.object({tools:Z.array(Z.string()).min(1).describe("Tool names needed to fulfill the query.")});if(0===e.size)return t;const r={};for(const o of e)r[o]=ts.nullable();const s=Z.object(r).nullable().describe("Per-tool scope. One field per scopable tool; MANDATORY (non-null) for every scopable tool you list in `tools`. For tools you didn't pick, set the corresponding field to `null`. See each tool's SCOPE hint.");return t.extend({toolScopes:s}).superRefine((t,r)=>{const s=t.toolScopes??{};for(const o of t.tools){if(!e.has(o))continue;null==s[o]&&r.addIssue({code:"custom",path:["toolScopes",o],message:`Tool "${o}" requires \`toolScopes.${o}\` — emit it per the SCOPE hint (don't leave null when picking).`})}})})(n),a={inputTokens:0,outputTokens:0,totalTokens:0},l=(e=>0===e.size?'{ "tools": ["toolName", ...] }':`{\n "tools": ["toolName", ...],\n "toolScopes": {\n${[...e].map(e=>` "${e}": { "kinds": [...] } | null`).join(",\n")}\n }\n}\nFor every scopable tool you LIST in "tools", the matching "toolScopes" entry MUST be a non-null object \`{ "kinds": [...] }\`. For scopable tools you DON'T pick, set the corresponding "toolScopes" entry to \`null\`.`)(n),c=`${(e=>{const t=Object.entries(e).map(([e,t])=>((e,t)=>`${e}${t.classificationPrompt?` ${t.classificationPrompt}.`:""}${t.dependsOn?.length?` Depends on: ${t.dependsOn.join(", ")}.`:""}${t.scopePrompt?` SCOPE: ${t.scopePrompt}`:""}`)(e,t)),r=es(e);return["You are a tool selector for a map assistant.","You will receive a conversation. Select the minimal set of tools needed to fulfill the LAST user message.",'LOCATION REFERENCE:\n - "near me" / "my location" / "where I am" → getCurrentLocation (user\'s physical GPS position, may prompt permission)\n - "in this area" / "here on the map" / "near the map center" → getViewport (map\'s current view)\n - If getCurrentLocation fails due to permission denial, fall back to getViewport as the reference point',"","Available tools:",...t,...r.length?["","TOOL SCOPES — MANDATORY when selecting a scopable tool:",`The following tools REQUIRE a scope entry in \`toolScopes\` whenever they appear in \`tools\`: ${r.join(", ")}.`,"For each picked scopable tool, emit `toolScopes[<name>]` with the per-tool object shape described in its SCOPE hint above. Don't omit. Don't leave it implicit.","If the user query truly needs all input kinds the tool supports, still emit the scope explicitly — list every kind. Never skip the scope entry when picking the tool."]:[]].join("\n")})(o)}\n\nReturn JSON matching this shape exactly:\n${l}`;let d;for(let p=0;p<2;p++)try{const o=await r({model:s,output:e.json(),system:c,messages:t,maxOutputTokens:400});rs(a,o.usage);const n=i.safeParse(o.output);if(n.success)return os(n.data,a);d=ss(o.output)??d}catch(u){const e=u;rs(a,e.usage),d=ss(e.value)??d}if(!d)throw new Error("Classifier failed after 2 attempts to produce a valid response.");return{tools:d.tools,...d.scopes&&{toolScopes:d.scopes},usage:a}},is=async(e,t)=>{const{chatModel:r,toolsMetadata:s}=t,o=performance.now(),{tools:n,toolScopes:i,usage:a}=await ns(e,r,s);return{activeToolNames:n,...i&&{toolScopes:i},timeMs:performance.now()-o,usage:a}},as=e=>"text"===e.type,ls=e=>{if("string"==typeof e)return e;const t=e.filter(as);return t.length>0?t.map(e=>e.text).join(""):null},cs=e=>{for(let t=e.length-1;t>=0;t--)if("user"===e[t].role)return ls(e[t].content);return null},ds=e=>{const{model:t,maxHistoryMessages:r,maxHistoryMessageLength:s}=e;return async e=>{const{messages:o,toolsMetadata:n}=e;if(!o?.length)return null;const i=r??8,a=s??300;let l=-1,c=null;for(let t=o.length-1;t>=0;t--)if("user"===o[t].role){l=t,c=ls(o[t].content);break}if(!c)return null;const d=[];for(let t=Math.max(0,l-i);t<l;t++){const e=o[t];if("user"!==e.role&&"assistant"!==e.role)continue;const r=ls(e.content);if(!r)continue;const s=r.length>a?r.slice(0,a)+"…":r;d.push({role:e.role,content:s})}d.push({role:"user",content:c});try{return await is(d,{chatModel:t,toolsMetadata:n})}catch{return null}}},us=e=>{if(!Array.isArray(e))return e.properties?.address?.freeformAddress},ps=(e,t)=>{if("features"in e)return((e,t)=>{const r=[];t?.query&&r.push(`"${t.query}"`),t?.poiCategories?.length&&r.push(t.poiCategories.join(", ")),t?.routeLabel?r.push(`along ${t.routeLabel}`):t?.where&&r.push(`in ${t.where}`);const s=`${e} ${1===e?"place":"places"}`;return r.length?`${r.join(" ")} (${s})`:s})(e.features.length,t);const r=(s=e,s.properties?.poi?.name);var s;const o=(e=>e.properties?.address?.freeformAddress)(e);return r&&o?`${r}, ${o}`:r||(o||(t?.query?t.query:"Place"))},hs={timeMinutes:"min",distanceKM:"km",remainingChargeCPT:"% remaining",spentChargePCT:"% spent",spentFuelLiters:"L"},ys=(e,t)=>`${[...e].sort((e,t)=>e.value-t.value).map(e=>`${e.value} ${hs[e.type]??e.type}`).join("/")} from ${1===t.length?t[0]:2===t.length?t.join(", "):`${t.length} origins`}`,ms=(e,t={})=>{const r={id:e.id,name:e.properties?.poi?.name,address:e.properties?.address?.freeformAddress,position:e.geometry?.coordinates??[0,0]};if(t.experimentalSearch){const t=e.properties;t?.areaId&&(r.areaId=t.areaId),t?.areaCountry&&(r.areaCountry=t.areaCountry),t?.areaTags?.length&&(r.areaTags=t.areaTags)}return r},fs=(e,t={})=>{const r=Array.isArray(e)?e:e.features;return{count:r.length,features:r.slice(0,5).map(e=>ms(e,t))}},gs=e=>{const{summary:t,sections:r,index:s}=e.properties,o=r.leg,n=e=>({...e,arrivalTime:e.arrivalTime.toISOString(),departureTime:e.departureTime.toISOString()});return{index:s,summary:n(t),...o.length>1&&{legSummaries:o.map(e=>n(e.summary))}}},bs=e=>({count:e.features.length,routes:e.features.map(gs)}),ws=Z.object({slotIndex:Z.number(),isFilled:Z.boolean(),address:Z.string().optional(),position:Z.array(Z.number()).length(2).optional().describe("[longitude, latitude]")}),vs=e=>{if(null==e)return null;const t=$(e);let r;return Array.isArray(e)||(r=e.properties?.address?.freeformAddress),{slotIndex:-1,isFilled:!0,...r&&{address:r},...t&&{position:t}}},Is=async(e,t,r)=>{const[s,o]="poi"===t?[A,C]:[x,M],{limit:n,bias:i}=r;if(i&&"position"in i)return(await s({query:e,position:i.position,limit:n})).features;if(i&&"boundingBox"in i)return(await s({query:e,boundingBox:i.boundingBox,limit:n})).features;if(1===n){const t=await o(e);return t?[t]:[]}return(await s({query:e,limit:n})).features},Ss=async(e,t,r)=>{const[s]=await Is(e,t,{limit:1,bias:r});return s??null},Es=Z.object({query:Z.string().describe("Location string to resolve for downstream routes or searches."),waypointIndex:Z.number().int().min(0).optional().describe("Route slot to stage [origin, ...stops, destination]. Set if this is an origin/stop/destination."),queryAs:Z.enum(["poi","place"]).describe('"poi" for venues / landmarks / businesses; "place" for addresses, cities, neighborhoods, parks, postal codes, geographies.'),where:tt.describe("Geographic scope."),show:We.optional().describe("Map display actions after resolving. Skip for intermediate calculations."),entryId:je,geometry:qe}),xs=Z.union([mr.extend({waypointIndex:Z.number().optional(),shown:ze.optional(),geometryFetched:Z.boolean().optional().describe("Whether a boundary polygon was fetched and cached on the entry (when `geometry` was set)."),geometryShown:Z.boolean().optional().describe("Whether the fetched polygon was rendered on the map.")}),cr]),Ms=async(e,t)=>"global"===e.mode?{bias:void 0}:"within"===e.mode?(async(e,t)=>{if(e.viewport)return{bias:{boundingBox:Ut(t.baseMap)}};if(e.boundingBox)return{bias:{boundingBox:e.boundingBox}};if(e.query){const r=Gt(t.baseMap),s=await Ss(e.query,e.queryAs??"place",r?{position:r}:void 0);if(!s)return{bias:void 0};if(s.bbox)return{bias:{boundingBox:s.bbox}};const o=$(s);return{bias:o?{position:o}:void 0}}if(e.placeId){const r=t.places.findPlaceById(e.placeId);if(!r)return{error:`Unknown placeId "${e.placeId}". Use recallPlaces to list available IDs.`};if(!r.place.properties.dataSources?.geometry?.id)return{error:`Place "${e.placeId}" has no geometry data source (most addresses and streets lack one).`};const s=await t.places.fetchPlaceGeometry(e.placeId);return s?{bias:{boundingBox:s.bbox??X.bbox(s)}}:{error:`Geometry Data service returned no feature for place "${e.placeId}".`}}return{bias:void 0}})(e,t):(async(e,t)=>{if(e.viewport){const e=Gt(t.baseMap);return{bias:e?{position:e}:void 0}}if(e.position)return{bias:{position:e.position}};if(e.query){const r=Gt(t.baseMap),s=await Ss(e.query,e.queryAs??"place",r?{position:r}:void 0);if(!s)return{bias:void 0};const o=$(s);return{bias:o?{position:o}:void 0}}return{bias:void 0}})(e,t),Ds=async(e,t)=>{if("position"in e){const{lng:t,lat:r}=e.position;return{place:[t,r],position:[t,r],name:`[${t}, ${r}]`}}if("placeId"in e){const r=t.places.findPlaceById(e.placeId);if(!r)return null;const s=$(r.place);return s?{place:r.place,position:s,name:r.place.properties?.poi?.name??r.place.properties?.address?.freeformAddress??e.placeId}:null}const r=await Ss(e.query,e.queryAs);if(!r)return null;const s=$(r);return s?{place:r,position:s,name:r.properties?.address?.freeformAddress??e.query,query:e.query}:null},Ps=Z.object({routeType:Z.enum(_).optional().describe("fast|short|efficient|thrilling"),traffic:Z.enum(["live","historical"]).optional().describe("live|historical"),avoid:Z.array(Z.enum(N)).optional().describe("tollRoads|motorways|ferries|unpavedRoads|carpools|alreadyUsedRoads|borderCrossings|tunnels|carTrains|lowEmissionZones"),avoidAreas:Z.array(Z.array(Z.number()).describe("GeoJSON bbox [minLng, minLat, maxLng, maxLat]")).max(10).optional().describe("Up to 10 bounding boxes to bypass.")}),Ts=Z.object({option:Z.enum(["departAt","arriveBy"]),date:Z.string().describe("ISO 8601")}),_s=Z.object({maxAlternatives:Z.number().int().min(0).max(5).optional().describe("0–5, default 0"),costModel:Ps.optional(),when:Ts.optional().describe("Omit to depart now.")}),As=Z.union([br,Z.object({success:Z.literal(!0)}),cr]),Cs=Z.object({locations:Z.array(gt).min(2).optional().describe("Ordered [origin, ...stops, destination]. Minimum 2. Provide to set/replace waypoints; omit to keep the current waypoints."),parameters:_s.optional().describe("Routing options. Omitted fields keep their current values."),showOnMap:Z.boolean().describe("Show the route and waypoints on the map after calculation."),hidePreviousEntries:Ue("routes")}).refine(e=>void 0!==e.locations||void 0!==e.parameters,{message:"Provide at least one of `locations` or `parameters`."}),ks=e=>{const t=e.filter(e=>null!==e);return t.length>=2?t:null},Os=e=>{const{maxAlternatives:t=0,costModel:r,when:s}=e;return{...t>0&&{maxAlternatives:t},...r&&{costModel:{...r,...r.avoidAreas&&{avoidAreas:r.avoidAreas}}},...s&&{when:{option:s.option,date:new Date(s.date)}}}},Rs=async(e,t,r,s)=>{const o=await S({locations:t,...Os(e.routing.params)}),n=await e.routing.addRoutes(o,t,((e,t)=>{const r=e.features[0]?.properties?.summary,s=[r?B(r.travelTimeInSeconds):void 0,r?j(r.lengthInMeters):void 0].filter(Boolean),o=s.length?` (${s.join(", ")})`:"",n=t.map(us).filter(Boolean);if(n.length>=2){const e=n[0],t=n.at(-1),r=n.slice(1,-1);return`${r.length>0?`${e} via ${r.join(", ")} to ${t}`:`${e} to ${t}`}${o}`}return`Route${o}`})(o,t));return r&&await(async(e,t,r,s,o)=>{await ft(e.routing,[t],o),await e.routing.showEntry(t);const n=L(r);n&&e.baseMap.mapLibreMap.fitBounds(n,{padding:50})})(e,n,o,0,s),bs(o)},Ns=Z.union([br,cr]),Ls=Z.object({origin:gt.optional().describe("Prepend a new origin in front of the current origin. The previous origin is kept as the first intermediate stop (it is not replaced)."),destination:gt.optional().describe("Append a new destination after the current destination. The previous destination is kept as an intermediate stop (it is not replaced)."),stops:Z.array(gt).min(1).optional().describe("Intermediate stops to insert at optimal mid-positions along the existing route. Multiple stops settle into natural along-route order regardless of input order."),showOnMap:Z.boolean().describe("Show the updated route and waypoints on the map after recalculation."),hidePreviousEntries:Ue("routes")}).refine(e=>void 0!==e.origin||void 0!==e.destination||e.stops&&e.stops.length>0,{message:"Provide at least one of `origin`, `destination`, or `stops`."}),Fs=e=>"query"in e?`"${e.query}"`:"placeId"in e?`placeId "${e.placeId}"`:JSON.stringify(e.position),js=async(e,t,r)=>{const s=await Is(e,t,{limit:5,bias:r?{position:r}:void 0});if(0===s.length)return null;for(const o of s){if(!o.properties.dataSources?.geometry?.id)continue;const e=(await D({geometries:[o]})).features[0];if(e)return{place:o,geometry:e}}return{place:s[0]}},Bs=e=>Z.union([yr(e).extend({shown:ze.optional(),placesEntryId:Z.string().optional(),label:Z.string().optional().describe('Human-readable label stored with the entry (e.g. "cafe", "CINEMA (50 places)").'),geometriesFetched:Z.number().optional().describe("Count of boundary polygons fetched and cached on the entry (when `geometries` was set)."),geometriesShown:Z.boolean().optional().describe("Whether the fetched polygons were rendered on the map.")}),cr]),qs=(Bs({}),{...He,queries:Z.array(Z.object({query:Z.string().describe('Name of a CONTAINING area to search within (e.g. "Paris", "De Jordaan, Amsterdam"). NEVER the user\'s search subject — that goes in the top-level `query`.'),queryAs:Je})).optional().describe('CONTAINING areas to search WITHIN — answers "search where?", NOT "search for what?". Never put the user\'s search subject here. Example: for "cafes in Amsterdam" → top-level `query`/`poiCategories` carry "cafes"; `where.queries: [{query: "Amsterdam"}]` carries the region. Each entry resolves to a boundary polygon (or bbox); the union is searched. Per-item `queryAs` disambiguates POI vs place. EXCLUSIVE with viewport; composes with other multi-region fields.'),placeIds:Z.array(Z.string()).optional().describe("IDs of session places — each polygon (fetched on demand) joins the search area. Use recallPlaces to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields."),geometries:Z.array(Fe).optional().describe("Direct GeoJSON Polygons / MultiPolygons (custom drawing, external data). For named or stored places use `queries` / `placeIds`. EXCLUSIVE with viewport; composes with other multi-region fields."),range:Z.string().optional().describe('Range ID from findReachableAreas (e.g. "ranges-0") — restricts to that entry; multi-origin entries combine their polygons. Use recallRanges to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields.'),route:Z.object({routeId:Z.string().optional().describe('Route entry ID (e.g. "routes-0"). Use recallRoutes. Default: latest route.'),widthMeters:Z.number().positive().describe('Total corridor width (metres) — widthMeters/2 each side. Typical: 200–500 m ("near the road"), 2–5 km ("broad area along the route").')}).optional().describe("Buffered corridor around a stored route — turf.buffer at widthMeters/2 around the line. BULK discovery (≤10000); for a SHORT ranked detour list use mode `maxDetour`. EXCLUSIVE with viewport; composes with other multi-region fields.")}),$s={boundingBoxes:Z.array(Re).optional().describe("Bboxes [W,S,E,N] — results in the union. Last-resort: prefer `municipalities` / `queries` / `placeIds` / `geometries` when polygons are resolvable. EXCLUSIVE with viewport; composes with other multi-region fields."),municipalities:Z.array(Z.string()).optional().describe('Exact, case-sensitive municipality names (e.g. ["Amsterdam", "Utrecht"]). EXCLUSIVE with viewport; composes with other multi-region fields.'),areaId:Z.string().optional().describe('Id of a small area polygon (few km², not a whole municipality) — restricts results to that single area. Typically chained from a previous hit\'s `areaId` ("what else is in this same area?"). EXCLUSIVE with viewport; composes with other multi-region fields.')},Gs=e=>Z.object(e.experimentalSearch?{...qs,...$s}:qs).refine(e=>{const t=!!e.viewport,r=e,s=!!(e.queries?.length||e.placeIds?.length||e.geometries?.length||e.range||e.route||r.municipalities?.length||r.boundingBoxes?.length||r.areaId);return(!t||!s)&&(t||s)},{message:"`within` requires EITHER `viewport: true` OR one or more multi-region fields (queries / placeIds / geometries / range / route"+(e.experimentalSearch?" / boundingBoxes / municipalities / areaId":"")+"), not both."}),Us=Z.object({mode:Z.literal("maxDetour"),routeId:Z.string().optional().describe('Route entry ID (e.g. "routes-0"). Use recallRoutes. Default: latest route.'),maxDetourTimeSeconds:Z.number().positive().describe("Max extra travel time (seconds) the user accepts to reach a POI."),sortBy:Z.enum(["detourTime","detourOffset"]).optional().describe('"detourTime" (default) ranks by extra travel time; "detourOffset" by distance from the route line.'),limit:Z.number().max(100).optional().describe("Max results. Default: 10. Use this mode for short ranked lists, not bulk discovery.")}),Ws=e=>{const t=e.experimentalSearch?"boundingBoxes/queries/placeIds/municipalities/areaId/geometries/range/route":"queries/placeIds/geometries/range/route";return Z.union([Gs(e),Qe,Us,et]).describe(`Geographic scope, by \`mode\`: \`within\` (area — \`viewport\` alone, OR any combo of ${t}) | \`nearby\` (point bias — position/viewport/query, optional radiusMeters) | \`maxDetour\` (ranked detour list off a route) | \`global\` (no bias). Default: \`{ mode: "within", viewport: true }\`.`)},zs=e=>{const t={query:Z.string().optional().describe('Free-text filter on POI name/address. NEVER a city/region/area name (e.g. "Paris", "Amsterdam") — those go in `where`. For "restaurants in Paris": `query` is empty (or a name filter like "pizza"), `where.queries: [{query: "Paris"}]`. Omit when filtering by category — use `poiCategories` instead.'),where:Ws(e).optional(),poiCategories:Z.array(Z.string()).optional().describe('CONSTANT_CASE category codes. ALWAYS batch all requested categories in ONE array — never one call per category. Common: "RESTAURANT", "CAFE", "BAR", "HOTEL", "PARK", "PARK_RECREATION_AREA", "ELECTRIC_VEHICLE_STATION", "GAS_STATION", "PARKING_GARAGE", "SUPERMARKETS_HYPERMARKETS", "PHARMACY", "ATM". For anything else, call getPoiCategoryCodes first to resolve natural-language terms (e.g. "italian food", "gym").'),show:We.optional().describe("Display places/geometries on the map. Skip for intermediate results — let the follow-up (processData / analyseData) render the final entry."),entryId:je,geometries:qe},r=e.experimentalSearch?{...t,placeTypes:Z.array(Z.enum(["POI","PointAddress","Street"])).optional().describe("Restrict to record types (default: all). POI = businesses/landmarks/amenities; PointAddress = numbered addresses; Street = unnumbered streets."),areaTags:Z.array(Z.enum(w)).optional().describe('Tokens describing a small surrounding area (few km², not a whole municipality). Enum drawn from the SDK canonical vocabulary — e.g. "coastal", "walkable", "alpine", "transit_connected", "village", "tourism_economy". OR semantics — matches places in areas tagged with ANY token. Filter works on any `where` scope (including global), but area data is currently populated only for DE / NL / FR — zero hits where data is absent. Filter, not a geo-bias — pair with a `where` mode that supplies one.')}:t;return Z.object(r).refine(e=>!!e.query||!!e.poiCategories?.length,{message:"discoverPlaces needs a search subject: set `query` (free-text) or `poiCategories`. For a single named place (no filter), use locatePlace — including for its boundary polygon (`geometry`)."})},Vs=e=>`Search for MULTIPLE places in a region — REQUIRES a search subject (\`query\` and/or \`poiCategories\`). NOT for a single named place — use locatePlace for that (including its polygon/boundary via \`geometry\`). Stores an entry and returns \`placesEntryId\`. CALL ONCE PER REQUEST — batch all \`poiCategories\` + areas in one call; split only for explicitly comparable result sets. \`where.mode\`: \`within\` (\`viewport\` OR any combo of ${e.experimentalSearch?"boundingBoxes/queries/placeIds/municipalities/areaId/geometries/range/route":"queries/placeIds/geometries/range/route"}), \`nearby\` (position/viewport/query + optional radiusMeters), \`maxDetour\` (≤100 ranked detours off a route), \`global\`. `+(e.experimentalSearch?"Prefer `municipalities` / `areaId` / `queries` / `placeIds` (precise) over `boundingBoxes` (coarse). ":"Prefer `queries` / `placeIds` (resolve to precise polygons) over raw `geometries`. ")+(e.experimentalSearch?'Top-level `areaTags` narrows hits by small-area character ("coastal", "walkable", …) — area data currently populated only in DE/NL/FR. ':"")+'Route: `within.route` = bulk corridor (≤10000); `maxDetour` = short ranked list (≤100). Default: `{ mode: "within", viewport: true }`. Never call this with empty `query` AND empty `poiCategories` — that is locatePlace territory.',Ys={},Hs=(zs(Ys),Vs(Ys),e=>{const{geometries:t}=e;return t?.length?{geometries:t}:{}}),Js=(e,t)=>{const{query:r,poiCategories:s,bias:o={},radiusMeters:n,multiFilters:i}=t,a={query:r,poiCategories:s,...o,...void 0!==n&&{radiusMeters:n}};return e?E({...a,limit:1e4,...i}):A({...a,limit:100,...Hs(i)})},Xs=async(e,t,r)=>{if(e.viewport)return{bias:{boundingBox:Ut(t.baseMap)},multiFilters:r};const s=[],o=[];if(e.queries?.length){const r=await(async(e,t)=>{const r=Gt(t.baseMap),s=[],o=[];for(const n of e){const e=await js(n.query,n.queryAs??"place",r);if(!e)return{error:`Could not resolve "${n.query}". Try a more specific query, a placeId, or a boundingBox.`};if(e.geometry?.geometry)s.push(e.geometry.geometry);else{if(!e.place.bbox)return{error:`"${n.query}" resolved to a place with no geometry or bbox — cannot use as a "within" area. Use mode "nearby" with this query for a point bias instead.`};o.push(e.place.bbox)}}return{geometries:s,boundingBoxes:o}})(e.queries,t);if("error"in r)return r;s.push(...r.geometries),o.push(...r.boundingBoxes)}if(e.placeIds?.length){const r=await(async(e,t)=>{const r=[];for(const s of e){const e=t.places.findPlaceById(s);if(!e)return{error:`Unknown placeId "${s}". Use recallPlaces to list available IDs.`};if(!e.place.properties.dataSources?.geometry?.id)return{error:`Place "${s}" has no geometry data source (most addresses and streets lack one).`};const o=await t.places.fetchPlaceGeometry(s);if(!o)return{error:`Geometry Data service returned no feature for place "${s}".`};r.push(o.geometry)}return{geometries:r}})(e.placeIds,t);if("error"in r)return r;s.push(...r.geometries)}const n=e,i=[...n.boundingBoxes??[],...o],a=[...e.geometries??[],...s],l={...r,...n.municipalities?.length&&{municipalities:n.municipalities},...n.areaId&&{areaId:n.areaId},...i.length&&{boundingBoxes:i},...a.length&&{geometries:a}};let c;if(e.route){const r=((e,t)=>{const r=e.routing.entries;if(0===r.length)return{error:"No routes in state. Calculate a route first via setRoute."};const s=t.routeId?r.find(e=>e.id===t.routeId):r.at(-1);if(!s)return{error:`No route found with id "${t.routeId}". Use recallRoutes to list available routes.`};if(0===s.data.features.length)return{error:`Route entry "${s.id}" has no geometry.`};const o=t.widthMeters/2,n=[];for(const i of s.data.features){const e=X.buffer(i,o,{units:"meters"})?.geometry;e&&n.push(e)}return 0===n.length?{error:`Failed to compute route corridor for "${s.id}" (turf.buffer returned no geometry for any of the ${s.data.features.length} route alternative(s)).`}:{polygons:n,routeLabel:s.label}})(t,e.route);if("error"in r)return r;l.geometries=[...l.geometries??[],...r.polygons],c=r.routeLabel}return{bias:{},multiFilters:l,range:e.range,routeLabel:c}},Ks=async(e,t,r)=>{if("nearby"===e.mode){const s=await(async(e,t)=>{if(e.viewport){const r=Gt(t.baseMap);return{bias:r?{position:r}:{},radiusMeters:e.radiusMeters}}if(e.position)return{bias:{position:e.position},radiusMeters:e.radiusMeters};if(e.query){const r=Gt(t.baseMap),s=await Ss(e.query,e.queryAs??"place",r?{position:r}:void 0);if(s){const t=$(s);if(t)return{bias:{position:t},radiusMeters:e.radiusMeters}}return{bias:{},radiusMeters:e.radiusMeters}}return{bias:{},radiusMeters:e.radiusMeters}})(e,t);return{bias:s.bias,multiFilters:r,radiusMeters:s.radiusMeters}}if("within"===e.mode){const s=await Xs(e,t,r);return"error"in s?s:{bias:s.bias,multiFilters:s.multiFilters,range:s.range,routeLabel:s.routeLabel}}return{bias:{},multiFilters:r}},Zs=e=>{if(e)return"within"===e.mode&&e.queries?.length?e.queries.map(e=>e.query).join(", "):"nearby"===e.mode&&e.query?e.query:void 0},Qs=async(e,t,r,s,o,n)=>{const i=s?await $t(e,[r],s):void 0;let a,l;if(o){const t=await qt(e,r,o);a=t.fetched,t.shown&&(l=!0)}const c=e.places.entries.find(e=>e.id===r)?.label;return{...fs(t,n),placesEntryId:r,...c&&{label:c},...i&&{shown:i},...void 0!==a&&{geometriesFetched:a},...void 0!==l&&{geometriesShown:l}}},eo=e=>{const t=!0===e.experimentalSearch;return async(r,s)=>{const{query:o,where:n,poiCategories:i,show:a,entryId:l,geometries:c}=r,d=r,u=d.placeTypes,p=d.areaTags,{resolved:h,unresolved:y}=await(async e=>{if(!e)return{resolved:void 0,unresolved:[]};const t=e.filter(e=>At.has(e)),r=e.filter(e=>!At.has(e));if(0===r.length)return{resolved:t.length?t:void 0,unresolved:[]};await P();const s=await Promise.all(r.map(async e=>({term:e,codes:await P({filters:[e]})}))),o=[],n=[];for(const{term:a,codes:l}of s)0===l.length?o.push(a):n.push(...l);const i=[...new Set([...t,...n])];return{resolved:i.length?i:void 0,unresolved:o}})(i);if(i?.length&&!h)return{error:`No POI categories matched: ${y.map(e=>`"${e}"`).join(", ")}. POI category codes must be exact CONSTANT_CASE values, or natural-language terms resolvable by the SDK catalog. Call getPoiCategoryCodes to find valid codes for your search subject (e.g. "italian food", "gym").`};const m=n??{mode:"within",viewport:!0},f={...u?.length&&{placeTypes:u},...p?.length&&{areaTags:p}};try{if("maxDetour"===m.mode){const t=await(async(e,t,r,s,o)=>{const n=e.routing.entries;if(0===n.length)return{error:"No routes in state. Calculate a route first via setRoute."};const i=t.routeId?n.find(e=>e.id===t.routeId):n.at(-1);if(!i)return{error:`No route found with id "${t.routeId}". Use recallRoutes to list available routes.`};const a=i.data.features[0];if(!a)return{error:`Route entry "${i.id}" has no geometry.`};const l=await v({route:a,maxDetourTimeSeconds:t.maxDetourTimeSeconds,...r&&{query:r},...s&&{poiCategories:s},...t.sortBy&&{sortBy:t.sortBy},...void 0!==t.limit&&{limit:t.limit}});return{result:l,placesEntryId:await e.places.addPlaceResult(l,ps(l,{query:r,poiCategories:s,routeLabel:i.label}),o)}})(s,m,o,h,l);return"error"in t?t:Qs(s,t.result,t.placesEntryId,a,c,e)}const r=await Ks(m,s,f);if("error"in r)return r;const{bias:n,multiFilters:i,radiusMeters:d,range:u,routeLabel:p}=r;if(u){const r=await(async(e,t,r,s,o,n,i,a)=>{const l=e.ranges.entries.find(e=>e.id===t);if(!l||0===l.ranges.length)return{error:`Range "${t}" not found. Use recallRanges to list available ranges.`};const c=l.ranges.map(e=>e.polygon?.features[0]?.geometry).filter(e=>!!e);if(0===c.length)return{error:`Range "${t}" has no polygons. Recompute via findReachableAreas.`};const d=[...c,...o.geometries??[]],u=await Js(a,{query:r,poiCategories:s,multiFilters:{...o,geometries:d}});return{result:u,placesEntryId:await e.places.addPlaceResult(u,ps(u,{query:r,poiCategories:s,...i&&{routeLabel:i}}),n)}})(s,u,o,h,i,l,p,t);return"error"in r?r:Qs(s,r.result,r.placesEntryId,a,c,e)}const y=await(async(e,t,r,s,o,n,i,a,l,c)=>{const d=await Js(c,{query:t,poiCategories:s,bias:r,radiusMeters:o,multiFilters:n});return{result:d,placesEntryId:await e.places.addPlaceResult(d,ps(d,{query:t,poiCategories:s,where:Zs(a),...l&&{routeLabel:l}}),i)}})(s,o,n,h,d,i,l,m,p,t);return Qs(s,y.result,y.placesEntryId,a,c,e)}catch(g){return{error:`Search failed: ${g instanceof Error?g.message:String(g)}`}}}},to=(eo(Ys),{classificationPrompt:'Search multiple places by query/category in a region. BATCH categories+areas in one call. `query` filters POI names ONLY — area names go in `where`. `where.mode`: within (area) / nearby (point bias) / maxDetour (route-relative ranked) / global. For "X in [named area]", put X in top-level `query`/`poiCategories` and the area in `where.queries: [{query: "..."}]` — geocoded in one step, no precursor locatePlace. Within-mode: route → corridor. Prefer geometries/municipalities/placeIds over bbox. Skip for already-stored places — use recallPlaces/processData.',tags:["discover","place","location"],examples:['discoverPlaces({ poiCategories: ["RESTAURANT", "CAFE", "BAR"], entryId: "amsterdam-food-spots" }) // batch many categories + semantic id','discoverPlaces({ query: "coffee", where: { mode: "nearby", position: [4.9, 52.4], radiusMeters: 500 } })','discoverPlaces({ query: "parking", where: { mode: "within", queries: [{ query: "De Jordaan, Amsterdam", queryAs: "place" }] } }) // area name goes in `where.queries`, NOT top-level `query`','discoverPlaces({ query: "coffee", where: { mode: "nearby", query: "Schiphol Airport", queryAs: "poi", radiusMeters: 2000 } }) // POI-resolved bias point','discoverPlaces({ poiCategories: ["HOTEL"], where: { mode: "within", municipalities: ["Amsterdam", "Utrecht"] }, entryId: "ams-utrecht-hotels" })','discoverPlaces({ query: "bakery", where: { mode: "within", range: "ranges-0" } })','discoverPlaces({ poiCategories: ["CAFE"], show: { markerType: "pin", zoomMode: "auto" } }) // omit `where` → defaults to viewport','discoverPlaces({ poiCategories: ["RESTAURANT"], where: { mode: "within", queries: [{ query: "Paris" }] } }) // "restaurants in Paris" — subject in `poiCategories`, area in `where.queries`','discoverPlaces({ poiCategories: ["ELECTRIC_VEHICLE_STATION"], where: { mode: "within", route: { routeId: "routes-0", widthMeters: 1000 } } }) // bulk corridor scan along the route','discoverPlaces({ query: "coffee", where: { mode: "maxDetour", maxDetourTimeSeconds: 300, limit: 5 } }) // top 5 coffee detours within 5 min off the latest route','discoverPlaces({ poiCategories: ["ELECTRIC_VEHICLE_STATION"], where: { mode: "maxDetour", routeId: "routes-1", maxDetourTimeSeconds: 600, sortBy: "detourOffset" } }) // ranked EV detours on a specific route','discoverPlaces({ query: "bakery", where: { mode: "within", placeIds: ["G55fc4abe-..."] } }) // search inside stored place polygons (auto-fetched if needed)','discoverPlaces({ query: "vegan", where: { mode: "global" } })'],examplePrompts:["Find coffee shops near me","Italian restaurants in Amsterdam","EV chargers in the current map area","Hotels in Amsterdam and Utrecht","Bakeries within the reachable range","Outline the Amsterdam neighbourhoods","Show the boundaries of these municipalities","Search for new EV stations along my route","Cafes in a 500 m corridor around the planned route","Best 5 coffee stops without losing more than 5 min off my route","EV chargers I can reach with under 10 min detour"],relatedTools:["getPOICategoryCodes","updatePlacesDisplay","updatePlacesDisplay","locatePlace","getViewport","findReachableAreas","processData","analyseData"],dependsOn:["getPOICategoryCodes","setRoute"]}),ro=Z.object({type:Z.enum(["timeMinutes","distanceKM"]).describe("Budget type including its unit."),value:Z.number().describe("Budget value. e.g. 30 for 30 minutes, 50 for 50 km.")}),so=Z.object({origins:Z.array(gt).min(1).describe("One or more starting points; one reachable area is computed per origin under the same budgets and bundled into a single ranges entry. Pass a single-element array for the classic one-origin call."),budgets:Z.array(ro).min(1).describe("One or more budget constraints applied to every origin. Pass multiple for nested ranges (e.g. 10, 20, 30 min or km). Order does not matter — sorted internally."),theme:Z.enum(["filled","inverted","outline"]).optional().describe("Visual theme for the polygons. Default: outline (transparent fill with thick colored border). Use inverted for a donut (reachable highlighted, outside dimmed) or filled for solid polygons."),showOnMap:Z.boolean().describe("Whether to render the range polygon(s) on the map."),showOriginPin:Z.boolean().optional().describe("Whether to drop a pin at each origin. Default: true."),hidePreviousEntries:Ue("ranges")}),oo=Z.object({rangesId:Z.string(),ranges:Z.array(Z.object({originName:Z.string(),budgets:Z.array(ro)})),skipped:Z.array(Z.object({origin:Z.string(),reason:Z.string()})).optional().describe("Origins that could not contribute (unresolved query, empty geometry).")}),no=Z.union([oo,Z.object({status:Z.enum(["no_results"])}),cr]),io=async(e,t)=>{const r=await I(e.map(e=>({origin:t,budget:{type:e.type,value:e.value}})));return 0===r.features.length?null:r},ao=e=>e.origin.query??`${e.origin.position[1]}, ${e.origin.position[0]}`,lo=Z.union([Z.object({count:Z.number().describe("Number of codes returned"),codes:Z.array(Z.string()).describe('POICategory enum values for discoverPlaces poiCategories. Generic codes (e.g. "RESTAURANT") also accepted by toggleTilesPOIs categories.')}),cr]),co=Z.object({filters:Z.array(Z.string()).optional().describe("Case-insensitive filter strings matched against category names and synonyms. Omit to return all codes."),language:Z.string().describe('Locale e.g. "en-GB", "fr-FR", "es-ES", "de-DE".')}),uo=Z.object({speed:Z.number().optional(),freeFlowSpeed:Z.number().optional(),congestionLevel:Z.number().optional(),travelTime:Z.number().optional(),networkLength:Z.number().optional()}),po=Z.union([Z.object({entryId:Z.string().describe("Id of the new traffic-area-analytics entry — use with updateTrafficAreaAnalyticsDisplay / analyseData / processData."),name:Z.string().optional(),timezone:Z.string().optional(),dateRange:Z.object({start:Z.string(),end:Z.string()}),baseData:uo,metrics:Z.array(Z.string()),tileCount:Z.number(),availableGranularities:Z.array(Z.string())}),cr]),ho=Z.object({location:gt.optional().describe("Named location (city, region, country) to fetch the boundary for. Resolved via geocoding + geometryData. Mutually exclusive with bbox and geometry."),bbox:Z.array(Z.number()).length(4).optional().describe("[minLng, minLat, maxLng, maxLat] — use a wide area (at least 5-10km across, city-level) for meaningful results. Mutually exclusive with location."),showOnMap:Z.boolean().optional().describe("Visualize the result immediately with default settings. Use `updateTrafficAreaAnalyticsDisplay` to customize."),startDate:Z.string().optional().describe("Start date 'YYYY-MM-DD'. Use with endDate for a continuous range (max 31 days)."),endDate:Z.string().optional().describe("End date 'YYYY-MM-DD' (inclusive). Defaults to today if omitted."),days:Z.array(Z.string()).optional().describe("Specific dates 'YYYY-MM-DD' for non-consecutive analysis. Mutually exclusive with startDate/endDate."),metrics:Z.union([Z.literal("all"),Z.array(Z.enum(["speed","congestionLevel","freeFlowSpeed","travelTime","networkLength"])).min(1)]).describe("Traffic metrics to analyze. Use 'all' (preferred) or an explicit array like ['speed', 'congestionLevel', 'freeFlowSpeed', 'travelTime', 'networkLength']."),functionalRoadClasses:Z.union([Z.literal("all"),Z.array(Z.enum(["MOTORWAY","MAJOR_ROAD","OTHER_MAJOR_ROAD","SECONDARY_ROAD","LOCAL_CONNECTING_ROAD","LOCAL_ROAD_HIGH_IMPORTANCE","LOCAL_ROAD","LOCAL_ROAD_MINOR_IMPORTANCE","OTHER_ROAD"]))]).optional().describe("Road classes to include. 'all' (default) or array of specific classes."),hours:Z.union([Z.literal("all"),Z.array(Z.number().int().min(0).max(23))]).optional().describe("Hours of the day (0-23) to include. 'all' (default) or array like [7,8,9,17,18] for rush hours.")}),yo=e=>`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`,mo=e=>e instanceof Date?Number.isNaN(e.getTime())?void 0:yo(e):"string"==typeof e?e.slice(0,10):void 0,fo=e=>e.trafficIncidents.entries.map(e=>({id:e.id,label:e.label,count:e.data.length,timestamp:e.timestamp})),go=Z.union([Z.object({count:Z.number(),entryId:Z.string().optional(),entries:Z.array(Z.object({id:Z.string(),label:Z.string(),count:Z.number(),timestamp:Z.number()}))}),cr]),bo=Z.object({...He,boundingBox:Re.optional().describe("Single bbox [W,S,E,N]. EXCLUSIVE with viewport; composes with other multi-region fields."),queries:Z.array(Z.object({query:Z.string().describe('Name of a CONTAINING area (e.g. "Paris", "De Jordaan, Amsterdam", "M25"). Resolved to its bbox (or polygon bbox) and merged into the search area.'),queryAs:Je})).optional().describe("CONTAINING areas to fetch incidents within. Each entry geocodes to a bbox; the union of all bboxes is used. Pair per-item `queryAs` to disambiguate POI vs place. EXCLUSIVE with viewport; composes with other multi-region fields."),placeIds:Z.array(Z.string()).optional().describe("IDs of session places — each polygon (fetched on demand) contributes its bbox to the area. Use recallPlaces to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields."),geometries:Z.array(Fe).optional().describe("Direct GeoJSON Polygons / MultiPolygons (custom drawing, external data). For named or stored places use `queries` / `placeIds`. EXCLUSIVE with viewport; composes with other multi-region fields."),range:Z.string().optional().describe('Range ID from findReachableAreas (e.g. "ranges-0") — restricts to that entry; multi-origin entries combine their polygons. Use recallRanges to list IDs. EXCLUSIVE with viewport; composes with other multi-region fields.'),route:Z.object({routeId:Z.string().optional().describe('Route entry ID (e.g. "routes-0"). Use recallRoutes. Default: latest route.'),widthMeters:Z.number().positive().describe('Total corridor width (metres) — widthMeters/2 each side. Typical: 200–500 m ("near the road"), 2–5 km ("broad area along the route").')}).optional().describe("Buffered corridor around a stored route — turf.buffer at widthMeters/2 around the line. EXCLUSIVE with viewport; composes with other multi-region fields.")}).refine(e=>So(e)||Eo(e),{message:"`within` requires `viewport: true` OR one or more multi-region fields (boundingBox / queries / placeIds / geometries / range / route) with a meaningful value."}),wo=e=>!!e&&4===e.length&&e.some(e=>0!==e),vo=e=>!!e&&e.widthMeters>0&&(void 0===e.routeId||e.routeId.length>0),Io=e=>!!e&&e.length>0,So=e=>!0===e.viewport,Eo=e=>wo(e.boundingBox)||!!e.queries?.length||!!e.placeIds?.length||!!e.geometries?.length||Io(e.range)||vo(e.route),xo=Z.union([bo]).describe('Geographic scope. Only `within` mode (single area, defaults to `{ mode: "within", viewport: true }`). Use `viewport` alone for "the network / right now / this area", or any combination of boundingBox / queries / placeIds / geometries / range / route — each resolves to a bbox and the union is sent to the SDK (≤10,000 km²).'),Mo=`Filter by incident category (server-side). Canonical values: ${H.join(", ")}. Map the user's wording to the closest value. If omitted, all categories are returned.`,Do=Z.object({where:xo.optional().describe('Geographic scope. Default: `{ mode: "within", viewport: true }`.'),categoryFilter:Z.array(Z.enum([...H])).optional().describe(Mo),timeValidityFilter:Z.array(Z.enum(["present","future"])).optional().describe("default: ['present']"),label:Z.string().optional().describe('Short human-readable label for this fetch — name the place ("Central London", "Amsterdam ring", "M25 corridor"), not coordinates. Surfaced verbatim in the monitor chip and on the entry chip in the UI; also used by analyseData / focusIncidents to disambiguate which entry to operate on. Always pass one when silent is false; the bbox-coordinate fallback ("bbox a,b → c,d") is for silent intermediate fetches only.'),silent:Z.boolean().optional().describe("Default: false. When true, suppress map rendering (use for intermediate calculations only). Normally, fetched incidents auto-render on the map so the user can visually validate the agent is looking at the right area."),fitBounds:Z.boolean().optional().describe("Default: true. Pass false to skip camera move after rendering.")}),Po=async(e,t)=>{const r=[];let s;if(wo(e.boundingBox)&&r.push(e.boundingBox),e.queries?.length){const s=await(async(e,t)=>{const r=Gt(t.baseMap),s=[];for(const o of e){const e=await Is(o.query,o.queryAs??"place",{limit:5,bias:r?{position:r}:void 0});if(0===e.length)return{error:`Could not resolve "${o.query}". Try a more specific query, a placeId, or a boundingBox.`};const t=L(e[0]);if(!t)return{error:`"${o.query}" resolved to a place with no bbox — cannot use as a "within" area. Provide a boundingBox or a different query.`};s.push(t)}return{bboxes:s}})(e.queries,t);if("error"in s)return s;r.push(...s.bboxes)}if(e.placeIds?.length){const s=await(async(e,t)=>{const r=[];for(const s of e){const e=t.places.findPlaceById(s);if(!e)return{error:`Unknown placeId "${s}". Use recallPlaces to list available IDs.`};const o=L(e.place);if(o){r.push(o);continue}const n=await t.places.fetchPlaceGeometry(s),i=n?L(n):void 0;if(!i)return{error:`Place "${s}" has no bbox or fetchable boundary polygon. Provide a boundingBox or a different placeId.`};r.push(i)}return{bboxes:r}})(e.placeIds,t);if("error"in s)return s;r.push(...s.bboxes)}if(r.push(...(e=>{if(!e?.length)return[];const t=[];for(const r of e){const e=L(r);e&&t.push(e)}return t})(e.geometries)),Io(e.range)){const s=((e,t)=>{const r=e.ranges.entries.find(e=>e.id===t);if(!r||0===r.ranges.length)return{error:`Range "${t}" not found. Use recallRanges to list available ranges.`};const s=[];for(const o of r.ranges){const e=o.polygon?.features[0],t=e?L(e):void 0;t&&s.push(t)}return 0===s.length?{error:`Range "${t}" has no polygons. Recompute via findReachableAreas.`}:{bboxes:s}})(t,e.range);if("error"in s)return s;r.push(...s.bboxes)}if(vo(e.route)){const o=((e,t)=>{const r=e.routing.entries;if(0===r.length)return{error:"No routes in state. Calculate a route first via setRoute."};const s=t.routeId?r.find(e=>e.id===t.routeId):r.at(-1);if(!s)return{error:`No route found with id "${t.routeId}". Use recallRoutes to list available routes.`};if(0===s.data.features.length)return{error:`Route entry "${s.id}" has no geometry.`};const o=t.widthMeters/2,n=[];for(const i of s.data.features){const e=X.buffer(i,o,{units:"meters"}),t=e?L(e):void 0;t&&n.push(t)}return 0===n.length?{error:`Failed to compute route corridor for "${s.id}" (turf.buffer returned no geometry for any of the ${s.data.features.length} route alternative(s)).`}:{bboxes:n,routeLabel:s.label}})(t,e.route);if("error"in o)return o;r.push(...o.bboxes),s=o.routeLabel}return{bboxes:r,...s&&{routeLabel:s}}},To=async(e,t)=>{if(Eo(e)){const r=await Po(e,t);if("error"in r)return r;const s=(e=>{if(0===e.length)return;let t=Number.POSITIVE_INFINITY,r=Number.POSITIVE_INFINITY,s=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY;for(const[n,i,a,l]of e)n<t&&(t=n),i<r&&(r=i),a>s&&(s=a),l>o&&(o=l);return[t,r,s,o]})(r.bboxes);return s?{bbox:s,...r.routeLabel&&{routeLabel:r.routeLabel}}:{error:"No area could be resolved from `where` — provide at least one within field."}}return So(e)?(e=>{try{return{bbox:e.baseMap.ttMap.getBBox()}}catch(t){return{error:`Viewport fallback failed: ${t instanceof Error?t.message:String(t)}`}}})(t):{error:"No area could be resolved from `where` — provide at least one within field."}},_o=Z.union([br,cr]),Ao=Z.object({waypointIndices:Z.array(Z.number().int()).min(1).describe("Indices of waypoints to remove. 0=origin, last=destination. Min 2 waypoints must remain. Duplicates are not allowed."),showOnMap:Z.boolean().describe("Show the updated route and waypoints on the map after recalculation."),hidePreviousEntries:Ue("routes")}),Co=Z.union([br,cr]),ko=Z.union([Z.literal("origin"),Z.literal("destination"),Z.number().int()]).describe("Which waypoint to overwrite. Use 'origin' or 'destination' for the endpoints, or a 0-based absolute index (0 = origin, last = destination, intermediate stops in between)."),Oo=Z.object({waypointIndex:ko,location:gt.describe("The new waypoint that replaces the targeted one."),showOnMap:Z.boolean().describe("Show the updated route and waypoints on the map after recalculation."),hidePreviousEntries:Ue("routes")}),Ro=e=>"query"in e?`"${e.query}"`:"placeId"in e?`placeId "${e.placeId}"`:JSON.stringify(e.position),No=Z.union([mr,cr]),Lo=Z.object({position:Z.array(Z.number()).length(2).describe("[longitude, latitude] GeoJSON position to reverse geocode")}),Fo=Z.union([Z.object({byodEntryId:Z.string().describe("Id of the new BYOD entry (use with `byodEntryIDs` on analyseData / processData)."),label:Z.string(),source:Z.union([Z.object({kind:Z.literal("url"),url:Z.string()}),Z.object({kind:Z.literal("inline")})]).describe("Where the data came from."),profile:dr.describe("Runtime-inferred shape — geometry types plus a per-property profile (types, coverage, examples). Use it to pick fields for analyseData / processData without re-fetching the raw GeoJSON, and to choose the `setByodLayers` layers that render this entry.")}),cr]),jo=Z.object({label:Z.string().describe('Human-readable label for the new entry (e.g. "Sales territories", "Customer pins 2026-Q1").'),url:Z.url().optional().describe("URL serving a GeoJSON FeatureCollection. Fetched once and stored on the entry. EXCLUSIVE with `data`."),data:Z.unknown().optional().describe('Inline GeoJSON FeatureCollection (`{ type: "FeatureCollection", features: [...] }`). EXCLUSIVE with `url`. Keep small — large inline payloads bloat the conversation.'),entryId:je}).refine(e=>e.url?!e.data:!!e.data,{message:"Pass EXACTLY ONE of `url` or `data`."}),Bo=26214400,qo=new Set(["http:","https:"]),$o=async e=>{const t=new AbortController,r=setTimeout(()=>t.abort(),15e3);try{const r=await fetch(e,{signal:t.signal});if(!r.ok)return{error:`Failed to fetch "${e}": HTTP ${r.status} ${r.statusText}`};const s=((e,t)=>{const r=e.headers.get("content-length");if(!r)return;const s=Number.parseInt(r,10);return Number.isFinite(s)&&s>Bo?{error:`Response too large for "${t}": advertised ${s} bytes (max 26214400).`}:void 0})(r,e);if(s)return s;const o=await(async(e,t)=>{const r=e.body?.getReader();if(!r){const r=await e.text();return r.length>Bo?{error:`Response too large for "${t}": ${r.length} bytes (max 26214400).`}:{value:r}}const s=[];let o=0;for(;;){const{done:e,value:n}=await r.read();if(e)break;if(o+=n.byteLength,o>Bo)return await r.cancel(),{error:`Response exceeded 26214400 bytes while streaming "${t}". Refusing to load.`};s.push(n)}const n=new Uint8Array(o);let i=0;for(const a of s)n.set(a,i),i+=a.byteLength;return{value:(new TextDecoder).decode(n)}})(r,e);return"error"in o?o:{value:JSON.parse(o.value)}}catch(s){return((e,t)=>e instanceof Error&&"AbortError"===e.name?{error:`Fetch timed out after 15000ms for "${t}".`}:{error:`Failed to fetch "${t}": ${e instanceof Error?e.message:String(e)}`})(s,e)}finally{clearTimeout(r)}},Go=(st(rt),Z.union([Z.object({affectedEntries:Z.array(Z.union([Z.object({kind:Z.literal("places"),id:Z.string()}),Z.object({kind:Z.literal("routes"),id:Z.string()}),Z.object({kind:Z.literal("incidents"),id:Z.string()}),Z.object({kind:Z.literal("customGeometries"),id:Z.string()}),Z.object({kind:Z.literal("trafficAreaAnalytics"),id:Z.string()}),Z.object({kind:Z.literal("byod"),id:Z.string()})])).describe("Entries the analysis was attached to: every contributing places / routes / incidents / customGeometries / trafficAreaAnalytics / byod entry. Ranges entries feed the aggregation but do not carry analyses."),sourceIds:Z.array(Z.unknown()).optional().describe(`${ke} (only present when \`geometriesEntryIDs\` was set).`),name:Z.string().describe("Unique name of the analysis within each affected entry."),description:Z.string().optional(),outputFormat:Z.enum(["json","chart"]),analysis:Z.unknown(),skipped:Z.array(bt).optional().describe(Ce),focused:Z.object({incidentsEntryID:Z.string(),focusedCount:Z.number(),droppedIds:Z.array(Z.string()),reason:Z.string().optional()}).optional().describe("Present when the analysis ran with `monitor` AND returned `focusIds: string[]` (+ optional `focusReason`). The tool applied that focus as a side-effect on the source incidents entry.")}),cr])),Uo=(e,t)=>{const r=nt(e,t),s={...ct(r,{verb:"analyse",requiredHint:`At least one of ${it(r)} is REQUIRED.`})};return s.name=Z.string().describe('Unique name within each affected entry (e.g. "incidents-per-cluster", "ev-near-route"). Reusing a name replaces the previous analysis on those entries.'),s.description=Z.string().optional().describe("Optional short description of what the analysis computes."),s.outputFormat=Z.enum(["json","chart"]).optional().describe('Shape of the returned value (default: "json"). `json` — compact JSON-serializable object for text display. `chart` — Chart.js `ChartConfiguration` rendered via chart.js in the chat UI. Pick `chart` whenever the user asks for a "chart", "graph", "plot", "histogram", "bar/pie/line chart", etc.'),s.code=Z.string().describe(((e,t)=>{const r=nt(e,t),s=at(r),o=t?lt(r):"",n=!!t&&r.length>1,i=!!t&&r.includes("customGeometries"),a=!t||r.includes("incidents")?"When `monitor: { entryId }` is set, the call ALSO registers as a recurring spec on that incidents entry. On every monitor tick the code re-runs against the fresh `incidents` snapshot and these EXTRA sandbox args become available:\n• `previous` — your last result for this `name` on the source entry, or `undefined` on the first run. Use it for id stability (e.g. reuse a prior cluster id when memberIds overlap by ≥50%) and short trend reads ('growing' / 'fading' / 'steady'). Always guard `previous === undefined` — true on every first run.\n• `now: Date` — call-start time; compare against `startTime` / `endTime` on incidents.\n• `log: (...args) => void` — writes into the response logs.\nConstraint: cross-kind reruns are NOT supported — when `monitor` is set, `incidentsEntryIDs: [monitor.entryId]` is the only allowed input. For one-shot cross-kind incidents analysis (no rerun), omit `monitor`.\nFOCUS SIDE-EFFECT: when `applyFocus !== false` AND the result has `focusIds: string[]` (+ optional `focusReason`), `setFocus` is called once on the source entry to dim/highlight the listed incidents. One-shot — the focus side-effect does NOT re-apply on monitor-tick replays.\n\n":"";return`Async JS that aggregates the injected inputs and returns the result.\n\n${Ct(Tt)}\n\nEach input is \`undefined\` when its \`*EntryIDs\` argument was omitted — guard before reading (\`if (places) ...\`, \`routes?.features.length\`, etc.). When defined:\n${s}\n\n`+a+(n?`${Pt}\n\n`:"")+`${((e,t="")=>`Return value MUST be pure JSON — plain objects, arrays, strings, numbers, booleans, null. No functions, no classes, no DOM/Map refs (it crosses a JSON boundary). Keep it small for chat display.\n• \`json\` (default) — compact object (${"counts, per-kind breakdowns, hex bins, intersections, distance bins"}, …).\n• \`chart\` — Chart.js config: \`{ type: "bar"|"line"|"pie"|"doughnut"|"radar"|"polarArea"|"scatter"|"bubble", data: { labels: (string | string[])[], datasets }, options? }\`. For per-point detail you would normally put in a \`tooltip.callbacks\` function, use a \`string[]\` label — Chart.js renders it multi-line on the axis tick AND in the tooltip title${t?` (e.g. ${t})`:""}.`)()}\n\n`+(i?`${Ae}\n\n`:"")+o})(e,t)),((e,t,r)=>e.includes(r)&&(!t||t.kinds.includes(r)))(e,t,"incidents")&&(s.monitor=Z.object({entryId:Z.string().describe("Incidents entry id to register the spec on. Must match `incidentsEntryIDs[0]`. The spec re-runs on every monitor-tick of that entry.")}).optional().describe("Opt-in: register this analysis as a recurring spec on the targeted incidents entry. The code re-runs on every monitor-tick with extra sandbox args (`previous`, `now`, `log`). When set, only `incidentsEntryIDs: [monitor.entryId]` is allowed alongside — cross-kind reruns are not supported."),s.applyFocus=Z.boolean().optional().describe("When `monitor` is set AND the result has `focusIds: string[]` (+ optional `focusReason`), call `setFocus` on the source entry to dim/highlight those incidents. Default: true. One-shot — the focus side-effect does NOT re-apply on monitor-tick replays.")),Z.object(s).refine(dt,{message:ut(r)}).refine(e=>{const t=e.monitor;if(!t)return!0;const r=e.incidentsEntryIDs;if(!Array.isArray(r)||1!==r.length||r[0]!==t.entryId)return!1;for(const s of rt)if("incidents"!==s&&e[`${"customGeometries"===s?"geometries":s}EntryIDs`]?.length)return!1;return!0},{message:"When `monitor` is set, only `incidentsEntryIDs: [monitor.entryId]` is allowed (cross-kind reruns are not supported in the lean merge). Omit `monitor` for one-shot cross-kind incidents analysis."})},Wo=(Uo(rt,void 0),(e,t)=>{const r=nt(e,t),s=r.join(" / "),o=t&&r.includes("incidents")?"Opt-in `monitor: { entryId }` registers the call as a recurring spec on the targeted incidents entry; opt-in `applyFocus` honours `focusIds: string[]` by calling `setFocus`.":"",n="CONTRAST `processData`, which writes NEW renderable map entries (places / custom-geometries / BYOD); `analyseData` never renders or creates entries — only attaches metadata.";if(t)return`Aggregate ${s} entries via dynamic JS — counts, groupings, charts, hex bins, cross-kind correlations. ${n} Each scoped input is exposed as a merged collection and a per-entry record; fields are \`undefined\` when their \`*EntryIDs\` argument is omitted (guard with \`?.\`). Result is attached as \`_analysis[name]\` on every contributing entry. \`outputFormat: "json"\` (default) or \`"chart"\` (Chart.js config). `+o;return`Aggregate ${s} entries via dynamic JS — counts, groupings, charts, hex bins, cross-kind correlations. ${n} Each input is exposed as a merged collection and a per-entry record; fields are \`undefined\` when their \`*EntryIDs\` argument is omitted (guard with \`?.\` / \`if (places) …\`). Result is attached as \`_analysis[name]\` on every contributing entry (ranges feed in but do not carry analyses). \`outputFormat: "json"\` (default) or \`"chart"\` (Chart.js config). `+(r.includes("incidents")?"Opt-in `monitor: { entryId }` registers the call as a recurring spec on the targeted incidents entry — the code re-runs on every monitor-tick with `previous` / `now` / `log` available in the sandbox. When `monitor` is set, only `incidentsEntryIDs: [monitor.entryId]` is allowed alongside (no cross-kind reruns). Opt-in `applyFocus` (default true under `monitor`) honours `focusIds: string[]` on the result by calling `setFocus` on the source entry to dim/highlight a subset.":"")}),zo=(Wo(rt,void 0),e=>`Emit \`{ kinds: [${e.map(e=>`"${e}"`).join(" | ")}] }\` listing only the entry kinds the user query touches. Omit when truly cross-kind (covers all).`),Vo=(e,t,r)=>{const s=r.timestamp??Date.now();for(const o of t)e.addAnalysisToEntry(o.id,{name:r.name,timestamp:s,...r.description&&{description:r.description},outputFormat:r.outputFormat,data:r.analysis})},Yo=async(e,t)=>{const{placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a,name:l,description:c,code:d,monitor:u,applyFocus:p}=e,h=e.outputFormat??"json";if(u)return Ho(t,u.entryId,l,c,d,h,!1!==p);const y=await xt({placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a},t);if("error"in y)return{error:y.error};const{resolved:m,sandbox:f,geometries:g}=y.value,b=await Lt(d,Tt,_t(f,{h3:K,turf:X}),"Analysis");if("error"in b)return{error:b.error};const w=Rt(b.value,h);if("error"in w)return{error:w.error};const v=w.value,I={name:l,description:c,outputFormat:h,analysis:v,timestamp:Date.now()};Vo(t.places,m.places,I),Vo(t.routing,m.routes,I),Vo(t.trafficIncidents,m.incidents,I),Vo(t.trafficAreaAnalytics,m.trafficAreaAnalytics,I),Vo(t.byod,m.byod,I),Vo(t.places,g.affectedEntries.filter(e=>"places"===e.kind).map(e=>({id:e.id})),I),Vo(t.customGeometries,g.affectedEntries.filter(e=>"customGeometries"===e.kind).map(e=>({id:e.id})),I);const S=[...m.places.map(e=>({kind:"places",id:e.id})),...m.routes.map(e=>({kind:"routes",id:e.id})),...m.incidents.map(e=>({kind:"incidents",id:e.id})),...m.trafficAreaAnalytics.map(e=>({kind:"trafficAreaAnalytics",id:e.id})),...m.byod.map(e=>({kind:"byod",id:e.id})),...g.affectedEntries.filter(e=>"places"===e.kind&&!m.places.some(t=>t.id===e.id)).map(e=>({kind:"places",id:e.id})),...g.affectedEntries.filter(e=>"customGeometries"===e.kind).map(e=>({kind:"customGeometries",id:e.id}))],E=n?g.sourceIds:void 0;return{affectedEntries:S,...E&&{sourceIds:E},name:l,...c&&{description:c},outputFormat:h,analysis:v,...g.skipped.length&&{skipped:g.skipped}}},Ho=async(e,t,r,s,o,n,i)=>{const a=e.trafficIncidents.entries.find(e=>e.id===t);if(!a)return{error:`No incidents entry with id "${t}". Call getTrafficIncidents first.`};const l={name:r,...s&&{description:s},outputFormat:n,code:o,source:a.id},c=a._analyses?.getResult(r)?.data;e.trafficIncidents.setAnalysisSpec(l);const d=a.timestamp,u=await Wt(l,a.data,c,d);if("error"in u)return{error:u.error};const{value:p}=u;Vo(e.trafficIncidents,[a],{name:r,description:s,outputFormat:n,analysis:p,timestamp:d});const h=i?Jo(p,a.id,e):void 0;return{affectedEntries:[{kind:"incidents",id:a.id}],name:r,...s&&{description:s},outputFormat:n,analysis:p,...h&&{focused:h}}},Jo=(e,t,r)=>{if(!e||"object"!=typeof e)return;const s=e.focusIds;if(!Array.isArray(s)||0===s.length)return;const o=s.filter(e=>"string"==typeof e);if(0===o.length)return;const n=e.focusReason,i="string"==typeof n?n:void 0,a=r.trafficIncidents.setFocus(t,o,i);return{incidentsEntryID:t,focusedCount:a.focusedCount,droppedIds:a.droppedIds,...i&&{reason:i}}},Xo="clusters",Ko=Z.object({incidentsEntryID:Z.string().describe("Entry id from getTrafficIncidents."),eps:Z.number().positive().optional().describe("DBSCAN radius in km. Default 0.5. Use 0.3–0.5 for dense urban, 1+ for highways."),minMembers:Z.number().int().min(1).optional().describe("Minimum incidents per cluster. Default 3."),maxClusters:Z.number().int().min(1).optional().describe("Top N clusters by total delay. Default 6.")}),Zo=Z.union([Z.object({incidentsEntryID:Z.string(),clusterCount:Z.number(),clusters:Z.array(Z.object({id:Z.string(),primaryRoads:Z.array(Z.string()),primaryCategory:Z.string(),size:Z.number(),totalDelaySeconds:Z.number(),peakDelaySeconds:Z.number(),trend:Z.enum(["growing","fading","steady","new"])}))}),cr]),Qo=Z.union([Z.object({incidentsEntryID:Z.string().describe("The entry the focus was applied to."),focusedCount:Z.number().describe("How many of the requested ids ended up in the focus subset."),droppedIds:Z.array(Z.string()).describe("Ids that were not present on the entry and were dropped from the focus."),reason:Z.string().optional().describe("Echo of the focus reason for downstream display.")}),cr]),en=Z.object({incidentsEntryID:Z.string().describe("Id of the incidents entry to focus on (from `getTrafficIncidents` or any registered analysis source)."),incidentIds:Z.array(Z.string()).min(1).describe("Ids of incidents within the entry to highlight. Unknown ids are dropped, not errored."),reason:Z.string().optional().describe('Short label shown in the UI FocusChip — what this subset represents (e.g. "top 3 by delay").')}),tn=Z.object({slotIndex:Z.number().int().min(0).optional().describe("Exact waypoint slot to retrieve. Use when you need one known origin/stop/destination slot."),includeEmptySlots:Z.boolean().optional().describe("Include unfilled slots in the result set. Defaults to false to avoid null-heavy responses."),offset:Z.number().int().min(0).optional().describe("Pagination offset within the filtered slot list. Ignored when slotIndex is provided."),limit:Z.number().int().min(1).max(25).optional().describe("Maximum slots to return. Defaults to 5. Ignored when slotIndex is provided.")}),rn=Z.object({totalSlots:Z.number().describe("Total waypoint slots currently staged"),filledSlots:Z.number().describe("Number of staged slots that already contain coordinates"),matchedCount:Z.number().describe("Total slots matching the request before pagination"),returnedCount:Z.number().describe("Number of slots returned in this page"),offset:Z.number().describe("Offset applied to the filtered slot list"),hasMore:Z.boolean().describe("True when more slots can be fetched with a higher offset"),nextOffset:Z.number().optional().describe("Offset to request the next page when hasMore=true"),waypoints:Z.array(ws)}),sn=(st(rt),{bboxFromGeoJSON:L,calculateProgressAtRoutePoint:F,getCoordinateAtRouteProgress:q,getProgressAtNearestRoutePoint:G,getRouteProgressBetween:U,getRouteProgressForSection:W,getSectionBBox:z}),on=[...Tt,"routeUtils"],nn=Z.object({theme:Z.enum(["filled","outline","inverted"]).optional().describe('Visual theme. Default: "outline". Use "inverted" for difference/inverse results.'),mode:Be,hidePreviousEntries:Ue("custom-geometries").optional()}),an={sourceIds:Z.array(Te).optional().describe(`${ke} (only when \`geometriesEntryIDs\` was set).`),skipped:Z.array(bt).optional().describe(Ce),fitted:pt.optional()},ln=Z.union([yr({}).extend({placesEntryId:Z.string(),label:Z.string().optional(),connectionsRendered:Z.number().optional(),placesGeometryCount:Z.number().optional().describe("Polygons attached to the new places entry (when `geometries` was returned alongside)."),shownPlaces:Z.boolean().optional(),...an}),Z.object({customGeometriesEntryId:Te,customGeometriesCount:Z.number(),label:Z.string(),operation:Z.string().optional(),shownCustomGeometries:Z.boolean().optional(),...an}),Z.object({byodEntryId:Z.string(),byodFeatureCount:Z.number(),label:Z.string(),fitted:pt.optional()}),Z.object({fitted:pt}),cr]),cn=(e,t)=>{const r=nt(e,t),s={...ct(r,{verb:"process",requiredHint:`At least one of ${it(r)} is REQUIRED.`})};return s.code=Z.string().describe(((e,t)=>{const r=nt(e,t),s=t?lt(r):"",o=!!t&&r.length>1,n=t&&r.includes("trafficAreaAnalytics")?"`trafficAreaAnalytics` is a FeatureCollection of tile/hex regions whose `feature.properties` carry metric fields (`congestionLevel`, `speed`, `freeFlowSpeed`, `travelTime`, `networkLength`); use `turf.booleanPointInPolygon` / `turf.lineIntersect` to bridge with places/routes.\n\n":"",i=t&&r.includes("routes")?"`routeUtils` (from @tomtom-org/maps-sdk/core, only useful when `routesEntryIDs` is set):\n• `getSectionBBox(route, section)` → `[W,S,E,N]`. Sections at `route.properties.sections.<type>`.\n• `getRouteProgressForSection(route, section)` / `getRouteProgressBetween(route, startIdx, endIdx)`.\n• `calculateProgressAtRoutePoint(route, pathIndex)`.\n• `getCoordinateAtRouteProgress(route, query)` — `query`: `{ traveledDistanceInMeters }` | `{ traveledTimeInSeconds }` | `{ clockTime: Date }`.\n• `getProgressAtNearestRoutePoint(route, { lng, lat })` — snap a point to the line.\n• `bboxFromGeoJSON(featureOrCollection)`.\n\n":"",a=t&&r.includes("customGeometries")?`${Ae}\n\n`:"",l=r.includes("byod")?"• `byod: FeatureCollection` — writes a NEW BYOD entry (any GeoJSON: Point/Line/Polygon/mixed). Use for derived BYOD layers (e.g. cleaned/filtered customer data).\n":"",c=t?`${at(r)}\n`:"";return"Async JS returning `{ places?, placeConnections?, geometries?, fitOnMap?, byod? }`. At least one of `places` / `geometries` / `fitOnMap` / `byod` must be present. `places` (with optional attached `geometries`), standalone `geometries`, and `byod` are MUTUALLY EXCLUSIVE — only one is written per call, others are dropped. `fitOnMap` composes with any of them.\n\n• `places: Places` — writes a NEW places entry. Filters/merges/subsets of `places.features`, or computed point features (must conform to the Place schema).\n• `placeConnections: { from, to, id? }[]` — lines between output places (endpoints: Place or place id). Requires `places`. Drawn on the new entry's PlacesModule.\n• `geometries: PolygonFeature[]` — polygons (Polygon/MultiPolygon, stable `id` recommended). When `places` is also present, attached to the new places entry; when `places` is absent, written as a NEW custom-geometries entry.\n• `fitOnMap` — bbox or `{ bbox, padding?, animate? }`. Camera move; when alone, no entry written.\n"+l+`\n${Ct(on)}\n\n`+c+"Each input is `undefined` when its `*EntryIDs` argument was omitted — guard before reading. "+n+(o?`${Pt}\n\n`:"")+i+"`fitOnMap` (opt): `[W,S,E,N]` BBox or `{ bbox: [W,S,E,N], padding?: number, animate?: boolean }` (default padding 50). Camera fits these bounds after the tool runs. Returning ONLY `fitOnMap` skips state writes (pure camera focus); combine with `places` / `geometries` to also write+focus. Derive bboxes via `turf.bbox(feature | FeatureCollection)`.\n\n"+a+s})(e,t)),s.label=Z.string().optional().describe("Short label for the new entry (used by both places and custom-geometries paths)."),s.entryId=je,s.operation=Z.string().optional().describe("Short operation label (`union`, `buffer`, `difference`, `h3-coverage`, …) stored as provenance on a new custom-geometries entry. Ignored on the places path."),s.show=Z.object({places:We.optional().describe("Render the new places entry (when `places` was returned)."),placesGeometries:Z.object({theme:Z.enum(["filled","outline","inverted"]).optional(),mode:Be}).optional().describe("Render the polygons attached to the new places entry (when both `places` and `geometries` were returned)."),customGeometries:nn.optional().describe("Render the new custom-geometries entry (when `geometries` was returned WITHOUT `places`).")}).optional().describe("Optional per-output render knobs. The executor only consults the field matching what the code returned (so leaving e.g. `customGeometries` set when `places` was returned is a silent no-op)."),s.connectMarkerType=Z.enum(["pin","base-map"]).optional().describe('Which PlacesModule renders `placeConnections`. Default: "pin". Match the module holding the endpoints.'),Z.object(s).refine(dt,{message:ut(r)})},dn=(cn(rt,void 0),(e,t)=>{const r=nt(e,t).join(" / "),s="CONTRAST `analyseData`, which attaches JSON/chart metadata to existing entries and never renders.";return t?`Run dynamic JS over ${r} entries to produce NEW renderable map data — a places, custom-geometries, or BYOD entry (with optional \`placeConnections\` / attached polygon footprints), and/or a \`fitOnMap\` camera move. ${s} Returns \`{ places?, placeConnections?, geometries?, fitOnMap?, byod? }\`: when \`places\` is returned, inline \`geometries\` attach to it; otherwise they become a standalone custom-geometries entry. Inputs are \`undefined\` when their \`*EntryIDs\` argument is omitted — guard before reading.`:`Run dynamic JS over any combination of ${r} entries to produce NEW renderable map data — a places, custom-geometries, or BYOD entry (with optional \`placeConnections\` / attached polygon footprints), and/or a \`fitOnMap\` camera move. ${s} Returns \`{ places?, placeConnections?, geometries?, fitOnMap?, byod? }\`: when \`places\` is returned, inline \`geometries\` attach to it as place footprints; otherwise they become a standalone custom-geometries entry. \`routeUtils\` is exposed alongside \`h3\` / \`turf\` for route section / progress work. Inputs are \`undefined\` when their \`*EntryIDs\` argument is omitted — guard before reading.`}),un=(dn(rt,void 0),e=>`Emit \`{ kinds: [${e.map(e=>`"${e}"`).join(" | ")}] }\` listing only the entry kinds the user query touches. Omit when truly cross-kind.`),pn=e=>{if(!e||"object"!=typeof e)return!1;const t=e;return"FeatureCollection"===t.type&&Array.isArray(t.features)},hn=e=>{if(!Array.isArray(e))return!1;const t=e=>"string"==typeof e||"object"==typeof e&&null!==e&&"geometry"in e;return e.every(e=>{if(!e||"object"!=typeof e)return!1;const r=e;return t(r.from)&&t(r.to)})},yn=(e,t,r)=>void 0===e?{value:void 0}:t(e)?{value:e}:{error:r},mn=e=>e&&e.features.length>0?e:void 0,fn=e=>{if(!e||"object"!=typeof e)return"Process code must return `{ places?, placeConnections?, geometries?, fitOnMap?, byod? }`.";const t=e,r={},s=yn(t.places,pn,"`places` must be a `Places` FeatureCollection of Point features.");if("error"in s)return s.error;r.places=mn(s.value);const o=yn(t.placeConnections,hn,"`placeConnections` must be an array of `{ from, to, id? }` (endpoints: Place or place id).");if("error"in o)return o.error;if(r.placeConnections=o.value,void 0!==t.geometries){const e=(e=>{if(!Array.isArray(e))return{error:"`geometries` must be an array of GeoJSON Polygon/MultiPolygon Features."};const t=[];for(let r=0;r<e.length;r++){const s=e[r];if(!Ot(s))return{error:`\`geometries[${r}]\` must be a GeoJSON Feature with Polygon or MultiPolygon geometry.`};t.push(s)}return{value:t}})(t.geometries);if("error"in e)return e.error;r.geometries=e.value.length>0?e.value:void 0}const n=yn(t.fitOnMap,yt,"`fitOnMap` must be `[W,S,E,N]` or `{ bbox: [W,S,E,N], padding?, animate? }`.");if("error"in n)return n.error;r.fitOnMap=n.value;const i=yn(t.byod,pn,"`byod` must be a GeoJSON FeatureCollection (any geometry types).");return"error"in i?i.error:(r.byod=mn(i.value),(e=>void 0===e.places&&void 0===e.geometries&&void 0===e.fitOnMap&&void 0===e.byod?"At least one of `places`, `geometries`, `fitOnMap`, `byod` must produce content (empty arrays / FeatureCollections do not count).":void 0===e.places&&void 0!==e.placeConnections?"`placeConnections` requires `places` to be returned alongside.":void 0)(r)??r)},gn=async(e,t,r,s)=>{const{label:o,entryId:n,show:i,connectMarkerType:a,geometriesEntryIDs:l}=t,{places:c,placeConnections:d,geometries:u,fitOnMap:p}=r,h=c.features.length,y=`${o?`processed: ${o}`:"processed"} (${h} ${1===h?"place":"places"})`,m=await e.places.addPlaceResult(c,y,n,d,u),f=i?.places?await $t(e,[m],i.places):void 0;let g,b;if(u?.length&&(g=u.length,i?.placesGeometries&&await e.places.showPlaceGeometries(u,i.placesGeometries.theme??"outline",i.placesGeometries.mode??"replace",m)),d){const t=await(async(e,t,r,s)=>{try{return await(await e.places.getEntryPlacesModule(t,s??"pin")).showConnections(r),{count:r.length}}catch(o){return{error:`Failed to render connections: ${o instanceof Error?o.message:String(o)}`}}})(e,m,d,a);if("error"in t)return t;b=t.count}const w=p?mt(e,p):void 0,v=e.places.entries.find(e=>e.id===m)?.label;return{...fs(c),placesEntryId:m,...v&&{label:v},...f&&{shownPlaces:!0},...void 0!==b&&{connectionsRendered:b},...void 0!==g&&{placesGeometryCount:g},...l&&{sourceIds:s.sourceIds},...s.skipped.length&&{skipped:s.skipped},...w&&{fitted:w}}},bn=async(e,t)=>{const{placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a,code:l}=e,c=await xt({placesEntryIDs:r,routesEntryIDs:s,incidentsEntryIDs:o,geometriesEntryIDs:n,trafficAreaAnalyticsEntryIDs:i,byodEntryIDs:a},t);if("error"in c)return{error:c.error};const{sandbox:d,geometries:u}=c.value,p=await Lt(l,on,[..._t(d,{h3:K,turf:X}),sn],"Process");if("error"in p)return{error:p.error};const h=fn(p.value);if("string"==typeof h)return{error:h};const{places:y,geometries:m,fitOnMap:f,byod:g}=h;return y?gn(t,e,{...h,places:y},u):m?.length?(async(e,t,r,s,o)=>{const{label:n,entryId:i,operation:a,show:l,geometriesEntryIDs:c}=t,d=n?`processed: ${n}`:`processed geometries (${r.length})`,u=await e.customGeometries.addEntry(r,{sourceIds:o.sourceIds,...a&&{operation:a}},d,i);let p=!1;l?.customGeometries&&(await ft(e.customGeometries,[u],l.customGeometries.hidePreviousEntries),await e.customGeometries.showEntry(u,l.customGeometries.theme??"outline",l.customGeometries.mode??"replace"),p=!0);const h=s?mt(e,s):void 0;return{customGeometriesEntryId:{kind:"customGeometries",id:u},customGeometriesCount:r.length,label:d,...a&&{operation:a},...c&&{sourceIds:o.sourceIds},...o.skipped.length&&{skipped:o.skipped},...p&&{shownCustomGeometries:!0},...h&&{fitted:h}}})(t,e,m,f,u):g?(async(e,t,r,s)=>{const{label:o,entryId:n}=t,i=o?`processed: ${o}`:`processed byod (${r.features.length})`,a=await e.byod.addEntry(r,i,{...n&&{explicitId:n}}),l=s?mt(e,s):void 0;return{byodEntryId:a,byodFeatureCount:r.features.length,label:i,...l&&{fitted:l}}})(t,e,g,f):f?{fitted:mt(t,f)}:{error:"Process code returned no `places`, `geometries`, `fitOnMap`, or `byod`."}},wn=uo.extend({date:Z.string().optional(),hour:Z.number().optional(),year:Z.number().optional(),month:Z.number().optional(),week:Z.number().optional(),day:Z.number().optional()}),vn=Z.object({entryId:Z.string().optional().describe('Traffic-area-analytics entry id to query (e.g. "tta-0"). Omit to query the most recently added entry. Use `recallState` to list available ids.'),granularity:Z.enum(["daily","hourly","weekly","monthly","yearly","average"]).optional().describe("Time granularity. Default: 'daily'. Use 'average' for day-of-week × hour patterns."),metric:Z.enum(["speed","congestionLevel","freeFlowSpeed","travelTime","networkLength"]).optional().describe("Filter to a single metric. Default: all requested metrics."),startDate:Z.string().optional().describe("Filter entries from this date (YYYY-MM-DD). Works with 'daily' and 'hourly'."),endDate:Z.string().optional().describe("Filter entries up to this date (YYYY-MM-DD). Works with 'daily' and 'hourly'."),hourStart:Z.number().int().min(0).max(23).optional().describe("Filter hourly/average entries from this hour (0-23)."),hourEnd:Z.number().int().min(0).max(23).optional().describe("Filter hourly/average entries up to this hour (0-23)."),dayOfWeek:Z.array(Z.number().int().min(1).max(7)).optional().describe("Filter 'average' entries by day of week (1=Mon…7=Sun).")}),In=Z.union([Z.object({granularity:Z.string(),count:Z.number(),entries:Z.array(wn),currentVisualization:Z.object({activeMetric:Z.string().optional(),displayMode:Z.string().optional()}).optional()}),cr]),Sn=Z.union([Z.object({kind:Z.literal("integrator"),description:Z.string().optional()}),Z.object({kind:Z.literal("url"),url:Z.string()}),Z.object({kind:Z.literal("inline"),description:Z.string().optional()})]),En=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),featureCount:Z.number(),geometryTypes:Z.array(Z.string()),propertyNames:Z.array(Z.string()).describe("Feature-property keys seen in the data, highest-coverage first and capped — wide datasets may omit low-coverage keys. Pass `id` for the full per-property profile (types/coverage/examples) plus any `propertiesOmitted` count."),source:Sn,shown:Z.boolean()}),xn=En.extend({profile:dr.describe("Full runtime-inferred shape of the data (per-property types/coverage/examples)."),features:Z.unknown().describe("GeoJSON FeatureCollection of the entry's features.")}),Mn=Z.union([Z.object({entries:Z.array(En),entryMode:Z.enum(["single","multiple"])}),xn,cr]),Dn=Z.object({id:Z.string().optional().describe("BYOD entry id to inspect in detail. Omit to list every BYOD entry currently in session state.")}),Pn=Z.object({id:Te.optional().describe('Tagged id to inspect in detail (e.g. `{ kind: "places", id: "ev-stations-oost" }`, `{ kind: "ranges", id: "ranges-0" }`, `{ kind: "customGeometries", id: "bakery-candidates-gap-oost" }`). Omit to list every available id across all slices.')}),Tn=Z.object({name:Z.string(),outputFormat:Z.enum(["json","chart"]),timestamp:Z.number(),description:Z.string().optional()}),_n=Z.object({id:Te.describe("Tagged id of this entry."),label:Z.string(),timestamp:Z.number(),featureCount:Z.number(),operation:Z.string().optional().describe("Operation label (custom-geometries entries only)."),sourceIds:Z.array(Te).optional().describe("Provenance: tagged ids of inputs that fed the producing op (custom only)."),analyses:Z.array(Tn).optional().describe("Analyses already attached to this entry (places and custom only).")}),An=_n.extend({features:Z.unknown().describe("GeoJSON FeatureCollection of the entry's polygon features.")}),Cn=Z.union([Z.object({entries:Z.array(_n)}),An,cr]),kn=e=>e?.length?e.map(({name:e,outputFormat:t,timestamp:r,description:s})=>({name:e,outputFormat:t,timestamp:r,...s&&{description:s}})):void 0,On=e=>{const t=[];for(const r of e.places.entries){if(!r.geometries?.length)continue;const e=kn(r._analysis);t.push({id:{kind:"places",id:r.id},label:r.label,timestamp:r.timestamp,featureCount:r.geometries.length,...e&&{analyses:e}})}for(const r of e.ranges.entries){const e=r.ranges.reduce((e,t)=>e+(t.polygon?.features.length??0),0);0!==e&&t.push({id:{kind:"ranges",id:r.id},label:r.label,timestamp:r.timestamp,featureCount:e})}for(const r of e.customGeometries.entries){const e=kn(r._analysis);t.push({id:{kind:"customGeometries",id:r.id},label:r.label,timestamp:r.timestamp,featureCount:r.features.length,...r.provenance.operation&&{operation:r.provenance.operation},...r.provenance.sourceIds.length&&{sourceIds:[...r.provenance.sourceIds]},...e&&{analyses:e}})}return t.sort((e,t)=>t.timestamp-e.timestamp)},Rn=(e,t)=>{switch(e.kind){case"places":return((e,t)=>{const r=t.places.entries.find(t=>t.id===e.id);if(!r)return{error:`No places entry with id "${e.id}"`};if(!r.geometries?.length)return{error:`Places entry "${e.id}" has no cached footprints. Call discoverPlaces / locatePlace with \`withGeometries: true\` first.`};const s=kn(r._analysis);return{id:e,label:r.label,timestamp:r.timestamp,featureCount:r.geometries.length,features:{type:"FeatureCollection",features:r.geometries},...s&&{analyses:s}}})(e,t);case"ranges":return((e,t)=>{const r=t.ranges.entries.find(t=>t.id===e.id);if(!r)return{error:`No ranges entry with id "${e.id}"`};const s=r.ranges.flatMap(e=>e.polygon?.features??[]);return 0===s.length?{error:`Ranges entry "${e.id}" has no polygons.`}:{id:e,label:r.label,timestamp:r.timestamp,featureCount:s.length,features:{type:"FeatureCollection",features:s}}})(e,t);case"customGeometries":return((e,t)=>{const r=t.customGeometries.findById(e.id);if(!r)return{error:`No custom-geometries entry with id "${e.id}"`};const s=kn(r._analysis);return{id:e,label:r.label,timestamp:r.timestamp,featureCount:r.features.length,features:{type:"FeatureCollection",features:r.features},...r.provenance.operation&&{operation:r.provenance.operation},...r.provenance.sourceIds.length&&{sourceIds:[...r.provenance.sourceIds]},...s&&{analyses:s}}})(e,t);default:return{error:'`{ kind: "place" }` is not a recallable detail target. Pass a places-entry id (`{ kind: "places", id }`) or use `recallPlaces` to inspect a single place.'}}},Nn=Z.object({id:Z.string().optional().describe('Entry ID to retrieve (e.g. "places-3"). Omit to list all entries.')}),Ln=Z.object({name:Z.string(),outputFormat:Z.enum(["json","chart"]),timestamp:Z.number(),description:Z.string().optional()}),Fn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),featureCount:Z.number().describe("Number of Place features stored in this entry."),shown:Z.boolean().describe("Whether this entry is currently rendered on the map."),markerType:Z.enum(["pin","base-map","pin-clustered"]).optional().describe("Which marker theme is rendering this entry (undefined when `shown` is false)."),analyses:Z.array(Ln).optional().describe("Analyses already attached to this entry via analyseData (name + output format).")}),jn=Z.enum(["single","multiple"]).describe('Display policy: `multiple` (default) lets several entries render simultaneously; `single` enforces "at most one entry on the map" — switching to it auto-clears non-latest entries.'),Bn=e=>{const t=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),places:yr(e)});return Z.union([Z.object({entries:Z.array(Fn),entryMode:jn}),t.extend({entryMode:jn}),cr])},qn=(Bn({}),e=>async(t,r)=>{const{id:s}=t,o=r.places.entries,n=r.places.entryMode,i=r.places.shownEntryIds;if(!s)return{entries:[...o].sort((e,t)=>t.timestamp-e.timestamp).map(({id:e,label:t,timestamp:s,places:o,_analysis:n})=>{const a=i.has(e),l=a?r.places.getShownMarkerType(e):void 0;return{id:e,label:t,timestamp:s,featureCount:o.length,shown:a,...l&&{markerType:l},...n?.length&&{analyses:n.map(({name:e,outputFormat:t,timestamp:r,description:s})=>({name:e,outputFormat:t,timestamp:r,...s&&{description:s}}))}}}),entryMode:n};const a=o.find(e=>e.id===s);if(!a)return{error:`No entry found with id "${s}"`};const l=fs(a.places,e);return{id:a.id,label:a.label,timestamp:a.timestamp,places:l,entryMode:n}}),$n=(qn({}),{classificationPrompt:"List/inspect places entries in state — pick valid IDs before any `placesEntryIDs` tool (processData, analyseData, updatePlacesDisplay).",tags:["place","location"],examples:["recallPlaces()",'recallPlaces({ id: "places-0" })'],examplePrompts:["What places did I search for?","List every places entry with its ID","Which IDs can I pass to processData or updatePlacesDisplay?","Recall the bakeries I discovered earlier","What is in entry places-2?"],relatedTools:["discoverPlaces","locatePlace","reverseGeocode","updatePlacesDisplay"],dependsOn:["locatePlace","discoverPlaces"]}),Gn=Z.object({id:Z.string().optional().describe('Entry ID to retrieve (e.g. "ranges-0"). Omit to list all entries.')}),Un=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),originCount:Z.number()}),Wn=Z.object({origin:Z.object({query:Z.string().optional(),position:Z.tuple([Z.number(),Z.number()])}),budgets:Z.array(Z.object({type:Z.string(),value:Z.number()}))}),zn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),ranges:Z.array(Wn)}),Vn=Z.enum(["single","multiple"]).describe('Display policy: `multiple` (default) lets several reachable-range entries render at once; `single` enforces "at most one entry on the map" — switching to it auto-clears non-latest entries.'),Yn=Z.union([Z.object({entries:Z.array(Un),entryMode:Vn}),zn.extend({entryMode:Vn}),cr]),Hn=Z.object({id:Z.string().optional().describe('Entry ID to retrieve (e.g. "routes-1"). Omit to list all entries.')}),Jn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),shown:Z.boolean().describe("Whether this entry is currently rendered on the map.")}),Xn=Z.object({maxAlternatives:Z.number().optional(),costModel:Ps.optional(),when:Ts.optional()}),Kn=Z.object({id:Z.string(),label:Z.string(),timestamp:Z.number(),routes:br,waypoints:Z.array(Z.object({position:Z.array(Z.number()).length(2).describe("[longitude, latitude]")})),params:Xn}),Zn=Z.enum(["single","multiple"]).describe('Display policy: `multiple` (default) lets several routes render at once; `single` enforces "at most one route on the map" — switching to it auto-clears non-latest entries.'),Qn=Z.union([Z.object({entries:Z.array(Jn),entryMode:Zn}),Kn.extend({entryMode:Zn}),cr]),ei=Z.object({id:Z.string(),label:Z.string()}),ti=Z.enum(["single","multiple"]),ri=Z.union([Z.object({places:Z.array(ei).describe("Places history entries — newest first."),routes:Z.array(ei).describe("Routes history entries — newest first."),ranges:Z.array(ei).describe("Reachable-range history entries — newest first."),entryModes:Z.object({places:ti,routes:ti,ranges:ti}).describe("Per-slice display policy. `multiple` (default) lets several entries render at once; `single` keeps only the latest entry — set via setEntryMode tools."),mapStyle:Z.object({id:Z.string().optional().describe('Standard style id (e.g. "standardLight").')}).nullable().describe("Current map style. `null` when the map has no style assigned yet."),mapPOIs:Z.object({visible:Z.boolean().optional(),filterMode:Z.enum(["only","all_except"]).optional(),categories:Z.array(Z.string()).optional()}).nullable().describe("Built-in POI overlay config. `null` when the POIsModule has not been initialised in this session."),traffic:Z.object({flow:Z.object({visible:Z.boolean().optional()}).nullable().describe("Traffic-flow layer config. `null` when the module is uninitialised."),incidents:Z.object({visible:Z.boolean().optional()}).nullable().describe("Traffic-incidents layer config. `null` when the module is uninitialised.")}).describe("Real-time traffic visualization configs.")}),cr]),si=Z.object({}),oi=["places","routes","ranges","customGeometries","trafficAreaAnalytics","trafficIncidents","byod","mapPOIs","trafficFlow","mapStyle"],ni=Z.union([Z.object({success:Z.literal(!0),cleared:Z.array(Z.enum(oi)).describe("Slices that were wiped / reset to defaults.")}),cr]),ii=Z.object({}),ai=Z.enum(["circle","line","fill","symbol","heatmap","fill-extrusion"]).describe("MapLibre layer type. Must suit the geometry: Point → circle/symbol/heatmap, Line → line, Polygon → fill/fill-extrusion."),li=Z.object({type:ai,paint:Z.record(Z.string(),Z.unknown()).optional().describe('MapLibre paint properties for this layer `type` (e.g. circle → `circle-radius` / `circle-color`, fill → `fill-color` / `fill-opacity`, line → `line-color` / `line-width`). Values may be data-driven expressions referencing feature properties seen in the entry `profile` (e.g. `["interpolate", ["linear"], ["get", "revenue"], 0, 4, 1000, 20]`).'),layout:Z.record(Z.string(),Z.unknown()).optional().describe("MapLibre layout properties (e.g. symbol `icon-image` / `text-field`, or `visibility`)."),filter:Z.unknown().optional().describe("Optional MapLibre filter expression restricting which features this layer renders."),id:Z.string().optional().describe("Stable layer id. Auto-generated when omitted."),beforeID:Z.string().optional().describe("Id of an existing map layer to insert this layer before.")}).describe("A single MapLibre layer rendering the entry. Pick `type` by geometry; drive `paint` from the profile."),ci=Z.object({byodEntryId:Z.string().describe("Id of the BYOD entry to restyle (from `addByodSource` or `recallByod`)."),layers:Z.array(li).min(1).describe("Replacement MapLibre layer specs, applied in array order (first drawn underneath). Replaces the entry's current layers wholesale. Use the entry `profile` (geometry types + per-property types/coverage/examples) to choose layer `type`s and data-driven `paint`."),show:Ve.optional().describe("Render the entry after restyling. Omit when the entry is already shown — the new layers apply live in place. Pass it to render a hidden entry (or to re-fit the camera via `zoomMode`).")}),di=Z.union([Z.object({byodEntryId:Z.string(),label:Z.string(),layerCount:Z.number().describe("Number of layers now configured on the entry."),layerTypes:Z.array(Z.string()).describe("The `type` of each configured layer, in draw order."),shown:Ye.optional().describe("Render report — present only when `show` was provided (or the entry re-rendered live).")}),cr]),ui=Z.enum(rr),pi=Z.enum(["single","multiple"]),hi=Z.union([Z.object({success:Z.literal(!0),slice:ui,mode:pi,droppedEntryIds:Z.array(Z.string()).describe("Entry IDs removed from history because the new mode is `single` and they were not the latest.")}),cr]),yi=Z.object({slice:ui.describe("Which entry-owning slice to reconfigure. Names match the keys on the agent state exactly — `routing` (not `routes`), `customGeometries` (not `geometries`), and so on."),mode:pi.describe("`multiple` (default) lets several entries render at once; `single` keeps only the latest entry — switching to it auto-clears every older entry.")}),mi=`Switch the display policy of one entry-owning slice between \`multiple\` and \`single\`. Supported slices: ${rr.join(", ")}. \`single\` ⇒ at most one entry on the map, and any older entries get dropped from history immediately. Use when the user asks for a "clean view" / "only show one at a time" or wants to compare overlays again.`,fi=Z.union([Z.object({incidentsEntryID:Z.string(),alreadyRunning:Z.boolean().describe("True when the entry already had a running monitor — start is idempotent. When true, any new intervalMs / filter context in this call was IGNORED; the monitor keeps running with whatever it was started with. Call stopTrafficIncidentsMonitor first if you need different settings.")}),cr]),gi=Z.object({incidentsEntryID:Z.string().describe("Id of the incidents entry to monitor (returned by `getTrafficIncidents`)."),intervalMs:Z.number().int().positive().optional().describe("Polling interval in milliseconds. Defaults to 60000 (60s).")}),bi=Z.union([Z.object({incidentsEntryID:Z.string(),wasRunning:Z.boolean().describe("True when a monitor was actually stopped; false when it was already idle.")}),cr]),wi=Z.object({incidentsEntryID:Z.string().describe("Id of the incidents entry whose monitor should stop.")}),vi=Z.enum(sr),Ii=Z.union([Z.object({success:Z.literal(!0),cleared:Z.array(vi)}),cr]),Si=Z.object({layers:Z.array(vi).optional().describe("Slice keys to clear (default: all). One per `ToolState` slice that renders on the map: "+sr.map(e=>`\`${e}\``).join(", ")+". Map-only — slice histories (`entries`) are kept.")}),Ei=(Z.union([Z.object({success:Z.literal(!0)}),cr]),Z.object({sectionType:Z.enum(Y).describe("e.g. country|traffic|motorway|toll"),id:Z.string().describe("From a shown route section"),padding:Z.number().optional().describe("pixels, default: 50")}),Z.union([Z.object({count:Z.number(),trafficIncidents:Z.array(Z.object({id:Z.string(),category:Z.string(),magnitudeOfDelay:Z.string().describe("unknown|minor|moderate|major|indefinite"),description:Z.string().optional(),delayInSeconds:Z.number().optional(),roadCategory:Z.string().optional(),averageSpeedKmph:Z.number().optional()}))}),cr])),xi=Z.object({}),Mi=Z.union([Z.object({styles:Z.array(Z.string()).readonly()}),cr]),Di=Z.object({}),Pi=Z.union([Z.object({success:Z.literal(!0),language:Z.string()}),cr]),Ti=Z.object({language:Z.string().describe('e.g. "en-US", "fr-FR"')}),_i=Z.union([Z.object({success:Z.literal(!0),style:Z.string()}),cr]),Ai=Z.object({style:Z.enum(g).describe("Map visual theme")}),Ci=Z.union([Z.object({success:Z.literal(!0),visible:Z.boolean(),layerGroups:Z.array(Z.string())}),cr]),ki=Z.object({visible:Z.boolean(),layerGroups:Z.array(Z.enum(m)).describe("Vector-tile base-map layer groups to show or hide.")}),Oi=Z.union([Z.object({success:Z.literal(!0),allMapPOIsVisible:Z.boolean().optional(),filterCategories:Z.object({show:Z.enum(["only","all_except"]),values:Z.array(Z.string())}).optional(),reset:Z.boolean().optional()}),cr]),Ri=Z.object({reset:Z.boolean().optional().describe("Reset all tile-POI configuration to defaults. Other params ignored when true."),allMapPOIsVisible:Z.boolean().optional().describe("Overall visibility of the vector-tile POI icons baked into the base map style."),filterCategories:Z.object({show:Z.enum(["only","all_except"]).describe('"only": keep only these categories. "all_except": hide these categories.'),values:Z.array(Z.string()).describe('MapStylePOICategory / POICategoryGroup values (e.g. "RESTAURANT", "FOOD_DRINKS_GROUP"). Fine-grained codes like "ITALIAN_RESTAURANT" not supported here.')}).optional().describe("Restrict which built-in vector-tile POI categories are shown.")}),Ni=Z.object({visible:Z.boolean()}),Li=Z.union([Z.object({success:Z.literal(!0),trafficFlowVisible:Z.boolean()}),cr]),Fi=Z.object({visible:Z.boolean()}),ji=Z.union([Z.object({success:Z.literal(!0),trafficIncidentsVisible:Z.boolean()}),cr]),Bi=Z.enum(["show","hide","remove"]).describe("`show` renders the listed entries (lazy-init their CustomGeoJSONModule); `hide` removes them from the map but keeps them in session state; `remove` hides AND drops them from history."),qi=Z.object({entryIds:Z.array(Z.string()).min(1).optional().describe('BYOD entry ids to act on (e.g. ["byod-0"]). Use `recallByod` to list. Omit when `action: "hide"` and `clearAll: true` to hide every shown BYOD entry in one call.'),action:Bi,clearAll:Z.boolean().optional().describe('Only valid with `action: "hide"`. When true, hides every currently-shown BYOD entry (ignores `entryIds`). Default: false.'),hideOthers:Z.boolean().optional().describe('Only valid with `action: "show"`. When true, hides every other BYOD entry before showing the listed ones. Default: false (stack).')}).refine(e=>!("hide"!==e.action||!e.clearAll)||!!e.entryIds?.length,{message:'`entryIds` is required unless `action: "hide"` with `clearAll: true`.'}).refine(e=>!e.clearAll||"hide"===e.action,{message:'`clearAll: true` is only valid with `action: "hide"`.'}).refine(e=>!e.hideOthers||"show"===e.action,{message:'`hideOthers: true` is only valid with `action: "show"`.'}),$i=Z.union([Z.object({action:Bi,affectedIds:Z.array(Z.string()).describe("Entry ids that were actually shown / hidden / removed."),shown:Z.array(Z.string()).describe("BYOD entry ids currently rendered on the map after this call.")}),cr]),Gi=Z.object({id:Z.string(),label:Z.string(),featureCount:Z.number(),markerType:Z.enum(["pin","base-map","pin-clustered"]).describe("How the entry is rendered on the map.")}),Ui=Z.union([Z.object({success:Z.literal(!0),totalShown:Z.number(),shown:Z.array(Gi).describe("Entries currently on the map, with their labels."),actuallyAdded:Z.array(Z.string()).describe("Ids from `add` / `addMatching` that were not already shown. Empty if it was a no-op."),actuallyRemoved:Z.array(Z.string()).describe("Ids from `remove` / `removeMatching` that were actually on the map. Empty if no-op."),geometriesRendered:Z.number().optional().describe("Number of boundary polygons rendered when `geometry` was set."),geometriesSkipped:Z.array(Z.object({placeId:Z.string(),reason:Z.string()})).optional().describe("Places whose boundary could not be rendered (no geometry data source, fetch failure).")}),cr]),Wi=Z.object({add:Z.array(Z.string()).optional().describe('Entry ids to show (e.g. ["places-1"]). Omit while still setting `markerType` or `geometry` to target the most recently stored entry.'),remove:Z.array(Z.string()).optional().describe("Entry ids to hide."),addMatching:Z.array(Z.string()).optional().describe("Category labels to show."),removeMatching:Z.array(Z.string()).optional().describe("Category labels to hide."),clear:Z.boolean().optional().describe("Hide every displayed entry."),markerType:Z.enum(["pin","base-map","pin-clustered"]).optional().describe('How newly-added entries are rendered as markers: "pin", "base-map", or "pin-clustered" (pins with a count badge where nearby ones aggregate). Defaults to "pin" unless `geometry` is set without an explicit `markerType`, in which case markers are skipped. Entries previously shown with another marker type are moved to this one.'),geometry:$e.optional(),fitBounds:Z.boolean().optional().describe("Pan/zoom to fit the resulting display. Defaults to true when entries are being added, false otherwise.")}).refine(e=>void 0!==e.add||void 0!==e.remove||void 0!==e.addMatching||void 0!==e.removeMatching||!0===e.clear||void 0!==e.markerType||void 0!==e.geometry,{message:"updatePlacesDisplay requires at least one of: add, remove, addMatching, removeMatching, clear, markerType, geometry."}),zi=(e,t)=>{if(0===t.length)return[];const r=t.map(e=>e.toLowerCase()),s=/* @__PURE__ */new Set;for(const o of e.places.entries){const e=o.label.toLowerCase();r.some(t=>e.includes(t))&&s.add(o.id)}return[...s]},Vi=Z.object({id:Z.string(),label:Z.string(),routeCount:Z.number().describe("Number of route alternatives in the entry."),selectedIndex:Z.number().describe("Highlighted alternative index (0 unless overridden by this call).")}),Yi=Z.union([Z.object({success:Z.literal(!0),totalShown:Z.number(),shown:Z.array(Vi).describe("Routes entries currently on the map, with their labels."),actuallyAdded:Z.array(Z.string()).describe("Ids from `add` / `addMatching` that were not already shown. Empty if it was a no-op."),actuallyRemoved:Z.array(Z.string()).describe("Ids from `remove` / `removeMatching` that were actually on the map. Empty if no-op."),mainColor:Z.string().optional().describe("Sticky main colour now applied to every shown route, when set by this or a previous call.")}),cr]),Hi=Z.object({add:Z.array(Z.string()).optional().describe('Entry ids to show (e.g. ["routes-1"]). Omit while still setting `selectedIndex` to target the most recently stored entry.'),remove:Z.array(Z.string()).optional().describe("Entry ids to hide."),addMatching:Z.array(Z.string()).optional().describe("Label substrings to show."),removeMatching:Z.array(Z.string()).optional().describe("Label substrings to hide."),clear:Z.boolean().optional().describe("Hide every displayed entry."),selectedIndex:Z.number().int().min(0).optional().describe("Which alternative variant to highlight on newly-added entries (default: 0). Use to switch the selected alternative on a multi-alternative result. Applied after the entry is shown — entries previously rendered with another index are re-rendered."),fitBounds:Z.boolean().optional().describe("Pan/zoom to fit the resulting display. Defaults to true when entries are being added, false otherwise."),mainColor:Z.string().optional().describe('Recolor the route line + waypoint icons. CSS named colour ("coral", "steelblue") or hex ("#FF0000"). Sticky — applied to every currently-shown entry AND remembered so subsequent shows pick it up automatically. Can be set by itself (no add/remove/clear) to recolor without changing visibility.')}).refine(e=>void 0!==e.add||void 0!==e.remove||void 0!==e.addMatching||void 0!==e.removeMatching||!0===e.clear||void 0!==e.selectedIndex||void 0!==e.mainColor,{message:"updateRoutesDisplay requires at least one of: add, remove, addMatching, removeMatching, clear, selectedIndex, mainColor."}),Ji=(e,t)=>{if(0===t.length)return[];const r=t.map(e=>e.toLowerCase()),s=/* @__PURE__ */new Set;for(const o of e.routing.entries){const e=o.label.toLowerCase();r.some(t=>e.includes(t))&&s.add(o.id)}return[...s]},Xi={mode:Z.enum(["hexgrid-3d","hexgrid-2d","square-3d","square-2d","heatmap"]).optional().describe("Visualization mode. 'hexgrid-3d' (default): 3D hexagons. 'hexgrid-2d': flat hexagons. 'square-3d': 3D squares. 'square-2d': flat squares. 'heatmap': density layer."),metric:Z.enum(["congestionLevel","speed","travelTime"]).optional().describe("Metric to visualize. Defaults to first fetched metric on the targeted entries."),colorTheme:Z.enum(["trafficLight","heat","monochrome","viridis","plasma"]).optional().describe("Preset color theme. 'trafficLight' (green→amber→red, default), 'heat' (blue→orange→red), 'monochrome' (grey), 'viridis' (purple→green→yellow), 'plasma' (purple→magenta→yellow)."),heightScale:Z.number().optional().describe("Extrusion height multiplier / visual maximum in meters. Default: metric-dependent."),scaleMode:Z.enum(["raw","predefinedRange","currentRange"]).optional().describe("Height scaling mode. 'raw' (default), 'predefinedRange' (normalize to SDK range), 'currentRange' (normalize to live data range)."),filter:Z.object({min:Z.number().optional(),max:Z.number().optional()}).refine(e=>void 0!==e.min||void 0!==e.max,{message:"Filter must have at least one of min or max"}).optional().describe("Filter visible tiles for the active metric by value range.")},Ki=Z.object({id:Z.string(),label:Z.string(),activeMetric:Z.string().optional(),displayMode:Z.string().optional()}),Zi=Z.object({add:Z.array(Z.string()).optional().describe('Entry ids to render (e.g. ["tta-0"]). Use `recallState` to list available ids.'),remove:Z.array(Z.string()).optional().describe("Entry ids to hide."),addMatching:Z.array(Z.string()).optional().describe("Label substrings to show."),removeMatching:Z.array(Z.string()).optional().describe("Label substrings to hide."),clear:Z.boolean().optional().describe("Hide every displayed entry."),fitBounds:Z.boolean().optional().describe("Pan/zoom to fit the union of currently-shown entry bboxes. Defaults to true when entries are added."),...Xi}).refine(e=>void 0!==e.add||void 0!==e.remove||void 0!==e.addMatching||void 0!==e.removeMatching||!0===e.clear||void 0!==e.mode||void 0!==e.metric||void 0!==e.colorTheme||void 0!==e.heightScale||void 0!==e.scaleMode||void 0!==e.filter,{message:"updateTrafficAreaAnalyticsDisplay requires at least one of: add, remove, addMatching, removeMatching, clear, mode, metric, colorTheme, heightScale, scaleMode, filter."}),Qi=Z.union([Z.object({success:Z.literal(!0),totalShown:Z.number(),shown:Z.array(Ki).describe("Traffic-area-analytics entries currently on the map."),actuallyAdded:Z.array(Z.string()),actuallyRemoved:Z.array(Z.string())}),cr]),ea=(e,t)=>{if(0===t.length)return[];const r=t.map(e=>e.toLowerCase()),s=/* @__PURE__ */new Set;for(const o of e.trafficAreaAnalytics.entries)r.some(e=>o.label.toLowerCase().includes(e))&&s.add(o.id);return[...s]},ta=async(e,t,r)=>{const s=e.trafficAreaAnalytics.findById(t);if(!s)return;const o=s.data.properties?.metrics?.[0]??"congestionLevel",n=r.mode??"hexgrid-3d",i=r.metric??o,a=r.colorTheme??"trafficLight",l=await e.trafficAreaAnalytics.getEntryModule(t);l.setMode(n),l.setMetric(i),l.setColor(a),void 0===r.heightScale&&void 0===r.scaleMode||l.setHeight({...void 0!==r.heightScale&&{maxHeightMeters:r.heightScale},...void 0!==r.scaleMode&&{scaleMode:r.scaleMode}}),r.filter?l.filter({min:r.filter.min,max:r.filter.max}):l.clearFilter()},ra=e=>({id:e.id,label:e.label,...e._config?.activeMetric&&{activeMetric:e._config.activeMetric},...e._config?.displayMode&&{displayMode:e._config.displayMode}}),sa=Z.union([Z.object({success:Z.literal(!0),visible:Z.boolean(),count:Z.number()}),cr]),oa=Z.object({visible:Z.boolean().optional().describe("Render the staged waypoints (true, default) or clear them (false). No route line is drawn either way.")}),na="Dynamic-JS sandbox over any combination of places / routes / incidents / geometries / trafficAreaAnalytics / byod entries. Each input arg is `undefined` when its `*EntryIDs` was omitted.",ia={locatePlace:{description:"Resolve ONE named location (landmark, venue, city, neighborhood, address, POI) to a normalized place summary. Use this for any SINGLE named place — including requests for its boundary / polygon / outline / footprint (via `geometry`), pin or zoom display (via `show`), or staging as a routing waypoint. For multi-place or category searches, use discoverPlaces.",classificationPrompt:"Resolve a single location string (landmark, city, address) to a place without showing it; optionally stages a waypoint slot. Do NOT call as a precursor to discoverPlaces — discoverPlaces resolves named areas itself via `where.query` (or `where.municipalities` / `where.placeId`).",inputSchema:Es,outputSchema:xs,execute:async(e,t)=>{const{query:r,queryAs:s,waypointIndex:o,where:n,show:i,entryId:a,geometry:l}=e;try{const e=await Ms(n,t);if("error"in e)return e;const c=await Ss(r,s,e.bias);if(!c)return{error:`No result found for "${r}"`};const d=await t.places.addPlaceResult(c,ps(c,{query:r}),a);void 0!==o&&t.routing.setWaypointAt(o,c);const u=i?await(async(e,t,r,s)=>{const o=await $t(e,[r],{...s,zoomMode:"none"});if("auto"===s.zoomMode){if(t.bbox)e.baseMap.mapLibreMap.fitBounds(t.bbox);else{const r=$(t);r&&e.baseMap.mapLibreMap.flyTo({center:r,zoom:13})}o.zoomMode=!0}return o})(t,c,d,i):void 0;let p,h;if(l){const e=await qt(t,d,l);p=e.fetched>0,e.shown&&(h=!0)}return{...ms(c),...void 0!==o&&{waypointIndex:o},...u&&{shown:u},...void 0!==p&&{geometryFetched:p},...void 0!==h&&{geometryShown:h}}}catch(c){return{error:`Location resolution failed: ${c instanceof Error?c.message:String(c)}`}}},tags:["locate","place","location","waypoint"],examples:['locatePlace("Buckingham Palace", queryAs: "poi", where: { mode: "within", viewport: true }, waypointIndex: 0)','locatePlace("Tower Bridge", queryAs: "poi", where: { mode: "nearby", viewport: true }, waypointIndex: 1)','locatePlace("Westminster Abbey", queryAs: "poi", where: { mode: "global" })','locatePlace("10 Downing Street", queryAs: "place", where: { mode: "nearby", position: [4.9, 52.37] })','locatePlace("Amsterdam", queryAs: "place", where: { mode: "global" }, entryId: "amsterdam-city", geometry: { show: true }) // outline the city','locatePlace("Utrecht", queryAs: "place", where: { mode: "global" }, entryId: "utrecht-city", geometry: { show: true, mode: "add" }) // also outline a second city'],examplePrompts:["Where is the Louvre?","Pin Central Station on the map","Show Times Square on the map","Pin Schiphol Airport on the map","Outline Amsterdam on the map","Show the boundary of Berlin"],relatedTools:["setRoute","addWaypointsToRoute","getCurrentWaypoints","recallPlaces"]},reverseGeocode:{description:"Convert [lng, lat] to an address and place summary. Use after a click / getViewport / getCurrentLocation. Coordinates only — for text queries use locatePlace or discoverPlaces.",classificationPrompt:"Convert [lng, lat] coordinates to an address; use after map clicks or any tool that returns a position.",inputSchema:Lo,outputSchema:No,execute:async(e,t)=>{const{position:r}=e,s=r;try{const e=await T({position:s});return e?(await t.places.addPlaceResult(e,ps(e)),ms(e)):{error:"No result found for the given coordinates"}}catch(o){return{error:`Reverse geocoding failed: ${o instanceof Error?o.message:String(o)}`}}},tags:["location","place"],examples:["reverseGeocode({ position: [4.9, 52.4] })"],examplePrompts:["What's the address at this point?","Which city is [4.9, 52.4] in?","Name the place where I clicked"],relatedTools:["getViewport","getCurrentLocation","updatePlacesDisplay","locatePlace"]},discoverPlaces:e=>((e,t)=>({...t,description:Vs(e),inputSchema:zs(e),outputSchema:Bs(e),execute:eo(e)}))(e.featureFlags??{},to),processData:(er={classificationPrompt:`${na} WRITES NEW renderable map entries — returns \`{ places?, placeConnections?, geometries?, fitOnMap?, byod? }\` to write a places, custom-geometries, or BYOD entry, move the camera, or any combination. NOT for digests/charts/counts — those are \`analyseData\` (metadata only, never renders). Use for: filter/merge places (set ops), connect places (hub-spoke), compute polygons (union/buffer/h3-coverage/difference), focus camera on route sections via \`routeUtils.getSectionBBox\`, cross-reference (POIs near route, places-within-reachable, places-inside-high-congestion-tile).`,tags:["turf","h3","geometry","place","route","aggregation","connections"],examples:['processData({ placesEntryIDs: ["places-2"], code: "return { places: { type: \'FeatureCollection\', features: places.features.filter(p => (p.properties.poi?.name ?? \'\').toLowerCase().includes(\'vegan\')) } };", label: "vegan only", show: { places: { markerType: "pin", zoomMode: "auto", hidePreviousEntries: "all" } } })','processData({ placesEntryIDs: ["places-1"], code: "const [hub, ...rest] = places.features; return { places, placeConnections: rest.map(p => ({ from: hub, to: p })) };" })','processData({ placesEntryIDs: ["places-1"], code: "const cells = new Set(); for (const p of places.features) cells.add(h3.latLngToCell(p.geometry.coordinates[1], p.geometry.coordinates[0], 8)); return { places, geometries: [...cells].map(c => turf.polygon([h3.cellToBoundary(c, true)], { cell: c })) };", show: { places: { markerType: "pin", zoomMode: "auto" }, placesGeometries: { theme: "outline" } } })','processData({ routesEntryIDs: ["routes-0"], code: "const r = routes.features[0]; const sec = (r.properties.sections.traffic ?? []).slice().sort((a,b) => (b.delayInSeconds ?? 0) - (a.delayInSeconds ?? 0))[0]; const bbox = sec ? routeUtils.getSectionBBox(r, sec) : turf.bbox(r); return { fitOnMap: { bbox, padding: 80 } };" })','processData({ routesEntryIDs: ["routes-0"], code: "const r = routes.features[0]; const sec = r.properties.sections.toll?.[0]; if (!sec) return { fitOnMap: turf.bbox(r) }; return { fitOnMap: routeUtils.getSectionBBox(r, sec) };" })','processData({ placesEntryIDs: ["ev-stations"], routesEntryIDs: ["routes-0"], code: "const r = routes.features[0]; const buf = turf.buffer(r, 0.5, { units: \'kilometers\' }); const near = places.features.filter(p => turf.booleanPointInPolygon(p, buf)); return { places: { type: \'FeatureCollection\', features: near }, fitOnMap: turf.bbox(turf.featureCollection([...near, r])) };", label: "ev near route", entryId: "ev-near-routes-0", show: { places: { markerType: "pin", zoomMode: "none", hidePreviousEntries: "all" } } })','processData({ geometriesEntryIDs: [{ kind: "place", id: "amsterdam-id" }, { kind: "place", id: "rotterdam-id" }], code: "return { geometries: [turf.union(turf.featureCollection(geometries))].filter(Boolean) };", label: "ams ∪ rdam", operation: "union" })','processData({ geometriesEntryIDs: [{ kind: "place", id: "paris-id" }], code: "return { geometries: geometries.map(f => ({ ...turf.buffer(f, 5, { units: \'kilometers\' }), id: `${f.id}-buf` })) };", label: "paris +5km", operation: "buffer" })','processData({ geometriesEntryIDs: [{ kind: "place", id: "amsterdam-id" }, { kind: "ranges", id: "ranges-0" }], code: "const outer = geometries.filter(g => g.properties._source.kind === \'place\'); const inner = geometries.filter(g => g.properties._source.kind === \'ranges\'); const env = turf.union(turf.featureCollection(outer)); const u = turf.union(turf.featureCollection(inner)); const inv = (env && u) ? turf.difference(turf.featureCollection([env, u])) : env; return { geometries: inv ? [{ ...inv, id: \'unreachable\' }] : [] };", label: "amsterdam minus reachable", operation: "difference", show: { customGeometries: { theme: "inverted", hidePreviousEntries: "all" } } })','processData({ geometriesEntryIDs: [{ kind: "ranges", id: "ranges-0" }, { kind: "places", id: "ev-stations" }], code: "const ranges = geometries.filter(g => g.properties._source.kind === \'ranges\'); const env = ranges.length ? turf.union(turf.featureCollection(ranges)) : null; const ev = (placesByEntry?.[\'ev-stations\']?.features ?? []); const kept = env ? ev.filter(p => turf.booleanPointInPolygon(p, env)) : ev; return { places: { type: \'FeatureCollection\', features: kept } };", label: "ev within reachable" })','processData({ placesEntryIDs: ["ev-stations"], trafficAreaAnalyticsEntryIDs: ["tta-0"], code: "const hot = trafficAreaAnalytics.features.filter(t => (t.properties.congestionLevel ?? 0) >= 75); const kept = places.features.filter(p => hot.some(t => turf.booleanPointInPolygon(p, t))); return { places: { type: \'FeatureCollection\', features: kept } };", label: "ev in high-congestion tiles" })','processData({ trafficAreaAnalyticsEntryIDs: ["tta-0"], code: "const hot = trafficAreaAnalytics.features.filter(t => (t.properties.congestionLevel ?? 0) >= 75).map((t, i) => ({ ...t, id: `hot-${i}` })); const u = hot.length ? turf.union(turf.featureCollection(hot)) : null; return { geometries: u ? [{ ...u, id: \'congestion-hot-zone\' }] : [] };", label: "congestion hot zone", operation: "union", show: { customGeometries: { theme: "filled" } } })'],examplePrompts:["Keep only the vegan restaurants","Draw lines from this hub to every nearby cafe","Show the h3 hex coverage of these places","Fit the map to the worst traffic section of the route","Fit the map to the first toll section of the route","Filter my loaded EV stations to those within 500 m of the route","Create a custom polygon by unioning these city boundaries","Compute a 500 m buffer around this neighbourhood","Areas of Amsterdam not reachable from the hospitals","Filter loaded places to those inside the reachable area","Keep only the EV stations that fall inside high-congestion tiles","Create a custom polygon layer outlining the congestion hot zone"],relatedTools:["analyseData","recallPlaces","recallRoutes","recallGeometries","recallState"],dependsOn:["discoverPlaces","locatePlace","setRoute","getTrafficIncidents","getTrafficAreaAnalytics","findReachableAreas","recallPlaces","recallRoutes","recallGeometries"]},e=>((e,t)=>{const r=e.enabledDataKinds??rt;return{description:dn(r,e.scope),inputSchema:cn(r,e.scope),outputSchema:ln,execute:bn,scopeSchema:st(r),scopePrompt:un(r),...t}})(e,er)),analyseData:(e=>t=>((e,t)=>{const r=e.enabledDataKinds??rt;return{description:Wo(r,e.scope),inputSchema:Uo(r,e.scope),outputSchema:Go,execute:Yo,scopeSchema:st(r),scopePrompt:zo(r),...t}})(t,e))({classificationPrompt:`${na} WRITES metadata only — returns JSON or a Chart.js config (\`outputFormat: "chart"\`), attached as \`_analysis[name]\` on every contributing entry. Never renders or creates new map entries (that is \`processData\`). Use for: counts / groupings / charts (by-category, hex density); route summaries (per-leg / per-country / alternative compare); route TRAFFIC analysis from \`route.properties.sections.traffic[]\` — pick THIS for "distribution / count / chart of <something> along the route", NOT \`getTrafficIncidents\`; geometry stats (areas, h3 coverage); historical traffic-area-analytics breakdowns (congestion / speed / travel-time per tile); cross-kind correlations (incidents-per-cluster, places-near-route, distance-to-route bins, route-segments-by-tile-metric). For recurring monitor-tick reruns with \`previous\` / \`now\` / \`log\` and optional \`focusIds\` side-effect, set \`monitor: { entryId }\` (only \`incidentsEntryIDs: [monitor.entryId]\` allowed alongside).`,tags:["analysis","turf","h3","place","route","geometry"],examples:['analyseData({ placesEntryIDs: ["places-2"], name: "by-category", code: "const counts = {}; for (const p of places.features) for (const c of (p.properties.poi?.categories ?? [])) counts[c] = (counts[c] ?? 0) + 1; return { total: places.features.length, byCategory: counts };" })','analyseData({ placesEntryIDs: ["places-1"], name: "hex-density-8", code: "const bins = {}; for (const p of places.features) { const [lng,lat] = p.geometry.coordinates; const cell = h3.latLngToCell(lat, lng, 8); bins[cell] = (bins[cell] ?? 0) + 1; } return { resolution: 8, bins };" })','analyseData({ placesEntryIDs: ["places-2"], name: "top-categories-bar", outputFormat: "chart", code: "const counts = {}; for (const p of places.features) for (const c of (p.properties.poi?.categories ?? [])) counts[c] = (counts[c] ?? 0) + 1; const entries = Object.entries(counts).sort((a,b) => b[1]-a[1]).slice(0,10); return { type: \'bar\', data: { labels: entries.map(e => e[0]), datasets: [{ label: \'Places\', data: entries.map(e => e[1]) }] } };" })','analyseData({ routesEntryIDs: ["routes-0"], name: "summary", code: "return routes.features.map(r => ({ index: r.properties.index, distanceKm: r.properties.summary.lengthInMeters / 1000, durationMin: r.properties.summary.travelTimeInSeconds / 60, trafficDelaySec: r.properties.summary.trafficDelayInSeconds }));" })','analyseData({ routesEntryIDs: ["routes-0"], name: "alternative-comparison", outputFormat: "chart", code: "const r = routes.features; return { type: \'bar\', data: { labels: r.map(x => `Route ${x.properties.index}`), datasets: [{ label: \'Travel time (min)\', data: r.map(x => Math.round(x.properties.summary.travelTimeInSeconds / 60)) }, { label: \'Distance (km)\', data: r.map(x => Math.round(x.properties.summary.lengthInMeters / 1000)) }] } };" })','analyseData({ routesEntryIDs: ["routes-0"], name: "on-route-incidents-by-category", outputFormat: "chart", code: "const sections = (routes.features[0].properties.sections.traffic ?? []); const counts = {}; for (const s of sections) for (const c of (s.categories ?? [])) counts[c] = (counts[c] ?? 0) + 1; const entries = Object.entries(counts).sort((a,b) => b[1]-a[1]); return { type: \'bar\', data: { labels: entries.map(e => e[0]), datasets: [{ label: \'Incidents on route\', data: entries.map(e => e[1]) }] }, options: { plugins: { title: { display: true, text: \'Traffic incidents along the route\' } } } };" })','analyseData({ routesEntryIDs: ["routes-0"], name: "delay-by-country", code: "const r = routes.features[0]; const byCountry = {}; for (const c of (r.properties.sections.country ?? [])) byCountry[c.countryCodeISO3] = (byCountry[c.countryCodeISO3] ?? 0) + (r.properties.sections.traffic ?? []).filter(t => t.startPointIndex >= c.startPointIndex && t.endPointIndex <= c.endPointIndex).reduce((s, t) => s + (t.delayInSeconds ?? 0), 0); return byCountry;" })','analyseData({ routesEntryIDs: ["routes-0"], placesEntryIDs: ["ev-stations"], name: "ev-near-route-500m", code: "const route = routes.features[0]; const out = {}; for (const [id, fc] of Object.entries(placesByEntry)) out[id] = fc.features.filter(p => turf.pointToLineDistance(p, route, { units: \'meters\' }) <= 500).length; return { withinMeters: 500, byEntry: out };" })','analyseData({ routesEntryIDs: ["routes-0"], placesEntryIDs: ["cafes"], name: "cafe-distance-bins-km", outputFormat: "chart", code: "const route = routes.features[0]; const distances = places.features.map(p => turf.pointToLineDistance(p, route, { units: \'kilometers\' })); const max = Math.max(0, ...distances); const binWidth = 1; const binCount = Math.max(1, Math.ceil(max / binWidth)); const bins = new Array(binCount).fill(0); for (const d of distances) bins[Math.min(binCount - 1, Math.floor(d / binWidth))]++; const labels = bins.map((_, i) => `${i}-${i + binWidth} km`); return { type: \'bar\', data: { labels, datasets: [{ label: \'cafes\', data: bins }] }, options: { plugins: { title: { display: true, text: \'Cafe distance to route\' } } } };" })','analyseData({ geometriesEntryIDs: [{ kind: "places", id: "cities" }], name: "areas", code: "return geometries.map(f => ({ id: f.id, km2: +(turf.area(f)/1e6).toFixed(2) }));" })','analyseData({ geometriesEntryIDs: [{ kind: "places", id: "cities" }], name: "area-bar", outputFormat: "chart", code: "return { type: \'bar\', data: { labels: geometries.map(f => f.id), datasets: [{ label: \'km²\', data: geometries.map(f => +(turf.area(f)/1e6).toFixed(2)) }] } };" })','analyseData({ placesEntryIDs: ["clusters"], incidentsEntryIDs: ["incidents-0"], name: "incidents-per-cluster", code: "const out = {}; for (const c of places.features) { const inCluster = incidents.filter(i => { const coord = i.geometry.type === \'Point\' ? i.geometry.coordinates : turf.centroid(i).geometry.coordinates; return turf.distance(coord, c.geometry.coordinates, { units: \'kilometers\' }) <= 0.5; }); out[c.id] = inCluster.length; } return out;" })','analyseData({ geometriesEntryIDs: [{ kind: "customGeometries", id: "buffered-routes" }], incidentsEntryIDs: ["incidents-0"], name: "incidents-in-corridors", code: "const out = []; for (const g of geometries) { const hits = incidents.filter(i => { const coord = i.geometry.type === \'Point\' ? i.geometry.coordinates : turf.centroid(i).geometry.coordinates; return turf.booleanPointInPolygon(coord, g); }); out.push({ id: g.id, count: hits.length, totalDelaySec: hits.reduce((s, i) => s + (i.properties.delayInSeconds ?? 0), 0) }); } return out;" })',"analyseData({ trafficAreaAnalyticsEntryIDs: [\"tta-0\"], name: \"congestion-distribution\", outputFormat: \"chart\", code: \"const bins = [0, 25, 50, 75, 100]; const counts = new Array(bins.length).fill(0); for (const t of trafficAreaAnalytics.features) { const c = t.properties.congestionLevel ?? 0; const idx = Math.min(bins.length - 1, Math.floor(c / 25)); counts[idx]++; } return { type: 'bar', data: { labels: ['0–24', '25–49', '50–74', '75+'], datasets: [{ label: 'Tiles', data: counts.slice(0, 4) }] } };\" })",'analyseData({ placesEntryIDs: ["ev-stations"], trafficAreaAnalyticsEntryIDs: ["tta-0"], name: "ev-stations-by-congestion", code: "const buckets = { low: 0, mid: 0, high: 0 }; for (const p of places.features) { const tile = trafficAreaAnalytics.features.find(t => turf.booleanPointInPolygon(p, t)); if (!tile) continue; const c = tile.properties.congestionLevel ?? 0; if (c < 25) buckets.low++; else if (c < 75) buckets.mid++; else buckets.high++; } return buckets;" })','analyseData({ routesEntryIDs: ["routes-0"], trafficAreaAnalyticsEntryIDs: ["tta-0"], name: "route-tile-coverage", code: "const r = routes.features[0]; const hits = trafficAreaAnalytics.features.filter(t => turf.lineIntersect(r, t).features.length > 0); return { tilesCrossed: hits.length, avgCongestion: hits.length ? +(hits.reduce((s, t) => s + (t.properties.congestionLevel ?? 0), 0) / hits.length).toFixed(1) : null };" })','analyseData({ incidentsEntryIDs: ["incidents-0"], name: "by-category", code: "const c = {}; for (const i of incidents) c[i.properties.category] = (c[i.properties.category] ?? 0) + 1; return { total: incidents.length, byCategory: c };" })','analyseData({ incidentsEntryIDs: ["incidents-0"], name: "top-delays", code: "return [...incidents].sort((a,b) => (b.properties.delayInSeconds ?? 0) - (a.properties.delayInSeconds ?? 0)).slice(0,5).map(i => ({ id: i.properties.id, delay: i.properties.delayInSeconds, road: i.properties.roadNumbers?.[0] }));" })',"analyseData({ incidentsEntryIDs: [\"incidents-0\"], monitor: { entryId: \"incidents-0\" }, name: \"rolling-count\", code: \"const trend = previous ? (incidents.length > previous.count ? 'growing' : incidents.length < previous.count ? 'fading' : 'steady') : 'new'; return { count: incidents.length, trend, at: now.toISOString() };\" })",'analyseData({ incidentsEntryIDs: ["incidents-0"], monitor: { entryId: "incidents-0" }, name: "top-3-focus", code: "const top = [...incidents].sort((a,b) => (b.properties.delayInSeconds ?? 0) - (a.properties.delayInSeconds ?? 0)).slice(0,3); return { count: top.length, focusIds: top.map(i => i.properties.id), focusReason: \'top 3 by delay\' };" })'],examplePrompts:["How many of each POI category are in these results?","h3 hex density of these places","Bar chart of the top POI categories","How long is each route alternative?","Compare travel times across alternatives","How many EV stations are within 500 m of the route?","Chart the distance from each cafe to the route, binned in km","Histogram of place-to-route distances","Distribution of traffic incidents along the drive from Paris to Brussels","Chart the incidents on the route by category","Total delay per country on this route","How big are these areas?","Bar chart of city areas","Incidents per place cluster","Total delay per traffic corridor","Congestion distribution across the analytics tiles","Average historical speed per tile, as a chart","Group these EV stations by the congestion level of the tile they fall in","For the route, which tiles does it cross and what is their average congestion?","How many incidents of each category?","Top 5 incidents by delay","Total delay grouped by road","Keep counting the incidents and tell me when it grows or fades","Highlight the 3 worst incidents by delay on the map","Bar chart of incidents per category","Which jams are growing vs fading on this monitored area?"],relatedTools:["processData","focusIncidents","recallPlaces","recallRoutes","recallGeometries","recallState"],dependsOn:["discoverPlaces","setRoute","getTrafficIncidents","getTrafficAreaAnalytics","findReachableAreas","recallPlaces","recallRoutes","recallGeometries"]}),setRoute:{description:"Calculate or recalculate a route. Provide `locations` to set/replace origin/stops/destination, `parameters` to update cost model / alternatives / when, or both in a single call. At least one of `locations` or `parameters` MUST be provided. If only `parameters` is given and waypoints already exist, recalculates with the new options; if no waypoints exist yet, parameters are stored and no recalculation runs. Car/driving only. Do NOT use to show, query, or modify an EXISTING displayed route — use updateRoutesDisplay / recallRoutes / addWaypointsToRoute / removeWaypointsFromRoute / replaceWaypointInRoute / discoverPlaces (detour|withinRoute) / getRouteProgress instead.",classificationPrompt:"Use to define a new route or change route waypoints/options. Pass `locations` for a new origin/stops/destination, `parameters` to update cost model / alternatives / when, or both together. Do not use to show, query, or modify an existing displayed route.",inputSchema:Cs,outputSchema:As,execute:async(e,t)=>{const{locations:r,parameters:s,showOnMap:o,hidePreviousEntries:n}=e;try{let e;if(s&&t.routing.setParams(s),r){const s=await Promise.all(r.map(e=>Ds(e,t))),o=[];for(let e=0;e<r.length;e++){if(null!==s[e])continue;const t=r[e];"query"in t?o.push(`"${t.query}"`):"placeId"in t&&o.push(`placeId "${t.placeId}"`)}if(o.length>0)return{error:`Could not resolve: ${o.join(", ")}`};if(e=ks(s.map(e=>e?.place??null)),!e)return{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}else if(e=ks(t.routing.planningSlots),!e)return{success:!0};return Rs(t,e,o,n)}catch(i){return{error:`Route calculation failed: ${i instanceof Error?i.message:String(i)}`}}},tags:["route","waypoint","location"],examples:['setRoute({ locations: [{ query: "Amsterdam" }, { query: "Brussels" }] })','setRoute({ locations: [{ query: "Eiffel Tower" }, { query: "Louvre Museum" }, { query: "Notre Dame" }] })',"setRoute({ parameters: { maxAlternatives: 2 } })",'setRoute({ parameters: { costModel: { routeType: "fast", avoid: ["tollRoads"] } } })','setRoute({ parameters: { when: { option: "departAt", date: "2025-06-01T09:00:00Z" } } })','setRoute({ locations: [{ query: "Paris" }, { query: "Lyon" }], parameters: { costModel: { routeType: "short" } } })'],examplePrompts:["Get directions from Paris to Lyon","Route me from Amsterdam to Brussels","Plan a new trip from Amsterdam to Schiphol airport","Recalculate my route to Brussels from Schiphol airport","Recalculate the route from Utrecht to Brussels","Avoid toll roads on my current route","Find the fastest route from Berlin to Munich","Show 2 alternative routes for my current trip","Depart at 9am on my current route"],relatedTools:["updateRoutesDisplay","addWaypointsToRoute","removeWaypointsFromRoute","replaceWaypointInRoute","recallRoutes"]},addWaypointsToRoute:{description:"Extend the current route — prepend a new origin in front of the existing one, append a new destination after the existing one, and/or insert intermediate stops at optimal positions along the existing route geometry. Existing origin/destination are preserved as intermediate stops, never replaced. Always recalculates. At least one of `origin`, `destination`, or `stops` MUST be provided. Requires an existing route. To outright replace waypoints, use setRoute instead.",classificationPrompt:"Extend the route by prepending a new origin and/or appending a new destination (preserving the existing endpoints as stops), and/or inserting intermediate stops at optimal positions, then recalculate. Use setRoute when the user wants to fully replace waypoints.",inputSchema:Ls,outputSchema:Ns,execute:async(e,t)=>{const{origin:r,destination:s,stops:o,showOnMap:n,hidePreviousEntries:i}=e;try{const e=await(async e=>{const t=e.routing.currentEntryModule;if(!t)return"No existing route on the map. Use calculate-route or updateRoutesDisplay first.";const r=t.getShown(),s=r?.mainLines,o=r?.waypoints;return s&&s.features.length?!o||o.features.length<2?"No existing waypoints available. Use calculate-route first to create a route with waypoints.":{shownRouteLines:s,shownWaypoints:o}:"No existing route available. Use calculate-route first."})(t);if("string"==typeof e)return{error:e};const{shownRouteLines:a,shownWaypoints:l}=e,c=await(async(e,t)=>{const{origin:r,destination:s,stops:o=[]}=e,n=[...r?[{kind:"origin",location:r}]:[],...s?[{kind:"destination",location:s}]:[],...o.map(e=>({kind:"stop",location:e}))],i=await Promise.all(n.map(({location:e})=>Ds(e,t))),a=[];for(let c=0;c<n.length;c++)null===i[c]&&a.push(Fs(n[c].location));if(a.length>0)return{error:`Could not resolve: ${a.join(", ")}`};const l={stops:[]};for(let c=0;c<n.length;c++){const e=i[c]?.place;"origin"===n[c].kind?l.origin=e:"destination"===n[c].kind?l.destination=e:l.stops.push(e)}return l})({origin:r,destination:s,stops:o},t);if("error"in c)return c;const d=[...c.origin?[c.origin]:[],...l.features,...c.destination?[c.destination]:[]],u=ks(c.stops.length>0?J(a.features[0],d,c.stops):d);return u?Rs(t,u,n,i):{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}catch(a){return{error:`Failed to add waypoints to route: ${a instanceof Error?a.message:String(a)}`}}},tags:["route","waypoint"],examples:['addWaypointsToRoute({ stops: [{ query: "Westminster Abbey" }] }) // single intermediate stop','addWaypointsToRoute({ stops: [{ query: "Reims" }, { query: "Metz" }] }) // multiple stops, settle into along-route order','addWaypointsToRoute({ origin: { query: "my house" } }) // prepend new origin; previous origin becomes a stop','addWaypointsToRoute({ destination: { query: "Heathrow Airport" } }) // append new destination; previous destination becomes a stop','addWaypointsToRoute({ origin: { query: "Schiphol" }, destination: { query: "Brussels" } }) // extend on both ends','addWaypointsToRoute({ origin: { query: "X" }, stops: [{ query: "Y" }] }) // combine endpoint extension and stops'],examplePrompts:["Add a stop at Westminster Abbey","Add stops at Reims and Metz on the way","Pick me up from my house first, then continue the trip","Continue past the destination to Heathrow"],relatedTools:["locatePlace","setRoute","updateRoutesDisplay","removeWaypointsFromRoute","replaceWaypointInRoute"],dependsOn:["setRoute"]},removeWaypointsFromRoute:{description:"Remove one or more waypoints from the current route by index; always recalculates. Index 0 = origin, last = destination (both protected when removing them would leave fewer than 2). Requires an existing route.",classificationPrompt:"Remove one or more waypoints by index from the current route and recalculate.",inputSchema:Ao,outputSchema:_o,execute:async(e,t)=>{const{waypointIndices:r,showOnMap:s,hidePreviousEntries:o}=e;try{const e=((e,t)=>{if(!e||e.length<2)return"No existing waypoints available. Use calculate-route first to create a route with waypoints.";const r=new Set(t);if(r.size!==t.length)return"Duplicate waypoint indices are not allowed.";for(const s of t)if(s<0||s>=e.length)return`Invalid waypoint index ${s}. Valid range is 0 to ${e.length-1}.`;return e.length-t.length<2?"Cannot remove waypoints: at least 2 waypoints (origin and destination) must remain.":e.filter((e,t)=>!r.has(t))})(t.routing.currentWaypoints,r);if("string"==typeof e)return{error:e};const n=ks(e);return n?Rs(t,n,s,o):{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}catch(n){return{error:`Failed to remove waypoints from route: ${n instanceof Error?n.message:String(n)}`}}},tags:["route","waypoint"],examples:["removeWaypointsFromRoute({ waypointIndices: [1] }) // remove a single stop","removeWaypointsFromRoute({ waypointIndices: [1, 2] }) // remove multiple stops in one call"],examplePrompts:["Remove the second stop from my route","Delete the Utrecht stop","Drop the last two intermediate stops"],relatedTools:["addWaypointsToRoute","replaceWaypointInRoute","setRoute","updateRoutesDisplay"],dependsOn:["setRoute"]},replaceWaypointInRoute:{description:"Replace a single waypoint in the current route in place — origin, destination, or any intermediate stop by 0-based index. Always recalculates. Other waypoints are preserved. Requires an existing route. Use addWaypointsToRoute to extend the route, removeWaypointsFromRoute to drop a waypoint, or setRoute to fully replace all waypoints.",classificationPrompt:"Overwrite a single waypoint of the current route in place — by 'origin', 'destination', or numeric index — and recalculate. Other waypoints are kept.",inputSchema:Oo,outputSchema:Co,execute:async(e,t)=>{const{waypointIndex:r,location:s,showOnMap:o,hidePreviousEntries:n}=e;try{const e=t.routing.currentWaypoints;if(!e||e.length<2)return{error:"No existing waypoints available. Use calculate-route first to create a route with waypoints."};const l=(i=r,a=e.length,"origin"===i?0:"destination"===i?a-1:i<0||i>=a?`Invalid waypoint index ${i}. Valid range is 0 to ${a-1}.`:i);if("string"==typeof l)return{error:l};const c=await Ds(s,t);if(!c)return{error:`Could not resolve location: ${Ro(s)}`};const d=[...e];d[l]=c.place;const u=ks(d);return u?Rs(t,u,o,n):{error:"Not enough valid waypoints to calculate a route (minimum 2)."}}catch(l){return{error:`Failed to replace waypoint in route: ${l instanceof Error?l.message:String(l)}`}}var i,a},tags:["route","waypoint"],examples:['replaceWaypointInRoute({ waypointIndex: "origin", location: { query: "Schiphol" } })','replaceWaypointInRoute({ waypointIndex: "destination", location: { query: "Heathrow" } })','replaceWaypointInRoute({ waypointIndex: 1, location: { query: "Utrecht" } }) // overwrite the first intermediate stop'],examplePrompts:["Change the origin to Schiphol","Make Heathrow my destination","Replace the second stop with Utrecht"],relatedTools:["addWaypointsToRoute","removeWaypointsFromRoute","setRoute","locatePlace"],dependsOn:["setRoute"]},getPOICategoryCodes:{description:'Resolve natural language (e.g. "gym", "italian food", "bookstore") into POICategory codes for use in the discoverPlaces poiCategories parameter. Returns all codes when no filter is given.',classificationPrompt:'Resolve natural-language category names (e.g. "gym", "italian food") to POICategory codes for discoverPlaces.',inputSchema:co,outputSchema:lo,execute:async(e,t)=>{const{filters:r,language:s}=e;try{const e=await P({filters:r,language:s});return{count:e.length,codes:e}}catch(o){return{error:`Failed to fetch POI category codes: ${o instanceof Error?o.message:String(o)}`}}},tags:["discover","place"],examples:["getPOICategoryCodes()",'getPOICategoryCodes(filters: ["gym"])','getPOICategoryCodes(filters: ["italian restaurant"]) // multi-word phrase, spaces removed before matching','getPOICategoryCodes(filters: ["restaurant"], language: "fr-FR")'],examplePrompts:["What POI categories are available?","What's the code for gym?"],relatedTools:["discoverPlaces","toggleTilesPOIs"]},getTrafficIncidents:{description:'LOADER. Fetches traffic incidents (present or future) into a cached entry, renders them on the map, returns `{ count, entryId, entries }` (`entries` lists every cached entry so the agent can pick a follow-up target). No per-incident details, no aggregation — those belong to analyseData/focusIncidents. \n\nAREA via `where` (within mode only). Omit `where` to use the current map viewport ("the network / right now / this area / here"). For named areas use `where.queries`; for stored places use `where.placeIds`; for an isochrone use `where.range`; for a corridor along a route use `where.route`; for raw bounds use `where.boundingBox`; for custom shapes use `where.geometries`. Multi-region fields compose into one union bbox (≤10,000 km²). \n\nCALL ONCE PER AREA. Each call writes a new entry; re-calling burns steps. Pick the right follow-up instead: analyseData (counts, top-N, charts, clusters, trend via `previous`); focusIncidents (highlight a subset); startTrafficIncidentsMonitor (keep the entry fresh in the background). \n\n`silent: true` suppresses rendering (intermediate calcs only); `label` names the snapshot in the UI.',classificationPrompt:`AREA-BOUND incident loader — bbox / viewport / "incidents around here" / "accidents in <city>". Covers every incident category: ${H.join(", ")}. DO NOT pick for route-bound queries ("incidents ALONG / ON the route", "delays on the drive from X to Y"): a calculated route already carries its incidents in \`route.properties.sections.traffic[]\` (category, magnitudeOfDelay, delayInSeconds, effectiveSpeedInKmh) — call \`analyseData\` with \`routesEntryIDs\` for those. When this tool IS the right pick: MANDATORY co-pick startTrafficIncidentsMonitor (they are a pair — data goes stale without it). Also co-pick analyseData (one-shot aggregations / charts / clusters; pass \`monitor: { entryId }\` for the recurring spec + focus side-effect) and focusIncidents (highlight a subset) when the query implies them.`,inputSchema:Do,outputSchema:go,execute:async(e,t)=>{const{where:r,categoryFilter:s,timeValidityFilter:o,label:n,silent:i=!1,fitBounds:a=!0}=e,l=r??{mode:"within",viewport:!0},c=await To(l,t);if("error"in c)return c;const{bbox:d,routeLabel:u}=c,p={...s&&{categoryFilter:s},...o&&{timeValidityFilter:o}};try{const e=await O({...p,bbox:d});if(!e.features.length)return{count:0,entries:fo(t)};const r={bbox:d,...p},s=await t.trafficIncidents.addIncidentsEntry(e.features,r,n??((e,t,r)=>{const s=[];let o;if(e.categoryFilter?.length&&s.push(`filtered: ${e.categoryFilter.join(",")}`),e.timeValidityFilter?.length&&s.push(`when: ${e.timeValidityFilter.join(",")}`),t&&!t.viewport&&(t.queries?.length?o=t.queries.map(e=>e.query).join(", "):r?o=`along ${r}`:t.range&&(o=t.range)),!o){const[t,r,s,n]=e.bbox;o=`bbox ${t.toFixed(2)},${r.toFixed(2)} → ${s.toFixed(2)},${n.toFixed(2)}`}return s.length?`${o} — ${s.join("; ")}`:o})(r,l,u),Date.now());return i||await t.trafficIncidents.showEntry(s,a),{count:e.features.length,entryId:s,entries:fo(t)}}catch(h){return{error:`Failed to get traffic incidents: ${h instanceof Error?h.message:String(h)}`}}},tags:["traffic","location"],examples:["getTrafficIncidents({}) // omit `where` → current viewport",'getTrafficIncidents({ where: { mode: "within", boundingBox: [4.728, 52.278, 5.080, 52.479] } })','getTrafficIncidents({ where: { mode: "within", queries: [{ query: "Amsterdam" }] } }) // named area','getTrafficIncidents({ where: { mode: "within", queries: [{ query: "Amsterdam" }] }, categoryFilter: ["accident", "jam"] })','getTrafficIncidents({ where: { mode: "within", route: { widthMeters: 1000 } }, categoryFilter: ["roadworks"], timeValidityFilter: ["future"] }) // corridor around latest route','getTrafficIncidents({ where: { mode: "within", range: "ranges-0" } }) // inside a reachable area'],examplePrompts:["Fetch traffic incidents in Amsterdam","Any accidents in this area?","Show roadworks across the city"],relatedTools:["calculateBBox","getViewport","analyseData","focusIncidents","startTrafficIncidentsMonitor"]},focusIncidents:{description:"Highlight a subset of incidents on an existing entry. Drives the dim/highlight on the map and the FocusChip in the UI. Compose with `analyseData` to compute the ids first (e.g. top-N by delay, members of a cluster). Unknown ids are dropped silently and returned in `droppedIds`. Errors only when the entry is unknown or NONE of the ids resolve.",classificationPrompt:'Highlight a subset of incidents on an existing entry (drives map dim/highlight + FocusChip). Pick for "focus on…", "show only…", "highlight the…". Compose with analyseData to compute the ids.',inputSchema:en,outputSchema:Qo,execute:async(e,t)=>{const{incidentsEntryID:r,incidentIds:s,reason:o}=e;if(!t.trafficIncidents.entries.find(e=>e.id===r))return{error:`No incidents entry with id "${r}". Call getTrafficIncidents first.`};const n=t.trafficIncidents.setFocus(r,s,o);return 0===n.focusedCount?{error:`None of the ${s.length} requested ids exist on entry "${r}". The snapshot may have rolled — call getTrafficIncidents and recompute the ids before retrying.`}:{incidentsEntryID:r,focusedCount:n.focusedCount,droppedIds:n.droppedIds,...o&&{reason:o}}},tags:["traffic","incident","state","ui"],examples:["focusIncidents({ incidentsEntryID: 'incidents-0', incidentIds: ['TTI-...', 'TTI-...'], reason: 'top 3 by delay' })"],examplePrompts:["Focus on the 3 worst incidents by delay","Highlight the closures","Show only the incidents on the A4"],relatedTools:["analyseData","getTrafficIncidents"],dependsOn:["getTrafficIncidents"]},clusterIncidents:{description:"Deterministic DBSCAN clustering of traffic incidents into compact geographic pockets. Produces stable cluster IDs (survive monitor ticks), road/category labels, delay aggregates, and multi-poll trend detection. Auto-registers as a monitor-rerunning spec — clusters stay live without re-calling. Returns structured fields (roads, category, size, delay, trend) the caller phrases into a read; the UI pins each group automatically. One clustering per entry — re-calling replaces it. Pair with getTrafficIncidents + startTrafficIncidentsMonitor.",classificationPrompt:'Deterministic DBSCAN clustering of loaded traffic incidents — "clusters / hotspots / dense pockets / worst areas". Produces stable IDs, road/category labels, delay aggregates, trend detection. Auto-reruns on monitor ticks. ALWAYS co-pick with getTrafficIncidents + startTrafficIncidentsMonitor.',inputSchema:Ko,outputSchema:Zo,execute:async(e,t)=>{const{incidentsEntryID:r,eps:s,minMembers:o,maxClusters:n}=e,i=t.trafficIncidents.entries.find(e=>e.id===r);if(!i)return{error:`No incidents entry with id "${r}". Call getTrafficIncidents first.`};const a={...null!=s&&{eps:s},...null!=o&&{minMembers:o},...null!=n&&{maxClusters:n}},l=await t.trafficIncidents.setDeterministicSpec({name:Xo,source:i.id,signature:JSON.stringify(a),run:(e,{previous:t,sampledAt:r,previousSampledAt:s})=>Xt(e,a,t?.groups,r,s)},{runNow:!0});return l?{incidentsEntryID:i.id,clusterCount:l.groups.length,clusters:l.groups.map(e=>({id:e.id,primaryRoads:e.primaryRoads,primaryCategory:e.primaryCategory,size:e.size,totalDelaySeconds:e.totalDelaySeconds,peakDelaySeconds:e.peakDelaySeconds,trend:e.trend}))}:{error:`Clustering failed for entry "${i.id}".`}},tags:["traffic","incident","analysis","clustering"],examples:["clusterIncidents({ incidentsEntryID: 'incidents-0' })","clusterIncidents({ incidentsEntryID: 'incidents-0', eps: 0.3, minMembers: 4, maxClusters: 8 })"],examplePrompts:["Cluster the incidents","Show me the hotspots","Where are the worst congestion pockets?","Group the incidents into clusters"],relatedTools:["getTrafficIncidents","startTrafficIncidentsMonitor","analyseData","focusIncidents"],dependsOn:["getTrafficIncidents"]},startTrafficIncidentsMonitor:{description:"Start a background monitor for an existing incidents entry. Re-fetches the entry's bbox + filters every interval (default 60s) and replaces its data; any registered analyses re-run on each tick. The first tick fires immediately, so this duplicates the most recent getTrafficIncidents fetch — only call it when the user wants the entry to stay current. \n\nIdempotent on a running monitor: a second call is a no-op and returns alreadyRunning: true. A new intervalMs is NOT applied to a running monitor — stop first, then start again to change settings.",classificationPrompt:'Polls an existing incidents entry on a fixed interval (default 60s); registered analyses re-run on each tick. ALWAYS pick this whenever getTrafficIncidents is picked — they are a mandatory pair, no exceptions. Without it the loaded data is a frozen snapshot. Also pick on its own when the user says "live"/"watch"/"keep updated"/"refresh every N seconds" against an already-loaded entry.',inputSchema:gi,outputSchema:fi,execute:async(e,t)=>{const{incidentsEntryID:r,intervalMs:s}=e,o=t.trafficIncidents.entries.find(e=>e.id===r);if(!o)return{error:`No incidents entry with id "${r}". Call getTrafficIncidents first.`};const n=t.trafficIncidents.isMonitored(r),{bbox:i,categoryFilter:a,timeValidityFilter:l}=o.params,c={...a&&{categoryFilter:a},...l&&{timeValidityFilter:l}};return t.trafficIncidents.startMonitoring(r,{bbox:i,capturedAt:o.timestamp,label:o.label},{fetchIncidents:async e=>(await O({...c,bbox:e})).features},s?{intervalMs:s}:void 0),{incidentsEntryID:r,alreadyRunning:n}},tags:["traffic","incident","state"],examples:["startTrafficIncidentsMonitor({ incidentsEntryID: 'incidents-0' })","startTrafficIncidentsMonitor({ incidentsEntryID: 'incidents-0', intervalMs: 30000 })"],examplePrompts:["Keep this updated","Refresh the incidents every 30 seconds","Watch this area for new accidents"],relatedTools:["getTrafficIncidents","stopTrafficIncidentsMonitor","analyseData"],dependsOn:["getTrafficIncidents"]},stopTrafficIncidentsMonitor:{description:"Stop the background monitor for an incidents entry. The entry itself stays in state with its last data — use removeEntry if you also want to drop the entry. No-op when no monitor is running for the entry.",classificationPrompt:'Stop polling an incidents entry; the entry stays in state with its last data. Pick for "stop refreshing"/"pause updates" or when ending a watch.',inputSchema:wi,outputSchema:bi,execute:async(e,t)=>{const{incidentsEntryID:r}=e;if(!t.trafficIncidents.entries.find(e=>e.id===r))return{error:`No incidents entry with id "${r}".`};const s=t.trafficIncidents.isMonitored(r);return t.trafficIncidents.stopMonitoring(r),{incidentsEntryID:r,wasRunning:s}},tags:["traffic","incident","state"],examples:["stopTrafficIncidentsMonitor({ incidentsEntryID: 'incidents-0' })"],examplePrompts:["Stop refreshing","Pause updates for this area"],relatedTools:["startTrafficIncidentsMonitor","getTrafficIncidents"],dependsOn:["startTrafficIncidentsMonitor"]},getTrafficAreaAnalytics:{description:"Fetch historical traffic analytics for an area (≤31 days) via location (named city/region) or bbox. Fetch ALL metrics in ONE call (visualization needs them together); use city-scale areas (~10 km+) for meaningful data. Returns period averages — `showOnMap` visualizes immediately; queryTrafficAnalytics for breakdowns.",classificationPrompt:"Fetch historical traffic analytics (speed, congestion, travel time) for an area. queryTrafficAnalytics drills down without re-fetching.",inputSchema:ho,outputSchema:po,execute:async(e,t)=>{const{location:r,bbox:s,showOnMap:o,startDate:n,endDate:i,days:a,metrics:l,functionalRoadClasses:c,hours:d}=e;if(!r&&!s)return{error:"Provide a location or bbox to define the analytics area."};const u=process.env.MOVE_PORTAL_KEY;if(!u)return{error:"MOVE_PORTAL_KEY environment variable is not set."};try{const e=await(async(e,t,r)=>{if(e){const t=await Ds(e,r);if(!t)return{error:"Could not resolve the provided location."};if(Array.isArray(t.place))return{error:"A bare position cannot define an analytics area. Provide a named location query instead."};const s=(await D({geometries:[t.place]}))?.features?.[0]?.geometry;return s||{error:`No boundary polygon found for "${t.name}". Try a bbox instead.`}}return t?(e=>{const[t,r,s,o]=e;return{type:"Polygon",coordinates:[[[t,r],[s,r],[s,o],[t,o],[t,r]]]}})(t):{error:"No area provided."}})(r,s,t);if("error"in e)return e;const p=((e,t,r)=>e&&e.length>0?{days:e}:t?r?{startDate:t,endDate:r}:{startDate:t}:{})(a,n,i),h=await k({apiKey:u,...p,metrics:l,functionalRoadClasses:c??"all",hours:d??"all",geometry:e}),y=h.features[0]?.properties?.name,m=y?`${y} (${p.startDate??"…"} → ${p.endDate??"…"})`:`analytics (${p.startDate??"…"} → ${p.endDate??"…"})`,f=await t.trafficAreaAnalytics.addEntry(h,m,{...p,metrics:l,functionalRoadClasses:c??"all",hours:d??"all"});return o&&await(async(e,t)=>{await e.trafficAreaAnalytics.showEntry(t);const r=e.trafficAreaAnalytics.findById(t);if(!r)return;const s=await e.trafficAreaAnalytics.getEntryModule(t),o=r.data.properties?.metrics?.[0]??"congestionLevel";s.setMetric(o);const n=L(r.data.features);n&&e.baseMap.mapLibreMap.fitBounds(n,{padding:50,pitch:45})})(t,f),((e,t,r,s)=>{const o=t.features[0]?.properties;if(!o)return{entryId:e,dateRange:{start:"",end:""},baseData:{},metrics:[],tileCount:0,availableGranularities:[]};const n=r??mo(t.properties?.startDate)??(()=>{const e=/* @__PURE__ */new Date;return e.setDate(e.getDate()-3),yo(e)})(),i=s??mo(t.properties?.endDate)??(()=>{const e=/* @__PURE__ */new Date;return e.setDate(e.getDate()-2),yo(e)})(),a=[];return o.timedData?.yearly?.length&&a.push("yearly"),o.timedData?.monthly?.length&&a.push("monthly"),o.timedData?.weekly?.length&&a.push("weekly"),o.timedData?.daily?.length&&a.push("daily"),o.timedData?.hourly?.length&&a.push("hourly"),o.timedData?.average?.length&&a.push("average"),{entryId:e,...o.name&&{name:o.name},...o.timezone&&{timezone:o.timezone},dateRange:{start:n,end:i},baseData:o.baseData,metrics:t.properties?.metrics??[],tileCount:o.tiledData?.tiles?.length??0,availableGranularities:a}})(f,h,p.startDate,p.endDate)}catch(p){return{error:`Failed to get traffic area analytics: ${p instanceof Error?p.message:String(p)}`}}},tags:["traffic","location"],examples:["getTrafficAreaAnalytics({ bbox: [4.728, 52.278, 5.080, 52.479], metrics: ['speed', 'congestionLevel'], startDate: '2025-03-01', endDate: '2025-03-07' })","getTrafficAreaAnalytics({ bbox: [...], metrics: ['congestionLevel'], hours: [7,8,9,17,18] })"],examplePrompts:["What was the average congestion in Amsterdam last week?","Analyze traffic patterns during rush hour in this area","Show me historical speed data for the city center"],relatedTools:["queryTrafficAnalytics","updateTrafficAreaAnalyticsDisplay","calculateBBox","getViewport"],dependsOn:["locatePlace"]},queryTrafficAnalytics:{description:'Query cached traffic analytics (from getTrafficAreaAnalytics) without re-fetching — filter by granularity, date/hour range, day, metric. Returns filtered entries plus the current map visualization state (metric, mode). Use for breakdowns and "what is shown" follow-ups.',classificationPrompt:"Query cached traffic analytics data or check what metric/mode is currently displayed on the map.",inputSchema:vn,outputSchema:In,execute:async(e,t)=>{const{entryId:r,granularity:s="daily",metric:o,startDate:n,endDate:i,hourStart:a,hourEnd:l,dayOfWeek:c}=e,d=r?t.trafficAreaAnalytics.findById(r):t.trafficAreaAnalytics.latestEntry;if(!d)return{error:r?`No traffic-area-analytics entry with id "${r}". Use recallState to list available ids.`:"No analytics entries available. Call getTrafficAreaAnalytics first."};const u=d.data,p=u.features[0]?.properties;if(!p)return{error:"Analytics result has no region data."};const h=p.timedData,y=h?.[s];if(!y||!Array.isArray(y)||0===y.length)return{error:`No '${s}' data available. Available: ${Object.keys(h??{}).join(", ")}`};const m=mo(u.properties?.startDate);let f=y.map((e,t)=>{const r={};if("daily"===s||"hourly"===s){const o=((e,t,r,s)=>{const o=mo(e);if(o)return o;if(s&&t>=0){const e=new Date(s);if(!Number.isNaN(e.getTime())){const s="hourly"===r?Math.floor(t/24):t;return e.setDate(e.getDate()+s),yo(e)}}})(e.date,t,s,m);o&&(r.date=o)}if(void 0!==e.hour&&(r.hour=e.hour),void 0!==e.year&&(r.year=e.year),void 0!==e.month&&(r.month=e.month),void 0!==e.week&&(r.week=e.week),void 0!==e.day&&(r.day=e.day),o){const t=e[o];void 0!==t&&(r[o]=t)}else Object.assign(r,(e=>({...void 0!==e.speed&&{speed:e.speed},...void 0!==e.freeFlowSpeed&&{freeFlowSpeed:e.freeFlowSpeed},...void 0!==e.congestionLevel&&{congestionLevel:e.congestionLevel},...void 0!==e.travelTime&&{travelTime:e.travelTime},...void 0!==e.networkLength&&{networkLength:e.networkLength}}))(e));return r});n&&(f=f.filter(e=>!e.date||e.date>=n)),i&&(f=f.filter(e=>!e.date||e.date<=i)),void 0!==a&&(f=f.filter(e=>void 0===e.hour||e.hour>=a)),void 0!==l&&(f=f.filter(e=>void 0===e.hour||e.hour<=l)),c&&c.length>0&&(f=f.filter(e=>void 0===e.day||c.includes(e.day)));const g=d._config,b=g?{activeMetric:g.activeMetric,displayMode:g.displayMode}:void 0;return{granularity:s,count:f.length,entries:f,...b&&{currentVisualization:b}}},tags:["traffic","location"],examples:["queryTrafficAnalytics({ granularity: 'daily' })","queryTrafficAnalytics({ granularity: 'hourly', hourStart: 7, hourEnd: 9, metric: 'congestionLevel' })","queryTrafficAnalytics({ granularity: 'average', dayOfWeek: [6, 7] })"],examplePrompts:["Show me the daily breakdown","What was rush hour congestion?","Which day had the worst traffic?","What metric am I looking at?","What is currently shown on the map?"],relatedTools:["getTrafficAreaAnalytics","updateTrafficAreaAnalyticsDisplay"],dependsOn:["getTrafficAreaAnalytics"]},updatePlacesDisplay:{description:'Show, hide, and swap which places entries are displayed on the map in one atomic call. Target entries by id (`add` / `remove`) or by category label (`addMatching` / `removeMatching`). `clear` hides every currently-shown entry; combine with `add` to swap the display ("hide everything, show these"). Markers (`markerType`) and boundary polygons (`geometry`) are independent display channels — set either or both for the targeted entries. Omit `add`/`addMatching` while still setting `markerType` or `geometry` to target the most recently stored entry. Does not delete from history.',classificationPrompt:"Show, hide, and swap places entries on the map — add some (as pins and/or boundary polygons), remove others, swap atomically, or render boundaries.",inputSchema:Wi,outputSchema:Ui,execute:async(e,t)=>{const{clear:r,markerType:s,geometry:o,fitBounds:n}=e;try{const i=((e,t)=>{const{add:r,remove:s,addMatching:o,removeMatching:n,markerType:i,geometry:a}=t;let l=[...r??[],...zi(e,o??[])];const c=[...s??[],...zi(e,n??[])];if(void 0===r&&void 0===o&&(void 0!==i||void 0!==a)){const t=e.places.entries.at(-1)?.id;if(!t)return{error:"No places available to display. Resolve a place or run a search first (e.g. discoverPlaces)."};l=[t]}const d=new Set(e.places.entries.map(e=>e.id)),u=l.filter(e=>!d.has(e));return u.length>0?{error:`Unknown places entry ids: ${u.join(", ")}. Use recallPlaces to list available entries.`}:{add:l,remove:c}})(t,e);if("error"in i)return i;const{add:a,remove:l}=i,c=new Set(t.places.shownEntryIds);r&&(await t.places.clearShownEntries(),await t.places.clearShownGeometries()),l.length>0&&(await t.places.removeShownEntries(l),await t.places.clearShownGeometries(l));const d=void 0!==s||!o;let u;a.length>0&&d&&await(async(e,t,r,s)=>{const o=void 0!==r?t:t.filter(e=>!s.has(e));o.length>0&&await e.places.addShownEntries(o,r??"pin")})(t,a,s,c),o&&a.length>0&&(u=await(async(e,t,r)=>{const s=r.theme??"outline",o=r.mode??"replace",n=r.placeIds?new Set(r.placeIds):void 0,i=[];let a=0;for(const c of t){const t=e.places.entries.find(e=>e.id===c);if(!t)continue;const r=n?t.places.filter(e=>n.has(e.id)):t.places,d=[];for(const s of r)if(s.properties.dataSources?.geometry?.id)try{const t=await e.places.fetchPlaceGeometry(s.id);if(!t){i.push({placeId:s.id,reason:"Geometry Data service returned no feature for this place."});continue}d.push(t)}catch(l){i.push({placeId:s.id,reason:`Geometry fetch failed: ${l instanceof Error?l.message:String(l)}`})}else i.push({placeId:s.id,reason:"Place has no geometry data source (e.g. addresses and streets often lack one)."});d.length>0&&(await e.places.showPlaceGeometries(d,s,o,c),a+=d.length)}return{rendered:a,skipped:i}})(t,a,o));const p=t.places.shownEntryIds,h=a.filter(e=>!c.has(e)&&p.has(e)),y=l.filter(e=>c.has(e)&&!p.has(e)),m=n??a.length>0,f=t.places.getShownFeatures();if(m&&f.length>0){const e=L(f);e&&t.baseMap.mapLibreMap.fitBounds(e,{padding:50})}const g=t.places.entries.filter(e=>p.has(e.id)).map(e=>({id:e.id,label:e.label,featureCount:e.places.length,markerType:t.places.getShownMarkerType(e.id)??"pin"}));return{success:!0,totalShown:f.length,shown:g,actuallyAdded:h,actuallyRemoved:y,...u&&{geometriesRendered:u.rendered},...u?.skipped.length&&{geometriesSkipped:u.skipped}}}catch(i){return{error:`updatePlacesDisplay failed: ${i instanceof Error?i.message:String(i)}`}}},tags:["place","location","map style","boundary","outline"],examples:['updatePlacesDisplay({ add: ["places-2"] })','updatePlacesDisplay({ add: ["places-1", "places-3"], markerType: "base-map" })','updatePlacesDisplay({ remove: ["places-1"] })','updatePlacesDisplay({ removeMatching: ["cinema"] }) // hide every entry labelled "cinema"','updatePlacesDisplay({ addMatching: ["cafe"] })',"updatePlacesDisplay({ clear: true })",'updatePlacesDisplay({ clear: true, add: ["places-2"] }) // atomic swap: hide everything, show this','updatePlacesDisplay({ markerType: "pin" }) // re-pin the most recently stored entry',"updatePlacesDisplay({ geometry: {} }) // outline the most recently stored entry",'updatePlacesDisplay({ add: ["places-2"], geometry: { mode: "add" } }) // also outline on top of existing boundaries','updatePlacesDisplay({ add: ["places-2"], markerType: "pin", geometry: { theme: "outline" } }) // pin AND outline','updatePlacesDisplay({ add: ["places-2"], geometry: { placeIds: ["G55fc4abe-..."], theme: "outline" } }) // outline a subset'],examplePrompts:["Show the results on the map","Pin the search results","Hide the banks","Also show the cafes on the map","Additionally pin the pubs","Remove the banks from the map",'Drop every entry labelled "parking" from the map',"Toggle off the hotels without clearing the rest","Replace the pins with the new set","Show entry places-3 as base-map POIs","Show the boundary of Amsterdam on the map","Outline these neighbourhoods","Highlight the shape of this place","Pin the cities and outline them too","Also outline Utrecht while keeping Amsterdam drawn","Add the neighbourhood boundaries on top of the pins"],relatedTools:["discoverPlaces","locatePlace","reverseGeocode","recallPlaces","clearMap","flyTo"],dependsOn:["discoverPlaces","locatePlace","processData"]},updateRoutesDisplay:{description:'Show, hide, swap, and recolor which routes entries are displayed on the map in one atomic call. Target entries by id (`add` / `remove`) or by label substring (`addMatching` / `removeMatching`). `clear` hides every currently-shown entry; combine with `add` to swap the display ("hide everything, show this"). `selectedIndex` re-highlights an alternative variant on the newly-added entries; with no `add`/`addMatching` it implicitly targets the most recently stored entry. `mainColor` recolors the route line + waypoint icons (sticky across entry swaps — applied to current shows AND remembered); it does NOT auto-show a hidden entry, so set `add`/`addMatching` too if nothing is on the map yet. Does not delete from history. Does not call any service.',classificationPrompt:"Show, hide, swap, and recolor routes entries on the map — add some, remove others, swap atomically, switch the highlighted alternative, or set `mainColor` (CSS color / hex; sticky across entry swaps).",inputSchema:Hi,outputSchema:Yi,execute:async(e,t)=>{const{clear:r,selectedIndex:s,fitBounds:o,mainColor:n}=e;try{const i=((e,t)=>{const{add:r,remove:s,addMatching:o,removeMatching:n,selectedIndex:i}=t;let a=[...r??[],...Ji(e,o??[])];const l=[...s??[],...Ji(e,n??[])];if(void 0===r&&void 0===o&&void 0!==i){const t=e.routing.entries.at(-1)?.id;if(!t)return{error:"No routes available to display. Calculate a route first via setRoute."};a=[t]}const c=new Set(e.routing.entries.map(e=>e.id)),d=a.filter(e=>!c.has(e));return d.length>0?{error:`Unknown routes entry ids: ${d.join(", ")}. Use recallRoutes to list available entries.`}:{add:a,remove:l}})(t,e);if("error"in i)return i;const{add:a,remove:l}=i;void 0!==n&&t.routing.setMainColor(n);const{beforeShown:c,afterShown:d}=await(async(e,t)=>{const r=new Set(e.routing.shownEntryIds);if(t.clear)for(const s of e.routing.shownEntryIds)await e.routing.hideEntry(s);for(const s of t.remove)await e.routing.hideEntry(s);for(const s of t.add)await e.routing.showEntry(s);return void 0!==t.selectedIndex&&await(async(e,t,r)=>{if(0!==r)for(const s of t){const t=e.routing.entries.find(e=>e.id===s);if(!t)continue;const o=await e.routing.getEntryRoutingModule(s);await o.showRoutes(t.data,{selectedIndex:r}),t.waypoints.length&&await o.showWaypoints(t.waypoints)}})(e,t.add,t.selectedIndex),{beforeShown:r,afterShown:e.routing.shownEntryIds}})(t,{add:a,remove:l,clear:!!r,selectedIndex:s}),u=a.filter(e=>!c.has(e)&&d.has(e)),p=l.filter(e=>c.has(e)&&!d.has(e));(o??a.length>0)&&((e,t)=>{const r=[];for(const o of e.routing.entries)t.has(o.id)&&r.push(...o.data.features);if(0===r.length)return;const s=L({type:"FeatureCollection",features:r});s&&e.baseMap.mapLibreMap.fitBounds(s,{padding:50})})(t,d);const h=t.routing.entries.filter(e=>d.has(e.id)).map(e=>({id:e.id,label:e.label,routeCount:e.data.features.length,selectedIndex:u.includes(e.id)?s??0:0})),y=t.routing.mainColor;return{success:!0,totalShown:d.size,shown:h,actuallyAdded:u,actuallyRemoved:p,...void 0!==y&&{mainColor:y}}}catch(i){return{error:`updateRoutesDisplay failed: ${i instanceof Error?i.message:String(i)}`}}},tags:["route","waypoint","map style"],examples:['updateRoutesDisplay({ add: ["routes-1"] })','updateRoutesDisplay({ add: ["routes-0", "routes-2"] })','updateRoutesDisplay({ remove: ["routes-1"] })','updateRoutesDisplay({ removeMatching: ["Brussels"] }) // hide every entry whose label mentions "Brussels"','updateRoutesDisplay({ addMatching: ["Edinburgh"] })',"updateRoutesDisplay({ clear: true })",'updateRoutesDisplay({ clear: true, add: ["routes-2"] }) // atomic swap: hide everything, show this',"updateRoutesDisplay({ selectedIndex: 1 }) // re-highlight alternative #1 on the most recent route",'updateRoutesDisplay({ add: ["routes-0"], selectedIndex: 2, fitBounds: false })','updateRoutesDisplay({ mainColor: "coral" }) // recolor every currently-shown route (and future shows)','updateRoutesDisplay({ add: ["routes-1"], mainColor: "#FF0000" }) // show in red'],examplePrompts:["Draw the route on the map","Show the directions","Display the calculated route","Show the Edinburgh route from earlier","Also show the Brussels route","Hide the Lyon route","Switch to the second alternative","Highlight alternative 2","Replace what is shown with this route","Make the route line red","Change the route color to coral","Style the route in steelblue"],relatedTools:["setRoute","processData","recallRoutes","clearMap"],dependsOn:["setRoute"]},updateTrafficAreaAnalyticsDisplay:{description:"Show, hide, swap, and restyle HISTORICAL traffic-area-analytics entries on the map in one atomic call. Target entries by id (`add` / `remove`) or by label substring (`addMatching` / `removeMatching`). `clear` hides every currently-shown entry; combine with `add` to swap. Render knobs (`mode`, `metric`, `colorTheme`, `heightScale`, `scaleMode`, `filter`) apply to every entry being added AND every entry that stays shown after this call. When only render knobs are set with no add/remove/clear, implicitly targets the most recently added entry. Not real-time — use `toggleTilesTrafficFlow` for the live vector-tile flow overlay.",classificationPrompt:"Show / hide / swap / restyle historical traffic-area-analytics entries on the map. `add` / `remove` / `addMatching` / `removeMatching` / `clear` for visibility; `mode` / `metric` / `colorTheme` / `heightScale` / `scaleMode` / `filter` for the rendering. Render knobs alone implicitly target the most recent entry.",inputSchema:Zi,outputSchema:Qi,execute:async(e,t)=>{try{const r=((e,t)=>{const{add:r,remove:s,addMatching:o,removeMatching:n,clear:i}=t;let a=[...r??[],...ea(e,o??[])];const l=[...s??[],...ea(e,n??[])],c=void 0===r&&void 0===o&&void 0===s&&void 0===n&&!0!==i,d=void 0!==t.mode||void 0!==t.metric||void 0!==t.colorTheme||void 0!==t.heightScale||void 0!==t.scaleMode||void 0!==t.filter;if(c&&d){const t=e.trafficAreaAnalytics.entries.at(-1)?.id;if(!t)return{error:"No traffic-area-analytics entries available. Call getTrafficAreaAnalytics first."};a=[t]}const u=new Set(e.trafficAreaAnalytics.entries.map(e=>e.id)),p=a.filter(e=>!u.has(e));return p.length>0?{error:`Unknown traffic-area-analytics entry ids: ${p.join(", ")}. Use recallState to list available ids.`}:{add:a,remove:l}})(t,e);if("error"in r)return r;const{add:s,remove:o}=r,n=new Set(t.trafficAreaAnalytics.shownEntryIds);if(e.clear)for(const e of t.trafficAreaAnalytics.shownEntryIds)await t.trafficAreaAnalytics.hideEntry(e);for(const e of o)await t.trafficAreaAnalytics.hideEntry(e);for(const e of s)await t.trafficAreaAnalytics.showEntry(e);const i=t.trafficAreaAnalytics.shownEntryIds,a=s.filter(e=>!n.has(e)&&i.has(e)),l=o.filter(e=>n.has(e)&&!i.has(e)),c=new Set([...a,...i]);for(const u of c)await ta(t,u,e);if((e.fitBounds??a.length>0)&&i.size>0){const e=L({type:"FeatureCollection",features:t.trafficAreaAnalytics.entries.filter(e=>i.has(e.id)).flatMap(e=>e.data.features)});e&&t.baseMap.mapLibreMap.fitBounds(e,{padding:50,pitch:45})}const d=t.trafficAreaAnalytics.entries.filter(e=>i.has(e.id)).map(ra);return{success:!0,totalShown:i.size,shown:d,actuallyAdded:a,actuallyRemoved:l}}catch(r){return{error:`Failed to update traffic-area-analytics display: ${r instanceof Error?r.message:String(r)}`}}},tags:["traffic","map style"],examples:['updateTrafficAreaAnalyticsDisplay({ add: ["tta-0"] })',"updateTrafficAreaAnalyticsDisplay({ mode: 'hexgrid-2d', metric: 'speed', colorTheme: 'heat' }) // re-style the latest (implicit)","updateTrafficAreaAnalyticsDisplay({ filter: { min: 50 } })","updateTrafficAreaAnalyticsDisplay({ scaleMode: 'currentRange', heightScale: 200 })","updateTrafficAreaAnalyticsDisplay({ clear: true })",'updateTrafficAreaAnalyticsDisplay({ clear: true, add: ["tta-1"] }) // atomic swap','updateTrafficAreaAnalyticsDisplay({ remove: ["tta-0"] })','updateTrafficAreaAnalyticsDisplay({ addMatching: ["Amsterdam"] })'],examplePrompts:["Show the analytics on the map","Visualize the congestion","Only show areas with congestion above 50%","Normalize height to the current data range","Switch to heatmap view","Also show the Amsterdam analytics","Hide the Rotterdam analytics","Replace what is on the map with this analytics"],relatedTools:["getTrafficAreaAnalytics","queryTrafficAnalytics","toggleTilesTrafficFlow","clearMap","analyseData","processData"],dependsOn:["getTrafficAreaAnalytics"]},updateWaypointsDisplay:{description:"Show or hide the staged origin/stop/destination markers held in `state.routing.planningSlots` (the slots being built up for the next `setRoute` — NOT the waypoints of a calculated route entry). No route line is drawn; for that use `updateRoutesDisplay`. Requires at least one routes entry to host the module.",classificationPrompt:"Show or hide the STAGED origin/stop/destination markers (`state.routing.planningSlots` — what is queued for the NEXT `setRoute`). No route line is drawn; for that use `updateRoutesDisplay`. Single-collection toggle: `visible: true` (default) renders the staged set, `visible: false` clears it.",inputSchema:oa,outputSchema:sa,execute:async(e,t)=>{const r=e.visible??!0;try{const e=[...t.routing.shownEntryIds][0],s=t.routing.entries.at(-1),o=e??s?.id;if(!o)return{error:"No route history yet — calculate a route via setRoute first."};const n=await t.routing.getEntryRoutingModule(o);if(!r)return await n.clearWaypoints(),{success:!0,visible:!1,count:0};const i=t.routing.planningSlots;return await n.showWaypoints(i),{success:!0,visible:!0,count:i.length}}catch(s){return{error:`Failed to update waypoints display: ${s instanceof Error?s.message:String(s)}`}}},tags:["waypoint","route"],examples:["updateWaypointsDisplay()","updateWaypointsDisplay({ visible: true })","updateWaypointsDisplay({ visible: false })"],examplePrompts:["Show the staged waypoints on the map","Mark my pending route stops","Hide the staged waypoints"],relatedTools:["setRoute","updateRoutesDisplay","getCurrentWaypoints","clearMap"],dependsOn:["setRoute","locatePlace"]},getShownTileIncidents:{description:'List the traffic incidents currently rendered by the TomTom traffic-tile overlay (the rasterised live tiles, not the GeoJSON `getTrafficIncidents` service). Reads `state.trafficTiles.getTrafficIncidentsModule().getShown()` — so it only reflects what the tile layer is showing in the current viewport. For full TrafficIncident objects (with start/end times, road numbers, lanes, …) call `getTrafficIncidents` instead; for "incidents along this route" read `route.properties.sections.traffic[]` via `analyseData`.',classificationPrompt:'Read traffic incidents currently rendered by the TomTom TRAFFIC-TILE OVERLAY (the rasterised live tiles toggled via `toggleTilesTrafficIncidents`). DO NOT confuse with `getTrafficIncidents` — that\'s the GeoJSON service that returns full structured incidents with start/end times, road numbers, lanes, etc. Pick this ONLY for "what\'s on the tile overlay right now"; for richer per-incident data use `getTrafficIncidents`, and for "incidents along the route" use `analyseData` over routes (`route.properties.sections.traffic[]`).',inputSchema:xi,outputSchema:Ei,execute:async(e,t)=>{try{const e=(await t.trafficTiles.getTrafficIncidentsModule()).getShown();return 0===e.trafficIncidents.length?{error:"No traffic incidents currently rendered by the traffic-tile overlay."}:{count:e.trafficIncidents.length,trafficIncidents:e.trafficIncidents.map(e=>({id:e.properties.id,category:e.properties.category,magnitudeOfDelay:e.properties.magnitudeOfDelay,description:e.properties.description,delayInSeconds:e.properties.delayInSeconds,roadCategory:e.properties.roadCategory,averageSpeedKmph:e.properties.averageSpeedKmph}))}}catch(r){return{error:`Failed to read traffic-tile overlay incidents: ${r instanceof Error?r.message:String(r)}`}}},tags:["traffic","location","map style"],examples:["getShownTileIncidents()"],examplePrompts:["What incidents does the traffic tile overlay show right now?","What's rendered on the traffic incidents layer?"],relatedTools:["toggleTilesTrafficIncidents","getTrafficIncidents","flyTo","getViewport"],dependsOn:["toggleTilesTrafficIncidents"]},clearMap:{description:"Hide everything a chosen set of state slices is rendering on the map (places, routing, ranges, customGeometries, byod, trafficAreaAnalytics, trafficIncidents). Map-only — slice `entries` history is untouched. Use to clean up before showing new results.",classificationPrompt:"Hide what the chosen state slices are rendering on the map. `layers` are SLICE KEYS: `places`, `routing`, `ranges`, `customGeometries`, `byod`, `trafficAreaAnalytics`, `trafficIncidents`. Omit to clear every slice. Map-only — slice history is kept.",inputSchema:Si,outputSchema:Ii,execute:async(e,t)=>{const{layers:r}=e,s=r&&r.length>0?r:sr;try{for(const e of s)await t[e].clearShown();return{success:!0,cleared:[...s]}}catch(o){return{error:`Failed to clear map: ${o instanceof Error?o.message:String(o)}`}}},tags:["place","route","map style"],examples:["clearMap()",'clearMap({ layers: ["places"] })','clearMap({ layers: ["places", "routing"] })','clearMap({ layers: ["trafficAreaAnalytics", "trafficIncidents"] })','clearMap({ layers: ["byod"] })'],examplePrompts:["Clear the map","Remove all markers","Start fresh","Hide the analytics overlay"],relatedTools:["updatePlacesDisplay","updateRoutesDisplay","updateTrafficAreaAnalyticsDisplay"]},flyTo:{description:"Move the map camera to a bounding box or a specific position. Use with boundingBox to frame a region or set of results. Use with position to center on a point.",classificationPrompt:"Move the camera to a bounding box or specific position; use after locating places or computing bounds.",inputSchema:Br,outputSchema:jr,execute:async(e,t)=>{const{where:r}=e;try{return"boundingBox"in r?t.baseMap.mapLibreMap.fitBounds(r.boundingBox,{padding:r.padding??50}):t.baseMap.mapLibreMap.flyTo({center:r.position,zoom:r.zoom??14}),{success:!0}}catch(s){return{error:`flyTo failed: ${s instanceof Error?s.message:String(s)}`}}},tags:["location","map style"],examples:["flyTo({ where: { boundingBox: [4.8, 52.3, 5.0, 52.5] } })","flyTo({ where: { boundingBox: [4.8, 52.3, 5.0, 52.5], padding: 100 } })","flyTo({ where: { position: [4.9, 52.4], zoom: 14 } })"],examplePrompts:["Go to Berlin","Center the map on Rome","Fly to these coordinates"],relatedTools:["getViewport","locatePlace","updatePlacesDisplay","updateRoutesDisplay","calculateBBox"]},getViewport:{description:'Get the current map viewport: center coordinates [longitude, latitude], zoom level, and bounding box [minLng, minLat, maxLng, maxLat]. Use for queries referencing the map view: "in this area", "what I can see", "near the map center". Does NOT return the user\'s physical location — use getCurrentLocation for "near me" queries. Reads map state. Does not call any service.',classificationPrompt:'Return the map center, zoom, and bounding box; use for area-relative queries like "in this area" or "near the map center".',inputSchema:Ur,outputSchema:Gr,execute:async(e,t)=>{try{const e=t.baseMap.mapLibreMap;return{center:e.getCenter().toArray(),zoom:e.getZoom(),bbox:t.baseMap.ttMap.getBBox(),pitch:e.getPitch(),bearing:e.getBearing()}}catch(r){return{error:`Failed to get viewport: ${r instanceof Error?r.message:String(r)}`}}},tags:["location","map style"],examples:["getViewport()"],examplePrompts:["Where is the map centered?","What's the current map area?","What are the current map bounds?"],relatedTools:["locatePlace","flyTo","discoverPlaces","reverseGeocode","getTrafficIncidents"]},recallGeometries:{description:"List or inspect every polygon source in session state — place footprints (cached on places entries via `withGeometries: true`), reachable-area isochrones, and custom-geometries entries produced by `processData`. ALWAYS call this before passing ids to `processData` or `analyseData` (`geometriesEntryIDs`); never guess. No args = index across all three slices; pass `id` to retrieve features and provenance for one entry. No service call.",classificationPrompt:"List/inspect every polygon source in state across places footprints, ranges isochrones, and custom-geometries entries. Returns tagged ids (`{ kind, id }`) the geometry tools accept.",inputSchema:Pn,outputSchema:Cn,execute:async(e,t)=>{const{id:r}=e;return r?Rn(r,t):{entries:On(t)}},tags:["geometry","boundary"],examples:["recallGeometries()",'recallGeometries({ id: { kind: "places", id: "ev-stations-oost" } })','recallGeometries({ id: { kind: "customGeometries", id: "bakery-candidates-gap-oost" } })'],examplePrompts:["What polygons are in state right now?","Which IDs can I pass to processData (geometriesEntryIDs)?","Recall the custom zone we just produced","What does the bakery-candidates entry contain?"],relatedTools:["processData","analyseData","recallPlaces","recallRanges"]},recallPlaces:e=>((e,t)=>({...t,description:"List / inspect places entries already in session state (from discoverPlaces / locatePlace / processData) — the same entries every `placesEntryIDs` parameter accepts. ALWAYS call this before referencing past places; never guess IDs. No args = index (id, label, timestamp, featureCount, analyses) + `entryMode`; pass `id` for full Place coordinates. No service call.",inputSchema:Nn,outputSchema:Bn(e),execute:qn(e)}))(e.featureFlags??{},$n),recallByod:{description:"List or inspect BYOD (bring-your-own-data) GeoJSON layers in session state. These come from either programmatic seeding by the embedding app OR from `addByodSource` calls. Call this BEFORE passing ids to `byodEntryIDs` on `analyseData` / `processData` / `updateByodDisplay` — never guess. No args → index (id, label, featureCount, geometryTypes, propertyNames, source, shown); pass `id` → full data profile + FeatureCollection.",classificationPrompt:"List/inspect BYOD (bring-your-own-data) layers in session state. Returns ids the model can pass to `byodEntryIDs` (analyseData / processData) or to `updateByodDisplay` for visibility control.",inputSchema:Dn,outputSchema:Mn,execute:async(e,t)=>{if(!e.id)return{entries:t.byod.entries.map(e=>({id:e.id,label:e.label,timestamp:e.timestamp,featureCount:e.profile.featureCount,geometryTypes:e.profile.geometryTypes,propertyNames:e.profile.properties.map(e=>e.name),source:e.source,shown:!!e._shown})),entryMode:t.byod.entryMode};const r=t.byod.findById(e.id);return r?{id:r.id,label:r.label,timestamp:r.timestamp,featureCount:r.profile.featureCount,geometryTypes:r.profile.geometryTypes,propertyNames:r.profile.properties.map(e=>e.name),profile:r.profile,source:r.source,shown:!!r._shown,features:r.data}:{error:`No BYOD entry with id "${e.id}". Call recallByod with no args to list available IDs.`}},tags:["recall","byod","state"],examples:["recallByod()",'recallByod({ id: "byod-0" })'],examplePrompts:["What customer-uploaded layers do we have?","List BYOD entries","Show details of the sales-territories BYOD layer"],relatedTools:["addByodSource","updateByodDisplay","analyseData","processData"]},addByodSource:{description:"Register a customer-owned GeoJSON FeatureCollection as a new BYOD source. Accepts either a URL (fetched once) or inline GeoJSON. Returns the new entry id (usable with `byodEntryIDs` on `analyseData` / `processData` and with `updateByodDisplay`) plus a `profile` of the data shape (geometry types + per-property types/coverage/examples) so you can reason about it without re-fetching. A new entry has NO layers and renders nothing on its own — there are no automatic geometry defaults; choosing the layers is always your job. For very large datasets, prefer programmatic seeding via `state.byod.addEntry(...)`. This tool only ingests — it never renders. ALWAYS follow it with `setByodLayers`, which sets the explicit layers AND draws the entry: read the returned `profile` and supply layers + data-driven paint that fit THIS data — graduate `circle-radius`/colour by a numeric field, colour a `fill` or `line` by a categorical field, switch points to a `symbol`, and so on. Until `setByodLayers` runs, the entry is in state but nothing is drawn. (`setByodLayers.show` controls the camera and clearing earlier BYOD layers.)",classificationPrompt:"Add a customer-owned GeoJSON FeatureCollection (URL fetch or inline) as a new BYOD source. Use when the user wants to import / load / overlay a customer-supplied dataset. Returns the new entry id plus a profile of the data shape (use the id with `byodEntryIDs` on analyseData / processData). The entry starts with NO layers and renders nothing — deciding the layers from that profile is a required follow-up `setByodLayers` step, so co-pick it on every ingest turn.",inputSchema:jo,outputSchema:Fo,execute:async(e,t)=>{let r,s;if(e.url){const t=(e=>{let t;try{t=new URL(e)}catch{return{error:`Invalid URL: "${e}".`}}return qo.has(t.protocol)?{url:t}:{error:`URL protocol "${t.protocol}" is not allowed. Only http: and https: are supported.`}})(e.url);if("error"in t)return t;const o=await $o(t.url);if("error"in o)return o;r=o.value,s={kind:"url",url:e.url}}else r=e.data,s={kind:"inline"};if(!(e=>{if(!e||"object"!=typeof e)return!1;const t=e;return"FeatureCollection"===t.type&&Array.isArray(t.features)})(r))return{error:'Payload is not a GeoJSON FeatureCollection (`{ type: "FeatureCollection", features: [...] }`).'};const o=await t.byod.addEntry(r,e.label,{source:s,...e.entryId&&{explicitId:e.entryId}}),n=t.byod.findById(o);return n?{byodEntryId:o,label:e.label,source:s,profile:n.profile}:{error:`addByodSource stored entry "${o}" but could not read it back.`}},tags:["byod","state","import"],examples:['addByodSource({ label: "Sales territories", url: "https://example.com/territories.geojson" }) // then setByodLayers to render','addByodSource({ label: "Inline pins", data: { type: "FeatureCollection", features: [/* … */] } })','addByodSource({ label: "Q2 pins", data: { type: "FeatureCollection", features: [/* … */] } })'],examplePrompts:["Load my territories layer from https://...","Import this GeoJSON as a BYOD layer","Add a customer-pins layer from this URL"],relatedTools:["recallByod","setByodLayers","updateByodDisplay","analyseData","processData"]},setByodLayers:{description:"Replace the MapLibre layers a BYOD entry renders under, so the model can style customer data based on its `profile` (geometry types + per-property types/coverage/examples) — e.g. graduate `circle-radius` by a numeric field, colour a `fill` by category, or switch a Point layer to `symbol`. Operates on an entry already in state (from `addByodSource` / `recallByod`); the supplied `layers` replace the current set wholesale. If the entry is already on the map the new layers apply live in place; pass `show` to render a hidden entry or re-fit the camera. Invalid layer specs leave the entry unchanged and return an error to correct and retry — so the entry's previous styling is never lost. This is the SECOND STEP OF EVERY BYOD INGEST and the ONLY way a BYOD entry becomes visible: `addByodSource` creates the entry with NO layers (nothing is drawn), so you must call this to decide the layers that fit the detected schema, in all cases. Read the `profile` and encode whatever the data offers: graduate `circle-radius`/colour by a numeric field via an `interpolate` expression, colour a `fill`/`line` by a categorical field via a `match` expression, or switch dense points to a `symbol`. Even when no single property dominates, choose sensible, legible layers suited to the geometry. STYLE FOR LEGIBILITY ON THE TOMTOM BASEMAP (no need to inspect the style): keep `fill` translucent (`fill-opacity` ~0.3–0.5) with a contrasting `fill-outline-color` so the basemap's roads and labels show through; give any `symbol` text a light halo (`text-halo-color: '#ffffff'`, `text-halo-width` ~1.5) at a modest `text-size` (~12, matching basemap labels); give `circle` a thin white stroke (`circle-stroke-color`/`-width`); and pick a small, high-contrast palette — avoid fully opaque pure black or white fills that fight the map.",classificationPrompt:'Restyle a BYOD entry by replacing the MapLibre layers it renders under — pick layer types and data-driven paint informed by the data `profile` (graduate point size by a numeric field, colour a fill by category, switch points to symbols). Use when the user wants to change how a loaded BYOD layer LOOKS. ALWAYS co-pick this alongside addByodSource on EVERY BYOD ingest turn — deciding the layers from the detected schema is the agent\'s job in all cases, whether or not the user mentioned styling (e.g. "load X" or "import these" just as much as "load X and colour it by region"). addByodSource creates the entry with no layers, so this is the only way it becomes visible. NOT for visibility (show/hide/remove → updateByodDisplay) nor for importing new data (→ addByodSource).',inputSchema:ci,outputSchema:di,execute:async(e,t)=>{const r=t.byod.findById(e.byodEntryId);if(!r)return{error:`No BYOD entry with id "${e.byodEntryId}". Call recallByod to list available IDs.`};const s=r.layers,o=e.layers;try{t.byod.setEntryLayers(e.byodEntryId,o)}catch(i){try{t.byod.setEntryLayers(e.byodEntryId,s)}catch{r.layers=s}return{error:`Failed to apply layers to "${e.byodEntryId}": ${i instanceof Error?i.message:String(i)}. The entry keeps its previous layers — fix the layer specs and retry.`}}let n;if(e.show)try{n=await(async(e,t,r)=>{await ft(e.byod,[t],r.hidePreviousEntries),await e.byod.showEntry(t);let s=!1;if("auto"===r.zoomMode){const t=(e=>{const t=[];for(const r of e.byod.shownEntryIds){const s=e.byod.findById(r);s&&t.push(...s.data.features)}return t})(e);if(t.length>0){const r=L({type:"FeatureCollection",features:t});r&&(e.baseMap.mapLibreMap.fitBounds(r,{padding:50}),s=!0)}}return{zoomMode:s}})(t,e.byodEntryId,e.show)}catch(i){return{error:`Layers applied to "${e.byodEntryId}" but rendering failed: ${i instanceof Error?i.message:String(i)}`}}return{byodEntryId:e.byodEntryId,label:r.label,layerCount:o.length,layerTypes:o.map(e=>e.type),...n&&{shown:n}}},tags:["byod","map style","display"],examples:['setByodLayers({ byodEntryId: "byod-0", layers: [{ type: "fill", paint: { "fill-color": ["match", ["get", "region"], "north", "#1f77b4", "south", "#ff7f0e", "#cccccc"], "fill-opacity": 0.5 } }] }) // colour polygons by category','setByodLayers({ byodEntryId: "byod-1", layers: [{ type: "circle", paint: { "circle-radius": ["interpolate", ["linear"], ["get", "revenue"], 0, 3, 100000, 20], "circle-color": "#d62728" } }] }) // size points by a numeric field','setByodLayers({ byodEntryId: "byod-2", layers: [{ type: "line", paint: { "line-color": "#2ca02c", "line-width": 3 } }], show: { zoomMode: "none" } }) // restyle a hidden entry and render it'],examplePrompts:["Colour the territories by region","Make the point size reflect revenue","Style this layer with thicker green lines","Shade the polygons by their value"],relatedTools:["recallByod","addByodSource","updateByodDisplay"],dependsOn:["addByodSource"]},updateByodDisplay:{description:'Toggle visibility (or drop) for one or more BYOD entries. Use after `addByodSource` or after seeing entries via `recallByod`. Three actions: `show` renders listed entries, `hide` removes them from the map but keeps them in state, `remove` hides AND drops them from history. Pair `action: "hide"` with `clearAll: true` to hide every shown BYOD entry in one call. Pair `action: "show"` with `hideOthers: true` to swap to a single set of entries (replaces what\'s already on the map).',classificationPrompt:"Toggle visibility (or drop) of BYOD entries already in session state. Use after `addByodSource` or after `recallByod` exposes the ids. Three actions: show / hide / remove. Pair with `clearAll` or `hideOthers` for bulk swaps.",inputSchema:qi,outputSchema:$i,execute:async(e,t)=>{const r=t.byod,{action:s,clearAll:o,hideOthers:n}=e,i=e.entryIds??[],a=((e,t)=>{for(const r of t)if(!e.findById(r))return{error:`No BYOD entry with id "${r}". Call recallByod to list available IDs.`}})(r,i);if(a)return a;let l;return l="show"===s?await(async(e,t,r)=>{const s=[];if(r){const r=new Set(t);for(const t of e.shownEntryIds)r.has(t)||(await e.hideEntry(t),s.push(t))}for(const o of t)await e.showEntry(o),s.push(o);return s})(r,i,!!n):"hide"===s?await(async(e,t,r)=>{const s=[],o=r?e.shownEntryIds:t;for(const n of o)await e.hideEntry(n),s.push(n);return s})(r,i,!!o):await(async(e,t)=>{const r=[];for(const s of t)await e.removeEntry(s),r.push(s);return r})(r,i),{action:s,affectedIds:l,shown:[...r.shownEntryIds]}},tags:["byod","display","visibility"],examples:['updateByodDisplay({ entryIds: ["byod-0"], action: "show" })','updateByodDisplay({ entryIds: ["byod-0", "byod-1"], action: "show", hideOthers: true })','updateByodDisplay({ entryIds: ["byod-0"], action: "hide" })','updateByodDisplay({ action: "hide", clearAll: true })','updateByodDisplay({ entryIds: ["byod-0"], action: "remove" })'],examplePrompts:["Hide the customer pins layer","Show only the territories BYOD layer","Hide all BYOD layers","Drop the imported file from the session"],relatedTools:["recallByod","addByodSource"],dependsOn:["addByodSource"]},recallRoutes:{description:"List calculated routes from session history, or retrieve one by `id` (route details + params). IDs are stable and accepted by updateRoutesDisplay. Never guess IDs — list first. No service call.",classificationPrompt:"Return previously calculated routes from this session.",inputSchema:Hn,outputSchema:Qn,execute:async(e,t)=>{const{id:r}=e,s=t.routing.entryMode,o=t.routing.shownEntryIds;if(!r)return{entries:[...t.routing.entries].reverse().map(({id:e,label:t,timestamp:r})=>({id:e,label:t,timestamp:r,shown:o.has(e)})),entryMode:s};const n=t.routing.entries.find(e=>e.id===r);if(!n)return{error:`No entry found with id "${r}"`};const i=bs(n.data),a=n.waypoints.map(e=>vs(e)).filter(e=>null!==e).map(e=>({position:e.position??[0,0]}));return{id:n.id,label:n.label,timestamp:n.timestamp,routes:i,waypoints:a,params:n.params,entryMode:s}},tags:["route"],examples:["recallRoutes()",'recallRoutes({ id: "routes-0" })'],examplePrompts:["What routes have I calculated?","Show the first route we planned","What was the route to Schiphol?"],relatedTools:["setRoute","updateRoutesDisplay","getCurrentWaypoints"],dependsOn:["setRoute"]},getCurrentWaypoints:{description:"Read STAGED waypoint slots queued for the NEXT setRoute call (origin / stops / destination not yet committed to a route). Distinct from `recallRoutes({ id })`, which returns the waypoints of an existing calculated route entry. `slotIndex` for one slot; otherwise paged (default 5), empty slots omitted unless `includeEmptySlots: true`. No service call.",classificationPrompt:"STAGED waypoint slots only — the origin/stops/destination being built up for the NEXT `setRoute` call (read off `state.routing.planningSlots`, NOT off any calculated route entry). For waypoints of an EXISTING calculated route, use `recallRoutes({ id })` — its detail mode returns the entry's own waypoints.",inputSchema:tn,outputSchema:rn,execute:async(e,t)=>{const{slotIndex:r,includeEmptySlots:s=!1,offset:o=0,limit:n=5}=e,i=t.routing.planningSlots.map((e,t)=>{const r=vs(e);return r?{...r,slotIndex:t}:{slotIndex:t,isFilled:!1}}),a=s?i:i.filter(e=>e.isFilled),l=void 0!==r,c=l?a.filter(e=>e.slotIndex===r):a.slice(o,o+n),d=o+c.length;return{totalSlots:i.length,filledSlots:i.filter(e=>e.isFilled).length,matchedCount:l?c.length:a.length,returnedCount:c.length,offset:l?0:o,hasMore:!l&&d<a.length,...!l&&d<a.length&&{nextOffset:d},waypoints:c}},tags:["waypoint","route"],examples:["getCurrentWaypoints()","getCurrentWaypoints({ slotIndex: 1 })","getCurrentWaypoints({ includeEmptySlots: true })","getCurrentWaypoints({ offset: 2, limit: 4 })"],examplePrompts:["What's currently staged for the next route?","What's my pending destination before I hit go?","Show the stops I have queued up"],relatedTools:["locatePlace","setRoute","updateWaypointsDisplay","recallRoutes"]},getStandardMapStyles:{description:"Get the list of available standard map style IDs. If you need more fine styling control, refer to getMapStyleLayers.",classificationPrompt:"List available standard map style IDs for use with setMapStandardStyle.",inputSchema:Di,outputSchema:Mi,execute:async(e,t)=>{try{return{styles:g}}catch(r){return{error:`Failed to get standard map styles: ${r instanceof Error?r.message:String(r)}`}}},tags:["map style"],examples:["getStandardMapStyles()"],examplePrompts:["What map styles are available?","List the map themes"],relatedTools:["setMapStandardStyle"]},setMapStandardStyle:{description:"Set the map style to a standard preset (light, dark, mono, driving, satellite, etc). For detailed or incremental style tweaking use getMapStyleLayers, setLayoutProperties, and setPaintProperties.",classificationPrompt:"Switch the map to a standard style preset (light, dark, satellite, driving, etc.).",inputSchema:Ai,outputSchema:_i,execute:async(e,t)=>{const{style:r}=e;try{return t.baseMap.ttMap.setStyle(r),{success:!0,style:r}}catch(s){return{error:`Failed to set map style: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setMapStandardStyle("standardDark")','setMapStandardStyle("satellite")'],examplePrompts:["Switch to dark mode","Use the satellite view","Change to the light theme"],relatedTools:["getStandardMapStyles","setLanguage","toggleTilesBaseMapLayerGroups","getMapStyleLayers"]},setLanguage:{description:"Set the language for map labels and all subsequent service API responses (search, geocoding, routing instructions).",classificationPrompt:"Change the language for map labels and all subsequent service API responses.",inputSchema:Ti,outputSchema:Pi,execute:async(e,t)=>{const{language:r}=e;try{return R.instance.put({language:r}),t.baseMap.ttMap.setLanguage(r),{success:!0,language:r}}catch(s){return{error:`Failed to set language: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setLanguage("fr-FR")','setLanguage("de-DE")'],examplePrompts:["Show map labels in French","Switch the map to German","Use Spanish for place names"],relatedTools:["setMapStandardStyle"]},toggleTilesBaseMapLayerGroups:{description:"Show / hide groups of vector-tile base-map style layers (water, buildings3D, roadLines, placeLabels, capitalLabels, …). Operates on the underlying MapLibre style — orthogonal to anything rendered through PlacesModule / RoutingModule / CustomGeoJSONModule (those layers are unaffected).",classificationPrompt:"Show / hide vector-tile base-map layer groups (buildings3D, roadLabels, water, placeLabels, …). Style-layer only — does NOT touch places / routes / custom-geometries rendered by the agent.",inputSchema:ki,outputSchema:Ci,execute:async(e,t)=>{const{visible:r,layerGroups:s}=e;try{return(await t.baseMap.getBaseMapModule()).setVisible(r,{layerGroups:{mode:"include",names:s}}),{success:!0,visible:r,layerGroups:s}}catch(o){return{error:`Failed to toggle tile base-map layer groups: ${o instanceof Error?o.message:String(o)}`}}},tags:["map style"],examples:['toggleTilesBaseMapLayerGroups({ visible: false, layerGroups: ["buildings3D"] })','toggleTilesBaseMapLayerGroups({ visible: true, layerGroups: ["roadLabels"] })'],examplePrompts:["Hide 3D buildings","Show road labels","Toggle the building layer"],relatedTools:["toggleTilesPOIs","getMapStyleLayers","setMapStandardStyle"]},toggleTilesPOIs:{description:"Show/hide the built-in vector-tile POI icons baked into the base map style (restaurants, hotels, gas stations, …). NOT the same as places shown via `discoverPlaces` / `updatePlacesDisplay` — those are session-state pins on the PlacesModule, untouched by this tool. `allMapPOIsVisible` toggles overall visibility; `filterCategories` restricts which categories show; `reset: true` restores defaults.",classificationPrompt:"Show / hide vector-tile POI icons baked into the base-map style (restaurants/hotels/gas stations etc. shipped with the map). NOT places shown via `discoverPlaces` / `updatePlacesDisplay` (those are PlacesModule pins, untouched).",inputSchema:Ri,outputSchema:Oi,execute:async(e,t)=>{try{const r=await t.mapPOIs.getPOIsModule();if(e.reset)return r.resetConfig(),{success:!0,reset:!0};const{allMapPOIsVisible:s,filterCategories:o}=e;return void 0!==s&&r.setVisible(s),o&&r.filterCategories({show:o.show,values:o.values}),{success:!0,allMapPOIsVisible:s,filterCategories:o}}catch(r){return{error:`Failed to toggle tile POIs: ${r instanceof Error?r.message:String(r)}`}}},tags:["place","map style"],examples:["toggleTilesPOIs({ allMapPOIsVisible: false })",'toggleTilesPOIs({ allMapPOIsVisible: true, filterCategories: { show: "only", values: ["RESTAURANT", "CAFE"] } })','toggleTilesPOIs({ filterCategories: { show: "all_except", values: ["PARKING_GARAGE"] } })',"toggleTilesPOIs({ reset: true })"],examplePrompts:["Hide all map POI icons","Show only restaurant icons on the map style","Turn off parking icons in the base map"],relatedTools:["toggleTilesBaseMapLayerGroups","discoverPlaces","setMapStandardStyle"]},toggleTilesTrafficFlow:{description:"Toggle the vector-tile traffic-flow overlay (live coloured road segments). NOT the same as `updateTrafficAreaAnalyticsDisplay` (historical analytics) or `route.properties.sections.traffic[]` (per-route incidents). Just a base-map style layer on/off.",classificationPrompt:"Toggle the vector-tile traffic-flow overlay (live coloured road segments). NOT the same as `updateTrafficAreaAnalyticsDisplay` (historical) or route-section traffic (read via `analyseData`).",inputSchema:Ni,outputSchema:Li,execute:async(e,t)=>{const{visible:r}=e;try{return(await t.trafficTiles.getTrafficFlowModule()).setVisible(r),{success:!0,trafficFlowVisible:r}}catch(s){return{error:`Failed to toggle tile traffic flow: ${s instanceof Error?s.message:String(s)}`}}},tags:["traffic","map style"],examples:["toggleTilesTrafficFlow({ visible: true })","toggleTilesTrafficFlow({ visible: false })"],examplePrompts:["Show traffic flow","Turn off traffic colors","Enable the congestion overlay"],relatedTools:["toggleTilesTrafficIncidents","setRoute","getTrafficIncidents"]},toggleTilesTrafficIncidents:{description:"Toggle the vector-tile traffic-incidents overlay (live jam/accident/closure markers). NOT the same as `getTrafficIncidents` (GeoJSON service → structured entries) or `analyseData` (sandbox over fetched entries). Just a base-map style layer on/off. Use `getShownTileIncidents` to read what this overlay is currently rendering.",classificationPrompt:"Toggle the vector-tile traffic-incidents overlay (live jam/accident/closure markers). NOT the GeoJSON `getTrafficIncidents` service and NOT `analyseData` (which aggregates fetched entries). Use `getShownTileIncidents` to read what this overlay currently renders.",inputSchema:Fi,outputSchema:ji,execute:async(e,t)=>{const{visible:r}=e;try{return(await t.trafficTiles.getTrafficIncidentsModule()).setVisible(r),{success:!0,trafficIncidentsVisible:r}}catch(s){return{error:`Failed to toggle tile traffic incidents: ${s instanceof Error?s.message:String(s)}`}}},tags:["traffic","map style"],examples:["toggleTilesTrafficIncidents({ visible: true })","toggleTilesTrafficIncidents({ visible: false })"],examplePrompts:["Show traffic incidents overlay","Hide accident icons","Enable incident markers on the map"],relatedTools:["toggleTilesTrafficFlow","getTrafficIncidents","getShownTileIncidents","setRoute"]},getMapStyleLayers:{description:"Get map layer IDs with their paint/layout properties from MapLibre style for advanced runtime manipulation. Use before setLayoutProperty/setPaintProperty for custom styling.",classificationPrompt:"List MapLibre layer IDs with their paint/layout properties for custom styling.",inputSchema:$r,outputSchema:qr,execute:async(e,t)=>{const{layerIdQuery:r,include:s}=e;try{let e=t.baseMap.mapLibreMap.getStyle().layers??[];return r&&(e=e.filter(e=>e.id.toLowerCase().includes(r.toLowerCase())),0===e.length)?{error:`No layers found matching query: "${r}"`}:{layers:e.map(e=>({id:e.id,...s.includes("paint")&&{paint:e.paint},...s.includes("layout")&&{layout:e.layout}}))}}catch(o){return{error:`Failed to get style: ${o instanceof Error?o.message:String(o)}`}}},tags:["map style"],examples:["getMapStyleLayers()",'getMapStyleLayers("road")','getMapStyleLayers("water")'],examplePrompts:["What are the paint properties of the water layer?","Show me the road layer layout"],relatedTools:["setLayoutProperties","setPaintProperties"]},setLayoutProperties:{description:"Set MapLibre layout properties (visibility, text-field, icon-size) on named layers — use getMapStyleLayers for valid IDs. For colors/widths use setPaintProperties.",classificationPrompt:"Set MapLibre layout properties (visibility, text-field, icon-size) on named layers.",inputSchema:Vr,outputSchema:zr,execute:async(e,t)=>{const{changes:r}=e;try{return{results:r.map(({layerId:e,propertyName:r,value:s})=>{try{return t.baseMap.mapLibreMap.setLayoutProperty(e,r,s),{layerId:e,propertyName:r,value:s,success:!0}}catch(o){return{layerId:e,propertyName:r,error:`Failed to set layout property: ${o instanceof Error?o.message:String(o)}`}}})}}catch(s){return{error:`Failed to set layout properties: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setLayoutProperties([{ layerId: "road-label", propertyName: "visibility", value: "none" }])','setLayoutProperties([{ layerId: "poi-label", propertyName: "text-field", value: "{name}" }, { layerId: "poi-label", propertyName: "icon-size", value: 1.2 }])'],examplePrompts:["Hide the road-label layer","Change the text size on building labels","Make the city labels bigger"],relatedTools:["getMapStyleLayers","setPaintProperties"],dependsOn:["getMapStyleLayers"]},setPaintProperties:{description:"Set MapLibre paint properties (colors, widths, opacity) on named layers — use getMapStyleLayers for valid IDs. For visibility/structure use setLayoutProperties; for route styling prefer `updateRoutesDisplay({ mainColor })`.",classificationPrompt:"Set MapLibre paint properties (colors, widths, opacity) on named layers.",inputSchema:Jr,outputSchema:Hr,execute:async(e,t)=>{const{changes:r}=e;try{return{results:r.map(({layerId:e,propertyName:r,value:s})=>{try{return t.baseMap.mapLibreMap.setPaintProperty(e,r,s),{layerId:e,propertyName:r,value:s,success:!0}}catch(o){return{layerId:e,propertyName:r,error:`Failed to set paint property: ${o instanceof Error?o.message:String(o)}`}}})}}catch(s){return{error:`Failed to set paint properties: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:['setPaintProperties([{ layerId: "water", propertyName: "fill-color", value: "#0000ff" }])','setPaintProperties([{ layerId: "road", propertyName: "line-color", value: "#ff0000" }, { layerId: "road", propertyName: "line-width", value: 3 }])'],examplePrompts:["Make the water bright blue","Change the road color to red","Make buildings semi-transparent","Make the country borders pink"],relatedTools:["getMapStyleLayers","setLayoutProperties","updateRoutesDisplay"],dependsOn:["getMapStyleLayers"]},setPitchBearing:{description:"Tilt and/or rotate the map camera. Pitch 0 = top-down view, 85 = near-horizontal (3D effect). Bearing 0 = north up, 90 = east up. Provide at least one of pitch or bearing.",classificationPrompt:"Tilt (pitch) and/or rotate (bearing) the map camera for a 3D perspective.",inputSchema:Kr,outputSchema:Xr,execute:async(e,t)=>{const{pitch:r,bearing:s}=e;if(void 0===r&&void 0===s)return{error:"Provide at least one of pitch or bearing"};try{return t.baseMap.mapLibreMap.easeTo({...void 0!==r&&{pitch:r},...void 0!==s&&{bearing:s}}),{success:!0}}catch(o){return{error:`Failed to set pitch/bearing: ${o instanceof Error?o.message:String(o)}`}}},tags:["map style"],examples:["setPitchBearing({ pitch: 45 })","setPitchBearing({ bearing: 90 })","setPitchBearing({ pitch: 60, bearing: -45 })"],examplePrompts:["Tilt the map to 45 degrees","Rotate the map to face north","Set a 3D perspective view"],relatedTools:["getViewport","locatePlace","flyTo"]},zoomInOrOut:{description:"Zoom the map in or out by a given amount relative to the current zoom level. Positive delta zooms in (closer), negative delta zooms out (further away). Returns the resulting zoom level.",classificationPrompt:"Adjust the map zoom level by a delta; positive zooms in, negative zooms out.",inputSchema:Qr,outputSchema:Zr,execute:async(e,t)=>{const{delta:r}=e;try{const e=t.baseMap.mapLibreMap,s=e.getZoom()+r;return e.zoomTo(s),{zoom:s}}catch(s){return{error:`Zoom failed: ${s instanceof Error?s.message:String(s)}`}}},tags:["map style"],examples:["zoomInOrOut({ delta: 2 })","zoomInOrOut({ delta: -1 })"],examplePrompts:["Zoom in","Zoom out 2 levels","Get a wider view"],relatedTools:["getViewport","flyTo"]},executeMaplibreCode:{description:"Run arbitrary MapLibre JS against the live `map` for anything no other tool covers — custom sources/layers, camera, style mutations, animations, event sequences. Full JS (loops, helpers, top-level `await`). Returns added/removed/updated sources & layers.",classificationPrompt:"Execute any MapLibre JS code against the live map for maximum flexibility — use when no other tool covers the required operation.",inputSchema:Or,outputSchema:Rr,execute:async(e,t)=>{const{code:r}=e;try{const e=t.baseMap.mapLibreMap,s=Nr(e),o=Lr(e),n=Object.getPrototypeOf(async()=>{}).constructor,i=await new n("map",r)(e),a=Fr(s,Nr(e));return{success:!0,result:i??void 0,sources:a,layers:Fr(o,Lr(e))}}catch(s){return{error:Bt("Code",s)}}},tags:["map style","maplibre","layers","sources","animation"],examples:["executeMaplibreCode({ code: \"map.addSource('pts', { type: 'geojson', data: fc }); map.addLayer({ id: 'pts', type: 'circle', source: 'pts', paint: { 'circle-color': '#f00' } })\" })",'executeMaplibreCode({ code: "map.easeTo({ center: [4.9, 52.37], zoom: 14, duration: 2000 })" })',"executeMaplibreCode({ code: \"for (let i = 0; i <= 20; i++) { map.setPaintProperty('fill', 'fill-opacity', i / 20); await new Promise(r => setTimeout(r, 50)); }\" })","executeMaplibreCode({ code: \"map.setFog({ color: 'white', 'horizon-blend': 0.1 })\" })"],examplePrompts:["Add a GeoJSON layer to the map","Animate the map camera","Fade in a layer gradually","Set map fog or atmosphere","Add a raster tile overlay"],relatedTools:["getMapStyleLayers","setLayoutProperties","setPaintProperties","flyTo"],dependsOn:["getMapStyleLayers"]},calculateBBox:{description:"Compute a `[minLng, minLat, maxLng, maxLat]` bbox from GeoJSON (longitude before latitude).",classificationPrompt:"Compute a [minLng, minLat, maxLng, maxLat] bounding box from GeoJSON features or tool results.",inputSchema:ir,execute:async(e,t)=>{const{geoJson:r}=e;try{const e=L(r);return e?{bbox:e,minLng:e[0],minLat:e[1],maxLng:e[2],maxLat:e[3]}:{error:"Could not calculate bounding box - input may be empty or invalid GeoJSON"}}catch(s){return{error:`Failed to calculate bbox: ${s instanceof Error?s.message:String(s)}`}}},tags:["utilities","location"],examples:["calculateBBox(place)","calculateBBox(routes)","calculateBBox([place1, place2])"],examplePrompts:["What's the bounding box of these places?","Calculate the extent of this route"],relatedTools:["flyTo","getViewport","updatePlacesDisplay","updateRoutesDisplay"]},formatDistance:{description:'Format a distance in meters into a human-readable string (e.g. "2.5 km", "1.5 mi"). Use after getSectionProgress, recallRoutes, or any tool that returns distances in meters.',classificationPrompt:'Format meters into a human-readable string (e.g. "2.5 km"); use after tools that return distances.',inputSchema:ar,execute:async(e,t)=>{const{meters:r,unitType:s}=e;try{return{formatted:j(r,s?{type:s}:void 0),input:{meters:r,unitType:s??"metric"}}}catch(o){return{error:`Failed to format distance: ${o instanceof Error?o.message:String(o)}`}}},tags:["utilities","route"],examples:["formatDistance(2500)",'formatDistance(5000, "imperial_us")'],examplePrompts:["Format 5000 meters as a distance","Convert meters to a readable string"],relatedTools:["formatDuration","setRoute"]},formatDuration:{description:'Format a duration in seconds into a human-readable time string (e.g. "1 h 30 min"). Use after getSectionProgress, recallRoutes, or any tool that returns durations in seconds.',classificationPrompt:'Format seconds into a human-readable string (e.g. "1 h 30 min"); use after tools that return durations.',inputSchema:lr,execute:async(e,t)=>{const{seconds:r}=e;try{const e=B(r);return void 0===e?{formatted:void 0,message:"< 1 min",input:{seconds:r}}:{formatted:e,input:{seconds:r}}}catch(s){return{error:`Failed to format duration: ${s instanceof Error?s.message:String(s)}`}}},tags:["utilities","route"],examples:["formatDuration(3600)","formatDuration(9000)"],examplePrompts:["Format 3600 seconds as a duration","Convert seconds to hours and minutes"],relatedTools:["formatDistance","setRoute"]},getCurrentLocation:{description:'Browser geolocation — the user\'s physical position [lng, lat] for "near me" / "where I am" queries (may prompt for permission). For map-based reference use getViewport.',classificationPrompt:'Get the user\'s physical GPS location from the browser; use for "near me" queries, not map center.',inputSchema:wr,outputSchema:vr,execute:async(e,t)=>{if(void 0===globalThis.window||void 0===globalThis.navigator)return{error:"Current location is only available in a browser environment"};if(!("geolocation"in globalThis.navigator))return{error:"Geolocation is not supported by this browser"};try{const e=await new Promise((e,t)=>{globalThis.navigator.geolocation.getCurrentPosition(e,t,{enableHighAccuracy:!0,timeout:15e3,maximumAge:0})});return{position:[e.coords.longitude,e.coords.latitude],accuracy:e.coords.accuracy,timestamp:e.timestamp}}catch(r){const e=(e=>{if("object"!=typeof e||null===e||!("code"in e))return;const t=e.code;return 1===t?"Location permission denied by the user":2===t?"Current location is unavailable":3===t?"Timed out while trying to get current location":void 0})(r);return e?{error:e}:{error:`Failed to get current location: ${r instanceof Error?r.message:String(r)}`}}},tags:["utilities","location"],examples:["getCurrentLocation()"],examplePrompts:["Where am I?","Get my GPS location","Use my current position"],relatedTools:["discoverPlaces","reverseGeocode","locatePlace"]},help:{description:Dr,classificationPrompt:"Return available capabilities in summary or searchable detail mode; call when the user asks what you can do.",inputSchema:Mr,outputSchema:xr,execute:async()=>{throw new Error("help tool requires setupTools() — it needs resolved metadata from all tools.")},tags:["utilities"],examples:['help({ mode: "summary" })','help({ mode: "detail", query: "traffic" })','help({ mode: "detail", tag: "route" })','help({ mode: "detail", query: "route", tag: "utilities" })'],examplePrompts:["What can you do?","Show me available tools","How do I search for places?"]},findReachableAreas:{description:"Compute reachable-area polygons (isochrone/isodistance) from one or more origins under shared time/distance budgets. Multiple origins → compared in one call (sequential to respect QPS); multiple budgets → nested ranges. Stored in state (recallRanges); `showOnMap: true` renders. Per-origin failures land under `skipped` instead of aborting the whole call.",classificationPrompt:"Calculate isochrone/isodistance polygons showing how far you can travel from one or more origins.",inputSchema:so,outputSchema:no,execute:async(e,t)=>{const{origins:r,budgets:s,theme:o,showOnMap:n,showOriginPin:i=!0,hidePreviousEntries:a}=e;try{const e=[...s].sort((e,t)=>t.value-e.value),l=[],c=[];for(const s of r){const r="query"in s?s.query:"position"in s?`${s.position.lat}, ${s.position.lng}`:"placeId"in s?s.placeId:"unnamed origin",o=await Ds(s,t);if(!o){c.push({origin:r,reason:"origin not found"});continue}const n=await io(e,o.position);n?l.push({origin:o.query?{query:o.query,position:o.position}:{position:o.position},budgets:e,polygon:n}):c.push({origin:o.name??r,reason:"no reachable area returned"})}if(0===l.length)return{status:"no_results"};const d=o??"outline",u=l.map(ao),p=await t.ranges.addEntry({label:ys(e,u),ranges:l});return(n||i)&&await ft(t.ranges,[p],a),await(async(e,t,r,s,o,n)=>{if(o){const o=(e=>{const t=e.flatMap(e=>e.polygon?.features??[]);return 0===t.length?null:{type:"FeatureCollection",features:t}})(r);o&&(await(await e.ranges.getEntryGeometriesModule(t,s)).show(o),o.bbox&&e.baseMap.mapLibreMap.fitBounds(o.bbox,{padding:50}))}if(n){const s=await e.ranges.getEntryPlacesModule(t),o=r.map((e,r)=>({type:"Feature",id:`range-origin-${t}-${r}`,geometry:{type:"Point",coordinates:e.origin.position},properties:{type:"Point Address",address:{freeformAddress:ao(e)}}}));await s.show(o)}})(t,p,l,d,n,i),(n||i)&&await t.ranges.markEntryShown(p),{rangesId:p,ranges:l.map(t=>({originName:ao(t),budgets:e})),...c.length&&{skipped:c}}}catch(l){return{error:`Reachable area calculation failed: ${l instanceof Error?l.message:String(l)}`}}},tags:["reachable-range","isochrone","range","EV","coverage","multi-origin"],examples:['findReachableAreas({ origins: [{ query: "Amsterdam", queryAs: "place" }], budgets: [{ type: "timeMinutes", value: 30 }], showOnMap: true })','findReachableAreas({ origins: [{ query: "Amsterdam", queryAs: "place" }, { query: "Rotterdam", queryAs: "place" }], budgets: [{ type: "timeMinutes", value: 30 }], showOnMap: true })','findReachableAreas({ origins: [{ position: { lng: 4.9, lat: 52.3 } }], budgets: [{ type: "timeMinutes", value: 10 }, { type: "timeMinutes", value: 30 }], showOnMap: true, theme: "inverted" })','findReachableAreas({ origins: [{ query: "my location", queryAs: "place" }], budgets: [{ type: "distanceKM", value: 50 }], showOnMap: false })'],examplePrompts:["Show me where I can reach in 30 minutes from Amsterdam","Compare 30-minute reach from Amsterdam, Rotterdam and Utrecht","What area is within 50 km of Berlin?","Show 10, 20, 30 minute drive zones from here"],relatedTools:["recallRanges","locatePlace"]},recallRanges:{description:"List stored reachable-range results, or retrieve one by `id` (returns each origin and its budgets). Multi-origin entries surface every origin under `ranges`. Never guess IDs — list first. No service call.",classificationPrompt:"Retrieve stored reachable range results from session history.",inputSchema:Gn,outputSchema:Yn,execute:async(e,t)=>{const{id:r}=e,s=t.ranges.entryMode;if(!r)return{entries:[...t.ranges.entries].reverse().map(({id:e,label:t,timestamp:r,ranges:s})=>({id:e,label:t,timestamp:r,originCount:s.length})),entryMode:s};const o=t.ranges.entries.find(e=>e.id===r);return o?{id:o.id,label:o.label,timestamp:o.timestamp,ranges:o.ranges.map(e=>({origin:{...e.origin,position:e.origin.position},budgets:e.budgets})),entryMode:s}:{error:`No entry found with id "${r}"`}},tags:["recall","reachable-range","state"],examples:["recallRanges()",'recallRanges({ id: "ranges-0" })'],examplePrompts:["What range did I calculate earlier?","Recall the isochrone from Amsterdam"],relatedTools:["findReachableAreas"]},recallState:{description:"Single-shot session snapshot: places / routes / ranges entries (id+label) + per-slice `entryMode` + active map style + POIs / traffic visualization configs. Read-only. Use as orientation before making decisions that depend on what is already loaded.",classificationPrompt:"Inventory the agent's session: list every places / routes / ranges entry by id+label, plus current map style and POIs/traffic visualization configs.",inputSchema:si,outputSchema:ri,execute:async(e,t)=>{const r=e=>[...e].sort((e,t)=>t.timestamp-e.timestamp),s=r(t.places.entries).map(({id:e,label:t})=>({id:e,label:t})),o=r(t.routing.entries).map(({id:e,label:t})=>({id:e,label:t})),n=r(t.ranges.entries).map(({id:e,label:t})=>({id:e,label:t})),i="string"==typeof(a=t.baseMap.ttMap.getStyle())?a:a&&"object"==typeof a&&"id"in a?a.id:void 0;var a;const l=void 0===i?null:{id:i},c=t.mapPOIs.poisModule?.getConfig(),d=c?{..."boolean"==typeof c.visible&&{visible:c.visible},...c.filters?.categories?.show&&{filterMode:c.filters.categories.show},...c.filters?.categories?.values&&{categories:[...c.filters.categories.values]}}:null,u=t.trafficTiles.trafficFlowModule?.getConfig(),p=t.trafficTiles.trafficIncidentsModule?.getConfig(),h={flow:u?{..."boolean"==typeof u.visible&&{visible:u.visible}}:null,incidents:p?{..."boolean"==typeof p.visible&&{visible:p.visible}}:null};return{places:s,routes:o,ranges:n,entryModes:{places:t.places.entryMode,routes:t.routing.entryMode,ranges:t.ranges.entryMode},mapStyle:l,mapPOIs:d,traffic:h}},tags:["recall","state","overview"],examples:["recallState()"],examplePrompts:["What do you have so far?","List everything you remember from this session","Show me the current map / data overview"],relatedTools:["recallPlaces","recallRoutes","recallRanges","resetState"]},resetState:{description:'Wipe the session: drops every places/routes/ranges entry + their map renders, resets POIs + traffic configs to defaults, reverts the map style to "standardLight". Destructive — use only when the user explicitly asks for a clean slate ("start over", "reset", "clear everything").',classificationPrompt:"Wipe the session: clear every places/routes/ranges entry, reset POIs/traffic configs, revert to standard style. Use only when the user asks for a clean slate.",inputSchema:ii,outputSchema:ni,execute:async(e,t)=>{try{await t.places.clearShownEntries();for(const e of t.places.entries)await(e._modules?.geometries?.clear());for(const e of t.routing.entries)e._module&&(await e._module.clearRoutes(),await e._module.clearWaypoints());for(const e of t.ranges.entries)await(e._modules?.geometries?.clear()),await(e._modules?.places?.clear());for(const e of t.trafficAreaAnalytics.entries)await(e._module?.clear());return t.mapPOIs.poisModule?.resetConfig(),t.trafficTiles.trafficFlowModule?.resetConfig(),t.trafficTiles.trafficIncidentsModule?.resetConfig(),t.places.reset(),t.routing.reset(),t.ranges.reset(),t.customGeometries.reset(),t.trafficAreaAnalytics.reset(),t.trafficIncidents.reset(),t.byod.reset(),t.baseMap.ttMap.setStyle("standardLight",{keepState:!1}),{success:!0,cleared:[...oi]}}catch(r){return{error:`Failed to reset state: ${r instanceof Error?r.message:String(r)}`}}},tags:["state","reset","destructive"],examples:["resetState()"],examplePrompts:["Start over","Reset everything","Clear all my data and the map"],relatedTools:["recallState","clearMap"]},setEntryMode:{description:mi,classificationPrompt:"Switch any entry-owning slice (places, routing, ranges, customGeometries, byod, trafficAreaAnalytics, trafficIncidents) between `multiple` (overlay several entries) and `single` (latest only). `single` drops older entries.",inputSchema:yi,outputSchema:hi,execute:async(e,t)=>{const{slice:r,mode:s}=e;try{const e=t[r],o=e.entries.map(e=>e.id);await e.setEntryMode(s);const n=new Set(e.entries.map(e=>e.id));return{success:!0,slice:r,mode:s,droppedEntryIds:o.filter(e=>!n.has(e))}}catch(o){return{error:`Failed to set entry mode: ${o instanceof Error?o.message:String(o)}`}}},tags:["state","mode","display"],examples:['setEntryMode({ slice: "places", mode: "single" })','setEntryMode({ slice: "routing", mode: "multiple" })','setEntryMode({ slice: "trafficIncidents", mode: "single" })'],examplePrompts:["Only show one route at a time","Let me overlay multiple places searches","Switch ranges back to single mode","Only keep the latest traffic incidents snapshot"],relatedTools:["recallState","recallPlaces","recallRoutes","recallRanges"]}},aa=ia,la=Object.keys(aa),ca={places:["recallPlaces","updatePlacesDisplay","locatePlace","discoverPlaces"],routes:["recallRoutes","updateRoutesDisplay","setRoute","addWaypointsToRoute","removeWaypointsFromRoute","replaceWaypointInRoute","getCurrentWaypoints","updateWaypointsDisplay"],incidents:["getTrafficIncidents","focusIncidents","startTrafficIncidentsMonitor","stopTrafficIncidentsMonitor","getShownTileIncidents","clusterIncidents"],customGeometries:["recallGeometries"],trafficAreaAnalytics:["getTrafficAreaAnalytics","queryTrafficAnalytics","updateTrafficAreaAnalyticsDisplay"],byod:["recallByod","addByodSource","setByodLayers","updateByodDisplay"],ranges:["findReachableAreas","recallRanges"]},da=()=>{const e={};for(const t of la){const r=aa[t];e[t]=("function"==typeof r?r({}):r).examplePrompts??[]}return e},ua=async(e,t)=>{((e,t)=>{for(const[r,s]of Object.entries(t)){const t=e[r];t&&(t.description=s.defaultDescription,t.inputSchema=s.defaultInputSchema)}})(e.tools,e.scopableTools);let r=null;if(e.classifier)try{r=await e.classifier({messages:t,toolsMetadata:e.toolsMetadata})}catch{r=null}return e.onClassify?.(r),((e,t,r)=>{if(r)for(const[s,o]of Object.entries(r)){const r=t[s];if(!r||null==o)continue;const n=e[s];if(!n)continue;const i=r.applyScope(o);i&&(n.description=i.description,n.inputSchema=i.inputSchema)}})(e.tools,e.scopableTools,r?.toolScopes),r},pa=(e,r)=>{if(!r.model)throw new Error("MapAgent requires a model option. Please provide an AI SDK LanguageModel instance.");const o=((e,t)=>{const r={places:new be(e),mapPOIs:new fe(e),routing:new ve(e),baseMap:new se(e),trafficTiles:new Qt(e),trafficAreaAnalytics:new Pe(e),trafficIncidents:new Zt(e),ranges:new we(e),customGeometries:new ce(e),byod:new De(e)};return t&&Object.assign(r,t),r})(e,r.state);if(r.dataEntries)for(const[t,s]of Object.entries(r.dataEntries)){const e=t;s?.entryMode&&o[tr[e]].setEntryMode(s.entryMode)}const n=e=>!1!==r.dataEntries?.[e]?.enabled,i=Object.keys(tr).filter(e=>!n(e)),a=rt.filter(e=>n(e)),l=new Set(i.flatMap(e=>ca[e])),c=!1===r.includeDefaultTools?{}:aa,{tools:d,toolsMetadata:u,scopableTools:p}=Ar(re(Object.fromEntries(Object.entries(c).filter(([e])=>!l.has(e))),r.tools),o,{outputSchemas:r.outputSchemas,featureFlags:r.featureFlags,enabledDataKinds:a}),h=!1===r.classifier?null:r.classifier??ds({model:r.model}),y=nr(r.systemPrompt,r.systemPromptSuffix);let m=null;const f=(()=>{let e=Promise.resolve();return async t=>{const r=e;let s=()=>{};e=new Promise(e=>{s=e});try{return await r,await t()}finally{s()}}})(),g={tools:d,scopableTools:p,toolsMetadata:u,classifier:h,onClassify:r.onClassify},b=new t({model:r.model,tools:d,instructions:y,stopWhen:s(r.maxSteps??10),prepareStep:e=>f(async()=>{0===e.stepNumber&&(m=await ua(g,e.messages));const t=m?.activeToolNames,s=await(r.prepareStep?.(e)),o=r.stepProviderOptions?.({activeTools:t,stepNumber:e.stepNumber});return((e,t,r)=>r?{...r,activeTools:Q(e,r.activeTools),providerOptions:te(t,r.providerOptions)}:{...e?{activeTools:e}:{},...t?{providerOptions:t}:{}})(t,o,s)}),providerOptions:r.providerOptions});return Object.assign(b,{state:o,destroy:()=>(e=>{for(const t of Object.values(e))ee(t)&&t.reset()})(o)})};export{or as BASE_SYSTEM_PROMPT,Xo as CLUSTER_ANALYSIS_NAME,tr as DATA_ENTRY_KIND_TO_SLICE,aa as DEFAULT_TOOLS,rr as ENTRY_MODE_SLICE_NAMES,ca as TOOLS_BY_DATA_ENTRY_KIND,la as TOOL_NAMES,nr as buildSystemPrompt,is as classifyUserIntent,ds as createDefaultClassifier,pa as createMapAgent,cs as extractLastUserText,me as formatStateDigestDiff,Te as geometriesIdSchema,da as getDefaultToolPrompts,pe as getStateDigest,re as resolveTools};
2
2
  //# sourceMappingURL=index.es.js.map