@dbx-tools/appkit-mastra 0.9.153 → 0.9.157

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.
Files changed (76) hide show
  1. package/README.md +4 -3
  2. package/index.ts +32 -24
  3. package/lib/index.d.ts +32 -24
  4. package/lib/index.js +32 -23
  5. package/lib/src/agents.d.ts +5 -6
  6. package/lib/src/agents.js +12 -15
  7. package/lib/src/chart.js +33 -33
  8. package/lib/src/chat.js +10 -13
  9. package/lib/src/config.d.ts +12 -13
  10. package/lib/src/config.js +4 -4
  11. package/lib/src/evaluation.js +7 -9
  12. package/lib/src/feedback-mode.d.ts +1 -1
  13. package/lib/src/feedback-mode.js +1 -1
  14. package/lib/src/filesystems.js +6 -6
  15. package/lib/src/follow-up-suggestions.js +10 -10
  16. package/lib/src/genie-chat.js +27 -29
  17. package/lib/src/genie.d.ts +2 -2
  18. package/lib/src/genie.js +29 -29
  19. package/lib/src/identity.d.ts +3 -30
  20. package/lib/src/identity.js +2 -27
  21. package/lib/src/memory.d.ts +1 -1
  22. package/lib/src/memory.js +24 -6
  23. package/lib/src/mlflow.d.ts +4 -4
  24. package/lib/src/mlflow.js +15 -18
  25. package/lib/src/model.d.ts +3 -3
  26. package/lib/src/model.js +6 -6
  27. package/lib/src/monty-sandbox.js +8 -8
  28. package/lib/src/observability.d.ts +6 -5
  29. package/lib/src/observability.js +8 -6
  30. package/lib/src/plugin.d.ts +1 -1
  31. package/lib/src/plugin.js +49 -51
  32. package/lib/src/remote-skills.d.ts +3 -4
  33. package/lib/src/remote-skills.js +46 -46
  34. package/lib/src/rest.d.ts +2 -2
  35. package/lib/src/rest.js +3 -3
  36. package/lib/src/server.js +17 -20
  37. package/lib/src/serving.js +10 -11
  38. package/lib/src/statement.js +7 -7
  39. package/lib/src/storage-schema.js +4 -4
  40. package/lib/src/summarize.js +5 -5
  41. package/lib/src/telemetry.d.ts +0 -6
  42. package/lib/src/telemetry.js +34 -41
  43. package/lib/src/validation.js +3 -4
  44. package/lib/src/workspaces.d.ts +2 -2
  45. package/lib/src/workspaces.js +34 -36
  46. package/lib/src/writer.d.ts +2 -2
  47. package/lib/src/writer.js +3 -3
  48. package/package.json +17 -305
  49. package/src/agents.ts +27 -21
  50. package/src/chart.ts +43 -37
  51. package/src/chat.ts +10 -12
  52. package/src/config.ts +14 -15
  53. package/src/evaluation.ts +6 -8
  54. package/src/feedback-mode.ts +1 -1
  55. package/src/filesystems.ts +5 -5
  56. package/src/follow-up-suggestions.ts +9 -9
  57. package/src/genie-chat.ts +32 -31
  58. package/src/genie.ts +29 -29
  59. package/src/identity.ts +3 -38
  60. package/src/memory.ts +24 -6
  61. package/src/mlflow.ts +21 -21
  62. package/src/model.ts +7 -7
  63. package/src/monty-sandbox.ts +7 -7
  64. package/src/observability.ts +8 -6
  65. package/src/plugin.ts +78 -65
  66. package/src/remote-skills.ts +63 -55
  67. package/src/rest.ts +4 -4
  68. package/src/server.ts +32 -20
  69. package/src/serving.ts +14 -11
  70. package/src/statement.ts +10 -6
  71. package/src/storage-schema.ts +3 -3
  72. package/src/summarize.ts +4 -4
  73. package/src/telemetry.ts +49 -55
  74. package/src/validation.ts +2 -3
  75. package/src/workspaces.ts +52 -42
  76. package/src/writer.ts +3 -3
package/src/genie-chat.ts CHANGED
@@ -13,16 +13,19 @@
13
13
  */
