@dbx-tools/appkit-mastra 0.1.75 → 0.1.77

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 +3 -0
  2. package/dist/index.js +129 -38
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -117,6 +117,9 @@ management (registered alongside `/route/history`):
117
117
  - `DELETE /route/threads` - delete the targeted thread (id from the
118
118
  thread-selection header). Ownership is enforced server-side: a thread
119
119
  is only removed when it belongs to the caller.
120
+ - `PATCH /route/threads` - rename the targeted thread (id from the
121
+ thread-selection header) to the `{ title }` in the JSON body. Same
122
+ ownership enforcement; existing thread metadata is preserved.
120
123
 
121
124
  Each thread is auto-titled from its opening turn (Mastra's
122
125
  `generateTitle`), but titling runs on the **small / fast chat tier**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ChartSchema, ChartTypeSchema, DEFAULT_COMMENT_NAME, DEFAULT_FEEDBACK_NAME, MASTRA_ROUTES, MLFLOW_TRACE_ID_HEADER, MODEL_OVERRIDE_BODY_FIELDS, MODEL_OVERRIDE_HEADER, MODEL_OVERRIDE_QUERY, MastraFeedbackRequestSchema, THREAD_ID_HEADER, THREAD_ID_QUERY } from "@dbx-tools/appkit-mastra-shared";
1
+ import { ChartSchema, ChartTypeSchema, DEFAULT_COMMENT_NAME, DEFAULT_FEEDBACK_NAME, MASTRA_ROUTES, MLFLOW_TRACE_ID_HEADER, MODEL_OVERRIDE_BODY_FIELDS, MODEL_OVERRIDE_HEADER, MODEL_OVERRIDE_QUERY, MastraFeedbackRequestSchema, MastraUpdateThreadRequestSchema, THREAD_ID_HEADER, THREAD_ID_QUERY } from "@dbx-tools/appkit-mastra-shared";
2
2
  import { DEFAULT_FUZZY_THRESHOLD, DEFAULT_MODEL_CACHE_TTL_MS, FALLBACK_MODEL_IDS, ModelClass, ModelClass as ModelClass$1, clearServingEndpointsCache, listServingEndpoints, parseModelClass, selectModel } from "@dbx-tools/model";
3
3
  import { apiUtils, appkitUtils, commonUtils, httpUtils, logUtils, netUtils, projectUtils, stringUtils } from "@dbx-tools/shared";
4
4
  import { Agent } from "@mastra/core/agent";
@@ -226,8 +226,8 @@ const SERVING_ENDPOINTS_PATH_PREFIX = "/serving-endpoints/";
226
226
  *
227
227
  * Safe to call from any hot path: {@link commonUtils.memoize} ensures
228
228
  * the wrapper is installed at most once per process, so subsequent
229
- * calls collapse to a single cached promise even when
230
- * {@link buildModel} fires on every agent step.
229
+ * calls are a no-op even when {@link buildModel} fires on every agent
230
+ * step.
231
231
  */
