@tomtom-org/maps-sdk-plugin-agent-toolkit 0.2.4 → 0.2.5

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.d.ts CHANGED
@@ -1,34 +1,52 @@
1
- import { BaseMapModule } from '../../map/dist';
2
- import { GeometriesModule } from '../../map/dist';
3
- import { GeometryTheme } from '../../map/dist';
4
- import { HillshadeModule } from '../../map/dist';
1
+ import { BaseMapModule } from '../../../../map/dist';
2
+ import { BBox } from '../../../../core/dist';
3
+ import { BBox as BBox_2 } from '../../../../../core/dist';
4
+ import { CommonPlaceProps } from '../../../core/dist';
5
+ import { CommonPlaceProps as CommonPlaceProps_2 } from '../../../../core/dist';
6
+ import { GeometriesModule } from '../../../map/dist';
7
+ import { GeometriesModule as GeometriesModule_2 } from '../../../../map/dist';
8
+ import { GeometryTheme } from '../../../../map/dist';
9
+ import { HillshadeModule } from '../../../../map/dist';
5
10
  import { LanguageModel } from 'ai';
6
11
  import { Map as Map_2 } from 'maplibre-gl';
7
12
  import { ModelMessage } from 'ai';
8
13
  import { Place } from '../../../core/dist';
9
- import { Place as Place_2 } from '../../core/dist';
10
- import { Places } from '../../core/dist';
11
- import { PlacesModule } from '../../map/dist';
12
- import { PlanningWaypoint } from '../../map/dist';
13
- import { POIsModule } from '../../map/dist';
14
+ import { Place as Place_2 } from '../../../../core/dist';
15
+ import { PlaceConnectionDisplay } from '../../../map/dist';
16
+ import { PlaceConnectionDisplay as PlaceConnectionDisplay_2 } from '../../../../map/dist';
17
+ import { Places } from '../../../../core/dist';
18
+ import { PlacesModule } from '../../../map/dist';
19
+ import { PlacesModule as PlacesModule_2 } from '../../../../map/dist';
20
+ import { PlanningWaypoint } from '../../../../map/dist';
21
+ import { POIsModule } from '../../../../map/dist';
22
+ import { PolygonFeature } from '../../../core/dist';
23
+ import { PolygonFeature as PolygonFeature_2 } from '../../../../core/dist';
14
24
  import { PolygonFeatures } from '../../../core/dist';
15
25
  import { Position } from 'geojson';
16
26
  import { PrepareStepFunction } from 'ai';
17
27
  import { ReachableRangeBudget } from '../../../services/dist';
18
28
  import { Routes } from '../../../core/dist';
19
- import { Routes as Routes_2 } from '../../core/dist';
20
- import { RoutingModule } from '../../map/dist';
21
- import { TomTomMap } from '../../map/dist';
29
+ import { Routes as Routes_2 } from '../../../../core/dist';
30
+ import { RoutingModule } from '../../../map/dist';
31
+ import { RoutingModule as RoutingModule_2 } from '../../../../map/dist';
32
+ import { TomTomMap } from '../../../../map/dist';
33
+ import { TomTomMap as TomTomMap_2 } from '../../map/dist';
22
34
  import { ToolLoopAgent } from 'ai';
23
- import { TrafficAreaAnalytics } from '../../core/dist';
24
- import { TrafficAreaAnalyticsConfig } from '../../map/dist';
25
- import { TrafficAreaAnalyticsModule } from '../../map/dist';
26
- import { TrafficFlowModule } from '../../map/dist';
27
- import { TrafficIncidentsModule } from '../../map/dist';
35
+ import { TrafficAreaAnalytics } from '../../../../core/dist';
36
+ import { TrafficAreaAnalyticsConfig } from '../../../../map/dist';
37
+ import { TrafficAreaAnalyticsModule } from '../../../../map/dist';
38
+ import { TrafficFlowModule } from '../../../../map/dist';
39
+ import { TrafficIncident } from '../../../../core/dist';
40
+ import { TrafficIncident as TrafficIncident_2 } from '../../../../../core/dist';
41
+ import { TrafficIncidentDetailsByBBoxParams } from '../../../../services/dist';
42
+ import { TrafficIncidentOverlayModule } from '../../../../map/dist';
43
+ import { TrafficIncidentsModule } from '../../../../map/dist';
28
44
  import { WaypointLike } from '../../../core/dist';
29
- import { WaypointLike as WaypointLike_2 } from '../../core/dist';
45
+ import { WaypointLike as WaypointLike_2 } from '../../../../core/dist';
30
46
  import { z } from 'zod';
31
47
 
48
+ declare type AnalysisOutputFormat = 'json' | 'chart';
49
+
32
50
  /**
33
51
  * The base system prompt that teaches the LLM how to use the agent toolkit tools.
34
52
  *
@@ -48,7 +66,7 @@ import { z } from 'zod';
48
66
  *
49
67
  * @group Agent Toolkit
50
68
  */
51
- export declare const BASE_SYSTEM_PROMPT = "You are a helpful map assistant with access to a TomTom interactive map and location services.\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 \u2014 use it to display results on the map in one step instead of calling a separate show tool afterward.\n- Use standalone showPlaces / showRoute / showWaypoints only when displaying results already in context from a previous step.\n\nLOCATION REFERENCE:\n- \"near me\" / \"my location\" / \"where I am\" \u2192 getCurrentLocation (user's physical GPS position, may prompt permission)\n- \"in this area\" / \"here on the map\" / \"near the map center\" \u2192 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 \u2014 lead with the most relevant information\n\n";
69
+ export declare const BASE_SYSTEM_PROMPT = "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 \u2014 use it to display results on the map in one step instead of calling a separate show tool afterward.\n- Use standalone updatePlacesDisplay / updateRoutesDisplay / showWaypoints only when displaying results already in context from a previous step.\n\nSESSION STATE & ENTRIES:\n- Three append-only histories live in the session: `places`, `routes`, `ranges`. Each tool call that produces results stores a new ENTRY (id like `places-3`, `routes-1`, `ranges-0`) \u2014 these ids stay stable and are accepted by every show / recall / processX tool.\n- Each slice has an `entryMode`: `multiple` (default \u2014 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`. 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 \u2014 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\" \u2192 getCurrentLocation (user's physical GPS position, may prompt permission)\n- \"in this area\" / \"here on the map\" / \"near the map center\" \u2192 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 \u2014 lead with the most relevant information\n\n";
52
70
 
53
71
  /**
54
72
  * State for base map display: style, language, viewport, layers, and hillshade.
@@ -206,83 +224,218 @@ export declare const createDefaultClassifier: (options: {
206
224
  * ```
207
225
  * @group Agent Toolkit
208
226
  */
209
- export declare const createMapAgent: <CS extends ToolState = ToolState>(map: TomTomMap, options: MapAgentOptions<CS>) => MapAgentInstance<CS>;
227
+ export declare const createMapAgent: <CS extends ToolState = ToolState>(map: TomTomMap_2, options: MapAgentOptions<CS>) => MapAgentInstance<CS>;
210
228
 
211
229
  /**
212
- * Factory that creates a fully initialized ToolState from a TomTomMap instance.
213
- * Optionally merges custom state slices alongside built-in state.
230
+ * A single aggregation/analysis result attached to a custom-geometries entry.
231
+ * Produced by the `analyseGeometries` tool; `data` is whatever the dynamic code returned.
214
232
  *
215
- * @param map - TomTomMap instance for module initialization
216
- * @param customSlices - Additional state slices accessible by custom tools
233
+ * @group Agent Toolkit
234
+ */
235
+ export declare type CustomGeometriesAnalysis = {
236
+ /** Unique name within the parent entry (used as a key for future UI). */
237
+ name: string;
238
+ timestamp: number;
239
+ /** Optional human-readable description of what the analysis computed. */
240
+ description?: string;
241
+ /** How `data` should be interpreted — plain JSON or a Chart.js configuration. */
242
+ outputFormat: AnalysisOutputFormat;
243
+ /** Arbitrary aggregation result returned by the dynamic code. */
244
+ data: unknown;
245
+ };
246
+
247
+ /**
248
+ * A single entry in the custom-geometries history — output of
249
+ * processGeometries (and any future polygon-producing tool that doesn't
250
+ * naturally belong to places or ranges).
217
251
  *
218
252
  * @group Agent Toolkit
219
253
  */
220
- export declare const createToolState: <T extends Record<string, unknown> = Record<string, never>>(map: TomTomMap, customSlices?: T) => ToolState & T;
254
+ export declare type CustomGeometriesEntry = {
255
+ id: string;
256
+ timestamp: number;
257
+ label: string;
258
+ /** The polygons stored on this entry (Polygon / MultiPolygon GeoJSON Features). */
259
+ features: PolygonFeature[];
260
+ /** Inputs and operation that produced this entry. */
261
+ provenance: GeometryProvenance;
262
+ /** Accumulated analysis results linked to this entry. */
263
+ _analysis?: CustomGeometriesAnalysis[];
264
+ /**
265
+ * Per-entry GeometriesModule. Each entry owns its own module so display state
266
+ * (theme, current features) lives on the entry instead of a shared slice-level
267
+ * module. Lazy-initialised via CustomGeometriesState helpers.
268
+ */
269
+ _module?: GeometriesModule;
270
+ /** Theme that the geometries module is currently configured with. */
271
+ _shownTheme?: 'filled' | 'outline' | 'inverted';
272
+ /** True while this entry's `_module` is rendering on the map. */
273
+ _shown?: boolean;
274
+ };
221
275
 