14
14
 
15
15
  import type { WorkspaceClient } from "@databricks/appkit";
16
- import { databricks } from "@dbx-tools/appkit";
17
- import { agentMode, chat } from "@dbx-tools/genie";
18
- import { errorUtils, hash, log, object } from "@dbx-tools/shared-core";
19
- import { event as genieEvent, genieModel, type GenieMessage } from "@dbx-tools/shared-genie";
16
+ import { toContext } from "@dbx-tools/appkit";
17
+ import { listAgentModeConversation, genieEventChat } from "@dbx-tools/genie";
18
+ import { log, toError, id, isRecord } from "@dbx-tools/shared-core";
19
+ import { type GenieMessage, eventsFromMessage, humanizeStatus } from "@dbx-tools/shared-genie";
20
20
  import {
21
- markers,
22
- wire,
23
21
  type GenieChatRequest,
24
22
  type GenieThread,
25
23
  type StartedEvent,
24
+ formatMarker,
25
+ GENIE_PROGRESS_PART_TYPE,
26
+ type GenieWriterEvent,
27
+ type GenieConversationPartData,
28
+ GENIE_CONVERSATION_PART_TYPE,
26
29
  } from "@dbx-tools/shared-mastra";
27
30
  import type { MastraDBMessage } from "@mastra/core/agent";
28
31
  import type { MastraMemory } from "@mastra/core/memory";
