@dbx-tools/appkit-mastra 0.1.0

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.
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Plugin configuration types and shared `RequestContext` keys.
3
+ *
4
+ * Kept in a leaf module so `plugin.ts`, `server.ts`, `model.ts`, and
5
+ * `memory.ts` can import them without creating a cycle.
6
+ */
7
+ import type { BasePluginConfig, getExecutionContext } from "@databricks/appkit";
8
+ import type { AgentConfig } from "@mastra/core/agent";
9
+ import type { PgVectorConfig, PostgresStoreConfig } from "@mastra/pg";
10
+ import type { MastraAgentDefinition, MastraTools } from "./agents.js";
11
+ /**
12
+ * `RequestContext` key under which {@link MastraServer} stores the
13
+ * resolved AppKit user. `model.ts` reads it to mint user-scoped
14
+ * Databricks tokens.
15
+ */
16
+ export declare const MASTRA_USER_KEY = "mastra__user";
17
+ /** AppKit execution context plus the canonical user id. */
18
+ export interface User {
19
+ id: string;
20
+ executionContext: ReturnType<typeof getExecutionContext>;
21
+ }
22
+ /** PgVector config with an optional Mastra store id. */
23
+ export type MastraMemoryConfig = PgVectorConfig & {
24
+ id?: string;
25
+ };
26
+ /** Configuration accepted by the Mastra AppKit plugin. */
27
+ export interface MastraPluginConfig extends BasePluginConfig {
28
+ /** Mastra OpenAI-compatible provider id. Defaults to `"databricks"`. */
29
+ providerId?: string;
30
+ /**
31
+ * PostgresStore for Mastra threads/messages. `true` reuses the
32
+ * `lakebase` plugin's pool; an object opens a dedicated store.
33
+ */
34
+ storage?: boolean | PostgresStoreConfig;
35
+ /**
36
+ * PgVector store for Mastra memory recall. `true` reuses the
37
+ * `lakebase` plugin's pool; an object opens a dedicated store.
38
+ */
39
+ memory?: boolean | MastraMemoryConfig;
40
+ /**
41
+ * Code-defined agents. Accepts three shapes for convenience:
42
+ *
43
+ * - **Record**: `{ analyst: def, helper: def }` - keys become the
44
+ * registered ids and the first key is the default.
45
+ * - **Single definition**: `def` - registered under
46
+ * `slugify(def.name)` (or `"default"` when `name` is omitted) and
47
+ * automatically marked as the default agent.
48
+ * - **Array**: `[def1, def2]` - each registered under
49
+ * `slugify(def.name)` (or `agent_${i}` when `name` is omitted);
50
+ * the first entry is the default.
51
+ *
52
+ * Each entry becomes a Mastra `Agent` reachable at
53
+ * `/api/<plugin>/route/chat/<id>` (the chat route also matches
54
+ * `:agentId`). When `agents` is omitted entirely, the plugin
55
+ * registers a single built-in `default` analyst so the bare
56
+ * `mastra()` call still mounts a working chat endpoint.
57
+ *
58
+ * @example Single-agent shorthand
59
+ * ```ts
60
+ * mastra({
61
+ * agents: createAgent({ instructions: "..." }),
62
+ * });
63
+ * ```
64
+ *
65
+ * @example Array
66
+ * ```ts
67
+ * mastra({
68
+ * agents: [
69
+ * createAgent({ name: "analyst", instructions: "..." }),
70
+ * createAgent({ name: "helper", instructions: "..." }),
71
+ * ],
72
+ * });
73
+ * ```
74
+ *
75
+ * @example Record (explicit ids)
76
+ * ```ts
77
+ * mastra({
78
+ * agents: {
79
+ * analyst: createAgent({ instructions: "..." }),
80
+ * helper: createAgent({ instructions: "..." }),
81
+ * },
82
+ * defaultAgent: "analyst",
83
+ * });
84
+ * ```
85
+ */
86
+ agents?: Record<string, MastraAgentDefinition> | MastraAgentDefinition | MastraAgentDefinition[];
87
+ /**
88
+ * Ambient tools spread into every registered agent's tools record;
89
+ * per-agent tools win on key collision. Use for a small shared
90
+ * library; for per-agent tools set `agents[id].tools` instead.
91
+ */
92
+ tools?: MastraTools;
93
+ /**
94
+ * Agent id used by `chatRoute` when the client doesn't specify one.
95
+ * Defaults to the first key in `agents` (or `"default"` when
96
+ * `agents` is omitted). Must match an id in `agents` when both are
97
+ * set; a mismatch throws at setup with the available candidates.
98
+ */
99
+ defaultAgent?: string;
100
+ /**
101
+ * Plugin-level default model applied to every agent that omits its
102
+ * own `model`. Mirrors AppKit's `agents({ defaultModel })`.
103
+ *
104
+ * - `string`: shorthand for "use the OBO auto-resolver but swap the
105
+ * `modelId`" (e.g. `"databricks-claude-sonnet-4-6"`).
106
+ * - Any other Mastra `DynamicArgument<MastraModelConfig>`: passed
107
+ * through verbatim. Use this when you need full control over auth
108
+ * or `providerId`.
109
+ *
110
+ * Resolution order per agent: `def.model` → `defaultModel` →
111
+ * built-in `/serving-endpoints` resolver.
112
+ */
113
+ defaultModel?: AgentConfig["model"] | string;
114
+ /**
115
+ * Allow loose model names (`"claude sonnet"`) to be fuzzy-matched
116
+ * against the workspace's Model Serving endpoints. Defaults to
117
+ * `true`; set `false` to require exact endpoint names everywhere.
118
+ */
119
+ modelFuzzyMatch?: boolean;
120
+ /**
121
+ * Fuse.js score threshold for the fuzzy matcher (0 = exact match,
122
+ * 1 = anything matches). Defaults to `0.4`. Lower values reject
123
+ * loose matches; raise it if you have a sprawling endpoint
124
+ * catalogue with similar-looking names.
125
+ */
126
+ modelFuzzyThreshold?: number;
127
+ /**
128
+ * TTL for the in-memory serving-endpoints list cache, in
129
+ * milliseconds. Defaults to 5 minutes. The cache is per workspace
130
+ * host and shared across users; concurrent callers coalesce on a
131
+ * single in-flight fetch.
132
+ */
133
+ modelCacheTtlMs?: number;
134
+ /**
135
+ * Allow clients to override the active model per request via the
136
+ * `X-Mastra-Model` header, `?model=` query string, or `model` body
137
+ * field. Defaults to `true`. Disable when running multi-tenant
138
+ * where untrusted clients shouldn't pick the backing endpoint.
139
+ */
140
+ modelOverride?: boolean;
141
+ /**
142
+ * Priority-ordered list of endpoint names tried when no agent /
143
+ * plugin / env / request-override model id is set. The resolver
144
+ * picks the first id that is actually present in the workspace's
145
+ * `/serving-endpoints` listing - this is what lets a workspace
146
+ * without Claude Opus still get a sensible default automatically.
147
+ *
148
+ * Defaults to the built-in list in `model.ts` (`FALLBACK_MODEL_IDS`):
149
+ * Claude (Opus -> Sonnet -> Haiku), then OpenAI GPT-5 family, then
150
+ * open weights (Llama 4, Llama 3.3, GPT-OSS, Qwen, Llama 3.1).
151
+ * Override here to pin a regulated workspace to an approved subset
152
+ * or to add custom endpoints in front of the public catalogue.
153
+ */
154
+ defaultModelFallbacks?: readonly string[];
155
+ /**
156
+ * Style guardrails appended to every agent's `instructions` to curb
157
+ * common LLM-isms (em dashes, emojis, sycophantic openers, throwaway
158
+ * closers, excessive hedging).
159
+ *
160
+ * - `undefined` (default): use the built-in
161
+ * `DEFAULT_STYLE_INSTRUCTIONS` from `agents.ts`.
162
+ * - `string`: replace the default with the supplied block.
163
+ * - `false`: disable entirely (agents see only their bespoke
164
+ * `instructions`).
165
+ *
166
+ * Appended (not prepended) so the agent's role and rules come first
167
+ * and the style block leans on the model's recency bias.
168
+ */
169
+ styleInstructions?: string | false;
170
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Plugin configuration types and shared `RequestContext` keys.
3
+ *
4
+ * Kept in a leaf module so `plugin.ts`, `server.ts`, `model.ts`, and
5
+ * `memory.ts` can import them without creating a cycle.
6
+ */
7
+ /**
8
+ * `RequestContext` key under which {@link MastraServer} stores the
9
+ * resolved AppKit user. `model.ts` reads it to mint user-scoped
10
+ * Databricks tokens.
11
+ */
12
+ export const MASTRA_USER_KEY = "mastra__user";
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Mastra tool wrappers around the AppKit `genie` plugin's exports.
3
+ *
4
+ * One `sendMessage` tool is registered per configured space alias so
5
+ * the LLM picks the space by tool selection (the description bakes the
6
+ * alias in). `getConversation` is registered once, taking `alias` as a
7
+ * parameter.
8
+ *
9
+ * All Genie payload types are inferred from the public `genie` factory
10
+ * (`genie().plugin` constructor → `exports()` return type), so any
11
+ * upstream change in `@databricks/appkit` flows in automatically.
12
+ *
13
+ * As Genie streams its long-running events (`FETCHING_METADATA` →
14
+ * `ASKING_AI` → `EXECUTING_QUERY` → `COMPLETED`, plus SQL queries and
15
+ * row data in `message_result.attachments` / `query_result`), the tool
16
+ * forwards a normalised {@link GenieProgress} discriminated union out
17
+ * through `ctx.writer` so the client can render incremental feedback
18
+ * (status pill, SQL code block, row count) while the LLM still sees a
19
+ * single clean final payload.
20
+ */
21
+ import { genie } from "@databricks/appkit";
22
+ import { createTool } from "@mastra/core/tools";
23
+ /** Live AppKit `GeniePlugin` instance. */
24
+ export type GeniePluginInstance = InstanceType<ReturnType<typeof genie>["plugin"]>;
25
+ /** Full `exports()` shape of the AppKit `genie` plugin. */
26
+ export type GenieExports = ReturnType<GeniePluginInstance["exports"]>;
27
+ /**
28
+ * Stream event yielded by `genie.exports().sendMessage`. Discriminated
29
+ * by `type` (`"message_start" | "status" | "message_result" |
30
+ * "query_result" | "error" | "history_info"`).
31
+ */
32
+ export type GenieStreamEvent = ReturnType<GenieExports["sendMessage"]> extends AsyncGenerator<infer E> ? E : never;
33
+ /** Conversation history returned by `genie.exports().getConversation`. */
34
+ export type GenieConversation = Awaited<ReturnType<GenieExports["getConversation"]>>;
35
+ /**
36
+ * Normalised progress event surfaced to the UI as a Mastra `tool-output`
37
+ * chunk. The discriminator (`kind`) keeps the union open for future
38
+ * Genie features (charts, attachments, retries) without forcing the
39
+ * client to know any Genie wire format.
40
+ */
41
+ export type GenieProgress = {
42
+ kind: "started";
43
+ conversationId: string;
44
+ messageId: string;
45
+ spaceId: string;
46
+ } | {
47
+ kind: "status";
48
+ status: string;
49
+ label: string;
50
+ } | {
51
+ kind: "sql";
52
+ sql: string;
53
+ title?: string;
54
+ description?: string;
55
+ statementId?: string;
56
+ } | {
57
+ kind: "data";
58
+ rowCount: number;
59
+ columns: string[];
60
+ } | {
61
+ kind: "text";
62
+ content: string;
63
+ } | {
64
+ kind: "suggested";
65
+ questions: string[];
66
+ } | {
67
+ kind: "error";
68
+ error: string;
69
+ };
70
+ /**
71
+ * Default tool name for a wired Genie alias. The well-known `default`
72
+ * alias collapses to `genie`; everything else gets a `genie_` prefix so
73
+ * multiple spaces stay disambiguated when an agent has more than one
74
+ * wired. Matches the `genie` / `genie_<alias>` naming used elsewhere in
75
+ * dbx-tools AppKit demos.
76
+ */
77
+ export declare function defaultGenieToolName(alias: string): string;
78
+ /**
79
+ * Build one `sendMessage` tool per configured Genie alias plus a single
80
+ * `getConversation` tool. Returns a record keyed by tool id, ready to
81
+ * spread into an `Agent`'s `tools` map.
82
+ */
83
+ export declare function buildGenieTools(opts: {
84
+ aliases: string[];
85
+ exports: GenieExports;
86
+ signal?: AbortSignal;
87
+ }): Record<string, ReturnType<typeof createTool>>;
88
+ /**
89
+ * Toolkit provider built from a live AppKit `GeniePlugin` instance.
90
+ * Returned by {@link buildGenieProvider} so that
91
+ * `plugins.genie?.toolkit()` inside an agent's `tools(plugins)` callback
92
+ * resolves to the streaming-aware {@link buildGenieTools} record instead
93
+ * of the AppKit default (which does one blocking call per tool with no
94
+ * mid-flight events).
95
+ *
96
+ * The returned `toolkit()` reads alias names off the plugin's
97
+ * `getAgentTools()` registry (each entry is `${alias}.sendMessage` or
98
+ * `${alias}.getConversation`), then mints one `sendMessage` tool per
99
+ * alias plus a shared `getConversation`. `sendMessage` / `getConversation`
100
+ * are bound back to the plugin instance so they keep their `this`
101
+ * (they are class methods, not free functions).
102
+ *
103
+ * `_opts` is accepted but unused for now - the streaming tools are an
104
+ * all-or-nothing bundle. Wire `only` / `except` / `prefix` / `rename`
105
+ * later if a caller needs them.
106
+ */
107
+ export declare function buildGenieProvider(plugin: GeniePluginInstance): {
108
+ toolkit(opts?: unknown): Record<string, ReturnType<typeof createTool>>;
109
+ };
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Mastra tool wrappers around the AppKit `genie` plugin's exports.
3
+ *
4
+ * One `sendMessage` tool is registered per configured space alias so
5
+ * the LLM picks the space by tool selection (the description bakes the
6
+ * alias in). `getConversation` is registered once, taking `alias` as a
7
+ * parameter.
8
+ *
9
+ * All Genie payload types are inferred from the public `genie` factory
10
+ * (`genie().plugin` constructor → `exports()` return type), so any
11
+ * upstream change in `@databricks/appkit` flows in automatically.
12
+ *
13
+ * As Genie streams its long-running events (`FETCHING_METADATA` →
14
+ * `ASKING_AI` → `EXECUTING_QUERY` → `COMPLETED`, plus SQL queries and
15
+ * row data in `message_result.attachments` / `query_result`), the tool
16
+ * forwards a normalised {@link GenieProgress} discriminated union out
17
+ * through `ctx.writer` so the client can render incremental feedback
18
+ * (status pill, SQL code block, row count) while the LLM still sees a
19
+ * single clean final payload.
20
+ */
21
+ import { genie } from "@databricks/appkit";
22
+ import { stringUtils } from "@dbx-tools/appkit-shared";
23
+ import { createTool } from "@mastra/core/tools";
24
+ import { z } from "zod";
25
+ const sendMessageSchema = z.object({
26
+ content: z.string().describe("Natural-language question to send to the Genie space."),
27
+ conversationId: z
28
+ .string()
29
+ .optional()
30
+ .describe("Optional Genie conversation id to continue an earlier thread. " +
31
+ "Omit on the first call; pass the id returned in the previous " +
32
+ "result's `conversationId` to follow up."),
33
+ });
34
+ const getConversationSchema = z.object({
35
+ alias: z
36
+ .string()
37
+ .describe("Alias of the Genie space the conversation belongs to (matches the " +
38
+ "key in the genie plugin's `spaces` config)."),
39
+ conversationId: z.string().describe("Genie conversation id whose history to fetch."),
40
+ });
41
+ /**
42
+ * Default tool name for a wired Genie alias. The well-known `default`
43
+ * alias collapses to `genie`; everything else gets a `genie_` prefix so
44
+ * multiple spaces stay disambiguated when an agent has more than one
45
+ * wired. Matches the `genie` / `genie_<alias>` naming used elsewhere in
46
+ * dbx-tools AppKit demos.
47
+ */
48
+ export function defaultGenieToolName(alias) {
49
+ if (alias === "default")
50
+ return "genie";
51
+ return stringUtils.toIdentifierWithOptions({ distinct: true }, "genie", alias);
52
+ }
53
+ /**
54
+ * Build one `sendMessage` tool per configured Genie alias plus a single
55
+ * `getConversation` tool. Returns a record keyed by tool id, ready to
56
+ * spread into an `Agent`'s `tools` map.
57
+ */
58
+ export function buildGenieTools(opts) {
59
+ const tools = {};
60
+ for (const alias of opts.aliases) {
61
+ const id = defaultGenieToolName(alias);
62
+ tools[id] = createTool({
63
+ id,
64
+ description: `Ask the Databricks Genie space "${alias}" a natural-language ` +
65
+ "question. Genie translates the question to SQL, runs it against " +
66
+ "the configured datasets, and returns a written answer plus any " +
67
+ "SQL statements it executed. Returns `{ conversationId, content, " +
68
+ "queries, ... }`; pass `conversationId` back in to follow up in " +
69
+ "the same Genie thread.",
70
+ inputSchema: sendMessageSchema,
71
+ execute: async ({ content, conversationId }, ctx) => {
72
+ const stream = opts.exports.sendMessage(alias, content, conversationId, {
73
+ signal: opts.signal,
74
+ });
75
+ return drainGenieStream(stream, ctx.writer);
76
+ },
77
+ });
78
+ }
79
+ tools.genie_get_conversation = createTool({
80
+ id: "genie_get_conversation",
81
+ description: "Fetch the full message history of a prior Genie conversation by id. " +
82
+ "Use when the user references an earlier Genie thread by id, or to " +
83
+ "inspect attachments / SQL from previous turns.",
84
+ inputSchema: getConversationSchema,
85
+ execute: async ({ alias, conversationId }) => {
86
+ return opts.exports.getConversation(alias, conversationId, opts.signal);
87
+ },
88
+ });
89
+ return tools;
90
+ }
91
+ /**
92
+ * Drain the genie `sendMessage` AsyncGenerator into a flat result the
93
+ * agent's calling LLM can reason about. Final assistant text is pulled
94
+ * from the last `message_result`; SQL statements are extracted from
95
+ * `query_result` events; conversation / message ids are surfaced so the
96
+ * caller can pass `conversationId` back into a follow-up tool call.
97
+ *
98
+ * When a Mastra `writer` is passed (i.e. the tool runs inside an agent
99
+ * stream), normalised {@link GenieProgress} events are pushed mid-flight
100
+ * so the UI can show status changes, SQL, and row counts as they
101
+ * happen instead of staring at a spinner for the full Genie round-trip.
102
+ */
103
+ async function drainGenieStream(stream, writer) {
104
+ let conversationId;
105
+ let messageId;
106
+ let spaceId;
107
+ let status;
108
+ let content;
109
+ let attachments;
110
+ let error;
111
+ const queries = [];
112
+ // Best-effort progress emission. Awaited so the underlying agent
113
+ // stream sees events in order; write failures are swallowed so a
114
+ // dead writer (e.g. closed downstream) can't take the tool down.
115
+ const emit = async (event) => {
116
+ if (!writer)
117
+ return;
118
+ try {
119
+ await writer.write(event);
120
+ }
121
+ catch {
122
+ // ignore: downstream stream is no longer interested
123
+ }
124
+ };
125
+ for await (const event of stream) {
126
+ switch (event.type) {
127
+ case "message_start":
128
+ conversationId = event.conversationId;
129
+ messageId = event.messageId;
130
+ spaceId = event.spaceId;
131
+ await emit({
132
+ kind: "started",
133
+ conversationId,
134
+ messageId,
135
+ spaceId,
136
+ });
137
+ break;
138
+ case "status":
139
+ status = event.status;
140
+ await emit({
141
+ kind: "status",
142
+ status: event.status,
143
+ label: humanizeGenieStatus(event.status),
144
+ });
145
+ break;
146
+ case "query_result": {
147
+ queries.push({
148
+ attachmentId: event.attachmentId,
149
+ statementId: event.statementId,
150
+ data: event.data,
151
+ });
152
+ const rowCount = event.data?.result?.data_array?.length ?? 0;
153
+ const columns = (event.data?.manifest?.schema?.columns ?? []).map((c) => c.name);
154
+ await emit({ kind: "data", rowCount, columns });
155
+ break;
156
+ }
157
+ case "message_result":
158
+ content = event.message.content;
159
+ attachments = event.message.attachments;
160
+ status = event.message.status;
161
+ for (const attachment of attachments ?? []) {
162
+ if (attachment.query?.query) {
163
+ await emit({
164
+ kind: "sql",
165
+ sql: attachment.query.query,
166
+ title: attachment.query.title,
167
+ description: attachment.query.description,
168
+ statementId: attachment.query.statementId,
169
+ });
170
+ }
171
+ if (attachment.text?.content) {
172
+ await emit({ kind: "text", content: attachment.text.content });
173
+ }
174
+ if (attachment.suggestedQuestions?.length) {
175
+ await emit({
176
+ kind: "suggested",
177
+ questions: attachment.suggestedQuestions,
178
+ });
179
+ }
180
+ }
181
+ break;
182
+ case "error":
183
+ error = event.error;
184
+ await emit({ kind: "error", error: event.error });
185
+ break;
186
+ default:
187
+ break;
188
+ }
189
+ }
190
+ return {
191
+ conversationId,
192
+ messageId,
193
+ spaceId,
194
+ status,
195
+ content,
196
+ attachments,
197
+ queries,
198
+ error,
199
+ };
200
+ }
201
+ /**
202
+ * Toolkit provider built from a live AppKit `GeniePlugin` instance.
203
+ * Returned by {@link buildGenieProvider} so that
204
+ * `plugins.genie?.toolkit()` inside an agent's `tools(plugins)` callback
205
+ * resolves to the streaming-aware {@link buildGenieTools} record instead
206
+ * of the AppKit default (which does one blocking call per tool with no
207
+ * mid-flight events).
208
+ *
209
+ * The returned `toolkit()` reads alias names off the plugin's
210
+ * `getAgentTools()` registry (each entry is `${alias}.sendMessage` or
211
+ * `${alias}.getConversation`), then mints one `sendMessage` tool per
212
+ * alias plus a shared `getConversation`. `sendMessage` / `getConversation`
213
+ * are bound back to the plugin instance so they keep their `this`
214
+ * (they are class methods, not free functions).
215
+ *
216
+ * `_opts` is accepted but unused for now - the streaming tools are an
217
+ * all-or-nothing bundle. Wire `only` / `except` / `prefix` / `rename`
218
+ * later if a caller needs them.
219
+ */
220
+ export function buildGenieProvider(plugin) {
221
+ return {
222
+ toolkit(_opts) {
223
+ const aliases = extractGenieAliases(plugin);
224
+ return buildGenieTools({
225
+ aliases,
226
+ exports: {
227
+ sendMessage: plugin.sendMessage.bind(plugin),
228
+ getConversation: plugin.getConversation.bind(plugin),
229
+ },
230
+ });
231
+ },
232
+ };
233
+ }
234
+ /**
235
+ * Pull the configured space aliases out of a live AppKit `GeniePlugin`.
236
+ * Reads them off `getAgentTools()` (public API) so we don't poke at the
237
+ * `protected config.spaces` field: the plugin registers tools named
238
+ * `${alias}.sendMessage` / `${alias}.getConversation`, so the unique
239
+ * set of name prefixes is the alias list.
240
+ */
241
+ function extractGenieAliases(plugin) {
242
+ const aliases = new Set();
243
+ for (const t of plugin.getAgentTools()) {
244
+ const dot = t.name.indexOf(".");
245
+ if (dot > 0)
246
+ aliases.add(t.name.slice(0, dot));
247
+ }
248
+ return [...aliases];
249
+ }
250
+ /**
251
+ * Convert raw Genie status codes (`FETCHING_METADATA`, `ASKING_AI`,
252
+ * `EXECUTING_QUERY`, `COMPLETED`, ...) into short, sentence-cased
253
+ * labels safe to drop straight into a UI pill. Unknown codes are
254
+ * lower-cased with underscores stripped so new states still render.
255
+ */
256
+ function humanizeGenieStatus(status) {
257
+ switch (status) {
258
+ case "FETCHING_METADATA":
259
+ return "Fetching metadata";
260
+ case "ASKING_AI":
261
+ return "Asking Genie";
262
+ case "EXECUTING_QUERY":
263
+ return "Running SQL query";
264
+ case "COMPLETED":
265
+ return "Completed";
266
+ case "FAILED":
267
+ return "Failed";
268
+ default:
269
+ return status.toLowerCase().replace(/_/g, " ");
270
+ }
271
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Lakebase-backed Mastra memory wiring.
3
+ *
4
+ * Provides a {@link MemoryBuilder} that mints one `Memory` per agent
5
+ * with two independent knobs:
6
+ *
7
+ * - **Storage** (threads / messages via `PostgresStore`): defaults to
8
+ * **per-agent** namespacing via `schemaName: "mastra_<agentId>"` so
9
+ * conversation history stays isolated between agents in the same
10
+ * database. `PostgresStore` auto-creates the schema with
11
+ * `CREATE SCHEMA IF NOT EXISTS` on init.
12
+ * - **Memory** (semantic recall via `PgVector`): defaults to a single
13
+ * **shared** instance across every agent. Cross-agent recall on one
14
+ * index is almost always what users want; opt into per-agent recall
15
+ * by passing a {@link MastraMemoryConfigOverride} on the agent.
16
+ *
17
+ * Plugin-level `config.storage` / `config.memory` act as the baseline
18
+ * (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
19
+ * is registered); per-agent settings cascade on top of that.
20
+ */
21
+ import { lakebase } from "@databricks/appkit";
22
+ import { pluginUtils } from "@dbx-tools/appkit-shared";
23
+ import { Memory } from "@mastra/memory";
24
+ import type { MastraAgentDefinition } from "./agents.js";
25
+ import type { MastraPluginConfig } from "./config.js";
26
+ /** Pool handle returned by the AppKit `lakebase` plugin `exports().pool`. */
27
+ export type LakebasePool = ReturnType<InstanceType<ReturnType<typeof lakebase>["plugin"]>["exports"]>["pool"];
28
+ /**
29
+ * True when any plugin-level or per-agent setting could need the
30
+ * Lakebase pool. Used by `plugin.ts` to gate pool acquisition; the
31
+ * builder also acquires lazily so missed cases still fail with a
32
+ * clear lakebase-not-registered error.
33
+ */
34
+ export declare function needsLakebase(config: MastraPluginConfig): boolean;
35
+ /**
36
+ * Look up the `lakebase` plugin and return its managed `pg.Pool`.
37
+ * Throws when the sibling plugin is not registered; enabling
38
+ * `storage` / `memory` without lakebase is a wiring bug, not a runtime
39
+ * condition we can recover from.
40
+ */
41
+ export declare function resolveLakebasePool(context: pluginUtils.PluginContextLike | undefined, caller: MastraPluginConfig): LakebasePool;
42
+ /**
43
+ * Construct a per-agent {@link Memory} factory. Caches the shared
44
+ * `PgVector` singleton (built on first need) and the lazily-resolved
45
+ * Lakebase pool so each agent build is O(1) after the first.
46
+ */
47
+ export declare function createMemoryBuilder(config: MastraPluginConfig, context: pluginUtils.PluginContextLike | undefined): MemoryBuilder;
48
+ /**
49
+ * Builds one `Memory` per agent. Per-instance state keeps the shared
50
+ * `PgVector` and the resolved Lakebase pool alive across calls so
51
+ * registering N agents stays cheap.
52
+ */
53
+ export declare class MemoryBuilder {
54
+ private readonly config;
55
+ private readonly context;
56
+ private sharedVector;
57
+ private pool;
58
+ constructor(config: MastraPluginConfig, context: pluginUtils.PluginContextLike | undefined);
59
+ /**
60
+ * Build a `Memory` for `agentId` after the plugin/agent cascade.
61
+ * Returns `undefined` when the agent has neither storage nor a
62
+ * vector store enabled - Mastra accepts a missing `memory` field
63
+ * and treats the agent as stateless.
64
+ */
65
+ forAgent(agentId: string, def: MastraAgentDefinition): Memory | undefined;
66
+ private buildStorage;
67
+ /**
68
+ * Resolve the agent's vector store. Cascade:
69
+ *
70
+ * - falsy: no vector.
71
+ * - `boolean` / `undefined-inheriting-true`: return the shared
72
+ * singleton (built lazily on first call). All agents that
73
+ * default-enable memory write into and recall from one index.
74
+ * - object: build a dedicated `PgVector` for this agent.
75
+ */
76
+ private buildVector;
77
+ private getSharedVector;
78
+ private requirePool;
79
+ }