222
276
  /**
223
- * Default tool set for the agent toolkit.
277
+ * State for derived / custom geometries — output of `processGeometries` and any future
278
+ * polygon-producing tool whose result doesn't belong to a place or a reachable-range entry.
279
+ *
280
+ * Each entry owns a dedicated GeometriesModule, lazy-initialised on first show, mirroring
281
+ * the per-entry display model used by {@link RangeState}. Provenance metadata captures the
282
+ * input sources (tagged ids) and the operation that produced the polygons.
283
+ *
284
+ * @group Agent Toolkit
285
+ */
286
+ declare class CustomGeometriesState implements ShownEntriesSlice, StateSlice {
287
+ private readonly _ttMap;
288
+ private _entries;
289
+ private _entryMode;
290
+ /** Subscribe to state changes — see {@link CustomGeometriesStateEvents}. */
291
+ readonly events: StateEvents<CustomGeometriesStateEvents>;
292
+ constructor(_ttMap: TomTomMap);
293
+ get entries(): readonly CustomGeometriesEntry[];
294
+ get latestEntry(): CustomGeometriesEntry | undefined;
295
+ /** Set of custom-geometries entry ids currently rendered on the map. */
296
+ get shownEntryIds(): ReadonlySet<string>;
297
+ /**
298
+ * Display policy: `multiple` (default) lets several derived-geometry entries render side
299
+ * by side; `single` enforces "at most one entry on the map" — switching to it auto-hides
300
+ * older entries.
301
+ */
302
+ get entryMode(): EntryMode;
303
+ setEntryMode(mode: EntryMode): Promise<void>;
304
+ findById(entryId: string): CustomGeometriesEntry | undefined;
305
+ /**
306
+ * Append a new entry. If `explicitId` collides, the slice auto-suffixes (`-2`, `-3`, ...).
307
+ * Returns the assigned id. Under `single` mode the new entry replaces every older one.
308
+ */
309
+ addEntry(features: PolygonFeature_2<CommonPlaceProps_2>[], provenance: CustomGeometriesEntry['provenance'], label: string, explicitId?: string): string;
310
+ /** Attach an analysis to an existing entry (replace if a result of the same name exists). */
311
+ addAnalysisToEntry(entryId: string, analysis: CustomGeometriesAnalysis): boolean;
312
+ /** Lazy-init the entry's GeometriesModule, applying / re-applying the requested theme. */
313
+ getEntryGeometriesModule(entryId: string, theme?: GeometryTheme): Promise<GeometriesModule_2>;
314
+ /**
315
+ * Render this entry's polygons. `mode` is scoped to this entry's own layer:
316
+ * - `'replace'` (default) — show exactly `entry.features`, clearing whatever the
317
+ * entry's module had rendered before.
318
+ * - `'add'` — merge `entry.features` with the polygons currently shown on this entry's
319
+ * layer (deduped by feature id). Mostly only meaningful when re-showing the same entry.
320
+ *
321
+ * Cross-entry visibility is handled separately: under `entryMode === 'single'` other
322
+ * shown entries are hidden first; otherwise the caller uses `hideEntry` / the
323
+ * tools' `hidePreviousEntries` option.
324
+ */
325
+ showEntry(entryId: string, theme?: GeometryTheme, mode?: 'add' | 'replace'): Promise<void>;
326
+ private _hideOthersUnderSingleMode;
327
+ hideEntry(entryId: string): Promise<void>;
328
+ /** Drop a single entry from history (clears its module first). No-op when unknown. */
329
+ removeEntry(entryId: string): Promise<void>;
330
+ private _requireEntry;
331
+ private _uniqueEntryId;
332
+ reset(): void;
333
+ }
334
+
335
+ /**
336
+ * Events fired by {@link CustomGeometriesState}. Subscribe via
337
+ * `state.customGeometries.events.on(type, handler)`.
338
+ *
339
+ * @group Agent Toolkit
340
+ */
341
+ declare type CustomGeometriesStateEvents = {
342
+ /** History changed — entry added or cleared via `reset()`. */
343
+ 'entries-change': readonly CustomGeometriesEntry[];
344
+ /** Set of entries currently rendered on the map changed. */
345
+ 'shown-change': ReadonlySet<string>;
346
+ /** A new analysis was attached to (or replaced on) an entry. */
347
+ 'analysis-added': {
348
+ entryId: string;
349
+ analysis: CustomGeometriesAnalysis;
350
+ };
351
+ /** Display policy switched between `single` and `multiple`. */
352
+ 'mode-change': EntryMode;
353
+ };
354
+
355
+ /**
356
+ * Default tool set for the agent toolkit. Each entry is a {@link ToolDefinition}
357
+ * — either a static `ToolEntry` or a `ToolEntryBuilder` that tailors the tool to
358
+ * {@link ToolBuildOptions} (e.g. feature flags) at agent-creation time.
359
+ *
224
360
  * Included automatically by createMapAgent unless includeDefaultTools is false.
225
361
  * Use resolveTools() for manual composition outside createMapAgent.
226
362
  *
227
363
  * @group Agent Toolkit
228
364
  */
229
365
  export declare const DEFAULT_TOOLS: {
230
- locatePlace: ToolEntry;
231
- reverseGeocode: ToolEntry;
232
- discoverPlaces: ToolEntry;
233
- setRouteLocations: ToolEntry;
234
- setRouteParameters: ToolEntry;
235
- addStopToRoute: ToolEntry;
236
- removeStopFromRoute: ToolEntry;
237
- getPOICategoryCodes: ToolEntry;
238
- getTrafficIncidents: ToolEntry;
239
- getTrafficAreaAnalytics: ToolEntry;
240
- queryTrafficAnalytics: ToolEntry;
241
- showPlaces: ToolEntry;
242
- managePlaces: ToolEntry;
243
- showRoute: ToolEntry;
244
- showTrafficAreaAnalytics: ToolEntry;
245
- showWaypoints: ToolEntry;
246
- getShownPlaces: ToolEntry;
247
- getShownRoutes: ToolEntry;
248
- getShownWaypoints: ToolEntry;
249
- getShownRouteTrafficIncidents: ToolEntry;
250
- getShownRouteSections: ToolEntry;
251
- getShownIncidents: ToolEntry;
252
- clearMap: ToolEntry;
253
- flyTo: ToolEntry;
254
- fitRouteSection: ToolEntry;
255
- getViewport: ToolEntry;
256
- recallPlaces: ToolEntry;
257
- recallRoutes: ToolEntry;
258
- getCurrentWaypoints: ToolEntry;
259
- getStandardMapStyles: ToolEntry;
260
- setMapStandardStyle: ToolEntry;
261
- setRouteTheme: ToolEntry;
262
- setLanguage: ToolEntry;
263
- toggleBaseMapLayerGroups: ToolEntry;
264
- togglePOIs: ToolEntry;
265
- toggleTrafficFlow: ToolEntry;
266
- toggleTrafficIncidents: ToolEntry;
267
- getMapStyleLayers: ToolEntry;
268
- setLayoutProperties: ToolEntry;
269
- setPaintProperties: ToolEntry;
270
- setPitchBearing: ToolEntry;
271
- zoomInOrOut: ToolEntry;
272
- executeMaplibreCode: ToolEntry;
273
- calculateBBox: ToolEntry;
274
- formatDistance: ToolEntry;
275
- formatDuration: ToolEntry;
276
- getSectionProgress: ToolEntry;
277
- getSectionBBox: ToolEntry;
278
- getRouteProgress: ToolEntry;
279
- getCurrentLocation: ToolEntry;
280
- help: ToolEntry;
281
- searchAlongRoute: ToolEntry;
282
- findReachableArea: ToolEntry;
283
- recallRanges: ToolEntry;
366
+ locatePlace: ToolDefinition;
367
+ reverseGeocode: ToolDefinition;
368
+ discoverPlaces: ToolDefinition;
369
+ processPlaces: ToolDefinition;
370
+ processGeometries: ToolDefinition;
371
+ processRoutes: ToolDefinition;
372
+ analyseGeometries: ToolDefinition;
373
+ analysePlaces: ToolDefinition;
374
+ analyseRoutes: ToolDefinition;
375
+ setRoute: ToolDefinition;
376
+ addWaypointsToRoute: ToolDefinition;
377
+ removeWaypointsFromRoute: ToolDefinition;
378
+ replaceWaypointInRoute: ToolDefinition;
379
+ getPOICategoryCodes: ToolDefinition;
380
+ getTrafficIncidents: ToolDefinition;
381
+ analyseIncidents: ToolDefinition;
382
+ focusIncidents: ToolDefinition;
383
+ startTrafficIncidentsMonitor: ToolDefinition;
384
+ stopTrafficIncidentsMonitor: ToolDefinition;
385
+ getTrafficAreaAnalytics: ToolDefinition;
386
+ queryTrafficAnalytics: ToolDefinition;
387
+ updatePlacesDisplay: ToolDefinition;
388
+ updateRoutesDisplay: ToolDefinition;
389
+ showTrafficAreaAnalytics: ToolDefinition;
390
+ showWaypoints: ToolDefinition;
391
+ getShownPlaces: ToolDefinition;
392
+ getShownRoutes: ToolDefinition;
393
+ getShownWaypoints: ToolDefinition;
394
+ getShownRouteTrafficIncidents: ToolDefinition;
395
+ getShownIncidents: ToolDefinition;
396
+ clearMap: ToolDefinition;
397
+ flyTo: ToolDefinition;
398
+ getViewport: ToolDefinition;
399
+ recallGeometries: ToolDefinition;
400
+ recallPlaces: ToolDefinition;
401
+ recallRoutes: ToolDefinition;
402
+ getCurrentWaypoints: ToolDefinition;
403
+ getStandardMapStyles: ToolDefinition;
404
+ setMapStandardStyle: ToolDefinition;
405
+ setRouteTheme: ToolDefinition;
406
+ setLanguage: ToolDefinition;
407
+ toggleBaseMapLayerGroups: ToolDefinition;
408
+ togglePOIs: ToolDefinition;
409
+ toggleTrafficFlow: ToolDefinition;
410
+ toggleTrafficIncidents: ToolDefinition;
411
+ getMapStyleLayers: ToolDefinition;
412
+ setLayoutProperties: ToolDefinition;
413
+ setPaintProperties: ToolDefinition;
414
+ setPitchBearing: ToolDefinition;
415
+ zoomInOrOut: ToolDefinition;
416
+ executeMaplibreCode: ToolDefinition;
417
+ calculateBBox: ToolDefinition;
418
+ formatDistance: ToolDefinition;
419
+ formatDuration: ToolDefinition;
420
+ getCurrentLocation: ToolDefinition;
421
+ help: ToolDefinition;
422
+ findReachableAreas: ToolDefinition;
423
+ recallRanges: ToolDefinition;
424
+ recallState: ToolDefinition;
425
+ resetState: ToolDefinition;
426
+ setEntryMode: ToolDefinition;
284
427
  };
285
428
 
429
+ /**
430
+ * Per-slice display policy. With `multiple` (default) several history entries can be on the
431
+ * map at once; with `single` only the most recent one stays. Switching from `multiple` to
432
+ * `single` automatically drops every entry except the latest (and clears their modules) so
433
+ * the map matches the new policy immediately.
434
+ *
435
+ * @group Agent Toolkit
436
+ */
437
+ export declare type EntryMode = 'single' | 'multiple';
438
+
286
439
  /**
287
440
  * Extracts the text content of the last user message in a message array.
288
441
  * Returns null if no user message is found or if content has no text.
@@ -291,6 +444,271 @@ export declare const DEFAULT_TOOLS: {
291
444
  */
292
445
  export declare const extractLastUserText: (messages: ReadonlyArray<ModelMessage>) => string | null;
293
446
 
