@dbx-tools/appkit-mastra 0.1.12 → 0.1.18

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
@@ -223,7 +223,7 @@ Genie tool-result shape (LLM-bound):
223
223
  ```
224
224
 
225
225
  `datasets[]` is metadata only - column names, row count, the SQL
226
- Genie ran. The actual rows ride a separate `kind: "chart"` writer
226
+ Genie ran. The actual rows ride a separate `type: "chart"` writer
227
227
  event so the LLM never has them in context (token cost stays flat
228
228
  regardless of dataset size). The model references each dataset by
229
229
  its `chartId` via the `[[chart:<chartId>]]` marker to display the
@@ -232,10 +232,10 @@ client resolves those markers.
232
232
 
233
233
  Genie data flow:
234
234
 
235
- - The writer emits `kind: "started" | "status" | "sql" |
235
+ - The writer emits `type: "started" | "status" | "sql" |
236
236
  "suggested" | "error"` events for the live loading pill. SQL
237
237
  text is shown via a small Shiki-highlighted block.
238
- - The writer **also** emits two `kind: "chart"` events per
238
+ - The writer **also** emits two `type: "chart"` events per
239
239
  executed SQL statement, sharing the same `chartId`: the first
240
240
  carries `{chartId, title, description?, data}` (rows converted
241
241
  to objects keyed by column name with best-effort numeric
@@ -263,41 +263,36 @@ upstream. Input is `{ title, description?, data: Row[] }` where
263
263
  `data` is an array of objects keyed by column name (a SQL row
264
264
  set, an API response, a hand-built array, etc.).
265
265
 
266
- #### How it works (shared pipeline with Genie)
266
+ #### How it works
267
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:
268
+ `render_data` is a thin wrapper around `runChartPlanner`, a
269
+ pure async function that takes a dataset and returns a fully-
270
+ resolved Echarts `EChartsOption`. No id-then-promise dance, no
271
+ background work: the planner runs inline, and the tool emits a
272
+ single chart event with everything attached.
271
273
 
272
274
  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.
275
+ 2. `await runChartPlanner({ title, description?, data })`. The
276
+ planner is its own Mastra `Agent` (`modelForTier(ModelTier.Fast)`)
277
+ whose `structuredOutput` schema is a compact "chart plan"
278
+ (chart type + axis labels + categories + series); the helper
279
+ expands the plan into an `EChartsOption` JSON.
280
+ 3. Push one `{ type: "chart", chartId, title, description?, data,
281
+ option }` event to `ctx.writer`. The chat client's
282
+ `<ChartSlot>` mounts straight to the rendered Echarts
283
+ visualisation - no skeleton frame, because the option arrives
284
+ in the same event as the dataset.
285
+ 4. If the planner throws, the tool emits a `type: "error"` writer
286
+ event instead. The slot transitions to a "couldn't render
287
+ chart" fallback frame.
288
+
289
+ Planner latency lands under the tool's trace span (it's
290
+ `await`ed directly), and the LLM-bound payload is just
291
+ `{ chartId }` so the model's context stays flat regardless of
292
+ dataset size. The Genie agent reuses `runChartPlanner` the
293
+ same way to embed charts in its structured summary, so the
294
+ backbone is shared without coupling the two paths to a
295
+ fire-and-forget writer contract.
301
296
 
302
297
  #### Inline placement contract
303
298
 
@@ -546,10 +541,10 @@ curl -s http://localhost:8000/api/mastra/models | jq
546
541
  Same payload from a sibling plugin or script (no HTTP round-trip):
547
542
 
548
543
  ```ts
549
- import { pluginUtils } from "@dbx-tools/appkit-shared";
544
+ import { appkitUtils } from "@dbx-tools/shared";
550
545
  import { mastra } from "@dbx-tools/appkit-mastra";
551
546
 
552
- const m = pluginUtils.require(this.context, mastra).exports();
547
+ const m = appkitUtils.require(this.context, mastra).exports();
553
548
  const endpoints = await m.asUser(req).listModels(); // user-scoped
554
549
  m.clearModelsCache(); // force the next call to re-fetch
555
550
  ```
@@ -582,10 +577,10 @@ the moment the `lakebase` plugin is registered. Bare `mastra()` next to
582
577
  zero extra config required.
583
578
 
584
579
 