232
232
  const setupFetchInterceptor = commonUtils.memoize(() => {
233
233
  const log = logUtils.logger("mastra/llm");
@@ -3788,6 +3788,26 @@ function attachRoutePatchMiddleware(app) {
3788
3788
 
3789
3789
  //#endregion
3790
3790
  //#region packages/appkit-mastra/src/threads.ts
3791
+ /**
3792
+ * Conversation-thread listing exposed as a Mastra custom API route.
3793
+ *
3794
+ * Backed entirely by native Mastra: looks up the active agent by id,
3795
+ * asks its `Memory` instance to `listThreads` filtered to the caller's
3796
+ * resource, and shapes each into the JSON-safe {@link MastraThread}
3797
+ * wire type. A sibling `DELETE` removes a single named thread.
3798
+ *
3799
+ * A sibling `DELETE` removes a single named thread; a `PATCH` renames
3800
+ * one ({@link renameThread}).
3801
+ *
3802
+ * Like {@link historyRoute} this registers through Mastra's
3803
+ * `registerApiRoute` so it shares the `MastraServer` auth-middleware
3804
+ * pipeline (in `./server.ts`), which has already stamped the resource
3805
+ * id (`MASTRA_RESOURCE_ID_KEY`) and the targeted thread id
3806
+ * (`MASTRA_THREAD_ID_KEY`, resolved from the thread-selection header /
3807
+ * cookie) on `RequestContext` by the time a handler runs. Resource
3808
+ * scoping lives here so a caller can only ever see, rename, or delete
3809
+ * its own threads; no cookie or user lookups happen in this module.
3810
+ */
3791
3811
  const log = logUtils.logger("mastra/threads");
3792
3812
  /** Default threads page size. */
3793
3813
  const DEFAULT_PER_PAGE = 30;
@@ -3882,7 +3902,47 @@ async function deleteThread(opts) {
3882
3902
  return { deleted: true };
3883
3903
  }
3884
3904
  /**
3885
- * Register the `<path>` Mastra custom API route. Handles two methods
3905
+ * Rename a single thread, returning the updated wire thread.
3906
+ *
3907
+ * Ownership is enforced the same way {@link deleteThread} enforces it:
3908
+ * the title is only changed when the thread belongs to the calling
3909
+ * resource, so a client can't rename another user's conversation by
3910
+ * guessing its id. Existing thread `metadata` is preserved untouched
3911
+ * (Mastra's `updateThread` replaces the row, so it must be passed back
3912
+ * in). Returns `null` when the thread doesn't exist, isn't owned by the
3913
+ * caller, or the agent has no memory configured, letting the route map
3914
+ * that to a 404.
3915
+ */
3916
+ async function renameThread(opts) {
3917
+ const memory = await opts.agent.getMemory();
3918
+ if (!memory) {
3919
+ log.debug("rename:no-memory", { agentId: opts.agent.id });
3920
+ return null;
3921
+ }
3922
+ const existing = await memory.getThreadById({ threadId: opts.threadId });
3923
+ if (!existing || existing.resourceId !== opts.resourceId) {
3924
+ log.debug("rename:not-owned", {
3925
+ agentId: opts.agent.id,
3926
+ threadId: opts.threadId,
3927
+ found: existing !== null
3928
+ });
3929
+ return null;
3930
+ }
3931
+ const startedAt = Date.now();
3932
+ const updated = await memory.updateThread({
3933
+ id: opts.threadId,
3934
+ title: opts.title,
3935
+ metadata: existing.metadata ?? {}
3936
+ });
3937
+ log.info("rename:done", {
3938
+ agentId: opts.agent.id,
3939
+ threadId: opts.threadId,
3940
+ elapsedMs: Date.now() - startedAt
3941
+ });
3942
+ return toWireThread(updated);
3943
+ }
3944
+ /**
3945
+ * Register the `<path>` Mastra custom API route. Handles three methods
3886
3946
  * on the same mount:
3887
3947
  *
3888
3948
  * - `GET`: a page of the resource's conversation threads
@@ -3892,6 +3952,10 @@ async function deleteThread(opts) {
3892
3952
  * `RequestContext` (the auth middleware resolves it the same way it
3893
3953
  * does for streaming and history), so the client deletes any of its
3894
3954
  * threads by stamping the target id - no separate path param.
3955
+ * - `PATCH`: rename the thread named by the thread-selection header /
3956
+ * `?threadId=` query to the `{ title }` in the JSON body
3957
+ * ({@link renameThread}). Targets a thread the same way `DELETE`
3958
+ * does; 404s when the thread isn't owned by the caller.
3895
3959
  *
3896
3960
  * Follows the `@mastra/ai-sdk` agent-binding convention: pass `agent`
3897
3961
  * for a fixed-agent mount, or include `:agentId` in the path for
@@ -3918,40 +3982,67 @@ function threadsRoute(options) {
3918
3982
  resourceId
3919
3983
  };
3920
3984
  };
3921
- return [registerApiRoute(path, {
3922
- method: "GET",
3923
- handler: async (c) => {
3924
- const ctx = resolveContext(c);
3925
- if ("error" in ctx) return ctx.error;
3926
- const payload = await listThreads({
3927
- agent: ctx.agent,
3928
- resourceId: ctx.resourceId,
3929
- page: parseIntParam(c.req.query("page")),
3930
- perPage: parseIntParam(c.req.query("perPage"))
3931
- });
3932
- return c.json(payload);
3933
- }
3934
- }), registerApiRoute(path, {
3935
- method: "DELETE",
3936
- handler: async (c) => {
3937
- const ctx = resolveContext(c);
3938
- if ("error" in ctx) return ctx.error;
3939
- const threadId = ctx.requestContext.get(MASTRA_THREAD_ID_KEY);
3940
- if (!threadId) return c.json({ error: "thread id missing from request context" }, 400);
3941
- const { deleted } = await deleteThread({
3942
- agent: ctx.agent,
3943
- threadId,
3944
- resourceId: ctx.resourceId
3945
- });
3946
- const payload = {
3947
- ok: true,
3948
- agentId: ctx.agentId,
3949
- threadId,
3950
- deleted
3951
- };
3952
- return c.json(payload);
3953
- }
3954
- })];
3985
+ return [
3986
+ registerApiRoute(path, {
3987
+ method: "GET",
3988
+ handler: async (c) => {
3989
+ const ctx = resolveContext(c);
3990
+ if ("error" in ctx) return ctx.error;
3991
+ const payload = await listThreads({
3992
+ agent: ctx.agent,
3993
+ resourceId: ctx.resourceId,
3994
+ page: parseIntParam(c.req.query("page")),
3995
+ perPage: parseIntParam(c.req.query("perPage"))
3996
+ });
3997
+ return c.json(payload);
3998
+ }
3999
+ }),
4000
+ registerApiRoute(path, {
4001
+ method: "DELETE",
4002
+ handler: async (c) => {
4003
+ const ctx = resolveContext(c);
4004
+ if ("error" in ctx) return ctx.error;
4005
+ const threadId = ctx.requestContext.get(MASTRA_THREAD_ID_KEY);
4006
+ if (!threadId) return c.json({ error: "thread id missing from request context" }, 400);
4007
+ const { deleted } = await deleteThread({
4008
+ agent: ctx.agent,
4009
+ threadId,
4010
+ resourceId: ctx.resourceId
4011
+ });
4012
+ const payload = {
4013
+ ok: true,
4014
+ agentId: ctx.agentId,
4015
+ threadId,
4016
+ deleted
4017
+ };
4018
+ return c.json(payload);
4019
+ }
4020
+ }),
4021
+ registerApiRoute(path, {
4022
+ method: "PATCH",
4023
+ handler: async (c) => {
4024
+ const ctx = resolveContext(c);
4025
+ if ("error" in ctx) return ctx.error;
4026
+ const threadId = ctx.requestContext.get(MASTRA_THREAD_ID_KEY);
4027
+ if (!threadId) return c.json({ error: "thread id missing from request context" }, 400);
4028
+ const body = MastraUpdateThreadRequestSchema.safeParse(await c.req.json());
4029
+ if (!body.success) return c.json({ error: body.error.message }, 400);
4030
+ const thread = await renameThread({
4031
+ agent: ctx.agent,
4032
+ threadId,
4033
+ resourceId: ctx.resourceId,
4034
+ title: body.data.title
4035
+ });
4036
+ if (!thread) return c.json({ error: `Unknown thread "${threadId}"` }, 404);
4037
+ const payload = {
4038
+ ok: true,
4039
+ agentId: ctx.agentId,
4040
+ thread
4041
+ };
4042
+ return c.json(payload);
4043
+ }
4044
+ })
4045
+ ];
3955
4046
  }
3956
4047
  /** Coerce a Mastra `StorageThreadType` into the JSON-safe wire shape. */
3957
4048
  function toWireThread(thread) {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@dbx-tools/appkit-mastra",
3
- "version": "0.1.75",
3
+ "version": "0.1.77",
4
4
  "dependencies": {
5
5
  "@databricks/sdk-experimental": "^0.17",
6
- "@dbx-tools/appkit-mastra-shared": "0.1.75",
7
- "@dbx-tools/genie": "0.1.75",
8
- "@dbx-tools/genie-shared": "0.1.75",
9
- "@dbx-tools/model": "0.1.75",
10
- "@dbx-tools/shared": "0.1.75",
6
+ "@dbx-tools/appkit-mastra-shared": "0.1.77",
7
+ "@dbx-tools/genie": "0.1.77",
8
+ "@dbx-tools/genie-shared": "0.1.77",
9
+ "@dbx-tools/model": "0.1.77",
10
+ "@dbx-tools/shared": "0.1.77",
11
11
  "@mastra/ai-sdk": "^1",
12
12
  "@mastra/core": "^1",
13
13
  "@mastra/express": "^1",