447
+ /**
448
+ * Feature-flag bag for {@link createMapAgent}. Toggles in-flight or
449
+ * opt-in agent-toolkit behaviour. Individual flags vary in stability —
450
+ * some may graduate to stable public options, others are internal-only
451
+ * experiments that can change or disappear without notice. Check each
452
+ * flag's own doc for its current status.
453
+ *
454
+ * @experimental
455
+ *
456
+ * @group Agent Toolkit
457
+ */
458
+ export declare type FeatureFlags = {
459
+ /* Excluded from this release type: experimentalSearch */
460
+ };
461
+
462
+ /**
463
+ * Produce a compact, LLM-readable summary of the differences between two digests, or `null` when
464
+ * nothing changed. The output is short enough to prepend to a user prompt without bloating the
465
+ * conversation; longer per-field diffs are deliberately omitted.
466
+ *
467
+ * @example
468
+ * ```ts
469
+ * const prev = getStateDigest(state);
470
+ * // ... user clicks around in the UI ...
471
+ * const diff = formatStateDigestDiff(prev, getStateDigest(state));
472
+ * // → '[map state changed since last response: places shown as pins: "amsterdam-cinemas"; route now "routes-2"]'
473
+ * ```
474
+ *
475
+ * @group Agent Toolkit
476
+ */
477
+ export declare const formatStateDigestDiff: (prev: StateDigest, next: StateDigest) => string | null;
478
+
479
+ /** Tagged id for any polygon source in session state. */
480
+ export declare type GeometriesId = z.infer<typeof geometriesIdSchema>;
481
+
482
+ /** Possible kinds in a geometries id. */
483
+ export declare type GeometriesIdKind = 'place' | 'places' | 'ranges' | 'custom';
484
+
485
+ /**
486
+ * Tagged geometries id. The Zod schema is exposed so tools can plug it
487
+ * straight into their input shape (`z.array(geometriesIdSchema).min(1)`)
488
+ * — no string parsing on the way in.
489
+ *
490
+ * @group Agent Toolkit
491
+ */
492
+ export declare const geometriesIdSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
493
+ kind: z.ZodLiteral<"place">;
494
+ id: z.ZodString;
495
+ }, z.core.$strip>, z.ZodObject<{
496
+ kind: z.ZodLiteral<"places">;
497
+ id: z.ZodString;
498
+ }, z.core.$strip>, z.ZodObject<{
499
+ kind: z.ZodLiteral<"ranges">;
500
+ id: z.ZodString;
501
+ }, z.core.$strip>, z.ZodObject<{
502
+ kind: z.ZodLiteral<"custom">;
503
+ id: z.ZodString;
504
+ }, z.core.$strip>], "kind">;
505
+
506
+ /**
507
+ * Provenance metadata for a custom-geometries entry — captures every input
508
+ * source that fed the polygon-producing operation. `sourceIds` holds tagged
509
+ * ids in the same form `geometriesEntryIDs` accepts: `{ kind: 'place' }` for
510
+ * a single place footprint, `{ kind: 'places' }` for every footprint in a
511
+ * places entry, `{ kind: 'ranges' }` for an isochrone entry,
512
+ * `{ kind: 'custom' }` for a derived entry. `operation` is a short label for
513
+ * the kind of computation that produced the entry (e.g. `'union'`, `'buffer'`,
514
+ * `'h3-coverage'`).
515
+ *
516
+ * @group Agent Toolkit
517
+ */
518
+ export declare type GeometryProvenance = {
519
+ /** Short label for the operation that produced this entry (free-form). */
520
+ operation?: string;
521
+ /** Tagged source ids that fed the producing operation. */
522
+ sourceIds: readonly GeometriesId[];
523
+ };
524
+
525
+ /**
526
+ * Snapshot the user-visible state surface. Call after every assistant turn (to record the
527
+ * "agent-known" baseline) and again before each user prompt (to detect what changed since).
528
+ *
529
+ * @group Agent Toolkit
530
+ */
531
+ export declare const getStateDigest: (state: ToolState) => StateDigest;
532
+
533
+ /**
534
+ * Snapshot of an entry's focus subset. Returned by `getFocus` / payload on `focus-change`.
535
+ *
536
+ * @group Agent Toolkit
537
+ */
538
+ declare type IncidentFocus = {
539
+ ids: Set<string>;
540
+ reason?: string;
541
+ };
542
+
543
+ /**
544
+ * One-shot polling loop scoped to a single bbox. Owns its timer, status, error,
545
+ * and captured area. Re-starting after `stop()` is allowed and resets all state.
546
+ *
547
+ * The monitor never knows about the entry that owns it — it surfaces ticks/errors
548
+ * through `events`, and the caller decides what to do with the snapshot.
549
+ *
550
+ * @group Agent Toolkit
551
+ */
552
+ declare class IncidentMonitor {
553
+ private readonly _bbox;
554
+ readonly events: StateEvents<IncidentMonitorEvents>;
555
+ private _status;
556
+ private _area?;
557
+ private _error?;
558
+ private _intervalHandle?;
559
+ private _clearInterval?;
560
+ private _fetchIncidents?;
561
+ constructor(_bbox: BBox_2);
562
+ get status(): PollingStatus;
563
+ get monitoredArea(): MonitoredArea | undefined;
564
+ get error(): string | undefined;
565
+ /**
566
+ * True when the monitor is *currently* polling. Distinct from {@link isActive},
567
+ * which also covers `stopped-error` — a state where the timer is gone but the
568
+ * monitor still carries an unacknowledged error. Use this for "is the entry
569
+ * being polled right now"; use `isActive` for "is there anything to clean up".
570
+ */
571
+ get isRunning(): boolean;
572
+ /** True when the monitor has any polling activity (running or stopped-error). */
573
+ get isActive(): boolean;
574
+ /**
575
+ * Begin polling. Captures `deps` for the lifetime of the run, fires the first tick
576
+ * immediately, then schedules an interval. No-op when already running.
577
+ */
578
+ start(area: MonitoredArea, deps: IncidentMonitorDeps, intervalMs?: number): void;
579
+ /** Stop polling and reset state. No-op when idle. */
580
+ stop(): void;
581
+ private _tick;
582
+ private _failed;
583
+ private _releaseTimer;
584
+ }
585
+
586
+ /**
587
+ * Dependency injection for an {@link IncidentMonitor}. Production code wires
588
+ * `fetchIncidents` to `trafficIncidentDetails`; tests can pass a deterministic fake
589
+ * and override the timer.
590
+ *
591
+ * The fetcher returns just the incidents — the monitor stamps `takenAt` itself
592
+ * so every consumer of one tick (entry data, derived analyses, sandbox `now`)
593
+ * agrees on the same canonical moment.
594
+ *
595
+ * @group Agent Toolkit
596
+ */
597
+ declare type IncidentMonitorDeps = {
598
+ fetchIncidents: (bbox: BBox_2) => Promise<TrafficIncident_2[]>;
599
+ /** Override for tests. Defaults to the global `setInterval`. */
600
+ setInterval?: (fn: () => void, ms: number) => unknown;
601
+ clearInterval?: (handle: unknown) => void;
602
+ };
603
+
604
+ /**
605
+ * Events fired by {@link IncidentMonitor}. The slice subscribes once and re-emits
606
+ * translated copies (`monitor-tick` / `monitor-error`) keyed by entry id.
607
+ */
608
+ declare type IncidentMonitorEvents = {
609
+ /** A poll succeeded. Payload: the fresh snapshot. */
610
+ tick: IncidentSnapshot;
611
+ /** A poll failed — the monitor has cleared its timer and moved to `stopped-error`. */
612
+ error: string;
613
+ };
614
+
615
+ /**
616
+ * Per-entry analysis registry: owns re-executing specs *and* the timeline of
617
+ * their results. One thing, three parts — name, recipe (spec), output history —
618
+ * kept together instead of split across sibling fields.
619
+ *
620
+ * Each named analysis keeps a bounded chronological history (see
621
+ * {@link MAX_HISTORY_PER_ANALYSIS}) so consumers can trend results over time;
622
+ * `getResult(name)` returns the most recent, `history(name)` the full timeline.
623
+ *
624
+ * Errors from individual specs are dropped silently — replay should never break
625
+ * the monitor tick that triggered it.
626
+ *
627
+ * @group Agent Toolkit
628
+ */
629
+ declare class IncidentsAnalyses {
630
+ private _specs;
631
+ private readonly _historyByName;
632
+ get specs(): readonly IncidentsAnalysisSpec[];
633
+ /**
634
+ * Latest result for each registered spec, in registration order. Specs whose
635
+ * history is empty (e.g. only failed runs) are skipped. UI layers that "render
636
+ * whatever analyses this entry has" should iterate this instead of poking at
637
+ * private history state.
638
+ */
639
+ get results(): readonly IncidentsAnalysis[];
640
+ /** Full timeline for a single named analysis, oldest first. Empty when unknown. */
641
+ history(name: string): readonly IncidentsAnalysis[];
642
+ /** Most recent result for a given analysis name, or undefined. */
643
+ getResult(name: string): IncidentsAnalysis | undefined;
644
+ /**
645
+ * Register a spec, or replace an existing one with the same name. When the new spec
646
+ * differs from the existing one (code, outputFormat, or description), drops history
647
+ * for that name — old results belong to a different analysis and must not leak into
648
+ * `getResult` / `results`, which describe the *current* spec. Identical re-registers
649
+ * are no-ops and preserve history (so trend continuity survives idempotent retries).
650
+ */
651
+ register(spec: IncidentsAnalysisSpec): void;
652
+ /** Drop a spec by name. No-op when unknown. Result history is preserved. */
653
+ unregister(name: string): void;
654
+ /**
655
+ * Append a result to the timeline for its name, evicting the oldest entry
656
+ * when the per-name cap is hit. Used by the slice for one-shot tool runs and
657
+ * by {@link replay} for each fresh tick.
658
+ */
659
+ attach(result: IncidentsAnalysis): void;
660
+ /**
661
+ * Re-run every registered spec against `data`, threading the previous result
662
+ * from the timeline. `sampledAt` is the canonical moment for this replay —
663
+ * shared with the entry data and the sandbox's `now`, so consumers can join
664
+ * across them safely. Each fresh result is appended before being returned.
665
+ * Failed specs are skipped.
666
+ */
667
+ replay(data: TrafficIncident[], sampledAt: number): Promise<IncidentsAnalysis[]>;
668
+ }
669
+
670
+ /**
671
+ * A single aggregation/analysis result attached to a traffic-incidents entry.
672
+ * Produced by the `analyseIncidents` tool; `data` is whatever the dynamic code returned.
673
+ *
674
+ * @group Agent Toolkit
675
+ */
676
+ declare type IncidentsAnalysis = {
677
+ /** Unique name within the parent entry (used as a key for future UI). */
678
+ name: string;
679
+ timestamp: number;
680
+ /** Optional human-readable description of what the analysis computed. */
681
+ description?: string;
682
+ /** How `data` should be interpreted — plain JSON or a Chart.js configuration. */
683
+ outputFormat: AnalysisOutputFormat;
684
+ /** Arbitrary aggregation result returned by the dynamic code. */
685
+ data: unknown;
686
+ };
687
+
688
+ /**
689
+ * A registered, re-executing analysis. Produced by `analyseIncidents`; replayed by
690
+ * an entry's {@link IncidentsAnalyses} on every monitor-tick of its source.
691
+ *
692
+ * @group Agent Toolkit
693
+ */
694
+ declare type IncidentsAnalysisSpec = {
695
+ name: string;
696
+ description?: string;
697
+ outputFormat: AnalysisOutputFormat;
698
+ code: string;
699
+ /** Source entry id the spec reads from. */
700
+ source: string;
701
+ };
702
+
703
+ export declare type IncidentSnapshot = {
704
+ takenAt: number;
705
+ incidents: TrafficIncident_2[];
706
+ };
707
+
708
+ declare type JSONValue = null | string | number | boolean | {
709
+ [key: string]: JSONValue;
710
+ } | JSONValue[];
711
+
294
712
  /**
295
713
  * The return type of createMapAgent().
296
714
  * A real ToolLoopAgent instance with state access and cleanup.
@@ -396,6 +814,50 @@ export declare type MapAgentOptions<CS extends ToolState = ToolState> = {
396
814
  outputSchemas?: boolean;
397
815
  /** Max multi-step tool loop iterations. Default: 10. */
398
816
  maxSteps?: number;
817
+ /**
818
+ * Opt into in-flight or experimental agent-toolkit behaviour. Stability
819
+ * varies per flag — see {@link FeatureFlags} and each flag's own doc.
820
+ *
821
+ * @experimental
822
+ */
823
+ featureFlags?: FeatureFlags;
824
+ /**
825
+ * Provider-specific options forwarded to the AI SDK on every step. Keyed by
826
+ * provider id (`openai`, `azure`, `anthropic`, ...).
827
+ *
828
+ * Use this to enable reasoning summaries for gpt-5.x via the Responses API
829
+ * — without `reasoningSummary` the model thinks but returns no reasoning text:
830
+ *
831
+ * @example
832
+ * ```typescript
833
+ * createMapAgent(map, {
834
+ * model: azure.responses(deploymentId),
835
+ * providerOptions: {
836
+ * openai: { reasoningEffort: 'low', reasoningSummary: 'auto' },
837
+ * },
838
+ * });
839
+ * ```
840
+ */
841
+ providerOptions?: ProviderOptions;
842
+ /**
843
+ * Per-step `providerOptions` override. Called once per step with the
844
+ * classifier-narrowed active toolset; the returned object is merged on top
845
+ * of `providerOptions` for that step. Use it to bump reasoning effort on
846
+ * turns that include a code-execution tool without paying the cost on
847
+ * simple fetch/toggle turns.
848
+ *
849
+ * @example
850
+ * ```typescript
851
+ * stepProviderOptions: ({ activeTools }) =>
852
+ * activeTools?.some((t) => CODE_EXEC.has(t))
853
+ * ? { openai: { reasoningEffort: 'medium' } }
854
+ * : undefined,
855
+ * ```
856
+ */
857
+ stepProviderOptions?: (info: {
858
+ activeTools: string[] | undefined;
859
+ stepNumber: number;
860
+ }) => ProviderOptions | undefined;
399
861
  };