@@ -90,7 +93,7 @@ export function genieAnswerText(message: GenieMessage): string {
90
93
  const queryId = queriesByVisualization.get(visualizationId);
91
94
  const statementId = queryId ? statementsByQuery.get(queryId) : undefined;
92
95
  if (statementId) {
93
- sections.push(markers.formatMarker("visualization", statementId));
96
+ sections.push(formatMarker("visualization", statementId));
94
97
  inlineStatements.add(statementId);
95
98
  }
96
99
  continue;
@@ -106,8 +109,8 @@ export function genieAnswerText(message: GenieMessage): string {
106
109
  if (description) sections.push(description);
107
110
  sections.push(
108
111
  attachment.attachment_id && visualizedQueries.has(attachment.attachment_id)
109
- ? markers.formatMarker("visualization", statementId)
110
- : markers.formatMarker("data", statementId),
112
+ ? formatMarker("visualization", statementId)
113
+ : formatMarker("data", statementId),
111
114
  );
112
115
  }
113
116
  if (sections.length === 0 && message.error?.error) sections.push(message.error.error);
@@ -115,19 +118,19 @@ export function genieAnswerText(message: GenieMessage): string {
115
118
  }
116
119
 
117
120
  function visualizationAttachmentId(metadata: unknown): string | undefined {
118
- if (!object.isRecord(metadata) || !object.isRecord(metadata.viz)) return undefined;
121
+ if (!isRecord(metadata) || !isRecord(metadata.viz)) return undefined;
119
122
  return typeof metadata.viz.attachment_id === "string" ? metadata.viz.attachment_id : undefined;
120
123
  }
121
124
 
122
125
  /** Whether a text attachment is Agent Mode's Markdown copy of a query result. */
123
126
  function isQueryOutputText(metadata: unknown): boolean {
124
- return object.isRecord(metadata) && typeof metadata.statement_id === "string";
127
+ return isRecord(metadata) && typeof metadata.statement_id === "string";
125
128
  }
126
129
 
127
130
  /** Error text for a terminal Genie message that did not complete, or `undefined`. */
128
131
  function genieFailure(message: GenieMessage): string | undefined {
129
132
  if (message.status === "COMPLETED") return undefined;
130
- return message.error?.error ?? `Genie ${genieModel.humanizeStatus(message.status!)}`;
133
+ return message.error?.error ?? `Genie ${humanizeStatus(message.status!)}`;
131
134
  }
132
135
 
133
136
  /** Recover the original user question from a context-augmented Genie input. */
@@ -140,9 +143,7 @@ export function visibleGenieQuestion(content: string): string {
140
143
  function messageText(message: MastraDBMessage): string | undefined {
141
144
  const text = message.content.parts
142
145
  .flatMap((part) =>
143
- object.isRecord(part) && part.type === "text" && typeof part.text === "string"
144
- ? [part.text]
145
- : [],
146
+ isRecord(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : [],
146
147
  )
147
148
  .join("\n")
148
149
  .trim();
@@ -230,7 +231,7 @@ async function saveGenieMemory(options: {
230
231
  saveThread: true,
231
232
  });
232
233
  }
233
- const messageId = message.message_id || message.id || hash.id();
234
+ const messageId = message.message_id || message.id || id();
234
235
  const createdAt = new Date();
235
236
  await memory.saveMessages({
236
237
  messages: [
@@ -269,9 +270,9 @@ export function genieMessageToUiMessages(
269
270
  const question = visibleGenieQuestion(message.content);
270
271
  const input = { question };
271
272
  const failure = genieFailure(message);
272
- const progress = [...genieEvent.eventsFromMessage(message, undefined, space.spaceId)].map(
273
+ const progress = [...eventsFromMessage(message, undefined, space.spaceId)].map(
273
274
  (progressEvent) => ({
274
- type: wire.GENIE_PROGRESS_PART_TYPE,
275
+ type: GENIE_PROGRESS_PART_TYPE,
275
276
  data: { toolCallId, event: progressEvent },
276
277
  }),
277
278
  );
@@ -312,7 +313,7 @@ export async function listGenieThreads(
312
313
  space: GenieChatSpace,
313
314
  signal?: AbortSignal,
314
315
  ): Promise<GenieThread[]> {
315
- const context = signal ? databricks.toContext(signal) : undefined;
316
+ const context = signal ? toContext(signal) : undefined;
316
317
  const threads: GenieThread[] = [];
317
318
  let pageToken: string | undefined;
318
319
  do {
@@ -325,7 +326,7 @@ export async function listGenieThreads(
325
326
  context,
326
327
  );
327
328
  for (const conversation of page.conversations ?? []) {
328
- if (!object.isRecord(conversation) || conversation.agent_type !== AGENT_CONVERSATION_TYPE) {
329
+ if (!isRecord(conversation) || conversation.agent_type !== AGENT_CONVERSATION_TYPE) {
329
330
  continue;
330
331
  }
331
332
  threads.push({
@@ -351,7 +352,7 @@ export async function listGenieThreadMessages(options: {
351
352
  signal?: AbortSignal;
352
353
  }): Promise<UIMessage[]> {
353
354
  const { space } = options;
354
- const messages = await agentMode.listAgentModeConversation(
355
+ const messages = await listAgentModeConversation(
355
356
  options.client,
356
357
  space.spaceId,
357
358
  options.conversationId,
@@ -379,7 +380,7 @@ export function genieChatStream(options: {
379
380
  signal?: AbortSignal;
380
381
  }): ReadableStream<UIMessageChunk> {
381
382
  const { client, space, request, memory, requestContext, signal } = options;
382
- const toolCallId = hash.id();
383
+ const toolCallId = id();
383
384
  const toolName = askGenieToolId(space.alias);
384
385
  return createUIMessageStream({
385
386
  onError: () => DIRECT_GENIE_ERROR,
@@ -396,13 +397,13 @@ export function genieChatStream(options: {
396
397
  } catch (error) {
397
398
  logger.warn("memory:recall-failed", {
398
399
  spaceId: space.spaceId,
399
- error: errorUtils.toError(error),
400
+ error: toError(error),
400
401
  });
401
402
  }
402
403
  const question = contextualQuestion(request.question, request.requestContext, memoryContext);
403
- const progress = (progressEvent: wire.GenieWriterEvent) =>
404
+ const progress = (progressEvent: GenieWriterEvent) =>
404
405
  writer.write({
405
- type: wire.GENIE_PROGRESS_PART_TYPE,
406
+ type: GENIE_PROGRESS_PART_TYPE,
406
407
  data: { toolCallId, event: progressEvent },
407
408
  });
408
409
  writer.write({ type: "start" });
@@ -420,10 +421,10 @@ export function genieChatStream(options: {
420
421
  };
421
422
  progress(started);
422
423
 
423
- let announced: wire.GenieConversationPartData | undefined;
424
+ let announced: GenieConversationPartData | undefined;
424
425
  let finalMessage: GenieMessage | undefined;
425
426
  try {
426
- for await (const chatEvent of chat.genieEventChat(space.spaceId, question, {
427
+ for await (const chatEvent of genieEventChat(space.spaceId, question, {
427
428
  workspaceClient: client,
428
429
  agentMode: true,
429
430
  agentModeFallback: false,
@@ -443,7 +444,7 @@ export function genieChatStream(options: {
443
444
  ) {
444
445
  announced = { conversationId, ...(messageId ? { messageId } : {}) };
445
446
  writer.write({
446
- type: wire.GENIE_CONVERSATION_PART_TYPE,
447
+ type: GENIE_CONVERSATION_PART_TYPE,
447
448
  data: announced,
448
449
  transient: true,
449
450
  });
@@ -454,7 +455,7 @@ export function genieChatStream(options: {
454
455
  } catch (error) {
455
456
  logger.warn("turn:error", {
456
457
  spaceId: space.spaceId,
457
- error: errorUtils.toError(error),
458
+ error: toError(error),
458
459
  });
459
460
  writer.write({
460
461
  type: "tool-output-error",
@@ -474,7 +475,7 @@ export function genieChatStream(options: {
474
475
  writer.write({ type: "finish-step" });
475
476
  const answer = genieAnswerText(finalMessage);
476
477
  if (answer) {
477
- const textId = hash.id();
478
+ const textId = id();
478
479
  writer.write({ type: "start-step" });
479
480
  writer.write({ type: "text-start", id: textId });
480
481
  writer.write({ type: "text-delta", id: textId, delta: answer });
@@ -495,7 +496,7 @@ export function genieChatStream(options: {
495
496
  } catch (error) {
496
497
  logger.warn("memory:save-failed", {
497
498
  spaceId: space.spaceId,
498
- error: errorUtils.toError(error),
499
+ error: toError(error),
499
500
  });
500
501
  }
501
502
  }
package/src/genie.ts CHANGED
@@ -44,11 +44,11 @@ import {
44
44
  ValidationError,
45
45
  } from "@databricks/appkit";
46
46
  import type { WorkspaceClient } from "@databricks/appkit";
47
- import { pluginRegistry } from "@dbx-tools/appkit";
48
- import { createCache } from "@dbx-tools/cache/cache";
49
- import { chat, space as genieSpace } from "@dbx-tools/genie";
50
- import { errorUtils, log, stringUtils } from "@dbx-tools/shared-core";
51
- import { genieModel, type GenieMessage } from "@dbx-tools/shared-genie";
47
+ import { type PluginContextLike, instance } from "@dbx-tools/appkit";
48
+ import { createCache } from "@dbx-tools/cache";
49
+ import { genieEventChat, getGenieSpace, genieSampleQuestions } from "@dbx-tools/genie";
50
+ import { log, toError, errorContext, toDescription, toIdentifier } from "@dbx-tools/shared-core";
51
+ import { type GenieMessage, GenieMessageSchema } from "@dbx-tools/shared-genie";
52
52
  import type { StartedEvent } from "@dbx-tools/shared-mastra";
53
53
  import type { RequestContext } from "@mastra/core/request-context";
54
54
  import { MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
@@ -249,7 +249,7 @@ async function readCachedConversationId(cacheKey: string | undefined): Promise<s
249
249
  return v ?? undefined;
250
250
  } catch (err) {
251
251
  logger.warn("conversation-cache:read-error", {
252
- error: errorUtils.toError(err),
252
+ error: toError(err),
253
253
  });
254
254
  return undefined;
255
255
  }
@@ -273,7 +273,7 @@ async function saveCachedConversationId(
273
273
  });
274
274
  } catch (err) {
275
275
  logger.warn("conversation-cache:write-error", {
276
- error: errorUtils.toError(err),
276
+ error: toError(err),
277
277
  });
278
278
  }
279
279
  }
@@ -285,7 +285,7 @@ async function evictCachedConversationId(cacheKey: string | undefined): Promise<
285
285
  await CacheManager.getInstanceSync().delete(cacheKey);
286
286
  } catch (err) {
287
287
  logger.warn("conversation-cache:delete-error", {
288
- error: errorUtils.toError(err),
288
+ error: toError(err),
289
289
  });
290
290
  }
291
291
  }
@@ -328,7 +328,7 @@ const prepareChartRequestSchema = chartPlannerRequestSchema
328
328
  .string()
329
329
  .min(1, "statement_id is required")
330
330
  .describe(
331
- stringUtils.toDescription(`
331
+ toDescription(`
332
332
  Genie \`statement_id\` to chart. Read from
333
333
  \`message.query_result.statement_id\` or
334
334
  \`message.attachments[*].query.statement_id\` returned by
@@ -349,7 +349,7 @@ const prepareChartRequestSchema = chartPlannerRequestSchema
349
349
  */
350
350
  function aliasSuffix(alias: string): string {
351
351
  if (alias === DEFAULT_GENIE_ALIAS) return "";
352
- const slug = stringUtils.toIdentifier(alias);
352
+ const slug = toIdentifier(alias);
353
353
  return slug ? `_${slug}` : "";
354
354
  }
355
355
 
@@ -390,7 +390,7 @@ function buildAskGenieTool(opts: {
390
390
  const hintLine = hint ? ` (${hint})` : "";
391
391
  return createTool({
392
392
  id: toolId,
393
- description: stringUtils.toDescription(`
393
+ description: toDescription(`
394
394
  Ask the Genie space "${alias}"${hintLine} ONE focused
395
395
  natural-language sub-question and wait for the turn to
396
396
  complete. Genie answers best when each call covers a
@@ -422,7 +422,7 @@ function buildAskGenieTool(opts: {
422
422
  .string()
423
423
  .min(1, "question is required")
424
424
  .describe(
425
- stringUtils.toDescription(`
425
+ toDescription(`
426
426
  ONE focused natural-language question about the data in this
427
427
  space, covering a single metric / dimension / time window
428
428
  (e.g. "What was Q3 revenue by region?"). Decompose a
@@ -432,7 +432,7 @@ function buildAskGenieTool(opts: {
432
432
  ),
433
433
  }),
434
434
  outputSchema: z.object({
435
- message: genieModel.GenieMessageSchema,
435
+ message: GenieMessageSchema,
436
436
  }),
437
437
  execute: async ({ question }, ctxRaw) => {
438
438
  const ctx = ctxRaw as ToolExecuteCtx;
@@ -485,7 +485,7 @@ function buildAskGenieTool(opts: {
485
485
  const runTurn = async (): Promise<GenieMessage> => {
486
486
  const seedConversationId = conversationId;
487
487
  let finalMessage: GenieMessage | undefined;
488
- for await (const event of chat.genieEventChat(spaceId, question, {
488
+ for await (const event of genieEventChat(spaceId, question, {
489
489
  workspaceClient: client,
490
490
  ...(agentMode !== undefined ? { agentMode } : {}),
491
491
  ...(seedConversationId ? { conversationId: seedConversationId } : {}),
@@ -525,11 +525,11 @@ function buildAskGenieTool(opts: {
525
525
  // A rejected conversation id is stale or inaccessible. Reset this
526
526
  // lane and retry once with a fresh Genie conversation.
527
527
  const rejectedConversationId = conversationId;
528
- if (rejectedConversationId && errorUtils.errorContext(err).notAccessible) {
528
+ if (rejectedConversationId && errorContext(err).notAccessible) {
529
529
  logger.warn("conversation-cache:stale, resetting", {
530
530
  spaceId,
531
531
  conversationId: rejectedConversationId,
532
- error: errorUtils.toError(err),
532
+ error: toError(err),
533
533
  });
534
534
  if (ownsReusableConversation) {
535
535
  await evictCachedConversationId(cacheKey);
@@ -559,7 +559,7 @@ function buildSpaceDescriptionTool(opts: { spaceId: string; alias: string }) {
559
559
  const toolId = `get_space_description${aliasSuffix(alias)}`;
560
560
  return createTool({
561
561
  id: toolId,
562
- description: stringUtils.toDescription(`
562
+ description: toDescription(`
563
563
  Return the Genie space "${alias}"'s title, description, and
564
564
  warehouse id. Cheap (single REST call, no LLM round-trip).
565
565
  Call this FIRST on any user turn that's going to touch
@@ -584,7 +584,7 @@ function buildSpaceDescriptionTool(opts: { spaceId: string; alias: string }) {
584
584
  // description surface (title / description / warehouse id) lives
585
585
  // on the directory-listing shape, so skip the larger
586
586
  // `serialized_space` payload here.
587
- const space = await genieSpace.getGenieSpace(spaceId, {
587
+ const space = await getGenieSpace(spaceId, {
588
588
  workspaceClient: client,
589
589
  serialized: false,
590
590
  ...(signal ? { context: signal } : {}),
@@ -604,7 +604,7 @@ function buildSpaceSerializedTool(opts: { spaceId: string; alias: string }) {
604
604
  const toolId = `get_space_serialized${aliasSuffix(alias)}`;
605
605
  return createTool({
606
606
  id: toolId,
607
- description: stringUtils.toDescription(`
607
+ description: toDescription(`
608
608
  Return the full \`GenieSpace\` JSON for the "${alias}" space.
609
609
  Use only when you need exact column / table identifiers
610
610
  \`get_space_description\` doesn't expose. Larger payload, so
@@ -619,7 +619,7 @@ function buildSpaceSerializedTool(opts: { spaceId: string; alias: string }) {
619
619
  // Central Genie space fetch with the opt-in `serialized_space`
620
620
  // blob (catalogs, tables, sample questions, prompts) that the
621
621
  // typed SDK `getSpace` omits - the whole point of this tool.
622
- const space = await genieSpace.getGenieSpace(spaceId, {
622
+ const space = await getGenieSpace(spaceId, {
623
623
  workspaceClient: client,
624
624
  ...(signal ? { context: signal } : {}),
625
625
  });
@@ -643,7 +643,7 @@ function buildGetStatementTool() {
643
643
  const toolId = "get_statement";
644
644
  return createTool({
645
645
  id: toolId,
646
- description: stringUtils.toDescription(`
646
+ description: toDescription(`
647
647
  Fetch the rows of a Genie statement by its \`statement_id\` (the
648
648
  value at \`message.query_result.statement_id\` or
649
649
  \`message.attachments[*].query.statement_id\` returned from
@@ -663,7 +663,7 @@ function buildGetStatementTool() {
663
663
  .string()
664
664
  .min(1, "statement_id is required")
665
665
  .describe(
666
- stringUtils.toDescription(`
666
+ toDescription(`
667
667
  Genie \`statement_id\` whose rows to read. Take it from
668
668
  \`message.query_result.statement_id\` or
669
669
  \`message.attachments[*].query.statement_id\` on an
@@ -728,7 +728,7 @@ function buildPrepareChartTool(opts: { config: MastraPluginConfig }) {
728
728
  const toolId = "prepare_chart";
729
729
  return createTool({
730
730
  id: toolId,
731
- description: stringUtils.toDescription([
731
+ description: toDescription([
732
732
  `
733
733
  Queue a chart for the rows of a Genie statement. Mints a
734
734
  \`chartId\` plus its complete \`marker\` synchronously and
@@ -800,7 +800,7 @@ function buildPrepareChartTool(opts: { config: MastraPluginConfig }) {
800
800
  * variant that names the suffixed per-space tools
801
801
  * (e.g. `ask_genie_sales`).
802
802
  */
803
- export const GENIE_INSTRUCTIONS = stringUtils.toDescription([
803
+ export const GENIE_INSTRUCTIONS = toDescription([
804
804
  "Genie orchestration. For every user question that needs SQL-backed data:",
805
805
  {
806
806
  numbered: [
@@ -993,7 +993,7 @@ type AppKitGenieConfig = NonNullable<Parameters<typeof genie>[0]>;
993
993
  */
994
994
  export function resolveGenieSpaces(
995
995
  config: MastraPluginConfig,
996
- context: pluginRegistry.PluginContextLike | undefined,
996
+ context: PluginContextLike | undefined,
997
997
  ): Record<string, GenieSpaceConfig> {
998
998
  const merged: Record<string, GenieSpaceConfig> = {};
999
999
 
@@ -1006,7 +1006,7 @@ export function resolveGenieSpaces(
1006
1006
  // Source 2: AppKit `genie()` plugin instance config. Use a
1007
1007
  // structural cast - `Plugin.config` is `protected` in TS only,
1008
1008
  // and the runtime layout is plain object property access.
1009
- const geniePlugin = pluginRegistry.instance(context, genie);
1009
+ const geniePlugin = instance(context, genie);
1010
1010
  if (geniePlugin) {
1011
1011
  const pluginSpaces = (geniePlugin as unknown as { config?: AppKitGenieConfig }).config?.spaces;
1012
1012
  if (pluginSpaces) {
@@ -1147,17 +1147,17 @@ export async function collectSpaceSuggestions(opts: {
1147
1147
  let questions = await readSuggestionCache(spaceId);
1148
1148
  if (!questions) {
1149
1149
  try {
1150
- const space = await genieSpace.getGenieSpace(spaceId, {
1150
+ const space = await getGenieSpace(spaceId, {
1151
1151
  workspaceClient: opts.client,
1152
1152
  ...(opts.signal ? { context: opts.signal } : {}),
1153
1153
  });
1154
- questions = genieSpace.genieSampleQuestions(space);
1154
+ questions = genieSampleQuestions(space);
1155
1155
  await writeSuggestionCache(spaceId, questions);
1156
1156
  } catch (err) {
1157
1157
  if (opts.signal?.aborted) return merged;
1158
1158
  logger.warn("suggestions:fetch-error", {
1159
1159
  spaceId,
1160
- error: errorUtils.toError(err),
1160
+ error: toError(err),
1161
1161
  });
1162
1162
  questions = [];
1163
1163
  }
package/src/identity.ts CHANGED
@@ -51,50 +51,15 @@
51
51
  * @module
52
52
  */
53
53
 
54
- import { identity } from "@dbx-tools/appkit";
55
-
56
- /** Identity a chat turn's Databricks calls run as. See the module docs. */
57
- export type MastraIdentityMode = identity.IdentityMode;
54
+ import { identity, type IdentityMode } from "@dbx-tools/appkit";
58
55
 
59
56
  /** Environment fallback for `config.genieIdentity`. */
60
57
  export const IDENTITY_ENV = "MASTRA_GENIE_IDENTITY";
61
58
 
62
- /** Every accepted {@link MastraIdentityMode}. */
63
- export const IDENTITY_MODES = identity.IDENTITY_MODES;
64
-
65
- /**
66
- * Default mode. `"user"` keeps OBO the only identity unless an app opts in, so
67
- * adopting this option can never silently widen an existing app's data access.
68
- */
69
- export const DEFAULT_IDENTITY_MODE = identity.DEFAULT_IDENTITY_MODE;
70
-
71
- /** Header Databricks Apps forward the signed-in user's id on. */
72
- export const USER_ID_HEADER = identity.USER_ID_HEADER;
73
-
74
- /** Header Databricks Apps forward the signed-in user's email on. */
75
- export const USER_EMAIL_HEADER = identity.USER_EMAIL_HEADER;
76
-
77
59
  /**
78
60
  * Resolve the configured mode: explicit plugin config, then {@link IDENTITY_ENV},
79
- * then {@link DEFAULT_IDENTITY_MODE}. An unrecognized value throws.
61
+ * then AppKit's default identity mode. An unrecognized value throws.
80
62
  */
81
- export function resolveIdentityMode(configured: string | undefined): MastraIdentityMode {
63
+ export function resolveIdentityMode(configured: string | undefined): IdentityMode {
82
64
  return identity.resolveIdentityMode(configured, IDENTITY_ENV, "genieIdentity");
83
65
  }
84
-
85
- /**
86
- * The forwarded user id on `req`, or `undefined`. Used to attribute a turn to
87
- * the real caller when the Databricks client is the app SP but memory / cache /
88
- * traces must still key off the user.
89
- */
90
- export const requestUserId = identity.requestUserId;
91
-
92
- /** The forwarded user email on `req`, or `undefined`. */
93
- export const requestUserEmail = identity.requestUserEmail;
94
-
95
- /**
96
- * Whether this request should run its Databricks calls as the app service
97
- * principal rather than OBO - `"service-principal"` always, `"user"` never, and
98
- * `"auto"` only when the request carries no OBO token for AppKit to use.
99
- */
100
- export const useServicePrincipal = identity.useServicePrincipal;
package/src/memory.ts CHANGED
@@ -32,8 +32,8 @@
32
32
  * @module
33
33
  */
34
34
 
35
- import { migration as appkitMigration } from "@dbx-tools/appkit";
36
- import { hash, log } from "@dbx-tools/shared-core";
35
+ import { handleOwnershipMigrationError } from "@dbx-tools/appkit";
36
+ import { log, id } from "@dbx-tools/shared-core";
37
37
  import type { AgentMemoryOption } from "@mastra/core/agent";
38
38
  import {
39
39
  MASTRA_RESOURCE_ID_KEY,
@@ -43,7 +43,7 @@ import {
43
43
  import { fastembed } from "@mastra/fastembed";
44
44
  import { Memory } from "@mastra/memory";
45
45
  import { PgVector, PostgresStore } from "@mastra/pg";
46
- import type { Pool } from "pg";
46
+ import { Pool, type PoolConfig } from "pg";
47
47
 
48
48
  import type { AnyMastraAgentDefinition, MastraMemoryConfigOverride } from "./agents.ts";
49
49
  import type { MastraPluginConfig } from "./config.ts";
@@ -108,7 +108,7 @@ export function withSoftStorageInit(store: PostgresStore): PostgresStore {
108
108
  await originalInit();
109
109
  } catch (err) {
110
110
  if (
111
- !appkitMigration.handleOwnershipMigrationError(err, {
111
+ !handleOwnershipMigrationError(err, {
112
112
  scope: "mastra-storage",
113
113
  logger,
114
114
  event: "mastra storage migration failed",
@@ -160,6 +160,24 @@ export function createMemoryBuilder(
160
160
  return new MemoryBuilder(config, servicePrincipalPool);
161
161
  }
162
162
 
163
+ /**
164
+ * Create the plugin-owned Lakebase pool used by durable workflow storage.
165
+ *
166
+ * @internal
167
+ *
168
+ * AppKit shuts plugin hooks down concurrently, so borrowing the native
169
+ * Lakebase plugin's pool lets its shutdown race Mastra's final snapshot writes.
170
+ * A caller-provided pool is not closed by PostgresStore; idle connections do
171
+ * not keep the process alive and remain available to runs Mastra is still
172
+ * draining after its bounded shutdown window.
173
+ */
174
+ export function createMastraLakebasePool(config: PoolConfig): Pool {
175
+ return new Pool({
176
+ ...config,
177
+ allowExitOnIdle: true,
178
+ });
179
+ }
180
+
163
181
  /**
164
182
  * Builds one `Memory` per agent against a shared service-principal
165
183
  * Lakebase pool. Per-instance state keeps the shared `PgVector` alive
@@ -329,7 +347,7 @@ export class MemoryBuilder {
329
347
  */
330
348
  function buildSharedPgVector(pool: Pool): PgVector {
331
349
  const vector = new PgVector({
332
- id: `pg${hash.id()}`,
350
+ id: `pg${id()}`,
333
351
  // Keep the recall index out of `public`: on a Lakebase database the app
334
352
  // service principal has no CREATE on `public` (PG15+ locks it down), so a
335
353
  // default-schema PgVector fails on CREATE INDEX with "permission denied for
@@ -353,7 +371,7 @@ function buildSharedPgVector(pool: Pool): PgVector {
353
371
  /** Per-agent dedicated `PgVector` (rare; opt-in via object override). */
354
372
  function buildPgVector(setting: MastraMemoryConfigOverride): PgVector {
355
373
  return new PgVector(
356
- withId(setting, `pg-vector__${hash.id()}`) as ConstructorParameters<typeof PgVector>[0],
374
+ withId(setting, `pg-vector__${id()}`) as ConstructorParameters<typeof PgVector>[0],
357
375
  );
358
376
  }
359
377