@dbx-tools/appkit-mastra 0.1.96 → 0.1.98

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 (3) hide show
  1. package/README.md +39 -40
  2. package/dist/index.js +64 -11
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -6,23 +6,23 @@ Lakebase-backed memory, and the standard Mastra agent stream the React
6
6
  client drives via `@mastra/client-js`.
7
7
 
8
8
  Wiring it up looks the same as the AppKit
9
- [`agents`](https://developers.databricks.com/docs/appkit/v0/plugins/agents)
9
+ `[agents](https://developers.databricks.com/docs/appkit/v0/plugins/agents)`
10
10
  plugin - same `createAgent` / `tool` helpers, same `tools(plugins)`
11
11
  callback, same `ToolkitOptions`. Switching a given agent between the two
12
12
  is a one-line import change.
13
13
 
14
14
  The implementation lives under `src/`: agent registration in
15
- [`agents.ts`](src/agents.ts), the plugin + routes in
16
- [`plugin.ts`](src/plugin.ts) / [`server.ts`](src/server.ts), thread
17
- history / listing in [`history.ts`](src/history.ts) /
18
- [`threads.ts`](src/threads.ts), Model Serving resolution in
19
- [`model.ts`](src/model.ts) / [`serving.ts`](src/serving.ts), Genie
20
- tooling in [`genie.ts`](src/genie.ts), the chart pipeline in
21
- [`chart.ts`](src/chart.ts), Databricks workspace mounts in
22
- [`workspaces.ts`](src/workspaces.ts) / [`filesystems.ts`](src/filesystems.ts),
15
+ `[agents.ts](src/agents.ts)`, the plugin + routes in
16
+ `[plugin.ts](src/plugin.ts)` / `[server.ts](src/server.ts)`, thread
17
+ history / listing in `[history.ts](src/history.ts)` /
18
+ `[threads.ts](src/threads.ts)`, Model Serving resolution in
19
+ `[model.ts](src/model.ts)` / `[serving.ts](src/serving.ts)`, Genie
20
+ tooling in `[genie.ts](src/genie.ts)`, the chart pipeline in
21
+ `[chart.ts](src/chart.ts)`, Databricks workspace mounts in
22
+ `[workspaces.ts](src/workspaces.ts)` / `[filesystems.ts](src/filesystems.ts)`,
23
23
  and MLflow feedback logging in
24
- [`mlflow.ts`](src/mlflow.ts) (over the shared REST helper in
25
- [`rest.ts`](src/rest.ts)).
24
+ `[mlflow.ts](src/mlflow.ts)` (over the shared REST helper in
25
+ `[rest.ts](src/rest.ts)`).
26
26
 
27
27
  ## Quick start
28
28
 
@@ -63,10 +63,10 @@ await createApp({
63
63
  `tool` is the AppKit-shaped factory (`{ description, schema, execute }`)
64
64
  that adapts to Mastra's `createTool` under the hood. The full config
65
65
  shapes (`MastraAgentDefinition`, the plugin config) - every field and
66
- default - are typed in [`config.ts`](src/config.ts).
66
+ default - are typed in `[config.ts](src/config.ts)`.
67
67
 
68
68
  On the React side, drop in the prebuilt chat UI from
69
- [`@dbx-tools/appkit-mastra-ui`](../appkit-mastra-ui); it wires itself
69
+ `[@dbx-tools/appkit-mastra-ui](../appkit-mastra-ui)`; it wires itself
70
70
  from the plugin's published client config and streams over
71
71
  `@mastra/client-js`, so there's no transport code to write:
72
72
 
@@ -83,34 +83,34 @@ export default function ChatPage() {
83
83
  With no config, memory and storage are driven entirely by whether the
84
84
  `lakebase` plugin is registered:
85
85
 
86
- - **No `lakebase()`** - the agent is fully stateless: no threads, no
86
+ - **No** `lakebase()` - the agent is fully stateless: no threads, no
87
87
  recall.
88
- - **`lakebase()` registered** - both auto-enable. Each agent gets its
88
+ - `lakebase()` **registered** - both auto-enable. Each agent gets its
89
89
  own `PostgresStore` schema (threads stay isolated per agent); every
90
90
  agent shares one `PgVector` semantic-recall index.
91
91
 
92
92
  Override per plugin or per agent by passing `memory` / `storage` as
93
93
  `false` (opt out), `true` (explicit on), or a config object (dedicated
94
- index / schema). The fields are typed in [`config.ts`](src/config.ts);
95
- the wiring is in [`memory.ts`](src/memory.ts).
94
+ index / schema). The fields are typed in `[config.ts](src/config.ts)`;
95
+ the wiring is in `[memory.ts](src/memory.ts)`.
96
96
 
97
97
  ## Workspace and Assistant skills
98
98
 
99
99
  Every `createAgent` call applies a Mastra `Workspace` by default via
100
- `createWorkspace()` ([`workspaces.ts`](src/workspaces.ts)). The
100
+ `createWorkspace()` (`[workspaces.ts](src/workspaces.ts)`). The
101
101
  workspace is resolved per request and backed by the OBO user's
102
- `WorkspaceClient` through [`filesystems.ts`](src/filesystems.ts).
102
+ `WorkspaceClient` through `[filesystems.ts](src/filesystems.ts)`.
103
103
 
104
104
  Unless you opt out, `assistantSkills: true` mounts read-only Databricks
105
105
  trees where Mastra looks for `SKILL.md` files:
106
106
 
107
- | Databricks path | Workspace mount |
108
- | --- | --- |
109
- | `/Workspace/.assistant/skills` | `/workspace_skills` |
107
+ | Databricks path | Workspace mount |
108
+ | ---------------------------------- | ------------------------ |
109
+ | `/Workspace/.assistant/skills` | `/workspace_skills` |
110
110
  | `/Users/<email>/.assistant/skills` | `/workspace_user_skills` |
111
111
 
112
112
  Production mounts require `workspace` or `all-apis` on the forwarded
113
- access token. [`server.ts`](src/server.ts) stamps parsed scopes on
113
+ access token. `[server.ts](src/server.ts)` stamps parsed scopes on
114
114
  `MASTRA_SCOPES_KEY` using `tokenUtils` from `@dbx-tools/shared`.
115
115
  `NODE_ENV=development` skips the scope gate.
116
116
 
@@ -176,10 +176,10 @@ management (registered alongside `/route/history`):
176
176
  Each thread is auto-titled from its opening turn (Mastra's
177
177
  `generateTitle`), but titling runs on the **small / fast chat tier**
178
178
  (`ModelClass.ChatFast`) via the dedicated summarizer in
179
- [`summarize.ts`](src/summarize.ts) - not the agent's primary model - so
179
+ `[summarize.ts](src/summarize.ts)` - not the agent's primary model - so
180
180
  naming a conversation never spends the heavyweight model. The route
181
- handlers live in [`threads.ts`](src/threads.ts); the thread-id
182
- resolution is in [`server.ts`](src/server.ts). The drop-in `MastraChat`
181
+ handlers live in `[threads.ts](src/threads.ts)`; the thread-id
182
+ resolution is in `[server.ts](src/server.ts)`. The drop-in `MastraChat`
183
183
  from `@dbx-tools/appkit-mastra-ui` renders the whole conversation sidebar
184
184
  over these routes with no extra wiring.
185
185
 
@@ -192,8 +192,7 @@ the small / fast chat tier instead of the agent's primary model. It takes
192
192
  items") and a `maxWords` soft cap, and returns `{ summary }`. The same
193
193
  small-tier summarizer backs conversation titling above. Shadow it by
194
194
  defining a same-named tool in `config.tools` (or a per-agent `tools`), and
195
- reuse it programmatically via the exported `summarizeText(config, text,
196
- opts)` / `buildSummarizeTool(config)`.
195
+ reuse it programmatically via the exported `summarizeText(config, text, opts)` / `buildSummarizeTool(config)`.
197
196
 
198
197
  ## The `tools(plugins)` callback
199
198
 
@@ -263,9 +262,9 @@ AppKit's stock `genie()` plugin is honored only for its `spaces` config
263
262
  directly via `@dbx-tools/genie` (`genieEventChat`) and the workspace
264
263
  `statementExecution` API. Each Genie turn forwards its wire
265
264
  `GenieChatEvent`s through `ctx.writer` for live UI progress. The exact
266
- tool set, ids, and writer-event contract live in [`genie.ts`](src/genie.ts)
265
+ tool set, ids, and writer-event contract live in `[genie.ts](src/genie.ts)`
267
266
  and the `GenieWriterEvent` docs in
268
- [`@dbx-tools/appkit-mastra-shared`](../appkit-mastra-shared).
267
+ `[@dbx-tools/appkit-mastra-shared](../appkit-mastra-shared)`.
269
268
 
270
269
  ## Embeds
271
270
 
@@ -283,8 +282,8 @@ The host UI splits the assistant text on these markers and long-polls
283
282
  the plugin's generic `${basePath}/embed/:type/:id` route until each
284
283
  entry settles. Cache entries carry a 1h TTL; after expiry the route
285
284
  404s and the marker falls through harmlessly. Implementation and the
286
- placement contract are in [`chart.ts`](src/chart.ts); the wire shapes
287
- are in [`@dbx-tools/appkit-mastra-shared`](../appkit-mastra-shared).
285
+ placement contract are in `[chart.ts](src/chart.ts)`; the wire shapes
286
+ are in `[@dbx-tools/appkit-mastra-shared](../appkit-mastra-shared)`.
288
287
 
289
288
  ## Feedback (MLflow)
290
289
 
@@ -312,14 +311,14 @@ Databricks MLflow REST API. Trace export is asynchronous, so a
312
311
  just-finished trace may not exist yet - the log call retries briefly on
313
312
  "not found" and otherwise fails softly (the chat stays usable). The
314
313
  current state is published to the client as
315
- `clientConfig().feedbackEnabled`; see [`mlflow.ts`](src/mlflow.ts) and
316
- the UI wiring in [`@dbx-tools/appkit-mastra-ui`](../appkit-mastra-ui).
314
+ `clientConfig().feedbackEnabled`; see `[mlflow.ts](src/mlflow.ts)` and
315
+ the UI wiring in `[@dbx-tools/appkit-mastra-ui](../appkit-mastra-ui)`.
317
316
 
318
317
  ## Model resolution
319
318
 
320
319
  Each agent call resolves a model lazily (so concurrent requests keep
321
320
  distinct user identities), delegating to
322
- [`@dbx-tools/model`](../model) for the actual workspace-aware selection
321
+ `[@dbx-tools/model](../model)` for the actual workspace-aware selection
323
322
  (fuzzy name matching, score-based class classification, offline
324
323
  fallbacks). Its exports are re-exported here, so `ModelClass` /
325
324
  `modelForClass` and friends are available straight off
@@ -341,7 +340,7 @@ const classifier = createAgent({
341
340
 
342
341
  Per-request overrides arrive via the `X-Mastra-Model` header, a
343
342
  `?model=` query, or a `model` / `modelId` body field (see
344
- [`serving.ts`](src/serving.ts)). The cached endpoint catalogue is
343
+ `[serving.ts](src/serving.ts)`). The cached endpoint catalogue is
345
344
  exposed at `GET ${basePath}/models` for model pickers; the same data is
346
345
  available in-process from a sibling plugin via
347
346
  `appkitUtils.require(this.context, mastra).exports()`.
@@ -349,7 +348,7 @@ available in-process from a sibling plugin via
349
348
  ## MCP server
350
349
 
351
350
  The plugin's agents are exposed as a Mastra
352
- [`MCPServer`](https://mastra.ai/docs/tools-mcp/mcp-overview) **by
351
+ `[MCPServer](https://mastra.ai/docs/tools-mcp/mcp-overview)` **by
353
352
  default** so external MCP clients - Claude Desktop, Cursor, the Mastra
354
353
  playground, or another agent - can call them over the standard MCP
355
354
  transports. The server is registered on the Mastra instance via
@@ -398,10 +397,10 @@ paths are available in-process via
398
397
  ## Client API surface (`apiAccess`)
399
398
 
400
399
  `@mastra/express` registers its full management route table under the
401
- plugin mount - agent inference *plus* admin / mutating routes (direct
400
+ plugin mount - agent inference _plus_ admin / mutating routes (direct
402
401
  tool execution, workflow control, raw memory read/write, telemetry,
403
402
  logs, scores). AppKit authenticates every request as the OBO user, but
404
- does not restrict *which* of those a browser client may call.
403
+ does not restrict _which_ of those a browser client may call.
405
404
 
406
405
  `config.apiAccess` gates that surface at the plugin's dispatch point:
407
406
 
@@ -421,7 +420,7 @@ mastra({ agents: support, apiAccess: "full" }); // opt into the full API
421
420
  ```
422
421
 
423
422
  The gate is a pure allowlist (`isMastraRequestAllowed` in
424
- [`server.ts`](src/server.ts)); the browser chat client only ever uses
423
+ `[server.ts](src/server.ts)`); the browser chat client only ever uses
425
424
  the agent stream and the `/route/*` routes, so the default breaks
426
425
  nothing.
427
426
 
package/dist/index.js CHANGED
@@ -882,6 +882,7 @@ const ASSISTANT_WORKSPACE_SKILLS_MOUNT = "/workspace_skills";
882
882
  const ASSISTANT_USER_SKILLS_MOUNT = "/workspace_user_skills";
883
883
  /** OAuth scopes that gate Databricks workspace file mounts. */
884
884
  const WORKSPACE_FILE_SCOPES = ["workspace", "all-apis"];
885
+ const log$8 = logUtils.logger("mastra/workspaces");
885
886
  /**
886
887
  * Create a Mastra {@link Workspace} with per-request Databricks mounts.
887
888
  *
@@ -907,6 +908,16 @@ function createWorkspace(options = {}) {
907
908
  const { id, name } = resolveWorkspaceIdentity(options);
908
909
  const resolvers = buildMountResolvers(options);
909
910
  const skills = options.skills ?? (resolvers.length > 0 ? buildWorkspaceSkillsResolver(resolvers) : void 0);
911
+ log$8.debug("workspace:create", {
912
+ id,
913
+ name,
914
+ resolverCount: resolvers.length,
915
+ assistantSkills: options.assistantSkills !== false,
916
+ customMountResolvers: options.mounts?.length ?? 0,
917
+ customSkillsResolver: Boolean(options.skills),
918
+ checkSkillFileMtime: options.checkSkillFileMtime ?? options.assistantSkills !== false,
919
+ bm25: options.bm25 !== false
920
+ });
910
921
  return new Workspace({
911
922
  id,
912
923
  name,
@@ -943,18 +954,39 @@ function hasWorkspaceFileScope(requestContext) {
943
954
  function resolveAssistantSkillsMounts(context) {
944
955
  const mounts = {};
945
956
  const requestContext = context.requestContext;
946
- if (!shouldMountAssistantSkills(requestContext)) return {
947
- mounts,
948
- skillPaths: []
949
- };
950
- const client = requestContext.get(MASTRA_USER_KEY)?.executionContext.client;
951
- if (!client) return {
952
- mounts,
953
- skillPaths: []
954
- };
957
+ if (!shouldMountAssistantSkills(requestContext)) {
958
+ log$8.debug("assistant-skills:skipped", {
959
+ reason: !requestContext ? "no-request-context" : "missing-workspace-scope",
960
+ nodeEnv: process.env.NODE_ENV,
961
+ scopes: context.requestContext?.get(MASTRA_SCOPES_KEY)
962
+ });
963
+ return {
964
+ mounts,
965
+ skillPaths: []
966
+ };
967
+ }
968
+ const user = requestContext.get(MASTRA_USER_KEY);
969
+ const client = user?.executionContext.client;
970
+ if (!client) {
971
+ log$8.debug("assistant-skills:skipped", {
972
+ reason: "missing-obo-client",
973
+ userId: user?.id
974
+ });
975
+ return {
976
+ mounts,
977
+ skillPaths: []
978
+ };
979
+ }
955
980
  mounts[ASSISTANT_WORKSPACE_SKILLS_MOUNT] = readOnlyDatabricksFilesystem(client, ASSISTANT_SHARED_SKILLS_PATH);
956
981
  const email = resolveScopedEmail(requestContext);
957
982
  if (email) mounts[ASSISTANT_USER_SKILLS_MOUNT] = readOnlyDatabricksFilesystem(client, userAssistantSkillsPath(email));
983
+ log$8.debug("assistant-skills:mounted", {
984
+ sharedPath: ASSISTANT_SHARED_SKILLS_PATH,
985
+ sharedMount: ASSISTANT_WORKSPACE_SKILLS_MOUNT,
986
+ userMount: email ? ASSISTANT_USER_SKILLS_MOUNT : void 0,
987
+ userPath: email ? userAssistantSkillsPath(email) : void 0,
988
+ mountKeys: Object.keys(mounts)
989
+ });
958
990
  return {
959
991
  mounts,
960
992
  skillPaths: Object.keys(mounts)
@@ -980,6 +1012,12 @@ function buildMountResolvers(options) {
980
1012
  const { assistantSkills = true, mounts } = options;
981
1013
  if (assistantSkills) resolvers.push(resolveAssistantSkillsMounts);
982
1014
  if (mounts?.length) resolvers.push(...mounts);
1015
+ log$8.debug("mounts:resolvers", {
1016
+ assistantSkills,
1017
+ builtInResolver: assistantSkills,
1018
+ customResolverCount: mounts?.length ?? 0,
1019
+ totalResolverCount: resolvers.length
1020
+ });
983
1021
  return resolvers;
984
1022
  }
985
1023
  /**
@@ -1012,11 +1050,20 @@ function readOnlyDatabricksFilesystem(client, basePath) {
1012
1050
  async function resolveWorkspaceContribution(resolvers, context) {
1013
1051
  const mounts = {};
1014
1052
  const skillPaths = [];
1015
- for (const resolver of resolvers) {
1053
+ for (const [index, resolver] of resolvers.entries()) {
1016
1054
  const contribution = await resolver(context);
1055
+ log$8.debug("mounts:resolver", {
1056
+ index,
1057
+ mountKeys: Object.keys(contribution.mounts),
1058
+ skillPaths: contribution.skillPaths ?? []
1059
+ });
1017
1060
  Object.assign(mounts, contribution.mounts);
1018
1061
  if (contribution.skillPaths?.length) skillPaths.push(...contribution.skillPaths);
1019
1062
  }
1063
+ log$8.debug("mounts:merged", {
1064
+ mountKeys: Object.keys(mounts),
1065
+ skillPaths
1066
+ });
1020
1067
  return {
1021
1068
  mounts,
1022
1069
  skillPaths
@@ -1030,7 +1077,12 @@ async function resolveWorkspaceContribution(resolvers, context) {
1030
1077
  */
1031
1078
  async function resolveWorkspaceFilesystem(resolvers, context) {
1032
1079
  const { mounts } = await resolveWorkspaceContribution(resolvers, context);
1033
- if (Object.keys(mounts).length === 0) return emptyFilesystem();
1080
+ const mountKeys = Object.keys(mounts);
1081
+ if (mountKeys.length === 0) {
1082
+ log$8.debug("filesystem:empty", { hasRequestContext: Boolean(context.requestContext) });
1083
+ return emptyFilesystem();
1084
+ }
1085
+ log$8.debug("filesystem:composite", { mountKeys });
1034
1086
  return new CompositeFilesystem({ mounts });
1035
1087
  }
1036
1088
  /**
@@ -1040,6 +1092,7 @@ async function resolveWorkspaceFilesystem(resolvers, context) {
1040
1092
  function buildWorkspaceSkillsResolver(resolvers) {
1041
1093
  return async (context) => {
1042
1094
  const { skillPaths } = await resolveWorkspaceContribution(resolvers, context);
1095
+ log$8.debug("skills:resolved", { skillPaths });
1043
1096
  return skillPaths ?? [];
1044
1097
  };
1045
1098
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@dbx-tools/appkit-mastra",
3
- "version": "0.1.96",
3
+ "version": "0.1.98",
4
4
  "dependencies": {
5
5
  "@databricks/sdk-experimental": "^0.17.0",
6
- "@dbx-tools/appkit-mastra-shared": "0.1.96",
7
- "@dbx-tools/genie": "0.1.96",
8
- "@dbx-tools/genie-shared": "0.1.96",
9
- "@dbx-tools/model": "0.1.96",
10
- "@dbx-tools/shared": "0.1.96",
6
+ "@dbx-tools/appkit-mastra-shared": "0.1.98",
7
+ "@dbx-tools/genie": "0.1.98",
8
+ "@dbx-tools/genie-shared": "0.1.98",
9
+ "@dbx-tools/model": "0.1.98",
10
+ "@dbx-tools/shared": "0.1.98",
11
11
  "@mastra/ai-sdk": "^1.6.0",
12
12
  "@mastra/core": "^1.47.0",
13
13
  "@mastra/express": "^1.4.2",