400
862
 
401
863
  /**
@@ -415,6 +877,30 @@ declare class MapPOIsState implements StateSlice {
415
877
  reset(): void;
416
878
  }
417
879
 
880
+ export declare type MonitoredArea = {
881
+ bbox: BBox_2;
882
+ capturedAt: number;
883
+ label?: string;
884
+ };
885
+
886
+ /**
887
+ * A single aggregation/analysis result attached to a places entry.
888
+ * Produced by the `analysePlaces` tool; `data` is whatever the dynamic code returned.
889
+ *
890
+ * @group Agent Toolkit
891
+ */
892
+ export declare type PlacesAnalysis = {
893
+ /** Unique name within the parent entry (used as a key for future UI). */
894
+ name: string;
895
+ timestamp: number;
896
+ /** Optional human-readable description of what the analysis computed. */
897
+ description?: string;
898
+ /** How `data` should be interpreted — plain JSON or a Chart.js configuration. */
899
+ outputFormat: AnalysisOutputFormat;
900
+ /** Arbitrary aggregation result returned by the dynamic code. */
901
+ data: unknown;
902
+ };
903
+
418
904
  /**
419
905
  * A single entry in the place search history.
420
906
  *
@@ -424,65 +910,234 @@ export declare type PlacesEntry = {
424
910
  id: string;
425
911
  timestamp: number;
426
912
  label: string;
427
- data: Place[];
913
+ places: Place[];
914
+ /** Connections (lines between places) produced alongside this entry, rendered via PlacesModule.showConnections. */
915
+ connections?: PlaceConnectionDisplay[];
916
+ /**
917
+ * Boundary polygons attached to this entry. One feature per place that has a geometry data
918
+ * source (populated lazily via PlacesState.fetchPlaceGeometry), or a standalone result list
919
+ * produced by tools like processGeometries. Each feature's `id` is the geometry-data-source id
920
+ * (`place.properties.dataSources.geometry.id`), matching the response shape of `geometryData`.
921
+ */
922
+ geometries?: PolygonFeature<CommonPlaceProps>[];
923
+ /** Accumulated analysis results linked to this entry. */
924
+ _analysis?: PlacesAnalysis[];
925
+ /** Lazy place-id → Place index, built on first ID lookup through PlacesState.findPlaceById. */
926
+ _byId?: Record<string, Place>;
927
+ /** Lazy place-id → boundary polygon index, built on first lookup through PlacesState.getGeometryForPlace. */
928
+ _geometryByPlaceId?: Record<string, PolygonFeature<CommonPlaceProps>>;
929
+ /**
930
+ * Per-entry display modules. Each entry owns its own PlacesModule per marker theme and a
931
+ * dedicated GeometriesModule, so display state lives on the entry rather than on a shared
932
+ * slice-level module. Lazy-initialised via PlacesState helpers — undefined until first show.
933
+ */
934
+ _modules?: {
935
+ pin?: PlacesModule;
936
+ baseMap?: PlacesModule;
937
+ pinClustered?: PlacesModule;
938
+ geometries?: GeometriesModule;
939
+ };
940
+ /** The marker theme currently rendering this entry, or undefined when hidden. */
941
+ _shownAs?: 'pin' | 'base-map' | 'pin-clustered';
428
942
  };
429
943
 
944
+ /** How an entry is rendered on the map: as individual pins, base-map POIs, or clustered pins. */
945
+ declare type PlacesMarkerType = 'pin' | 'base-map' | 'pin-clustered';
946
+
430
947
  /**
431
- * State for place search, geocoding, and geometries.
948
+ * State for the places layer: an append-only history of place results plus per-entry display
949
+ * modules.
432
950
  *
433
- * Holds lazy-initialized PlacesModule and GeometriesModule alongside an
434
- * append-only history of place and search results produced during the session.
951
+ * Each entry owns its own PlacesModule (per marker theme: pin / base-map / pin-clustered) and
952
+ * a dedicated GeometriesModule, lazy-initialised on first show. The slice no longer holds
953
+ * shared modules — display state lives on the entry, so two entries can be rendered side by
954
+ * side under different themes without sharing a single source.
435
955
  *
436
956
  * @group Agent Toolkit
437
957
  */
438
958
  declare class PlacesState implements StateSlice {
439
959
  private readonly _ttMap;
440
- private _placesModule?;
441
960
  private _entries;
442
- /** IDs of entries currently rendered on the map. */
443
- private _shownEntryIds;
961
+ private _entryMode;
962
+ /** Subscribe to state changes — see {@link PlacesStateEvents}. */
963
+ readonly events: StateEvents<PlacesStateEvents>;
444
964
  constructor(_ttMap: TomTomMap);
445
- getPlacesModule(): Promise<PlacesModule>;
446
- get placesModule(): PlacesModule | undefined;
965
+ /**
966
+ * Display policy: `multiple` (default) keeps every history entry available to render
967
+ * simultaneously; `single` enforces "at most one entry on the map at a time" and trims
968
+ * the history down to the latest entry whenever it's set.
969
+ */
970
+ get entryMode(): EntryMode;
971
+ /**
972
+ * Switch the display policy. When moving from `multiple` → `single` we hide every entry
973
+ * older than the latest, drop them from history, and emit `entries-change` so listeners
974
+ * (the recents rail, recall tools, …) re-render. No-op when the mode hasn't changed.
975
+ */
976
+ setEntryMode(mode: EntryMode): Promise<void>;
977
+ /**
978
+ * Lazy-init and return the entry's PlacesModule for the given marker theme. Each entry
979
+ * keeps a separate module per theme so flipping themes for one entry doesn't disturb
980
+ * another. Throws when the entry id is unknown.
981
+ */
982
+ getEntryPlacesModule(entryId: string, markerType: PlacesMarkerType): Promise<PlacesModule_2>;
983
+ /**
984
+ * Lazy-init and return the entry's place-geometries module (for POI footprints, municipality
985
+ * outlines). Re-applies themed config when the theme changes. Throws on unknown entry id.
986
+ */
987
+ getEntryGeometriesModule(entryId: string, theme?: GeometryTheme): Promise<GeometriesModule_2>;
988
+ private _requireEntry;
447
989
  get entries(): readonly PlacesEntry[];
448
990
  /**
449
991
  * This is used purely to pass data between tools in a context-dependent way,
450
992
  * so we assume the most recent entry is the relevant one to show on the map.
451
993
  */
452
994
  get latestPlace(): Place_2[] | undefined;
453
- addPlaceResult(place: Place_2 | Places, label: string): string;
454
- /** IDs of entries currently shown on the map. */
455
- get shownEntryIds(): ReadonlySet<PlacesEntry['id']>;
995
+ addPlaceResult(place: Place_2 | Places, label: string, explicitId?: string, connections?: PlaceConnectionDisplay_2[], geometries?: PolygonFeature_2<CommonPlaceProps_2>[]): string;
996
+ private _uniqueEntryId;
997
+ /**
998
+ * Attach an analysis result to an existing entry. Analysis names are unique
999
+ * within a single entry — adding one with an existing name replaces it.
1000
+ * Returns true on success, false if the entry doesn't exist.
1001
+ */
1002
+ addAnalysisToEntry(entryId: string, analysis: PlacesAnalysis): boolean;
1003
+ findPlaceById(placeId: string): {
1004
+ place: Place_2;
1005
+ entryId: string;
1006
+ } | undefined;
1007
+ /**
1008
+ * Returns the cached boundary polygon for a place id, or undefined if no geometry has been
1009
+ * fetched for it yet. Builds a lazy place-id → feature index per entry on first lookup.
1010
+ */
1011
+ getGeometryForPlace(placeId: string): PolygonFeature_2<CommonPlaceProps_2> | undefined;
1012
+ /**
1013
+ * Fetches the boundary polygon for a place from the Geometry Data service and stores it on
1014
+ * the owning entry. Returns the cached feature if already fetched, or undefined if the place
1015
+ * is unknown or has no geometry data source.
1016
+ */
1017
+ fetchPlaceGeometry(placeId: string): Promise<PolygonFeature_2<CommonPlaceProps_2> | undefined>;
1018
+ /**
1019
+ * Batch-fetches boundary polygons for every place in an entry that has a geometry data source
1020
+ * but is not yet cached. Issues one Geometry Data request per chunk of {@link GEOMETRY_DATA_BATCH_SIZE}
1021
+ * place ids (the API caps each request) and stores the merged result on `entry.geometries`.
1022
+ * Returns the full geometries list for the entry, or an empty array if the entry is unknown.
1023
+ */
1024
+ fetchGeometriesForEntry(entryId: string): Promise<PolygonFeature_2<CommonPlaceProps_2>[]>;
1025
+ /**
1026
+ * Render the given polygons via the entry's GeometriesModule. The `mode` flag is
1027
+ * scoped to **this entry's layer only** — it does not touch other entries (use
1028
+ * the slice's per-entry hide/show methods for cross-entry visibility).
1029
+ *
1030
+ * - `mode: 'replace'` (default) — calls `module.show` with exactly these features, replacing
1031
+ * whatever was previously on this entry's layer. An empty array clears the layer.
1032
+ * - `mode: 'add'` — merges the new features with the polygons currently shown on this
1033
+ * entry's layer (deduped by `feature.id`) and re-renders the union. An empty array is a
1034
+ * no-op.
1035
+ *
1036
+ * `entryId` defaults to the latest entry that owns geometries — handy when callers only ever
1037
+ * render the most-recently produced footprints. Pass an explicit id to target an older one.
1038
+ */
1039
+ showPlaceGeometries(features: PolygonFeature_2<CommonPlaceProps_2>[], theme?: GeometryTheme, mode?: 'add' | 'replace', entryId?: string): Promise<void>;
1040
+ private _latestGeometriesEntryId;
1041
+ private _buildGeometryIndex;
1042
+ /** IDs of all entries currently shown on the map. */
1043
+ get shownEntryIds(): ReadonlySet<string>;
1044
+ /** IDs of entries currently shown as individual pins. */
1045
+ get shownAsPinIds(): ReadonlySet<string>;
1046
+ /** IDs of entries currently shown as base-map POIs. */
1047
+ get shownAsBaseMapIds(): ReadonlySet<string>;
1048
+ /** IDs of entries currently shown as clustered pins. */
1049
+ get shownAsPinClusteredIds(): ReadonlySet<string>;
1050
+ private _idsShownAs;
1051
+ /** Marker type for a specific entry, or undefined if it is not shown. */
1052
+ getShownMarkerType(id: string): PlacesMarkerType | undefined;
456
1053
  /** Return the union of all features belonging to currently-shown entries. */
457
1054
  getShownFeatures(): Place_2[];
458
- /** Replace the shown set with exactly these ids and re-render. */
459
- setShownEntries(ids: readonly PlacesEntry['id'][]): Promise<Place_2[]>;
460
- /** Add ids to the shown set and re-render. */
461
- addShownEntries(ids: readonly PlacesEntry['id'][]): Promise<Place_2[]>;
462
- /** Remove ids from the shown set and re-render. */
463
- removeShownEntries(ids: readonly PlacesEntry['id'][]): Promise<Place_2[]>;
1055
+ /**
1056
+ * Replace the shown set for `markerType` with exactly these ids and re-render.
1057
+ * Entries currently shown as `markerType` but absent from `ids` are hidden;
1058
+ * entries in `ids` are switched to `markerType` (lazy-creating their module).
1059
+ */
1060
+ setShownEntries(ids: readonly string[], markerType: PlacesMarkerType): Promise<Place_2[]>;
1061
+ /**
1062
+ * Add ids to the shown set under `markerType`. Any id previously shown as
1063
+ * a different marker type is moved (its previous module is cleared, the
1064
+ * new theme's module renders).
1065
+ *
1066
+ * Under `entryMode === 'single'` everything else is hidden first, so the
1067
+ * effective behaviour collapses to "show exactly the last id in the list".
1068
+ */
1069
+ addShownEntries(ids: readonly string[], markerType: PlacesMarkerType): Promise<Place_2[]>;
1070
+ /** Remove ids from the shown set and clear their modules. */
1071
+ removeShownEntries(ids: readonly string[]): Promise<Place_2[]>;
464
1072
  /** Clear everything from the map (does not touch entries history). */
465
1073
  clearShownEntries(): Promise<void>;
466
- private _renderShown;
1074
+ clearShownGeometries(ids?: readonly string[]): Promise<void>;
1075
+ /**
1076
+ * Drop a single entry from history. Hides it from the map first (clears every theme module
1077
+ * it owns) then removes it from `_entries`. Emits `entries-change` and, when the entry was
1078
+ * shown, `shown-change`. No-op when the id is unknown.
1079
+ */
1080
+ removeEntry(id: string): Promise<void>;
1081
+ private _showEntryAs;
1082
+ private _hideEntry;
467
1083
  reset(): void;
468
1084
  }
