@dbx-tools/appkit-mastra 0.1.4 → 0.1.12

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/README.md CHANGED
@@ -195,14 +195,148 @@ intact.
195
195
  streaming-aware tools record built on top of the plugin's
196
196
  `exports().sendMessage` AsyncGenerator. Each Genie wire event
197
197
  (`FETCHING_METADATA`, `ASKING_AI`, `EXECUTING_QUERY`, attached SQL,
198
- `query_result` row counts, errors) is normalised into a `GenieProgress`
199
- payload and pushed mid-flight through Mastra's `ctx.writer`, surfacing
200
- as `tool-output` chunks the React client can render as inline status
201
- pills, SQL blocks, and row-count badges while the LLM is still waiting
202
- on the final `tool-result`. Tool ids are stable: `genie` for the
203
- default alias, `genie-<alias>` for additional aliases, and one shared
204
- `genie_get_conversation`. The LLM still receives a single clean final
205
- payload so the streaming UI doesn't leak into the assistant message.
198
+ errors) is normalised into a `GenieProgress` payload and pushed
199
+ mid-flight through Mastra's `ctx.writer`, surfacing as
200
+ `tool-output` chunks the React client can render as inline status
201
+ pills and SQL blocks while the LLM is still waiting on the final
202
+ `tool-result`. Tool ids are stable: `genie` for the default alias,
203
+ `genie-<alias>` for additional aliases, and one shared
204
+ `genie_get_conversation`.
205
+
206
+ Genie tool-result shape (LLM-bound):
207
+
208
+ ```
209
+ {
210
+ conversationId?: string, // pass back to continue the same Genie thread
211
+ genieAnswer?: string, // Genie's prose answer; pass through verbatim
212
+ datasets?: [{ // metadata only, one per executed SQL statement
213
+ chartId: string, // embed [[chart:<chartId>]] to render inline
214
+ title?: string,
215
+ description?: string,
216
+ columns: string[],
217
+ rowCount: number,
218
+ sql?: string,
219
+ }],
220
+ suggestedFollowUps?: string[], // UI shows as buttons; don't list in reply
221
+ error?: string,
222
+ }
223
+ ```
224
+
225
+ `datasets[]` is metadata only - column names, row count, the SQL
226
+ Genie ran. The actual rows ride a separate `kind: "chart"` writer
227
+ event so the LLM never has them in context (token cost stays flat
228
+ regardless of dataset size). The model references each dataset by
229
+ its `chartId` via the `[[chart:<chartId>]]` marker to display the
230
+ chart inline; see the `render_data` section below for how the
231
+ client resolves those markers.
232
+
233
+ Genie data flow:
234
+
235
+ - The writer emits `kind: "started" | "status" | "sql" |
236
+ "suggested" | "error"` events for the live loading pill. SQL
237
+ text is shown via a small Shiki-highlighted block.
238
+ - The writer **also** emits two `kind: "chart"` events per
239
+ executed SQL statement, sharing the same `chartId`: the first
240
+ carries `{chartId, title, description?, data}` (rows converted
241
+ to objects keyed by column name with best-effort numeric
242
+ coercion); the second, when the chart-planner agent finishes,
243
+ carries `{chartId, option}` (the resolved Echarts spec). The
244
+ chat client's `<ChartSlot>` merges them by `chartId` and
245
+ renders inline at the matching `[[chart:<chartId>]]` marker.
246
+ This is the exact same wire format as the `render_data` tool,
247
+ so Genie and hand-built charts are indistinguishable on the
248
+ client.
249
+ - After a hard reload, `synthesizeToolEventsFromHistory` rebuilds
250
+ `suggested` events from the persisted tool-result; the SQL pill
251
+ and chart events are live-only and don't replay.
252
+
253
+ ### `render_data` (system-default ambient tool)
254
+
255
+ `buildAgents` registers a system-level `render_data` tool on
256
+ every agent so the model can submit any tabular dataset for
257
+ inline charting. Users can shadow it by including a same-named
258
+ tool in `config.tools` or in a per-agent `tools` map; otherwise
259
+ it's just there.
260
+
261
+ The tool is generic - not coupled to Genie or any particular
262
+ upstream. Input is `{ title, description?, data: Row[] }` where
263
+ `data` is an array of objects keyed by column name (a SQL row
264
+ set, an API response, a hand-built array, etc.).
265
+
266
+ #### How it works (shared pipeline with Genie)
267
+
268
+ Both `render_data` and Genie's `drainGenieStream` route through
269
+ one helper, `emitChartWithPlanning`, so the wire format and
270
+ trace shape are consistent everywhere:
271
+
272
+ 1. Mint a short `chartId` (8 hex chars).
273
+ 2. Push a `{ kind: "chart", chartId, title, description?, data }`
274
+ event to `ctx.writer` immediately. The chat client mounts a
275
+ `<ChartSlot>` showing a "Rendering chart" skeleton.
276
+ 3. Kick off the chart-planner agent
277
+ (`modelForTier(ModelTier.Fast)`) with the dataset in the
278
+ background. The agent's `structuredOutput` schema is a compact
279
+ "chart plan" (chart type + axis labels + categories +
280
+ series); the helper expands the plan into an Echarts
281
+ `EChartsOption` JSON.
282
+ 4. When the planner resolves, push a follow-up
283
+ `{ kind: "chart", chartId, option }` event. The client's
284
+ `<ChartSlot>` merges it with the dataset event (last write
285
+ wins per field) and swaps in `<ReactECharts>`.
286
+ 5. If the planner fails, no follow-up event fires. Once the
287
+ parent tool finishes, the slot transitions to a "couldn't
288
+ render chart" fallback frame.
289
+
290
+ The parent tool (`render_data` or Genie) `await`s the planner
291
+ promise(s) before its `execute` returns, so chart latency shows
292
+ up under the tool's trace span. The dataset event fires
293
+ **immediately**, though, so the calling LLM gets back the
294
+ `chartId` synchronously and the chat client has a layout slot
295
+ ready before the planner resolves.
296
+
297
+ The LLM-bound payload is just `{ chartId }` (for `render_data`)
298
+ or `datasets[]` metadata (for Genie); row data and option JSON
299
+ never reach the LLM, so token cost stays flat regardless of
300
+ dataset size.
301
+
302
+ #### Inline placement contract
303
+
304
+ The model embeds `[[chart:<chartId>]]` on its own line in its
305
+ markdown reply at the position where the chart should appear:
306
+
307
+ ```markdown
308
+ ## Audit Score
309
+
310
+ Audit Score is stable at ~94%, hovering between 93.5 and 95.0.
311
+
312
+ [[chart:a3f9c1d2]]
313
+
314
+ ## Service Time
315
+
316
+ Service time is the outlier at 162.5s, up from a target of 150s.
317
+
318
+ [[chart:b7e2d4f1]]
319
+ ```
320
+
321
+ The chat client splits the assistant text on these markers and
322
+ drops a `<ChartSlot>` in at each spot. A marker that arrives
323
+ before its `chart` event (rare; only during fast streaming) shows
324
+ a "Queueing chart" skeleton; a chart whose marker the model
325
+ forgot to place falls through to the end of the reply as a
326
+ fallback. All three states (queueing, rendering, rendered) share
327
+ the same fixed-height frame so the layout doesn't jump as
328
+ charts resolve.
329
+
330
+ #### Trade-offs
331
+
332
+ - Both events ride the writer, not the persisted tool-result, so
333
+ charts don't re-render after a hard reload. The model can call
334
+ `render_data` again (or re-ask Genie) on the next turn if the
335
+ user wants the chart back.
336
+ - The chart-planner is a separate model call per dataset (fast
337
+ tier, but still ~1-3s each). For an N-chart turn, latency is
338
+ `Genie + max(planners)` since the planners run concurrently
339
+ with the rest of Genie's stream and with each other.
206
340
 