585
- | Knob | Default when `lakebase()` is registered | What it backs |
586
- | --------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
587
- | `storage` | **Per-agent** `PostgresStore` namespaced by `schemaName: "mastra_<agentId>"` so threads + messages stay isolated. | Mastra threads, messages, working memory. |
588
- | `memory` | **Shared singleton** `PgVector` across every agent (cross-agent semantic recall on one index). | RAG-style recall over past messages via FastEmbed vectors. |
580
+ | Knob | Default when `lakebase()` is registered | What it backs |
581
+ | --------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
582
+ | `storage` | **Per-agent** `PostgresStore` namespaced by `schemaName: "mastra_<agentId>"` so threads + messages stay isolated, plus a Mastra-instance-level `PostgresStore` in schema `mastra_instance`. | Mastra threads, messages, working memory; Mastra-instance-level workflow snapshots (`requireApproval` / `agent.resumeStream()`). |
583
+ | `memory` | **Shared singleton** `PgVector` across every agent (cross-agent semantic recall on one index). | RAG-style recall over past messages via FastEmbed vectors. |
589
584
 
590
585
 
591
586
  Override either at the plugin level, the agent level, or both. The agent
@@ -635,8 +630,15 @@ mastra({
635
630
  Notes:
636
631
 
637
632
  - `PostgresStore` runs `CREATE SCHEMA IF NOT EXISTS` on `init()`, so
638
- per-agent schemas spring into existence the first time an agent saves
639
- a message. No bundle / migration step required.
633
+ per-agent schemas (and the shared `mastra_instance` schema) spring
634
+ into existence the first time the agent saves anything. No bundle /
635
+ migration step required.
636
+ - The `mastra_instance` schema exists so that Mastra-instance-level
637
+ artifacts (workflow snapshots used by `agent.resumeStream()`,
638
+ `requireApproval` flows, etc.) live in their own namespace and never
639
+ collide with per-agent thread / message tables. Disable the instance
640
+ store by setting `storage: false` at plugin level - approval-gated
641
+ tool calls will then error on resume.
640
642
  - Disabling `lakebase()` from your plugin list while leaving `storage` /
641
643
  `memory` truthy fails fast at setup with a clear "lakebase plugin not
642
644
  registered" error.
@@ -651,10 +653,10 @@ Other plugins / route handlers can introspect the registry via the
651
653
  `exports()` surface, modeled on AppKit's:
652
654
 
653
655
  ```ts
654
- import { pluginUtils } from "@dbx-tools/appkit-shared";
656
+ import { appkitUtils } from "@dbx-tools/shared";
655
657
  import { mastra } from "@dbx-tools/appkit-mastra";
656
658
 
657
- const m = pluginUtils.require(this.context, mastra).exports();
659
+ const m = appkitUtils.require(this.context, mastra).exports();
658
660
  m.list(); // ["analyst", "helper"]
659
661
  m.get("analyst"); // Agent | null
660
662
  m.getDefault(); // Agent | null
@@ -12,7 +12,7 @@
12
12
  * built-in analyst so the bare `mastra()` call still mounts a working
13
13
  * `chatRoute` agent for demos.
14
14
  */
15
- import { logUtils, pluginUtils } from "@dbx-tools/appkit-shared";
15
+ import { appkitUtils, logUtils } from "@dbx-tools/shared";
16
16
  import { Agent } from "@mastra/core/agent";
17
17
  import type { AgentConfig, ToolsInput } from "@mastra/core/agent";
18
18
  import type { Tool } from "@mastra/core/tools";
@@ -300,7 +300,7 @@ export declare const DEFAULT_STYLE_INSTRUCTIONS: string;
300
300
  */
301
301
  export declare function buildAgents(opts: {
302
302
  config: MastraPluginConfig;
303
- context: pluginUtils.PluginContextLike | undefined;
303
+ context: appkitUtils.PluginContextLike | undefined;
304
304
  memoryBuilder?: MemoryBuilder;
305
305
  log: logUtils.Logger;
306
306
  }): Promise<BuiltAgents>;
@@ -12,13 +12,12 @@
12
12
  * built-in analyst so the bare `mastra()` call still mounts a working
13
13
  * `chatRoute` agent for demos.
14
14
  */
15
- import { genie } from "@databricks/appkit";
16
- import { logUtils, pluginUtils, stringUtils } from "@dbx-tools/appkit-shared";
15
+ import { appkitUtils, logUtils, stringUtils } from "@dbx-tools/shared";
17
16
  import { Agent } from "@mastra/core/agent";
18
17
  import { createTool } from "@mastra/core/tools";
19
18
  import { buildRenderDataTool } from "./chart.js";
20
- import { buildGenieProvider } from "./genie.js";
21
- import { buildModel } from "./model.js";
19
+ import { buildGenieToolkitProvider, resolveGenieSpaces } from "./genie.js";
20
+ import { buildModel, FALLBACK_MODEL_IDS } from "./model.js";
22
21
  import { stripStaleChartsProcessor } from "./processors/strip-stale-charts.js";
23
22
  /** Re-export of Mastra's native `createTool` for full-feature access. */
24
23
  export { createTool } from "@mastra/core/tools";
@@ -196,13 +195,52 @@ export async function buildAgents(opts) {
196
195
  ...(memory ? { memory } : {}),
197
196
  ...(inputProcessors.length > 0 ? { inputProcessors } : {}),
198
197
  });
198
+ // Surface the effective default model per agent so operators can
199
+ // see at a glance which endpoint each agent points at without
200
+ // having to fire a request and inspect a trace. The value is the
201
+ // *static* default; per-request overrides (header / query /
202
+ // body) and the workspace-catalogue fuzzy match still apply at
203
+ // call time.
204
+ log.info("agent registered", {
205
+ id,
206
+ name: def.name ?? id,
207
+ defaultModel: describeAgentDefaultModel(config, def),
208
+ tools: Object.keys(tools),
209
+ });
199
210
  }
200
211
  if (!agents[defaultAgentId]) {
201
212
  throw new Error(`mastra: defaultAgent "${defaultAgentId}" not found in registered agents (${ids.join(", ") || "none"})`);
202
213
  }
203
- log.info("agents registered", { ids, defaultAgentId });
214
+ log.info("agents ready", { ids, defaultAgentId });
204
215
  return { agents, defaultAgentId };
205
216
  }
217
+ /**
218
+ * Best-effort description of the *static* default model an agent will
219
+ * resolve to at call time. Walks the same precedence ladder as
220
+ * {@link resolveModel} / {@link buildModel}:
221
+ *
222
+ * 1. Per-agent `def.model` (string sugar -> the literal id;
223
+ * function / `DynamicArgument` -> `"<dynamic>"` because the
224
+ * resolver decides at call time).
225
+ * 2. Plugin-level `config.defaultModel` (same rules).
226
+ * 3. `DATABRICKS_SERVING_ENDPOINT_NAME` env var.
227
+ * 4. First entry of `config.defaultModelFallbacks ?? FALLBACK_MODEL_IDS`.
228
+ *
229
+ * Used for the startup `agent registered` log so operators can see
230
+ * which endpoint each agent points at by default. Per-request
231
+ * overrides (`X-Mastra-Model` etc.) and the workspace-catalogue
232
+ * fuzzy match are still applied at runtime.
233
+ */
234
+ function describeAgentDefaultModel(config, def) {
235
+ const effective = def.model ?? config.defaultModel;
236
+ if (typeof effective === "string")
237
+ return effective;
238
+ if (effective !== undefined)
239
+ return "<dynamic>";
240
+ return (process.env.DATABRICKS_SERVING_ENDPOINT_NAME ??
241
+ config.defaultModelFallbacks?.[0] ??
242
+ FALLBACK_MODEL_IDS[0]);
243
+ }
206
244
  /**
207
245
  * Normalize `config.agents` into a `Record<id, definition>`. Accepts
208
246
  * any of the three shapes documented on
@@ -341,19 +379,33 @@ function buildPluginsMap(config, context) {
341
379
  }
342
380
  /**
343
381
  * Pick the right {@link MastraPluginToolkitProvider} for a sibling
344
- * plugin lookup. Returns the streaming-aware Genie adapter when the
345
- * caller asks for `genie`; falls back to the generic AppKit
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.
382
+ * plugin lookup. Returns the Genie agent-backed adapter when
383
+ * the caller asks for `genie` AND at least one space is reachable
384
+ * via {@link resolveGenieSpaces} (the explicit
385
+ * `config.genieSpaces`, the registered AppKit `genie()` plugin's
386
+ * `spaces` config, or the `DATABRICKS_GENIE_SPACE_ID` env var).
387
+ * Falls back to the generic AppKit `ToolProvider` adapter for
388
+ * every other plugin name. `config` is threaded through so the
389
+ * Genie agent inherits the same model resolver / fallback
390
+ * ladder the calling agents use.
391
+ *
392
+ * The legacy AppKit `genie` plugin's tools are no longer consulted
393
+ * at runtime - the Genie agent talks to Genie directly via
394
+ * `@dbx-tools/genie` (`genieEventChat`) and the workspace
395
+ * `statementExecution.getStatement` API. But the plugin's
396
+ * resource manifest and `spaces` config are still honored so
397
+ * existing `app.yaml` configs and `genie({ spaces })` wiring
398
+ * keep working without change.
350
399
  */
351
400
  function resolveProvider(config, context, propName) {
352
401
  if (propName === "genie") {
353
- const geniePlugin = pluginUtils.instance(context, genie);
354
- if (!geniePlugin)
402
+ const spaces = resolveGenieSpaces(config, context);
403
+ if (Object.keys(spaces).length === 0)
355
404
  return null;
356
- return buildGenieProvider(geniePlugin, { config });
405
+ return buildGenieToolkitProvider({
406
+ spaces,
407
+ config,
408
+ });
357
409
  }
358
410
  const plugin = context?.getPlugins().get(propName);
359
411
  return adaptPluginToolkit(plugin);
@@ -1,37 +1,30 @@
1
1
  /**
2
2
  * Chart-rendering primitives.
3
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.
4
+ * Two surfaces, one shared brain:
21
5
  *
22
6
  * - {@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.
7
+ * expansion as a plain async function. Takes a dataset and
8
+ * returns a promise that resolves to a full `EChartsOption`
9
+ * JSON plus the chosen `chartType`. No background work, no
10
+ * writer side-effects, no id allocation - callers stitch the
11
+ * result into whatever shape their producer needs.
12
+ *
13
+ * - {@link buildRenderDataTool}: a Mastra tool the model calls
14
+ * ("here is a dataset, render it as a chart"). Mints a short
15
+ * `chartId`, `await`s {@link runChartPlanner} so the planner
16
+ * latency is attributed to this tool's trace span, emits one
17
+ * `type: "chart"` writer event carrying the dataset + resolved
18
+ * `option`, and returns `{ chartId }` to the model. The
19
+ * LLM-bound output stays flat regardless of dataset size.
27
20
  *
28
21
  * The model wires the chart into its reply by emitting the marker
29
22
  * `[[chart:<chartId>]]` on its own line in markdown. The chat
30
23
  * 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.
24
+ * `<ChartSlot>` in at the position the model placed it. The slot
25
+ * resolves directly to the rendered Echarts visualisation - no
26
+ * skeleton state, because the option is in the same event as the
27
+ * dataset.
35
28
  */
36
29
  import type { RequestContext } from "@mastra/core/request-context";
37
30
  import { z } from "zod";
@@ -60,10 +53,10 @@ declare const chartPlanSchema: z.ZodObject<{
60
53
  categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
54
  series: z.ZodArray<z.ZodObject<{
62
55
  name: z.ZodString;
63
- data: z.ZodArray<z.ZodUnion<readonly [z.ZodNumber, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, z.ZodObject<{
56
+ data: z.ZodArray<z.ZodCatch<z.ZodPreprocess<z.ZodUnion<readonly [z.ZodNumber, z.ZodNull, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, z.ZodObject<{
64
57
  name: z.ZodString;
65
58
  value: z.ZodNumber;
66
- }, z.core.$strip>]>>;
59
+ }, z.core.$strip>]>>>>;
67
60
  }, z.core.$strip>>;
68
61
  }, z.core.$strip>;
69
62
  type ChartPlan = z.infer<typeof chartPlanSchema>;
@@ -74,6 +67,12 @@ export interface RunChartPlannerOptions {
74
67
  title: string;
75
68
  description?: string;
76
69
  data: ReadonlyArray<Record<string, unknown>>;
70
+ /**
71
+ * Cooperative cancellation. Forwarded to the planner agent's
72
+ * `generate({ abortSignal })` call so concurrent renders can be
73
+ * aborted as a group when the parent Genie agent's signal fires.
74
+ */
75
+ signal?: AbortSignal;
77
76
  }
78
77
  /** Output of {@link runChartPlanner}: a fully-formed Echarts spec. */
79
78
  export interface RunChartPlannerResult {
@@ -82,89 +81,24 @@ export interface RunChartPlannerResult {
82
81
  }
83
82
  /**
84
83
  * 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).
84
+ * full Echarts `EChartsOption` JSON. Pure async function: no
85
+ * writer side-effects, no id minting, no background work.
86
+ * Producers (the `render_data` tool, the Genie agent,
87
+ * anything else that needs a chart) await this and stitch the
88
+ * result into whatever shape their wire contract needs.
89
89
  */
90
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
91
  /**
156
92
  * Build the `render_data` tool bound to the given plugin config.
157
93
  *
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.
94
+ * The tool awaits {@link runChartPlanner} so the planner's
95
+ * latency is attributed to this tool's trace span, then emits
96
+ * one `type: "chart"` writer event carrying the dataset and the
97
+ * resolved `EChartsOption`. The LLM-bound output is just
98
+ * `{ chartId }` so the model's context stays flat regardless of
99
+ * dataset size. Planner failures are caught and surfaced as a
100
+ * `type: "error"` writer event so the slot can fall back to
101
+ * "couldn't render chart" without taking the parent agent down.
168
102
  */
169
103
  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
104
  export {};