469
1085
 
470
1086
  /**
471
- * A single entry in the reachable range history.
1087
+ * Events fired by {@link PlacesState}. Subscribe via `state.places.events.on(type, handler)`.
1088
+ *
1089
+ * @group Agent Toolkit
1090
+ */
1091
+ declare type PlacesStateEvents = {
1092
+ /** History changed — entry added, replaced, or cleared via `reset()`. Payload: the full entries snapshot. */
1093
+ 'entries-change': readonly PlacesEntry[];
1094
+ /** Set of entries currently rendered on the map changed (or marker type swapped). */
1095
+ 'shown-change': ReadonlySet<string>;
1096
+ /** A new analysis was attached to (or replaced on) an entry. */
1097
+ 'analysis-added': {
1098
+ entryId: string;
1099
+ analysis: PlacesAnalysis;
1100
+ };
1101
+ /** Boundary polygons were fetched and cached on an entry. */
1102
+ 'geometries-change': {
1103
+ entryId: string;
1104
+ count: number;
1105
+ };
1106
+ /** Display policy switched between `single` and `multiple`. */
1107
+ 'mode-change': EntryMode;
1108
+ };
1109
+
1110
+ export declare type PollingStatus = 'idle' | 'running' | 'stopped-error';
1111
+
1112
+ declare type ProviderOptions = Record<string, Record<string, JSONValue | undefined>>;
1113
+
1114
+ /**
1115
+ * A single entry in the reachable range history. Holds one or more
1116
+ * {@link ReachableRange} results that were computed together — typically
1117
+ * one per origin in a multi-origin call to `findReachableAreas`.
472
1118
  *
473
1119
  * @group Agent Toolkit
474
1120
  */