207
341
  Plugins that aren't registered (or don't implement the toolkit
208
342
  interface) resolve to `undefined` at runtime, so guard with `?.` /
@@ -256,8 +390,9 @@ const saveDoc = createTool({
256
390
  ```
257
391
 
258
392
  When `tool()`'s `id` is omitted it's auto-derived from a slugified
259
- description plus a 6-char SHA-1 prefix - stable across runs so traces
260
- stay readable. Pass an explicit `id` when you want to pin one.
393
+ description plus a 6-char FNV-1a base-32 suffix - stable across runs
394
+ so traces stay readable. Pass an explicit `id` when you want to pin
395
+ one.
261
396
 
262
397
  Reach for `createTool` when you need Mastra-only fields (`outputSchema`,
263
398
  `suspendSchema`, `requireApproval`, `mcp`, etc.).
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ export * from "./src/plugin.js";
13
13
  export * from "@dbx-tools/appkit-mastra-shared";
14
14
  export * from "./src/config.js";
15
15
  export * from "./src/agents.js";
16
+ export * from "./src/chart.js";
16
17
  export * from "./src/genie.js";
18
+ export * from "./src/tools/email.js";
17
19
  export { clearServingEndpointsCache, extractModelOverride, listServingEndpoints, MASTRA_MODEL_OVERRIDE_KEY, MODEL_OVERRIDE_BODY_FIELDS, MODEL_OVERRIDE_HEADER, MODEL_OVERRIDE_QUERY, resolveModelId, type ResolvedModel, type ResolveModelOptions, type ServingEndpointSummary, } from "./src/serving.js";
18
20
  export { FALLBACK_MODEL_IDS, MODEL_CATALOG, modelForTier, modelsForTier, ModelTier, } from "./src/model.js";
package/dist/index.js CHANGED
@@ -13,6 +13,8 @@ export * from "./src/plugin.js";
13
13
  export * from "@dbx-tools/appkit-mastra-shared";
14
14
  export * from "./src/config.js";
15
15
  export * from "./src/agents.js";
16
+ export * from "./src/chart.js";
16
17
  export * from "./src/genie.js";
18
+ export * from "./src/tools/email.js";
17
19
  export { clearServingEndpointsCache, extractModelOverride, listServingEndpoints, MASTRA_MODEL_OVERRIDE_KEY, MODEL_OVERRIDE_BODY_FIELDS, MODEL_OVERRIDE_HEADER, MODEL_OVERRIDE_QUERY, resolveModelId, } from "./src/serving.js";
18
20
  export { FALLBACK_MODEL_IDS, MODEL_CATALOG, modelForTier, modelsForTier, ModelTier, } from "./src/model.js";
@@ -284,7 +284,7 @@ export declare const FALLBACK_AGENT_ID = "default";
284
284
  * Override globally via {@link MastraPluginConfig.styleInstructions}
285
285
  * (pass `false` to disable entirely, or a string to replace).
286
286
  */
287
- export declare const DEFAULT_STYLE_INSTRUCTIONS = "Output style:\n\n- Plain prose. Use hyphens (-) only. Never use em dashes (\u2014) or en dashes (\u2013).\n- Never use emojis.\n- Skip openers like \"Great question\", \"Absolutely\", \"I'd be happy to help\".\n- Skip closers like \"Let me know if you have any questions\".\n- Skip self-disclaimers (\"I should mention\", \"It's important to note\").\n- Answer directly. No preamble before the actual answer.\n- Use lists and headers only when they clarify a multi-part answer; not for short replies.\n- Quote numbers, code, identifiers, and tool output verbatim. Never paraphrase them.";
287
+ export declare const DEFAULT_STYLE_INSTRUCTIONS: string;
288
288
  /**
289
289
  * Resolve every entry in `config.agents` into a Mastra `Agent`
290
290
  * instance. When `config.agents` is omitted the plugin registers a
@@ -16,8 +16,10 @@ import { genie } from "@databricks/appkit";
16
16
  import { logUtils, pluginUtils, stringUtils } from "@dbx-tools/appkit-shared";
17
17
  import { Agent } from "@mastra/core/agent";
18
18
  import { createTool } from "@mastra/core/tools";
19
+ import { buildRenderDataTool } from "./chart.js";
19
20
  import { buildGenieProvider } from "./genie.js";
20
21
  import { buildModel } from "./model.js";
22
+ import { stripStaleChartsProcessor } from "./processors/strip-stale-charts.js";
21
23
  /** Re-export of Mastra's native `createTool` for full-feature access. */
22
24
  export { createTool } from "@mastra/core/tools";
23
25
  /**
@@ -63,8 +65,8 @@ export function tool(opts) {
63
65
  /**
64
66
  * Build a deterministic Mastra tool id from a description.
65
67
  * Delegates to {@link stringUtils.toUniqueSlug}: slug + always-on
66
- * SHA-1 suffix so two tools with the same leading words don't
67
- * collide in traces. Stable across runs.
68
+ * 6-char FNV-1a base-32 suffix so two tools with the same leading
69
+ * words don't collide in traces. Stable across runs.
68
70
  */
69
71
  function deriveToolId(description) {
70
72
  return stringUtils.toUniqueSlug(description, { fallbackPrefix: "tool" });
@@ -110,16 +112,21 @@ Rules:
110
112
  * Override globally via {@link MastraPluginConfig.styleInstructions}
111
113
  * (pass `false` to disable entirely, or a string to replace).
112
114
  */
113
- export const DEFAULT_STYLE_INSTRUCTIONS = `Output style:
114
-
115
- - Plain prose. Use hyphens (-) only. Never use em dashes (—) or en dashes (–).
116
- - Never use emojis.
117
- - Skip openers like "Great question", "Absolutely", "I'd be happy to help".
118
- - Skip closers like "Let me know if you have any questions".
119
- - Skip self-disclaimers ("I should mention", "It's important to note").
120
- - Answer directly. No preamble before the actual answer.
121
- - Use lists and headers only when they clarify a multi-part answer; not for short replies.
122
- - Quote numbers, code, identifiers, and tool output verbatim. Never paraphrase them.`;
115
+ export const DEFAULT_STYLE_INSTRUCTIONS = [
116
+ "Output style:",
117
+ "",
118
+ "Use markdown formatting, including headings, lists, and code blocks.",
119
+ "Avoid lists and headers for short replies.",
120
+ "Plain prose.",
121
+ "Use hyphens (-) only. Never use em dashes or en dashes.",
122
+ "Never use emojis.",
123
+ "Skip openers like 'Great question', 'Absolutely', and 'I'd be happy to help'.",
124
+ "Skip closers like 'Let me know if you have any questions'.",
125
+ "Skip self-disclaimers like 'I should mention' and 'It's important to note'.",
126
+ "Answer directly.",
127
+ "Do not include a preamble before the actual answer.",
128
+ "Use lists and headers only when they clarify a multi-part answer.",
129
+ ].join("\n");
123
130
  /**
124
131
  * Resolve the style block to append to every agent's instructions.
125
132
  * Returns `null` when the caller opted out (`styleInstructions: false`).
@@ -159,9 +166,22 @@ export async function buildAgents(opts) {
159
166
  const definitions = resolveDefinitions(config);
160
167
  const ids = Object.keys(definitions);
161
168
  const defaultAgentId = config.defaultAgent ?? ids[0] ?? FALLBACK_AGENT_ID;
162
- const plugins = buildPluginsMap(context);
163
- const ambientTools = config.tools ?? {};
169
+ const plugins = buildPluginsMap(config, context);
170
+ // System-default ambient tools every agent gets out of the box.
171
+ // Currently just `render_data` for inline visualizations; the
172
+ // user can shadow it by including a same-named tool in their own
173
+ // `config.tools` or per-agent `tools`. Order in {@link resolveTools}
174
+ // is `system -> user-ambient -> per-agent`, last write wins.
175
+ const systemTools = {
176
+ render_data: buildRenderDataTool(config),
177
+ };
178
+ const ambientTools = { ...systemTools, ...(config.tools ?? {}) };
164
179
  const style = resolveStyleInstructions(config);
180
+ // Default-on protection against the model copying turn-scoped
181
+ // chartIds from prior assistant tool results into the new
182
+ // turn's `[[chart:<id>]]` markers. Opt out per-plugin via
183
+ // `config.stripStaleCharts: false`.
184
+ const inputProcessors = config.stripStaleCharts === false ? [] : [stripStaleChartsProcessor];
165
185
  const agents = {};
166
186
  for (const [id, def] of Object.entries(definitions)) {
167
187
  const tools = await resolveTools(def.tools, plugins, ambientTools);
@@ -174,6 +194,7 @@ export async function buildAgents(opts) {
174
194
  model: resolveModel(config, def.model),
175
195
  tools,
176
196
  ...(memory ? { memory } : {}),
197
+ ...(inputProcessors.length > 0 ? { inputProcessors } : {}),
177
198
  });
178
199
  }
179
200
  if (!agents[defaultAgentId]) {
@@ -304,7 +325,7 @@ async function resolveTools(defTools, plugins, ambientTools) {
304
325
  * Mastra `ctx.writer`, so the UI gets `tool-output` chunks in real
305
326
  * time instead of staring at a spinner for the full Genie round-trip.
306
327
  */
307
- function buildPluginsMap(context) {
328
+ function buildPluginsMap(config, context) {
308
329
  const cache = new Map();
309
330
  return new Proxy({}, {
310
331
  get(_target, propName) {
@@ -312,7 +333,7 @@ function buildPluginsMap(context) {
312
333
  return undefined;
313
334
  if (cache.has(propName))
314
335
  return cache.get(propName) ?? undefined;
315
- const provider = resolveProvider(context, propName);
336
+ const provider = resolveProvider(config, context, propName);
316
337
  cache.set(propName, provider);
317
338
  return provider ?? undefined;
318
339
  },
@@ -322,14 +343,17 @@ function buildPluginsMap(context) {
322
343
  * Pick the right {@link MastraPluginToolkitProvider} for a sibling
323
344
  * plugin lookup. Returns the streaming-aware Genie adapter when the
324
345
  * caller asks for `genie`; falls back to the generic AppKit
325
- * `ToolProvider` adapter for every other plugin name.
346
+ * `ToolProvider` adapter for every other plugin name. `config` is
347
+ * threaded through so Genie's tool can run the chart planner
348
+ * inline against the same model resolver / fallback ladder the
349
+ * agents use.
326
350
  */
327
- function resolveProvider(context, propName) {
351
+ function resolveProvider(config, context, propName) {
328
352
  if (propName === "genie") {
329
353
  const geniePlugin = pluginUtils.instance(context, genie);
330
354
  if (!geniePlugin)
331
355
  return null;
332
- return buildGenieProvider(geniePlugin);
356
+ return buildGenieProvider(geniePlugin, { config });
333
357
  }
334
358
  const plugin = context?.getPlugins().get(propName);
335
359
  return adaptPluginToolkit(plugin);
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Chart-rendering primitives.
3
+ *
4
+ * Three surfaces, one shared brain:
5
+ *
6
+ * - {@link buildRenderDataTool}: a Mastra tool the model calls
7
+ * ("here is a dataset, render it as a chart"). The tool's
8
+ * `execute` emits a `kind: "chart"` event with the raw rows to
9
+ * `ctx.writer` synchronously, kicks off the chart-planner agent,
10
+ * and `await`s the planner promise before returning so the
11
+ * planner's latency is attributed to this tool's trace span.
12
+ * The LLM-bound output is just `{ chartId }`, so its context
13
+ * stays flat regardless of dataset size.
14
+ *
15
+ * - {@link emitChartWithPlanning}: the underlying helper that both
16
+ * `render_data` and Genie's `drainGenieStream` call. Mints the
17
+ * `chartId`, fires the dataset event immediately, runs the
18
+ * planner in the background, and returns `{ chartId,
19
+ * plannerPromise }` so callers can choose to await for trace
20
+ * shape or fire-and-forget.
21
+ *
22
+ * - {@link runChartPlanner}: the chart-planner Agent + ECOption
23
+ * expansion as a plain async function. Used internally by
24
+ * {@link emitChartWithPlanning}; producers shouldn't reach for
25
+ * it directly so chart events keep a single wire-format
26
+ * contract.
27
+ *
28
+ * The model wires the chart into its reply by emitting the marker
29
+ * `[[chart:<chartId>]]` on its own line in markdown. The chat
30
+ * client splits the assistant text on these markers and drops a
31
+ * `<ChartSlot>` in at the position the model placed it; the slot
32
+ * shows a skeleton until the second `kind: "chart"` event (with
33
+ * the resolved `EChartsOption`) arrives, then swaps in the
34
+ * rendered Echarts visualisation.
35
+ */
36
+ import type { RequestContext } from "@mastra/core/request-context";
37
+ import { z } from "zod";
38
+ import type { MastraPluginConfig } from "./config.js";
39
+ /**
40
+ * Compact, model-friendly representation of an Echarts spec. The
41
+ * planner agent emits this; {@link planToEchartsOption} expands it
42
+ * into a real `EChartsOption` JSON. Two layers because letting the
43
+ * model fill in a fully-typed `EChartsOption` is brittle (hundreds
44
+ * of optional fields, deep unions, version-dependent shapes). A
45
+ * small "chart plan" schema is much more reliable for a fast model
46
+ * and keeps animation / tooltip / styling defaults consistent
47
+ * across charts.
48
+ */
49
+ declare const chartPlanSchema: z.ZodObject<{
50
+ chartType: z.ZodEnum<{
51
+ bar: "bar";
52
+ line: "line";
53
+ area: "area";
54
+ scatter: "scatter";
55
+ pie: "pie";
56
+ }>;
57
+ title: z.ZodOptional<z.ZodString>;
58
+ xAxisLabel: z.ZodOptional<z.ZodString>;
59
+ yAxisLabel: z.ZodOptional<z.ZodString>;
60
+ categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
+ series: z.ZodArray<z.ZodObject<{
62
+ name: z.ZodString;
63
+ data: z.ZodArray<z.ZodUnion<readonly [z.ZodNumber, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, z.ZodObject<{
64
+ name: z.ZodString;
65
+ value: z.ZodNumber;
66
+ }, z.core.$strip>]>>;
67
+ }, z.core.$strip>>;
68
+ }, z.core.$strip>;
69
+ type ChartPlan = z.infer<typeof chartPlanSchema>;
70
+ /** Inputs to {@link runChartPlanner}. */
71
+ export interface RunChartPlannerOptions {
72
+ config: MastraPluginConfig;
73
+ requestContext?: RequestContext;
74
+ title: string;
75
+ description?: string;
76
+ data: ReadonlyArray<Record<string, unknown>>;
77
+ }
78
+ /** Output of {@link runChartPlanner}: a fully-formed Echarts spec. */
79
+ export interface RunChartPlannerResult {
80
+ option: Record<string, unknown>;
81
+ chartType: ChartPlan["chartType"];
82
+ }
83
+ /**
84
+ * Run the chart planner against the given dataset and return a
85
+ * full Echarts `EChartsOption` JSON. Used by
86
+ * {@link emitChartWithPlanning}; tools and producers shouldn't
87
+ * call this directly (use the helper instead so chart events
88
+ * follow the same wire-format contract everywhere).
89
+ */
90
+ export declare function runChartPlanner(opts: RunChartPlannerOptions): Promise<RunChartPlannerResult>;
91
+ /**
92
+ * Minimal `ToolStream`-shaped writer surface. Defined locally so
93
+ * helpers can take any object with a `.write` method without
94
+ * importing Mastra's full `ToolStream` (which would also drag in
95
+ * agent / tool types this module doesn't otherwise need).
96
+ */
97
+ interface MinimalWriter {
98
+ write: (chunk: unknown) => unknown;
99
+ }
100
+ /** Inputs to {@link emitChartWithPlanning}. */
101
+ export interface EmitChartWithPlanningOptions {
102
+ /** Mastra `ctx.writer`; missing or closed writers are tolerated. */
103
+ writer?: MinimalWriter;
104
+ /** Plugin config; used to resolve the planner's model. */
105
+ config: MastraPluginConfig;
106
+ /** Per-request context (OBO auth). */
107
+ requestContext?: RequestContext;
108
+ /** Title shown above the rendered chart. Required. */
109
+ title: string;
110
+ /** Optional one-line intent biasing the planner. */
111
+ description?: string;
112
+ /** Tabular dataset to chart (one object per row). */
113
+ data: ReadonlyArray<Record<string, unknown>>;
114
+ }
115
+ /** Output of {@link emitChartWithPlanning}. */
116
+ export interface EmitChartWithPlanningResult {
117
+ /** Short id matching the marker `[[chart:<chartId>]]`. */
118
+ chartId: string;
119
+ /**
120
+ * Promise that resolves once the planner has finished and the
121
+ * `kind: "chart"` event with the option has been emitted (or
122
+ * once the planner has failed silently). Callers that want
123
+ * trace observability should `await` this before returning
124
+ * from their tool's `execute`; callers that want pure
125
+ * fire-and-forget can ignore it.
126
+ */
127
+ plannerPromise: Promise<void>;
128
+ }
129
+ /**
130
+ * Shared chart-emission primitive used by both the `render_data`
131
+ * tool and Genie's `drainGenieStream`. Keeps both producers on
132
+ * one wire-format contract so the chat client only ever has to
133
+ * understand a single chart event shape.
134
+ *
135
+ * Behaviour:
136
+ *
137
+ * 1. Generates a short `chartId` (8 hex chars).
138
+ * 2. Immediately emits `{ kind: "chart", chartId, title,
139
+ * description?, data }` via the writer so the chat client can
140
+ * mount its `<ChartSlot>` with the rows in hand.
141
+ * 3. Kicks off the chart-planner agent in the background. On
142
+ * success, emits a second `{ kind: "chart", chartId, option }`
143
+ * event - same `chartId`, just the spec - so the client merges
144
+ * the two into one rendered chart. On failure, no follow-up
145
+ * event fires; the client falls back to whatever it can do
146
+ * with the dataset alone (typically a "render failed" frame
147
+ * after the parent tool finishes).
148
+ *
149
+ * Returns `chartId` synchronously so the caller can include it in
150
+ * the tool result (model uses it in `[[chart:<chartId>]]`
151
+ * markers), and `plannerPromise` so the caller can choose
152
+ * trace-spanning vs. snappy-return semantics.
153
+ */
154
+ export declare function emitChartWithPlanning(opts: EmitChartWithPlanningOptions): Promise<EmitChartWithPlanningResult>;
155
+ /**
156
+ * Build the `render_data` tool bound to the given plugin config.
157
+ *
158
+ * The tool is a thin wrapper around {@link emitChartWithPlanning}:
159
+ * a single `kind: "chart"` writer event ships the raw rows to
160
+ * the client immediately, the chart-planner agent runs alongside
161
+ * (so the calling LLM stays unblocked while the planner thinks),
162
+ * and a follow-up `kind: "chart"` event with the resolved
163
+ * `EChartsOption` lands when it's ready. The tool's `execute`
164
+ * awaits the planner promise so the planner work shows up under
165
+ * the tool's trace span; the LLM still gets back just
166
+ * `{ chartId }`, so its context stays small regardless of dataset
167
+ * size.
168
+ */
169
+ export declare function buildRenderDataTool(config: MastraPluginConfig): import("@mastra/core/tools").Tool<any, any, any, any, import("@mastra/core/tools").ToolExecutionContext<any, any, unknown>, "render_data", unknown>;
170
+ export {};