475
- export declare type RangeEntry = {
1121
+ export declare type RangesEntry = {
476
1122
  id: string;
477
1123
  timestamp: number;
478
1124
  label: string;
479
- origin: {
480
- query?: string;
481
- position: Position;
1125
+ /** One or more ranges sharing this entry — typically one per origin. */
1126
+ ranges: ReachableRange[];
1127
+ /**
1128
+ * Per-entry display modules. Each entry owns a dedicated GeometriesModule (for the
1129
+ * isochrone polygons) and PlacesModule (for the origin pin(s)), so display state lives
1130
+ * on the entry instead of a shared slice-level module. Lazy-initialised via RangeState
1131
+ * helpers — undefined until first show.
1132
+ */
1133
+ _modules?: {
1134
+ geometries?: GeometriesModule;
1135
+ places?: PlacesModule;
482
1136
  };
483
- budgets: ReachableRangeBudget[];
484
- /** Raw polygon result for use in subsequent geometry searches. Not exposed to the agent. */
485
- polygon?: PolygonFeatures;
1137
+ /** Theme that the geometries module is currently configured with. */
1138
+ _shownTheme?: 'filled' | 'outline' | 'inverted';
1139
+ /** True while any of this entry's modules are rendering on the map. */
1140
+ _shown?: boolean;
486
1141
  };
487
1142
 
488
1143
  /**
@@ -494,40 +1149,115 @@ export declare type RangeEntry = {
494
1149
  *
495
1150
  * @group Agent Toolkit
496
1151
  */
497
- declare class RangeState implements StateSlice {
1152
+ declare class RangeState implements ShownEntriesSlice {
498
1153
  private readonly _ttMap;
499
1154
  private _entries;
500
- private _placesModule?;
501
- private _geometriesModule?;
502
- private _currentTheme?;
1155
+ private _entryMode;
1156
+ /** Subscribe to state changes — see {@link RangeStateEvents}. */
1157
+ readonly events: StateEvents<RangeStateEvents>;
503
1158
  constructor(_ttMap: TomTomMap);
504
- getPlacesModule(): Promise<PlacesModule>;
505
- getGeometriesModule(theme: GeometryTheme): Promise<GeometriesModule>;
506
- get geometriesModule(): GeometriesModule | undefined;
507
- get entries(): readonly RangeEntry[];
508
- get latestEntry(): RangeEntry | undefined;
509
- addEntry(entry: Omit<RangeEntry, 'id' | 'timestamp'>): string;
1159
+ /** Set of ranges-entry ids currently rendered on the map. */
1160
+ get shownEntryIds(): ReadonlySet<string>;
1161
+ /**
1162
+ * Display policy: `multiple` (default) lets several reachable-area entries render side
1163
+ * by side; `single` forces "at most one ranges entry on the map" — `markEntryShown`
1164
+ * then hides any previously-shown entry first.
1165
+ */
1166
+ get entryMode(): EntryMode;
1167
+ /**
1168
+ * Switch the display policy. When moving from `multiple` → `single` we hide every
1169
+ * non-latest entry, drop them from history, and emit `entries-change`. No-op when the
1170
+ * mode hasn't changed.
1171
+ */
1172
+ setEntryMode(mode: EntryMode): Promise<void>;
1173
+ /**
1174
+ * Lazy-init the entry's GeometriesModule (for the isochrone/isodistance polygons),
1175
+ * applying the requested theme. Throws on unknown id.
1176
+ */
1177
+ getEntryGeometriesModule(entryId: string, theme?: GeometryTheme): Promise<GeometriesModule_2>;
1178
+ /**
1179
+ * Lazy-init the entry's origin-pin PlacesModule. Each entry maintains its own pin module
1180
+ * so multiple ranges entries can be on the map simultaneously without the pins of one
1181
+ * overwriting another.
1182
+ */
1183
+ getEntryPlacesModule(entryId: string): Promise<PlacesModule_2>;
1184
+ /**
1185
+ * Mark this entry as shown — records `_shown = true` and emits `shown-change`. The
1186
+ * caller is expected to push features into the lazy modules itself; this method only
1187
+ * keeps the `_shown` flag in sync with what the user sees.
1188
+ *
1189
+ * Under `entryMode === 'single'` any other shown entry is hidden first so two range
1190
+ * layers don't overlap.
1191
+ */
1192
+ markEntryShown(entryId: string): Promise<void>;
1193
+ /** Hide every module on the entry (clear). No-op when the entry is unknown. */
1194
+ hideEntry(entryId: string): Promise<void>;
1195
+ /**
1196
+ * Drop a single entry from history. Hides it from the map first (clears its geometries +
1197
+ * origin-pin modules) then removes it from `_entries`. Emits `entries-change`. No-op when
1198
+ * the id is unknown.
1199
+ */
1200
+ removeEntry(entryId: string): Promise<void>;
1201
+ private _requireEntry;
1202
+ get entries(): readonly RangesEntry[];
1203
+ get latestEntry(): RangesEntry | undefined;
1204
+ addEntry(entry: Omit<RangesEntry, 'id' | 'timestamp'>): string;
510
1205
  reset(): void;
511
1206
  }
512
1207
 
1208
+ /**
1209
+ * Events fired by {@link RangeState}. Subscribe via `state.ranges.events.on(type, handler)`.
1210
+ *
1211
+ * @group Agent Toolkit
1212
+ */
1213
+ declare type RangeStateEvents = {
1214
+ /** Range history changed — entry added or cleared via `reset()`. */
1215
+ 'entries-change': readonly RangesEntry[];
1216
+ /** Set of ranges entries currently rendered on the map changed (multi-show under `multiple`). */
1217
+ 'shown-change': ReadonlySet<string>;
1218
+ /** Display policy switched between `single` and `multiple`. */
1219
+ 'mode-change': EntryMode;
1220
+ };
1221
+
1222
+ /**
1223
+ * A single reachable area, scoped to one origin and one set of budgets.
1224
+ * One {@link RangesEntry} bundles one or more of these — useful for comparing
1225
+ * coverage from several origins under the same query.
1226
+ *
1227
+ * @group Agent Toolkit
1228
+ */
1229
+ export declare type ReachableRange = {
1230
+ origin: {
1231
+ query?: string;
1232
+ position: Position;
1233
+ };
1234
+ budgets: ReachableRangeBudget[];
1235
+ /** Raw polygon result for use in subsequent geometry searches. Not exposed to the agent. */
1236
+ polygon?: PolygonFeatures;
1237
+ };
1238
+
513
1239
  /**
514
1240
  * Merges default tools with user overrides.
515
1241
  *
516
- * - `ToolEntry` values add or replace tools in `defaults`.
1242
+ * - `ToolEntry` (or builder) values add or replace tools in `defaults`.
517
1243
  * - `false` values remove tools from `defaults`.
518
1244
  * - Returns a new object — `defaults` is never mutated.
519
1245
  *
1246
+ * Default entries may be {@link ToolEntryBuilder builders} that produce
1247
+ * their {@link ToolEntry} from build-time options. Builders pass through
1248
+ * untouched and are materialized later by `setupTools`.
1249
+ *
520
1250
  * @param defaults - Base tool record (typically DEFAULT_TOOLS or empty).
521
1251
  * @param overrides - User-provided additions, replacements, and exclusions.
522
1252
  * @returns Resolved tool record ready for setupTools.
523
1253
  *
524
1254
  * @group Agent Toolkit
525
1255
  */
526
- export declare const resolveTools: <S extends ToolState>(defaults: Record<string, ToolEntry<S>>, overrides?: { [K in ToolNameHint]?: ToolEntry<S> | false | undefined; }) => Record<string, ToolEntry<S>>;
1256
+ export declare const resolveTools: <S extends ToolState>(defaults: Record<string, ToolDefinition<S>>, overrides?: { [K in ToolNameHint]?: ToolEntry<S> | false | undefined; }) => Record<string, ToolDefinition<S>>;
527
1257
 
528
1258
  /**
529
1259
  * Route planning parameters stored in routing state.
530
- * Consumed by setRouteLocations, addStopToRoute, and removeStopFromRoute.
1260
+ * Consumed by setRoute, addWaypointsToRoute, removeWaypointsFromRoute, and replaceWaypointInRoute.
531
1261
  *
532
1262
  * @group Agent Toolkit
533
1263
  */
@@ -545,6 +1275,24 @@ export declare type RouteParams = {
545
1275
  };
546
1276
  };
547
1277
 
1278
+ /**
1279
+ * A single aggregation/analysis result attached to a routes entry.
1280
+ * Produced by the `analyseRoutes` tool; `data` is whatever the dynamic code returned.
1281
+ *
1282
+ * @group Agent Toolkit
1283
+ */
1284
+ export declare type RoutesAnalysis = {
1285
+ /** Unique name within the parent entry (used as a key for future UI). */
1286
+ name: string;
1287
+ timestamp: number;
1288
+ /** Optional human-readable description of what the analysis computed. */
1289
+ description?: string;
1290
+ /** How `data` should be interpreted — plain JSON or a Chart.js configuration. */
1291
+ outputFormat: AnalysisOutputFormat;
1292
+ /** Arbitrary aggregation result returned by the dynamic code. */
1293
+ data: unknown;
1294
+ };
1295
+
548
1296
  /**
549
1297
  * A single entry in the route calculation history.
550
1298
  *
@@ -557,38 +1305,226 @@ export declare type RoutesEntry = {
557
1305
  data: Routes;
558
1306
  waypoints: WaypointLike[];
559
1307
  params: RouteParams;
1308
+ /** Accumulated analysis results linked to this entry. */
1309
+ _analysis?: RoutesAnalysis[];
1310
+ /**
1311
+ * Per-entry RoutingModule. Each entry owns its own module so display state (which route
1312
+ * variant is selected, which waypoints are shown, layer theme) lives on the entry instead
1313
+ * of a shared slice-level module. Lazy-initialised via RoutingState helpers — undefined
1314
+ * until first show.
1315
+ */
1316
+ _module?: RoutingModule;
1317
+ /** True while this entry's `_module` is rendering routes/waypoints on the map. */
1318
+ _shown?: boolean;
560
1319
  };
561
1320
 
562
1321
  /**
563
1322
  * State for route calculation, waypoint management, and route planning parameters.
564
1323
  *
565
- * Holds the lazy-initialized RoutingModule alongside an append-only history of
566
- * calculated routes, sparse planning slots (being assembled before a calculation),
567
- * and current route parameters.
1324
+ * Each entry carries its own lazy-initialised RoutingModule (`entry._module`), so display
1325
+ * state lives on the entry instead of a shared slice-level module. Two routes can be rendered
1326
+ * concurrently by showing both entries, and switching the active route hides the previous
1327
+ * entry's module without disturbing its calculated data.
568
1328
  *
569
1329
  * @group Agent Toolkit
570
1330
  */
571
- declare class RoutingState implements StateSlice {
1331
+ declare class RoutingState implements ShownEntriesSlice {
572
1332
  private readonly _ttMap;
573
- private _routingModule?;
574
1333
  private _entries;
575
1334
  private _planningSlots;
576
1335
  private _params;
1336
+ private _entryMode;
1337
+ /**
1338
+ * The most recently `.showEntry`-ed id. Tracks "who was last brought up" so tools that
1339
+ * operate on the focused module (`set-route-theme`, `get-shown-routes`, …) have a single
1340
+ * pointer to read; the authoritative shown set is `shownEntryIds` (derived from each
1341
+ * entry's `_shown` flag).
1342
+ */
1343
+ private _lastShownEntryId?;
1344
+ /** Subscribe to state changes — see {@link RoutingStateEvents}. */
1345
+ readonly events: StateEvents<RoutingStateEvents>;
577
1346
  constructor(_ttMap: TomTomMap);
578
- getRoutingModule(): Promise<RoutingModule>;
579
- get routingModule(): RoutingModule | undefined;
1347
+ /** Set of routes-entry ids currently rendered on the map. */
1348
+ get shownEntryIds(): ReadonlySet<string>;
1349
+ /**
1350
+ * Display policy: `multiple` (default) lets several entries render side-by-side;
1351
+ * `single` forces "at most one route on the map" — `showEntry` then hides any
1352
+ * previously-shown entry first.
1353
+ */
1354
+ get entryMode(): EntryMode;
1355
+ /**
1356
+ * Switch the display policy. When moving from `multiple` → `single` we hide every
1357
+ * non-latest entry, drop them from history, and emit `entries-change`. No-op when the
1358
+ * mode hasn't changed.
1359
+ */
1360
+ setEntryMode(mode: EntryMode): Promise<void>;
1361
+ /**
1362
+ * Lazy-init and return an entry's RoutingModule. Throws on unknown id. Callers that need
1363
+ * to show/hide should prefer the higher-level {@link showEntry} / {@link hideEntry}.
1364
+ */
1365
+ getEntryRoutingModule(entryId: string): Promise<RoutingModule_2>;
1366
+ /**
1367
+ * Render the given entry's routes + waypoints on its own RoutingModule.
1368
+ *
1369
+ * Under `entryMode === 'single'` any other shown entry is hidden first so two route
1370
+ * layers don't pile up. Under `entryMode === 'multiple'` previously-shown entries stay
1371
+ * rendered, letting the caller overlay several routes at once.
1372
+ *
1373
+ * Emits `shown-change` once the new entry is up.
1374
+ */
1375
+ showEntry(entryId: string): Promise<void>;
1376
+ /**
1377
+ * Hide the entry's routes + waypoints on its module (clear; the module stays cached).
1378
+ * No-op when the entry is unknown or already hidden.
1379
+ */
1380
+ hideEntry(entryId: string): Promise<void>;
1381
+ /**
1382
+ * Drop a single entry from history. Hides it from the map first (clears its routing module)
1383
+ * then removes it from `_entries`. Emits `entries-change`. No-op when the id is unknown.
1384
+ */
1385
+ removeEntry(entryId: string): Promise<void>;
1386
+ private _requireEntry;
1387
+ /**
1388
+ * RoutingModule of the most-recently-shown entry, if any. Tools that operate on the
1389
+ * "focused route" (e.g. setRouteTheme, getShownRoutes) read off it. Returns undefined
1390
+ * when no entry is currently shown.
1391
+ */
1392
+ get currentEntryModule(): RoutingModule_2 | undefined;
580
1393
  get entries(): readonly RoutesEntry[];
581
1394
  get currentRoutes(): Routes_2 | undefined;
582
1395
  get currentWaypoints(): WaypointLike_2[] | undefined;
583
1396
  /** Sparse nullable slots being assembled before triggering a route calculation. */
584
1397
  get planningSlots(): PlanningWaypoint[];
585
1398
  get params(): RouteParams;
586
- addRoutes(routes: Routes_2, waypoints: WaypointLike_2[], label: string): void;
1399
+ addRoutes(routes: Routes_2, waypoints: WaypointLike_2[], label: string): string;
1400
+ /**
1401
+ * Attach an analysis result to an existing routes entry. Names are unique within a single
1402
+ * entry — adding one with an existing name replaces it. Returns true on success, false if
1403
+ * the entry doesn't exist.
1404
+ */
1405
+ addAnalysisToEntry(entryId: string, analysis: RoutesAnalysis): boolean;
587
1406
  setWaypointAt(index: number, waypoint: WaypointLike_2): void;
588
1407
  setParams(params: Partial<RouteParams>): void;
589
1408
  reset(): void;
590
1409
  }
591
1410
 
1411
+ /**
1412
+ * Events fired by {@link RoutingState}. Subscribe via `state.routing.events.on(type, handler)`.
1413
+ *
1414
+ * @group Agent Toolkit
1415
+ */
1416
+ declare type RoutingStateEvents = {
1417
+ /** Route history changed — new calculation stored, analysis attached, or cleared via `reset()`. */
1418
+ 'entries-change': readonly RoutesEntry[];
1419
+ /** Sparse waypoint planning slots changed (origin/stop/destination assignment). */
1420
+ 'planning-change': readonly PlanningWaypoint[];
1421
+ /** Route parameters (alternatives, cost model, depart/arrive time) changed. */
1422
+ 'params-change': RouteParams;
1423
+ /** A new analysis was attached to (or replaced on) a routes entry. */
1424
+ 'analysis-added': {
1425
+ entryId: string;
1426
+ analysis: RoutesAnalysis;
1427
+ };
1428
+ /** Set of routes entries currently rendered on the map changed (multi-show under `multiple`). */
1429
+ 'shown-change': ReadonlySet<string>;
1430
+ /** Display policy switched between `single` and `multiple`. */
1431
+ 'mode-change': EntryMode;
1432
+ };
1433
+
1434
+ /**
1435
+ * State slice that owns a set of "entries" (routes, ranges) where each entry can be hidden
1436
+ * individually by id and the slice exposes the currently-shown set. Implemented by
1437
+ * {@link RoutingState} and {@link RangeState}; consumed by helpers that need to hide
1438
+ * previously-shown entries before rendering a new one (see `hidePreviousShownEntries`).
1439
+ *
1440
+ * Not implemented by {@link PlacesState}, which uses a wider per-entry marker-type model
1441
+ * (`removeShownEntries(ids)` rather than `hideEntry(id)`).
1442
+ *
1443
+ * @group Agent Toolkit
1444
+ */
1445
+ declare interface ShownEntriesSlice extends StateSlice {
1446
+ readonly entryMode: EntryMode;
1447
+ readonly shownEntryIds: ReadonlySet<string>;
1448
+ hideEntry(entryId: string): Promise<void>;
1449
+ }
1450
+
1451
+ /**
1452
+ * Compact snapshot of the user-visible parts of `ToolState`. Designed to be diffable: every field
1453
+ * is serialisable to JSON and stable for `===` comparison when the underlying state hasn't changed.
1454
+ *
1455
+ * @group Agent Toolkit
1456
+ */
1457
+ export declare type StateDigest = {
1458
+ places: {
1459
+ /** Entry ids currently rendered as individual pins, sorted ascending. */
1460
+ shownAsPin: string[];
1461
+ /** Entry ids currently rendered as base-map POIs, sorted ascending. */
1462
+ shownAsBaseMap: string[];
1463
+ /** Display policy in force on the slice — `multiple` (default) or `single`. */
1464
+ entryMode: EntryMode;
1465
+ };
1466
+ routes: {
1467
+ /** Routes-entry ids currently drawn on the map, sorted ascending. */
1468
+ shown: string[];
1469
+ /** Display policy in force on the slice — `multiple` (default) or `single`. */
1470
+ entryMode: EntryMode;
1471
+ };
1472
+ ranges: {
1473
+ /** Range-entry ids currently drawn on the map, sorted ascending. */
1474
+ shown: string[];
1475
+ /** Display policy in force on the slice — `multiple` (default) or `single`. */
1476
+ entryMode: EntryMode;
1477
+ };
1478
+ traffic: {
1479
+ /** Whether a TrafficAreaAnalytics visualization is active. */
1480
+ analyticsActive: boolean;
1481
+ };
1482
+ incidents: {
1483
+ /** Traffic-incidents entry ids currently rendered on the map, sorted ascending. */
1484
+ shown: string[];
1485
+ /** Entry ids currently being polled by the per-entry network monitor, sorted ascending. */
1486
+ monitored: string[];
1487
+ /** Display policy in force on the slice — `multiple` (default) or `single`. */
1488
+ entryMode: EntryMode;
1489
+ };
1490
+ };
1491
+
1492
+ /**
1493
+ * @module agent-toolkit-state
1494
+ *
1495
+ * Tiny typed event emitter used by every state slice (PlacesState, RoutingState, ...) to notify
1496
+ * subscribers when their internals change. The shape mirrors the SDK's `ModuleEvents` API
1497
+ * (`on(type, handler) => unsubscribe`, `off(type)`) so consumers see one consistent pattern across
1498
+ * the SDK and the agent toolkit. Designed to plug straight into React's `useSyncExternalStore`.
1499
+ *
1500
+ * @group Agent Toolkit
1501
+ */
1502
+ /**
1503
+ * Generic typed event bus. The `EventMap` parameter declares the set of event names this emitter
1504
+ * supports along with each event's payload type — `on` and `emit` are then strongly typed against it.
1505
+ *
1506
+ * Usage from outside the slice (subscribe-only):
1507
+ * ```ts
1508
+ * const unsub = state.places.events.on('entries-change', (entries) => render(entries));
1509
+ * // ...
1510
+ * unsub();
1511
+ * ```
1512
+ *
1513
+ * @group Agent Toolkit
1514
+ */
1515
+ declare class StateEvents<EventMap extends Record<string, unknown>> {
1516
+ private readonly handlers;
1517
+ /**
1518
+ * Subscribe to an event. Returns an unsubscribe function that removes only this handler.
1519
+ */
1520
+ on<K extends keyof EventMap>(type: K, handler: (payload: EventMap[K]) => void): () => void;
1521
+ /** Remove all handlers for a given event type. Use the unsubscribe from `on` for single-handler removal. */
1522
+ off<K extends keyof EventMap>(type: K): void;
1523
+ /* Excluded from this release type: emit */
1524
+ /** Drop every handler for every type. Used by slice `reset()` to detach lingering listeners. */
1525
+ clear(): void;
1526
+ }
1527
+
592
1528
  /**
593
1529
  * Common interface for state slices. Implement `reset()` to participate
594
1530
  * in cleanup when `destroy()` is called on the agent.
@@ -609,6 +1545,30 @@ export declare interface StateSlice {
609
1545
  */
610
1546
  export declare const TOOL_NAMES: ToolName[];
611
1547
 
1548
+ /**
1549
+ * Build-time options passed to a {@link ToolEntryBuilder}. Lets each tool
1550
+ * tailor its description, schema, or executor to flags or other config that
1551
+ * are only known at agent-creation time.
1552
+ *
1553
+ * @group Agent Toolkit
1554
+ */
1555
+ export declare type ToolBuildOptions = {
1556
+ /** Active feature flags. */
1557
+ featureFlags?: FeatureFlags;
1558
+ };
1559
+
1560
+ /**
1561
+ * A registered tool definition: either a static {@link ToolEntry} or a
1562
+ * {@link ToolEntryBuilder} that builds one from {@link ToolBuildOptions} at
1563
+ * agent-creation time. Used wherever the tool registry, resolver, and
1564
+ * setup pipeline accept either form interchangeably.
1565
+ *
1566
+ * @typeParam S - The ToolState shape this tool requires. Default: base `ToolState`.
1567
+ *
1568
+ * @group Agent Toolkit
1569
+ */
1570
+ export declare type ToolDefinition<S extends ToolState = ToolState> = ToolEntry<S> | ToolEntryBuilder<S>;
1571
+
612
1572
  /**
613
1573
  * A tool definition — the universal format for both built-in and third-party tools.
614
1574
  * Combines execution (inputSchema + execute) with classifier metadata.
@@ -640,6 +1600,17 @@ export declare type ToolEntry<S extends ToolState = ToolState> = {
640
1600
  dependsOn?: string[];
641
1601
  };
642
1602
 
1603
+ /**
1604
+ * A builder that produces a {@link ToolEntry} from {@link ToolBuildOptions}.
1605
+ * Use this when a tool's metadata, schema, or executor depends on options
1606
+ * resolved at agent-creation time (e.g. feature flags).
1607
+ *
1608
+ * @typeParam S - The ToolState shape this tool requires. Default: base `ToolState`.
1609
+ *
1610
+ * @group Agent Toolkit
1611
+ */
1612
+ export declare type ToolEntryBuilder<S extends ToolState = ToolState> = (options: ToolBuildOptions) => ToolEntry<S>;
1613
+
643
1614
  /**
644
1615
  * Metadata for an agent toolkit tool.
645
1616
  *
@@ -691,7 +1662,7 @@ export declare type ToolNameHint = ToolName | (string & {});
691
1662
  * function createMyTool(state: ToolState) {
692
1663
  * return tool({
693
1664
  * execute: async () => {
694
- * const module = await state.places.getPlacesModule();
1665
+ * const pins = await state.places.getPinPlacesModule();
695
1666
  * state.routing.currentRoutes; // most recent routes
696
1667
  * state.baseMap.mapLibreMap; // MapLibre instance
697
1668
  * }
@@ -700,7 +1671,7 @@ export declare type ToolNameHint = ToolName | (string & {});
700
1671
  * ```
701
1672
  */
702
1673
  export declare type ToolState = {
703
- /** Place search, geocoding, and geometries state. */
1674
+ /** Places layer: PlacesModule plus an append-only search history. */
704
1675
  places: PlacesState;
705
1676
  /** Map POI layer: visibility, filtering, and category management. */
706
1677
  mapPOIs: MapPOIsState;
@@ -708,36 +1679,335 @@ export declare type ToolState = {
708
1679
  routing: RoutingState;
709
1680
  /** Base map display: style, language, viewport, layers, and hillshade. */
710
1681
  baseMap: BaseMapState;
711
- /** Traffic flow and incident overlay state. */
712
- traffic: TrafficState;
1682
+ /** Traffic tile overlays: flow tiles + incident overlay tiles. */
1683
+ trafficTiles: TrafficTilesState;
1684
+ /** Traffic area analytics: aggregation module + last result + viz config. */
1685
+ trafficAreaAnalytics: TrafficAreaAnalyticsState;
1686
+ /** Traffic incidents: fetched entry history and per-entry rendering. */
1687
+ trafficIncidents: TrafficIncidentsState;
713
1688
  /** Reachable range results: origin, budgets, and bbox summaries. */
714
1689
  ranges: RangeState;
1690
+ /** Derived/custom polygon entries — output of processGeometries. */
1691
+ customGeometries: CustomGeometriesState;
715
1692
  };
716
1693
 
717
1694
  /**
718
- * State for traffic: flow layer, incident overlay, and area analytics modules.
1695
+ * State for traffic area analytics — the lazy-init module, the last fetched
1696
+ * aggregation result, and its currently-applied visualization config. Tile
1697
+ * overlays (flow + incidents) live in {@link TrafficTilesState}.
719
1698
  *
720
1699
  * @group Agent Toolkit
721
1700
  */
722
- declare class TrafficState implements StateSlice {
1701
+ declare class TrafficAreaAnalyticsState implements StateSlice {
723
1702
  private readonly _ttMap;
724
- private _trafficFlowModule?;
725
- private _trafficIncidentsModule?;
726
1703
  private _trafficAreaAnalyticsModule?;
727
1704
  private _lastAreaAnalytics?;
728
1705
  private _currentAnalyticsConfig?;
729
1706
  private _configChangeUnsub?;
1707
+ /** Subscribe to state changes — see {@link TrafficAreaAnalyticsStateEvents}. */
1708
+ readonly events: StateEvents<TrafficAreaAnalyticsStateEvents>;
730
1709
  constructor(_ttMap: TomTomMap);
731
- getTrafficFlowModule(): Promise<TrafficFlowModule>;
732
- getTrafficIncidentsModule(): Promise<TrafficIncidentsModule>;
733
1710
  getTrafficAreaAnalyticsModule(): Promise<TrafficAreaAnalyticsModule>;
734
1711
  get trafficAreaAnalyticsModule(): TrafficAreaAnalyticsModule | undefined;
735
1712
  get lastAreaAnalytics(): TrafficAreaAnalytics | undefined;
736
1713
  setLastAreaAnalytics(result: TrafficAreaAnalytics): void;
737
1714
  /** Current visualization config, updated by module configChange events. */
738
1715
  get currentAnalyticsConfig(): TrafficAreaAnalyticsConfig | undefined;
739
- onAnalyticsShown?: (analytics: TrafficAreaAnalytics, module: TrafficAreaAnalyticsModule) => void;
740
- onAnalyticsCleared?: () => void;
1716
+ /**
1717
+ * Notify subscribers that an analytics result is now active on the map. Called from
1718
+ * tool implementations after `module.show(...)` succeeds.
1719
+ */
1720
+ notifyAnalyticsShown(analytics: TrafficAreaAnalytics, module: TrafficAreaAnalyticsModule): void;
1721
+ /** Notify subscribers that the active analytics visualization was cleared. */
1722
+ notifyAnalyticsCleared(): void;
1723
+ reset(): void;
1724
+ }
1725
+
1726
+ /**
1727
+ * Events fired by {@link TrafficAreaAnalyticsState}. Subscribe via
1728
+ * `state.trafficAreaAnalytics.events.on(type, handler)`.
1729
+ *
1730
+ * @group Agent Toolkit
1731
+ */
1732
+ declare type TrafficAreaAnalyticsStateEvents = {
1733
+ /** Last fetched analytics result and its module are now active on the map. */
1734
+ 'analytics-shown': {
1735
+ analytics: TrafficAreaAnalytics;
1736
+ module: TrafficAreaAnalyticsModule;
1737
+ };
1738
+ /** The active analytics visualization was cleared or replaced. */
1739
+ 'analytics-cleared': void;
1740
+ /** Module-level visualization config changed (mode, metric, scaleMode, ...). */
1741
+ 'config-change': TrafficAreaAnalyticsConfig | undefined;
1742
+ };
1743
+
1744
+ declare type TrafficIncidentsEntry = {
1745
+ id: string;
1746
+ timestamp: number;
1747
+ label: string;
1748
+ data: TrafficIncident[];
1749
+ /**
1750
+ * The captured query that produced this entry. Replayed by the monitor on each tick so
1751
+ * polling stays in the same slice the entry was loaded with. Reuses the loader's own
1752
+ * params shape; the entry stores a resolved {@link BBox} tuple in `bbox` (the loader
1753
+ * normalises any GeoJSON value before handing the params off).
1754
+ */
1755
+ params: TrafficIncidentDetailsByBBoxParams & {
1756
+ bbox: BBox;
1757
+ };
1758
+ /**
1759
+ * Per-entry analysis registry: owns re-executing specs *and* their results.
1760
+ * Lazy-created on first {@link TrafficIncidentsState.setAnalysisSpec} or
1761
+ * {@link TrafficIncidentsState.addAnalysisToEntry}.
1762
+ */
1763
+ _analyses?: IncidentsAnalyses;
1764
+ /**
1765
+ * Per-entry incident-details module. Each entry owns its own module so two areas can be
1766
+ * rendered on the map at the same time without sharing a single source. Lazy-initialised
1767
+ * via `TrafficIncidentsState.getEntryModule` — undefined until first show. Subscribe to
1768
+ * clicks via `module.events.on('click', ...)` once retrieved.
1769
+ */
1770
+ _module?: TrafficIncidentOverlayModule;
1771
+ /** True while this entry is currently rendered on the map. */
1772
+ _shown?: boolean;
1773
+ /**
1774
+ * Focused subset on this entry — the agent's `focusIds` write-side. Drives the dim/highlight
1775
+ * affordance on `_module` and feeds `focusedFeatures(entryId)` for stepping through.
1776
+ */
1777
+ _focus?: {
1778
+ ids: Set<string>;
1779
+ reason?: string;
1780
+ };
1781
+ /**
1782
+ * Per-entry polling loop. Lazy-created on first {@link TrafficIncidentsState.startMonitoring}
1783
+ * call; reused across stop/start cycles. Owns timer, status, error, captured area.
1784
+ */
1785
+ _monitor?: IncidentMonitor;
1786
+ /**
1787
+ * Monotonic counter incremented on every {@link TrafficIncidentsState.replaceEntryData}
1788
+ * call. Used to detect out-of-order ticks: if a slow `module.show()` from an earlier
1789
+ * tick resolves after a newer tick has already overwritten `data`, the late path bails
1790
+ * before reapplying focus / replaying analyses.
1791
+ */
1792
+ _tick?: number;
1793
+ };
1794
+
1795
+ /**
1796
+ * State for traffic incidents: entry history and map rendering via per-entry
1797
+ * TrafficIncidentOverlayModule. Each entry can be a separate area; under the default
1798
+ * `multiple` mode several entries can render at once.
1799
+ *
1800
+ * Each entry owns its own module (lazy-init on first show) plus an optional per-entry
1801
+ * background monitor. Subscribe to changes via `events.on('...')`.
1802
+ *
1803
+ * @group Agent Toolkit
1804
+ */
1805
+ declare class TrafficIncidentsState implements StateSlice {
1806
+ private readonly _ttMap;
1807
+ private _entries;
1808
+ private _entryMode;
1809
+ /** Subscribe to state changes — see {@link TrafficIncidentsStateEvents}. */
1810
+ readonly events: StateEvents<TrafficIncidentsStateEvents>;
1811
+ constructor(_ttMap: TomTomMap);
1812
+ get entries(): readonly TrafficIncidentsEntry[];
1813
+ /**
1814
+ * Display policy: `multiple` (default) keeps every history entry available to render
1815
+ * simultaneously; `single` enforces "at most one entry on the map at a time" and trims
1816
+ * the history down to the latest entry whenever it's set.
1817
+ */
1818
+ get entryMode(): EntryMode;
1819
+ /**
1820
+ * Switch the display policy. When moving from `multiple` → `single` we hide every entry
1821
+ * older than the latest, drop them from history, and emit `entries-change`. No-op when the
1822
+ * mode hasn't changed.
1823
+ */
1824
+ setEntryMode(mode: EntryMode): Promise<void>;
1825
+ /** Set of entries currently rendered on the map. */
1826
+ get shownEntryIds(): ReadonlySet<TrafficIncidentsEntry['id']>;
1827
+ /**
1828
+ * Append a new entry to the history. Under `single` mode the new entry replaces every
1829
+ * older one (and tears their modules + monitors down). `sampledAt` is the moment the
1830
+ * data was captured — pass one canonical value per "moment" so future joins (entry
1831
+ * data ↔ derived analyses) are byte-equal. Returns the assigned id.
1832
+ */
1833
+ addIncidentsEntry(data: TrafficIncident[], params: TrafficIncidentDetailsByBBoxParams & {
1834
+ bbox: BBox;
1835
+ }, label: string, sampledAt: number, explicitId?: string): string;
1836
+ /**
1837
+ * Replace an entry's data in place and repaint its module without moving the camera.
1838
+ * Used by the per-entry monitor tick so incident geometries and any open detail
1839
+ * panel stay in sync with the freshest snapshot. `sampledAt` is the canonical moment
1840
+ * for this update — propagated to the entry timestamp and to every analysis the
1841
+ * registry replays, so all derived data shares one tick. Returns true on success,
1842
+ * false when the entry is unknown.
1843
+ */
1844
+ replaceEntryData(entryId: TrafficIncidentsEntry['id'], data: TrafficIncident[], sampledAt: number): Promise<boolean>;
1845
+ /**
1846
+ * Lazy-init and return the entry's TrafficIncidentOverlayModule. Consumers subscribe to
1847
+ * clicks directly on the returned module via `module.events.on('click', ...)`; use
1848
+ * `module.distinctIncidents(features)` inside the handler if a stacked-click count is
1849
+ * needed. Throws on unknown entry id.
1850
+ */
1851
+ getEntryModule(entryId: TrafficIncidentsEntry['id']): Promise<TrafficIncidentOverlayModule>;
1852
+ /**
1853
+ * Render an entry on the map. Lazy-inits its module, pushes the entry's incidents, and
1854
+ * (when `fitBounds` is true) fits the camera to the entry's bbox. Emits `shown-change`
1855
+ * once the entry transitions to shown.
1856
+ */
1857
+ showEntry(entryId: TrafficIncidentsEntry['id'], fitBounds?: boolean): Promise<void>;
1858
+ /**
1859
+ * Hide an entry — clears its module + drops focus. The module reference stays on the
1860
+ * entry for cheap re-show. No-op when the entry is unknown or already hidden.
1861
+ */
1862
+ hideEntry(entryId: TrafficIncidentsEntry['id']): Promise<void>;
1863
+ /**
1864
+ * Drop an entry from history. Hides it from the map and stops its monitor if any.
1865
+ * Emits `entries-change` and, when the entry was shown, `shown-change`. No-op when the
1866
+ * id is unknown.
1867
+ */
1868
+ removeEntry(entryId: TrafficIncidentsEntry['id']): Promise<void>;
1869
+ /**
1870
+ * Attach an analysis result to an existing entry. Names are unique within a single
1871
+ * entry — adding one with an existing name replaces it. Returns true on success, false
1872
+ * if the entry doesn't exist. Lazy-creates the entry's {@link IncidentsAnalyses}.
1873
+ */
1874
+ addAnalysisToEntry(entryId: TrafficIncidentsEntry['id'], analysis: IncidentsAnalysis): boolean;
1875
+ /**
1876
+ * Register (or replace) a re-executing analysis spec on its source entry. Names are unique
1877
+ * per entry — adding one with an existing name replaces it. Lazy-creates the entry's
1878
+ * {@link IncidentsAnalyses} on first call.
1879
+ */
1880
+ setAnalysisSpec(spec: IncidentsAnalysisSpec): void;
1881
+ /** Remove a spec by name from every entry that carries it. */
1882
+ removeAnalysisSpec(name: string): void;
1883
+ /** Snapshot of an entry's focus subset, or null when no focus is active or the entry is unknown. */
1884
+ getFocus(entryId: TrafficIncidentsEntry['id']): IncidentFocus | null;
1885
+ /**
1886
+ * Set the focus subset on a specific entry. Filters ids against the entry's current
1887
+ * incidents (drops unknown ones) and pushes the result onto the entry's module.
1888
+ * Returns the kept count and any dropped ids.
1889
+ */
1890
+ setFocus(entryId: TrafficIncidentsEntry['id'], ids: readonly string[], reason?: string): {
1891
+ focusedCount: number;
1892
+ droppedIds: string[];
1893
+ };
1894
+ clearFocus(entryId: TrafficIncidentsEntry['id']): void;
1895
+ /**
1896
+ * The focused incidents in the order passed to {@link setFocus}. Empty when no focus is
1897
+ * active on the entry or when the entry is unknown. Used by UI layers that step through
1898
+ * the focused subset (prev/next).
1899
+ */
1900
+ focusedFeatures(entryId: TrafficIncidentsEntry['id']): TrafficIncident[];
1901
+ /**
1902
+ * Fly the camera to the i-th focused incident on an entry (0-based). Wraps out-of-range
1903
+ * indices so callers can pass raw prev/next counters. No-op when the entry has no active
1904
+ * focus. Line geometries fitBounds; points flyTo with a sensible zoom.
1905
+ */
1906
+ navigateFocus(entryId: TrafficIncidentsEntry['id'], index: number): void;
1907
+ /**
1908
+ * Walk entries newest-first to find which one currently holds an incident with this id.
1909
+ * Useful for UI layers that hold an incident id without remembering which entry produced it.
1910
+ */
1911
+ findEntryWithIncident(incidentId: string): TrafficIncidentsEntry | undefined;
1912
+ /**
1913
+ * Start polling for an entry. Lazy-creates an {@link IncidentMonitor}, wires its events
1914
+ * once, then delegates to {@link IncidentMonitor.start}. No-op when polling is already
1915
+ * running. Throws on unknown entry id.
1916
+ */
1917
+ startMonitoring(entryId: TrafficIncidentsEntry['id'], area: MonitoredArea, deps: IncidentMonitorDeps, opts?: {
1918
+ intervalMs?: number;
1919
+ }): void;
1920
+ /** Stop polling. No-op when idle or unknown. */
1921
+ stopMonitoring(entryId: TrafficIncidentsEntry['id']): void;
1922
+ /**
1923
+ * True when the entry is *currently* being polled. A monitor stuck in
1924
+ * `stopped-error` returns false here so the start tool can report a truthful
1925
+ * `alreadyRunning` and a fresh `start` call actually re-arms the timer
1926
+ * instead of silently being treated as a no-op.
1927
+ */
1928
+ isMonitored(entryId: TrafficIncidentsEntry['id']): boolean;
1929
+ private _createMonitorForEntry;
1930
+ private _requireEntry;
1931
+ private _uniqueEntryId;
1932
+ private _rerunRegisteredSpecs;
1933
+ private _teardownEntry;
1934
+ reset(): void;
1935
+ }
1936
+
1937
+ /**
1938
+ * Events fired by {@link TrafficIncidentsState}. Subscribe via
1939
+ * `state.trafficIncidents.events.on(type, handler)` — returns an unsubscribe function.
1940
+ *
1941
+ * @group Agent Toolkit
1942
+ */
1943
+ declare type TrafficIncidentsStateEvents = {
1944
+ /** History changed — entry added, replaced, removed, or cleared via `reset()`. Payload: full entries snapshot. */
1945
+ 'entries-change': readonly TrafficIncidentsEntry[];
1946
+ /** Set of entries currently rendered on the map changed. */
1947
+ 'shown-change': ReadonlySet<TrafficIncidentsEntry['id']>;
1948
+ /**
1949
+ * A fresh analysis result landed on an entry. Fires when a spec is first
1950
+ * registered, when it is replaced under the same name, AND on every monitor-tick
1951
+ * replay — the event is intentionally agnostic about whether the spec is new
1952
+ * or recomputed; subscribers re-render off the latest result either way.
1953
+ */
1954
+ 'analysis-change': {
1955
+ entryId: TrafficIncidentsEntry['id'];
1956
+ analysis: IncidentsAnalysis;
1957
+ };
1958
+ /** Focus subset for an entry changed (or cleared, when `focus === null`). */
1959
+ 'focus-change': {
1960
+ entryId: TrafficIncidentsEntry['id'];
1961
+ focus: IncidentFocus | null;
1962
+ };
1963
+ /** Display policy switched between `single` and `multiple`. */
1964
+ 'mode-change': EntryMode;
1965
+ /** A monitored entry produced a fresh snapshot (per-tick). */
1966
+ 'monitor-tick': {
1967
+ entryId: TrafficIncidentsEntry['id'];
1968
+ snap: IncidentSnapshot;
1969
+ };
1970
+ /** A monitored entry's poll failed — slice has cleared the timer. */
1971
+ 'monitor-error': {
1972
+ entryId: TrafficIncidentsEntry['id'];
1973
+ error: string;
1974
+ };
1975
+ /**
1976
+ * Polling began on an entry (idle → running). Skipped when `startMonitoring`
1977
+ * was a no-op against an already-running monitor.
1978
+ */
1979
+ 'monitor-start': {
1980
+ entryId: TrafficIncidentsEntry['id'];
1981
+ };
1982
+ /**
1983
+ * Polling halted on an entry. `reason: 'manual'` covers explicit
1984
+ * `stopMonitoring` and entry teardown (remove / single-mode trim / reset).
1985
+ * `reason: 'error'` fires alongside `monitor-error` — error-only listeners
1986
+ * keep using `monitor-error`; lifecycle listeners can rely on this alone.
1987
+ */
1988
+ 'monitor-stop': {
1989
+ entryId: TrafficIncidentsEntry['id'];
1990
+ reason: 'manual' | 'error';
1991
+ };
1992
+ };
1993
+
1994
+ /**
1995
+ * State for traffic tile overlays — flow tiles and incident overlay tiles. These are
1996
+ * raster/tile layers that the user toggles on top of the base map; result caching and
1997
+ * per-result rendering for incident *features* lives in {@link TrafficIncidentsState},
1998
+ * and analytics aggregations in {@link TrafficAreaAnalyticsState}.
1999
+ *
2000
+ * @group Agent Toolkit
2001
+ */
2002
+ declare class TrafficTilesState implements StateSlice {
2003
+ private readonly _ttMap;
2004
+ private _trafficFlowModule?;
2005
+ private _trafficIncidentsModule?;
2006
+ constructor(_ttMap: TomTomMap);
2007
+ getTrafficFlowModule(): Promise<TrafficFlowModule>;
2008
+ getTrafficIncidentsModule(): Promise<TrafficIncidentsModule>;
2009
+ get trafficFlowModule(): TrafficFlowModule | undefined;
2010
+ get trafficIncidentsModule(): TrafficIncidentsModule | undefined;
741
2011
  reset(): void;
742
2012
  }
743
2013