@dbx-tools/appkit-mastra 0.3.44 → 0.4.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.
Files changed (56) hide show
  1. package/lib/index.d.ts +71 -0
  2. package/lib/index.js +56 -0
  3. package/lib/src/agents.d.ts +347 -0
  4. package/lib/src/agents.js +554 -0
  5. package/lib/src/chart.d.ts +192 -0
  6. package/lib/src/chart.js +638 -0
  7. package/lib/src/config.d.ts +479 -0
  8. package/lib/src/config.js +190 -0
  9. package/lib/src/defaults.d.ts +68 -0
  10. package/lib/src/defaults.js +107 -0
  11. package/lib/src/filesystems.d.ts +208 -0
  12. package/lib/src/filesystems.js +958 -0
  13. package/lib/src/genie.d.ts +166 -0
  14. package/lib/src/genie.js +969 -0
  15. package/lib/src/history.d.ts +97 -0
  16. package/lib/src/history.js +264 -0
  17. package/lib/src/mcp.d.ts +66 -0
  18. package/lib/src/mcp.js +65 -0
  19. package/lib/src/memory.d.ts +111 -0
  20. package/lib/src/memory.js +275 -0
  21. package/lib/src/mlflow.d.ts +63 -0
  22. package/lib/src/mlflow.js +117 -0
  23. package/lib/src/model.d.ts +62 -0
  24. package/lib/src/model.js +168 -0
  25. package/lib/src/observability.d.ts +81 -0
  26. package/lib/src/observability.js +98 -0
  27. package/lib/src/pagination.d.ts +23 -0
  28. package/lib/src/pagination.js +31 -0
  29. package/lib/src/plugin.d.ts +352 -0
  30. package/lib/src/plugin.js +1015 -0
  31. package/lib/src/processors.d.ts +62 -0
  32. package/lib/src/processors.js +162 -0
  33. package/lib/src/rest.d.ts +36 -0
  34. package/lib/src/rest.js +46 -0
  35. package/lib/src/server.d.ts +155 -0
  36. package/lib/src/server.js +336 -0
  37. package/lib/src/serving-sanitize.d.ts +104 -0
  38. package/lib/src/serving-sanitize.js +228 -0
  39. package/lib/src/serving.d.ts +61 -0
  40. package/lib/src/serving.js +78 -0
  41. package/lib/src/statement.d.ts +51 -0
  42. package/lib/src/statement.js +83 -0
  43. package/lib/src/storage-schema.d.ts +14 -0
  44. package/lib/src/storage-schema.js +34 -0
  45. package/lib/src/summarize.d.ts +70 -0
  46. package/lib/src/summarize.js +142 -0
  47. package/lib/src/threads.d.ts +109 -0
  48. package/lib/src/threads.js +301 -0
  49. package/lib/src/validation.d.ts +19 -0
  50. package/lib/src/validation.js +17 -0
  51. package/lib/src/workspaces.d.ts +68 -0
  52. package/lib/src/workspaces.js +246 -0
  53. package/lib/src/writer.d.ts +25 -0
  54. package/lib/src/writer.js +40 -0
  55. package/lib/tsconfig.tsbuildinfo +1 -0
  56. package/package.json +17 -13
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Thread history loader exposed as a Mastra custom API route.
3
+ *
4
+ * Backed entirely by native Mastra: looks up the active agent by id,
5
+ * asks its `Memory` instance to `recall` a page of `MastraDBMessage`s,
6
+ * and converts the result to AI SDK V5 `UIMessage`s with the official
7
+ * {@link toAISdkV5Messages} helper from `@mastra/ai-sdk/ui`. No direct
8
+ * database reads.
9
+ *
10
+ * The route is registered through {@link historyRoute} as a Mastra
11
+ * `registerApiRoute` so it sits in the same dispatcher pipeline as
12
+ * the standard agent routes. That means the `MastraServer` auth
13
+ * middleware (in `./server.ts`) has already populated `RequestContext` with
14
+ * `MASTRA_THREAD_ID_KEY` and `MASTRA_RESOURCE_ID_KEY` by the time
15
+ * the handler runs - no cookie or user lookups happen here, and the
16
+ * session-cookie logic stays the single source of truth in `server.ts`.
17
+ *
18
+ * @module
19
+ */
20
+ import type { MastraHistoryResponse } from "@dbx-tools/shared-mastra";
21
+ import type { Agent } from "@mastra/core/agent";
22
+ /** Inputs accepted by {@link loadHistory}. */
23
+ export interface LoadHistoryOptions {
24
+ agent: Agent;
25
+ threadId: string;
26
+ resourceId?: string;
27
+ page?: number;
28
+ perPage?: number;
29
+ /** When true, returns the *oldest* page first (chronological). */
30
+ ascending?: boolean;
31
+ }
32
+ /**
33
+ * Fetch a page of UI-formatted messages for a thread.
34
+ *
35
+ * Uses the agent's resolved `Memory` (`getMemory()`) so per-agent
36
+ * storage namespaces (`mastra_<agentId>` schemas) and any future
37
+ * memory-side filters apply automatically. When the agent has no
38
+ * memory configured the response is a successful empty page so
39
+ * callers don't have to special-case stateless agents.
40
+ *
41
+ * Pagination is descending-by-default: page 0 is the most recent
42
+ * page, page 1 the page before that, etc. The returned `uiMessages`
43
+ * are always re-sorted into chronological order (oldest -> newest)
44
+ * so the client can prepend them above the existing transcript
45
+ * without sorting locally.
46
+ */
47
+ export declare function loadHistory(opts: LoadHistoryOptions): Promise<MastraHistoryResponse>;
48
+ /** Inputs accepted by {@link clearHistory}. */
49
+ export interface ClearHistoryOptions {
50
+ agent: Agent;
51
+ threadId: string;
52
+ }
53
+ /**
54
+ * Wipe every persisted message tied to a thread. Returns the count
55
+ * of messages that were on the thread at delete time so the caller
56
+ * can render a "cleared N messages" affordance without an
57
+ * additional round-trip.
58
+ *
59
+ * Agents without a configured `Memory` resolve to a no-op (count
60
+ * 0), matching {@link loadHistory}'s "stateless agents return an
61
+ * empty page" stance so callers don't have to special-case them.
62
+ * Threads that don't exist yet are also a successful no-op - the
63
+ * operation is idempotent so the UI can fire-and-forget without
64
+ * tracking thread existence.
65
+ */
66
+ export declare function clearHistory(opts: ClearHistoryOptions): Promise<{
67
+ cleared: number;
68
+ }>;
69
+ /** Options accepted by {@link historyRoute}. */
70
+ export type HistoryRouteOptions = {
71
+ path: `${string}:agentId${string}`;
72
+ agent?: never;
73
+ } | {
74
+ path: string;
75
+ agent: string;
76
+ };
77
+ /**
78
+ * Register the `<path>` Mastra custom API route. Handles two
79
+ * methods on the same mount:
80
+ *
81
+ * - `GET`: return a page of AI SDK V5 `UIMessage`s for the
82
+ * caller's current thread ({@link loadHistory}).
83
+ * - `DELETE`: wipe every persisted message on the caller's
84
+ * thread ({@link clearHistory}). The session cookie that
85
+ * anchors the thread id is left alone so the user keeps the
86
+ * same thread - only the contents go away.
87
+ *
88
+ * Follows the `@mastra/ai-sdk` `chatRoute` convention for agent
89
+ * binding: pass `agent` for a fixed-agent mount, or include
90
+ * `:agentId` in the path for dynamic routing. The plugin registers
91
+ * both `/route/history` (default agent) and `/route/history/:agentId`.
92
+ *
93
+ * The handler reads `threadId` and `resourceId` from `RequestContext`
94
+ * (populated upstream by `MastraServer.registerAuthMiddleware`), so
95
+ * no cookie or user lookups happen here.
96
+ */
97
+ export declare function historyRoute(options: HistoryRouteOptions): import("@mastra/core/server").ApiRoute[];
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Thread history loader exposed as a Mastra custom API route.
3
+ *
4
+ * Backed entirely by native Mastra: looks up the active agent by id,
5
+ * asks its `Memory` instance to `recall` a page of `MastraDBMessage`s,
6
+ * and converts the result to AI SDK V5 `UIMessage`s with the official
7
+ * {@link toAISdkV5Messages} helper from `@mastra/ai-sdk/ui`. No direct
8
+ * database reads.
9
+ *
10
+ * The route is registered through {@link historyRoute} as a Mastra
11
+ * `registerApiRoute` so it sits in the same dispatcher pipeline as
12
+ * the standard agent routes. That means the `MastraServer` auth
13
+ * middleware (in `./server.ts`) has already populated `RequestContext` with
14
+ * `MASTRA_THREAD_ID_KEY` and `MASTRA_RESOURCE_ID_KEY` by the time
15
+ * the handler runs - no cookie or user lookups happen here, and the
16
+ * session-cookie logic stays the single source of truth in `server.ts`.
17
+ *
18
+ * @module
19
+ */
20
+ import { ValidationError } from "@databricks/appkit";
21
+ import { error, log } from "@dbx-tools/shared-core";
22
+ import { toAISdkV5Messages } from "@mastra/ai-sdk/ui";
23
+ import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
24
+ import { registerApiRoute } from "@mastra/core/server";
25
+ import { clampPerPage, parseIntParam } from "./pagination.js";
26
+ const logger = log.logger("mastra/history");
27
+ /** Default history page size; matches the Mastra storage default. */
28
+ const DEFAULT_PER_PAGE = 20;
29
+ /** Hard cap so a misbehaving client can't fetch the whole thread at once. */
30
+ const MAX_PER_PAGE = 200;
31
+ /**
32
+ * Fetch a page of UI-formatted messages for a thread.
33
+ *
34
+ * Uses the agent's resolved `Memory` (`getMemory()`) so per-agent
35
+ * storage namespaces (`mastra_<agentId>` schemas) and any future
36
+ * memory-side filters apply automatically. When the agent has no
37
+ * memory configured the response is a successful empty page so
38
+ * callers don't have to special-case stateless agents.
39
+ *
40
+ * Pagination is descending-by-default: page 0 is the most recent
41
+ * page, page 1 the page before that, etc. The returned `uiMessages`
42
+ * are always re-sorted into chronological order (oldest -> newest)
43
+ * so the client can prepend them above the existing transcript
44
+ * without sorting locally.
45
+ */
46
+ export async function loadHistory(opts) {
47
+ const perPage = clampPerPage(opts.perPage, {
48
+ fallback: DEFAULT_PER_PAGE,
49
+ max: MAX_PER_PAGE,
50
+ });
51
+ const page = Math.max(0, Math.trunc(opts.page ?? 0));
52
+ const memory = await opts.agent.getMemory();
53
+ if (!memory) {
54
+ logger.debug("recall:no-memory", { agentId: opts.agent.id, threadId: opts.threadId });
55
+ return { uiMessages: [], page, perPage, total: 0, hasMore: false };
56
+ }
57
+ const startedAt = Date.now();
58
+ const result = await memory.recall({
59
+ threadId: opts.threadId,
60
+ ...(opts.resourceId ? { resourceId: opts.resourceId } : {}),
61
+ page,
62
+ perPage,
63
+ orderBy: {
64
+ field: "createdAt",
65
+ direction: opts.ascending ? "ASC" : "DESC",
66
+ },
67
+ });
68
+ const chronological = sortChronological(result.messages);
69
+ const uiMessages = toAISdkV5Messages(chronological);
70
+ logger.debug("recall:done", {
71
+ agentId: opts.agent.id,
72
+ threadId: opts.threadId,
73
+ page,
74
+ perPage,
75
+ returned: uiMessages.length,
76
+ total: result.total,
77
+ hasMore: result.hasMore,
78
+ elapsedMs: Date.now() - startedAt,
79
+ });
80
+ return {
81
+ uiMessages,
82
+ page,
83
+ perPage,
84
+ total: result.total,
85
+ hasMore: result.hasMore,
86
+ };
87
+ }
88
+ /**
89
+ * Wipe every persisted message tied to a thread. Returns the count
90
+ * of messages that were on the thread at delete time so the caller
91
+ * can render a "cleared N messages" affordance without an
92
+ * additional round-trip.
93
+ *
94
+ * Agents without a configured `Memory` resolve to a no-op (count
95
+ * 0), matching {@link loadHistory}'s "stateless agents return an
96
+ * empty page" stance so callers don't have to special-case them.
97
+ * Threads that don't exist yet are also a successful no-op - the
98
+ * operation is idempotent so the UI can fire-and-forget without
99
+ * tracking thread existence.
100
+ */
101
+ export async function clearHistory(opts) {
102
+ const memory = await opts.agent.getMemory();
103
+ if (!memory) {
104
+ logger.debug("clear:no-memory", { agentId: opts.agent.id, threadId: opts.threadId });
105
+ return { cleared: 0 };
106
+ }
107
+ // Mastra's `deleteThread` cascades to the message table, so we
108
+ // can't ask for a count after the fact. Read it pre-delete with a
109
+ // one-page recall sized to fit common threads in a single round
110
+ // trip; the value is for telemetry / UI, not correctness.
111
+ let cleared = 0;
112
+ try {
113
+ const probe = await memory.recall({
114
+ threadId: opts.threadId,
115
+ page: 0,
116
+ perPage: 1,
117
+ });
118
+ cleared = probe.total;
119
+ }
120
+ catch (err) {
121
+ // A missing-thread error is the happy-path "nothing to count";
122
+ // every other error is logged but doesn't block the delete.
123
+ logger.debug("clear:probe-failed", {
124
+ agentId: opts.agent.id,
125
+ threadId: opts.threadId,
126
+ error: error.errorMessage(err),
127
+ });
128
+ }
129
+ const startedAt = Date.now();
130
+ try {
131
+ await memory.deleteThread(opts.threadId);
132
+ }
133
+ catch (err) {
134
+ // Mastra's `deleteThread` raises when the thread row was never
135
+ // created (e.g. clearing an empty session). Surface as a soft
136
+ // warn and treat as success - the user-facing semantic is
137
+ // "history is now empty" which is already true.
138
+ logger.warn("clear:delete-soft-failed", {
139
+ agentId: opts.agent.id,
140
+ threadId: opts.threadId,
141
+ error: error.errorMessage(err),
142
+ });
143
+ }
144
+ logger.info("clear:done", {
145
+ agentId: opts.agent.id,
146
+ threadId: opts.threadId,
147
+ cleared,
148
+ elapsedMs: Date.now() - startedAt,
149
+ });
150
+ return { cleared };
151
+ }
152
+ /**
153
+ * Register the `<path>` Mastra custom API route. Handles two
154
+ * methods on the same mount:
155
+ *
156
+ * - `GET`: return a page of AI SDK V5 `UIMessage`s for the
157
+ * caller's current thread ({@link loadHistory}).
158
+ * - `DELETE`: wipe every persisted message on the caller's
159
+ * thread ({@link clearHistory}). The session cookie that
160
+ * anchors the thread id is left alone so the user keeps the
161
+ * same thread - only the contents go away.
162
+ *
163
+ * Follows the `@mastra/ai-sdk` `chatRoute` convention for agent
164
+ * binding: pass `agent` for a fixed-agent mount, or include
165
+ * `:agentId` in the path for dynamic routing. The plugin registers
166
+ * both `/route/history` (default agent) and `/route/history/:agentId`.
167
+ *
168
+ * The handler reads `threadId` and `resourceId` from `RequestContext`
169
+ * (populated upstream by `MastraServer.registerAuthMiddleware`), so
170
+ * no cookie or user lookups happen here.
171
+ */
172
+ export function historyRoute(options) {
173
+ const { path } = options;
174
+ const fixedAgent = "agent" in options ? options.agent : undefined;
175
+ if (!fixedAgent && !path.includes(":agentId")) {
176
+ throw ValidationError.invalidValue("historyRoute.path", path, "a path containing `:agentId`, or an explicit `agent`");
177
+ }
178
+ // Tiny resolver shared by GET / DELETE: derive the active agent
179
+ // and thread id, returning a JSON error response when either is
180
+ // missing. Keeps both handlers thin and gives them identical
181
+ // validation behaviour.
182
+ const resolveContext = (c) => {
183
+ const mastra = c.get("mastra");
184
+ const requestContext = c.get("requestContext");
185
+ const agentId = fixedAgent ?? c.req.param("agentId");
186
+ if (!agentId) {
187
+ return { error: c.json({ error: "agentId is required" }, 400) };
188
+ }
189
+ const agent = mastra.getAgentById(agentId);
190
+ if (!agent) {
191
+ return {
192
+ error: c.json({ error: `Unknown agent "${agentId}"` }, 404),
193
+ };
194
+ }
195
+ const threadId = requestContext.get(MASTRA_THREAD_ID_KEY);
196
+ if (!threadId) {
197
+ return {
198
+ error: c.json({ error: "thread id missing from request context" }, 400),
199
+ };
200
+ }
201
+ const resourceId = requestContext.get(MASTRA_RESOURCE_ID_KEY);
202
+ return { agentId, agent, threadId, resourceId };
203
+ };
204
+ return [
205
+ registerApiRoute(path, {
206
+ method: "GET",
207
+ handler: async (c) => {
208
+ const ctx = resolveContext(c);
209
+ if ("error" in ctx)
210
+ return ctx.error;
211
+ const payload = await loadHistory({
212
+ agent: ctx.agent,
213
+ threadId: ctx.threadId,
214
+ ...(ctx.resourceId ? { resourceId: ctx.resourceId } : {}),
215
+ page: parseIntParam(c.req.query("page")),
216
+ perPage: parseIntParam(c.req.query("perPage")),
217
+ });
218
+ return c.json(payload);
219
+ },
220
+ }),
221
+ registerApiRoute(path, {
222
+ method: "DELETE",
223
+ handler: async (c) => {
224
+ const ctx = resolveContext(c);
225
+ if ("error" in ctx)
226
+ return ctx.error;
227
+ const { cleared } = await clearHistory({
228
+ agent: ctx.agent,
229
+ threadId: ctx.threadId,
230
+ });
231
+ const payload = {
232
+ ok: true,
233
+ agentId: ctx.agentId,
234
+ threadId: ctx.threadId,
235
+ cleared,
236
+ };
237
+ return c.json(payload);
238
+ },
239
+ }),
240
+ ];
241
+ }
242
+ /**
243
+ * Sort messages oldest-first by `createdAt`, falling back to whatever
244
+ * order the storage returned them in. The native `recall` call honors
245
+ * `orderBy` but doesn't guarantee a stable secondary sort, so we
246
+ * normalize here before handing the page to the AI SDK converter.
247
+ */
248
+ function sortChronological(messages) {
249
+ return [...messages].sort((a, b) => {
250
+ const ta = toEpoch(a.createdAt);
251
+ const tb = toEpoch(b.createdAt);
252
+ return ta - tb;
253
+ });
254
+ }
255
+ function toEpoch(value) {
256
+ if (value instanceof Date)
257
+ return value.getTime();
258
+ if (typeof value === "string" || typeof value === "number") {
259
+ const parsed = new Date(value).getTime();
260
+ return Number.isNaN(parsed) ? 0 : parsed;
261
+ }
262
+ return 0;
263
+ }
264
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGlzdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oaXN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFFSCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQU1wRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUd0RCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUU1RixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUV2RCxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUUzRCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7QUFFNUMscUVBQXFFO0FBQ3JFLE1BQU0sZ0JBQWdCLEdBQUcsRUFBRSxDQUFDO0FBQzVCLDZFQUE2RTtBQUM3RSxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUM7QUFhekI7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFdBQVcsQ0FBQyxJQUF3QjtJQUN4RCxNQUFNLE9BQU8sR0FBRyxZQUFZLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRTtRQUN6QyxRQUFRLEVBQUUsZ0JBQWdCO1FBQzFCLEdBQUcsRUFBRSxZQUFZO0tBQ2xCLENBQUMsQ0FBQztJQUNILE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3JELE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtB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QUFTLE9BQU8sQ0FBQyxLQUFjO0lBQzdCLElBQUksS0FBSyxZQUFZLElBQUk7UUFBRSxPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNsRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUMzRCxNQUFNLE1BQU0sR0FBRyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUN6QyxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzNDLENBQUM7SUFDRCxPQUFPLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRocmVhZCBoaXN0b3J5IGxvYWRlciBleHBvc2VkIGFzIGEgTWFzdHJhIGN1c3RvbSBBUEkgcm91dGUuXG4gKlxuICogQmFja2VkIGVudGlyZWx5IGJ5IG5hdGl2ZSBNYXN0cmE6IGxvb2tzIHVwIHRoZSBhY3RpdmUgYWdlbnQgYnkgaWQsXG4gKiBhc2tzIGl0cyBgTWVtb3J5YCBpbnN0YW5jZSB0byBgcmVjYWxsYCBhIHBhZ2Ugb2YgYE1hc3RyYURCTWVzc2FnZWBzLFxuICogYW5kIGNvbnZlcnRzIHRoZSByZXN1bHQgdG8gQUkgU0RLIFY1IGBVSU1lc3NhZ2VgcyB3aXRoIHRoZSBvZmZpY2lhbFxuICoge0BsaW5rIHRvQUlTZGtWNU1lc3NhZ2VzfSBoZWxwZXIgZnJvbSBgQG1hc3RyYS9haS1zZGsvdWlgLiBObyBkaXJlY3RcbiAqIGRhdGFiYXNlIHJlYWRzLlxuICpcbiAqIFRoZSByb3V0ZSBpcyByZWdpc3RlcmVkIHRocm91Z2gge0BsaW5rIGhpc3RvcnlSb3V0ZX0gYXMgYSBNYXN0cmFcbiAqIGByZWdpc3RlckFwaVJvdXRlYCBzbyBpdCBzaXRzIGluIHRoZSBzYW1lIGRpc3BhdGNoZXIgcGlwZWxpbmUgYXNcbiAqIHRoZSBzdGFuZGFyZCBhZ2VudCByb3V0ZXMuIFRoYXQgbWVhbnMgdGhlIGBNYXN0cmFTZXJ2ZXJgIGF1dGhcbiAqIG1pZGRsZXdhcmUgKGluIGAuL3NlcnZlci50c2ApIGhhcyBhbHJlYWR5IHBvcHVsYXRlZCBgUmVxdWVzdENvbnRleHRgIHdpdGhcbiAqIGBNQVNUUkFfVEhSRUFEX0lEX0tFWWAgYW5kIGBNQVNUUkFfUkVTT1VSQ0VfSURfS0VZYCBieSB0aGUgdGltZVxuICogdGhlIGhhbmRsZXIgcnVucyAtIG5vIGNvb2tpZSBvciB1c2VyIGxvb2t1cHMgaGFwcGVuIGhlcmUsIGFuZCB0aGVcbiAqIHNlc3Npb24tY29va2llIGxvZ2ljIHN0YXlzIHRoZSBzaW5nbGUgc291cmNlIG9mIHRydXRoIGluIGBzZXJ2ZXIudHNgLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBWYWxpZGF0aW9uRXJyb3IgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBlcnJvciwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB0eXBlIHtcbiAgTWFzdHJhQ2xlYXJIaXN0b3J5UmVzcG9uc2UsXG4gIE1hc3RyYUhpc3RvcnlSZXNwb25zZSxcbiAgTWFzdHJhSGlzdG9yeVVJTWVzc2FnZSxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1hc3RyYVwiO1xuaW1wb3J0IHsgdG9BSVNka1Y1TWVzc2FnZXMgfSBmcm9tIFwiQG1hc3RyYS9haS1zZGsvdWlcIjtcbmltcG9ydCB0eXBlIHsgQWdlbnQgfSBmcm9tIFwiQG1hc3RyYS9jb3JlL2FnZW50XCI7XG5pbXBvcnQgdHlwZSB7IE1hc3RyYURCTWVzc2FnZSB9IGZyb20gXCJAbWFzdHJhL2NvcmUvYWdlbnQvbWVzc2FnZS1saXN0XCI7XG5pbXBvcnQgeyBNQVNUUkFfUkVTT1VSQ0VfSURfS0VZLCBNQVNUUkFfVEhSRUFEX0lEX0tFWSB9IGZyb20gXCJAbWFzdHJhL2NvcmUvcmVxdWVzdC1jb250ZXh0XCI7XG5pbXBvcnQgdHlwZSB7IENvbnRleHRXaXRoTWFzdHJhIH0gZnJvbSBcIkBtYXN0cmEvY29yZS9zZXJ2ZXJcIjtcbmltcG9ydCB7IHJlZ2lzdGVyQXBpUm91dGUgfSBmcm9tIFwiQG1hc3RyYS9jb3JlL3NlcnZlclwiO1xuXG5pbXBvcnQgeyBjbGFtcFBlclBhZ2UsIHBhcnNlSW50UGFyYW0gfSBmcm9tIFwiLi9wYWdpbmF0aW9uXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJtYXN0cmEvaGlzdG9yeVwiKTtcblxuLyoqIERlZmF1bHQgaGlzdG9yeSBwYWdlIHNpemU7IG1hdGNoZXMgdGhlIE1hc3RyYSBzdG9yYWdlIGRlZmF1bHQuICovXG5jb25zdCBERUZBVUxUX1BFUl9QQUdFID0gMjA7XG4vKiogSGFyZCBjYXAgc28gYSBtaXNiZWhhdmluZyBjbGllbnQgY2FuJ3QgZmV0Y2ggdGhlIHdob2xlIHRocmVhZCBhdCBvbmNlLiAqL1xuY29uc3QgTUFYX1BFUl9QQUdFID0gMjAwO1xuXG4vKiogSW5wdXRzIGFjY2VwdGVkIGJ5IHtAbGluayBsb2FkSGlzdG9yeX0uICovXG5leHBvcnQgaW50ZXJmYWNlIExvYWRIaXN0b3J5T3B0aW9ucyB7XG4gIGFnZW50OiBBZ2VudDtcbiAgdGhyZWFkSWQ6IHN0cmluZztcbiAgcmVzb3VyY2VJZD86IHN0cmluZztcbiAgcGFnZT86IG51bWJlcjtcbiAgcGVyUGFnZT86IG51bWJlcjtcbiAgLyoqIFdoZW4gdHJ1ZSwgcmV0dXJucyB0aGUgKm9sZGVzdCogcGFnZSBmaXJzdCAoY2hyb25vbG9naWNhbCkuICovXG4gIGFzY2VuZGluZz86IGJvb2xlYW47XG59XG5cbi8qKlxuICogRmV0Y2ggYSBwYWdlIG9mIFVJLWZvcm1hdHRlZCBtZXNzYWdlcyBmb3IgYSB0aHJlYWQuXG4gKlxuICogVXNlcyB0aGUgYWdlbnQncyByZXNvbHZlZCBgTWVtb3J5YCAoYGdldE1lbW9yeSgpYCkgc28gcGVyLWFnZW50XG4gKiBzdG9yYWdlIG5hbWVzcGFjZXMgKGBtYXN0cmFfPGFnZW50SWQ+YCBzY2hlbWFzKSBhbmQgYW55IGZ1dHVyZVxuICogbWVtb3J5LXNpZGUgZmlsdGVycyBhcHBseSBhdXRvbWF0aWNhbGx5LiBXaGVuIHRoZSBhZ2VudCBoYXMgbm9cbiAqIG1lbW9yeSBjb25maWd1cmVkIHRoZSByZXNwb25zZSBpcyBhIHN1Y2Nlc3NmdWwgZW1wdHkgcGFnZSBzb1xuICogY2FsbGVycyBkb24ndCBoYXZlIHRvIHNwZWNpYWwtY2FzZSBzdGF0ZWxlc3MgYWdlbnRzLlxuICpcbiAqIFBhZ2luYXRpb24gaXMgZGVzY2VuZGluZy1ieS1kZWZhdWx0OiBwYWdlIDAgaXMgdGhlIG1vc3QgcmVjZW50XG4gKiBwYWdlLCBwYWdlIDEgdGhlIHBhZ2UgYmVmb3JlIHRoYXQsIGV0Yy4gVGhlIHJldHVybmVkIGB1aU1lc3NhZ2VzYFxuICogYXJlIGFsd2F5cyByZS1zb3J0ZWQgaW50byBjaHJvbm9sb2dpY2FsIG9yZGVyIChvbGRlc3QgLT4gbmV3ZXN0KVxuICogc28gdGhlIGNsaWVudCBjYW4gcHJlcGVuZCB0aGVtIGFib3ZlIHRoZSBleGlzdGluZyB0cmFuc2NyaXB0XG4gKiB3aXRob3V0IHNvcnRpbmcgbG9jYWxseS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRIaXN0b3J5KG9wdHM6IExvYWRIaXN0b3J5T3B0aW9ucyk6IFByb21pc2U8TWFzdHJhSGlzdG9yeVJlc3BvbnNlPiB7XG4gIGNvbnN0IHBlclBhZ2UgPSBjbGFtcFBlclBhZ2Uob3B0cy5wZXJQYWdlLCB7XG4gICAgZmFsbGJhY2s6IERFRkFVTFRfUEVSX1BBR0UsXG4gICAgbWF4OiBNQVhfUEVSX1BBR0UsXG4gIH0pO1xuICBjb25zdCBwYWdlID0gTWF0aC5tYXgoMCwgTWF0aC50cnVuYyhvcHRzLnBhZ2UgPz8gMCkpO1xuICBjb25zdCBtZW1vcnkgPSBhd2FpdCBvcHRzLmFnZW50LmdldE1lbW9yeSgpO1xuICBpZiAoIW1lbW9yeSkge1xuICAgIGxvZ2dlci5kZWJ1ZyhcInJlY2FsbDpuby1tZW1vcnlcIiwgeyBhZ2VudElkOiBvcHRzLmFnZW50LmlkLCB0aHJlYWRJZDogb3B0cy50aHJlYWRJZCB9KTtcbiAgICByZXR1cm4geyB1aU1lc3NhZ2VzOiBbXSwgcGFnZSwgcGVyUGFnZSwgdG90YWw6IDAsIGhhc01vcmU6IGZhbHNlIH07XG4gIH1cbiAgY29uc3Qgc3RhcnRlZEF0ID0gRGF0ZS5ub3coKTtcbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgbWVtb3J5LnJlY2FsbCh7XG4gICAgdGhyZWFkSWQ6IG9wdHMudGhyZWFkSWQsXG4gICAgLi4uKG9wdHMucmVzb3VyY2VJZCA/IHsgcmVzb3VyY2VJZDogb3B0cy5yZXNvdXJjZUlkIH0gOiB7fSksXG4gICAgcGFnZSxcbiAgICBwZXJQYWdlLFxuICAgIG9yZGVyQnk6IHtcbiAgICAgIGZpZWxkOiBcImNyZWF0ZWRBdFwiLFxuICAgICAgZGlyZWN0aW9uOiBvcHRzLmFzY2VuZGluZyA/IFwiQVNDXCIgOiBcIkRFU0NcIixcbiAgICB9LFxuICB9KTtcbiAgY29uc3QgY2hyb25vbG9naWNhbCA9IHNvcnRDaHJvbm9sb2dpY2FsKHJlc3VsdC5tZXNzYWdlcyk7XG4gIGNvbnN0IHVpTWVzc2FnZXMgPSB0b0FJU2RrVjVNZXNzYWdlcyhjaHJvbm9sb2dpY2FsKSBhcyB1bmtub3duIGFzIE1hc3RyYUhpc3RvcnlVSU1lc3NhZ2VbXTtcbiAgbG9nZ2VyLmRlYnVnKFwicmVjYWxsOmRvbmVcIiwge1xuICAgIGFnZW50SWQ6IG9wdHMuYWdlbnQuaWQsXG4gICAgdGhyZWFkSWQ6IG9wdHMudGhyZWFkSWQsXG4gICAgcGFnZSxcbiAgICBwZXJQYWdlLFxuICAgIHJldHVybmVkOiB1aU1lc3NhZ2VzLmxlbmd0aCxcbiAgICB0b3RhbDogcmVzdWx0LnRvdGFsLFxuICAgIGhhc01vcmU6IHJlc3VsdC5oYXNNb3JlLFxuICAgIGVsYXBzZWRNczogRGF0ZS5ub3coKSAtIHN0YXJ0ZWRBdCxcbiAgfSk7XG4gIHJldHVybiB7XG4gICAgdWlNZXNzYWdlcyxcbiAgICBwYWdlLFxuICAgIHBlclBhZ2UsXG4gICAgdG90YWw6IHJlc3VsdC50b3RhbCxcbiAgICBoYXNNb3JlOiByZXN1bHQuaGFzTW9yZSxcbiAgfTtcbn1cblxuLyoqIElucHV0cyBhY2NlcHRlZCBieSB7QGxpbmsgY2xlYXJIaXN0b3J5fS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2xlYXJIaXN0b3J5T3B0aW9ucyB7XG4gIGFnZW50OiBBZ2VudDtcbiAgdGhyZWFkSWQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBXaXBlIGV2ZXJ5IHBlcnNpc3RlZCBtZXNzYWdlIHRpZWQgdG8gYSB0aHJlYWQuIFJldHVybnMgdGhlIGNvdW50XG4gKiBvZiBtZXNzYWdlcyB0aGF0IHdlcmUgb24gdGhlIHRocmVhZCBhdCBkZWxldGUgdGltZSBzbyB0aGUgY2FsbGVyXG4gKiBjYW4gcmVuZGVyIGEgXCJjbGVhcmVkIE4gbWVzc2FnZXNcIiBhZmZvcmRhbmNlIHdpdGhvdXQgYW5cbiAqIGFkZGl0aW9uYWwgcm91bmQtdHJpcC5cbiAqXG4gKiBBZ2VudHMgd2l0aG91dCBhIGNvbmZpZ3VyZWQgYE1lbW9yeWAgcmVzb2x2ZSB0byBhIG5vLW9wIChjb3VudFxuICogMCksIG1hdGNoaW5nIHtAbGluayBsb2FkSGlzdG9yeX0ncyBcInN0YXRlbGVzcyBhZ2VudHMgcmV0dXJuIGFuXG4gKiBlbXB0eSBwYWdlXCIgc3RhbmNlIHNvIGNhbGxlcnMgZG9uJ3QgaGF2ZSB0byBzcGVjaWFsLWNhc2UgdGhlbS5cbiAqIFRocmVhZHMgdGhhdCBkb24ndCBleGlzdCB5ZXQgYXJlIGFsc28gYSBzdWNjZXNzZnVsIG5vLW9wIC0gdGhlXG4gKiBvcGVyYXRpb24gaXMgaWRlbXBvdGVudCBzbyB0aGUgVUkgY2FuIGZpcmUtYW5kLWZvcmdldCB3aXRob3V0XG4gKiB0cmFja2luZyB0aHJlYWQgZXhpc3RlbmNlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJIaXN0b3J5KG9wdHM6IENsZWFySGlzdG9yeU9wdGlvbnMpOiBQcm9taXNlPHsgY2xlYXJlZDogbnVtYmVyIH0+IHtcbiAgY29uc3QgbWVtb3J5ID0gYXdhaXQgb3B0cy5hZ2VudC5nZXRNZW1vcnkoKTtcbiAgaWYgKCFtZW1vcnkpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJjbGVhcjpuby1tZW1vcnlcIiwgeyBhZ2VudElkOiBvcHRzLmFnZW50LmlkLCB0aHJlYWRJZDogb3B0cy50aHJlYWRJZCB9KTtcbiAgICByZXR1cm4geyBjbGVhcmVkOiAwIH07XG4gIH1cbiAgLy8gTWFzdHJhJ3MgYGRlbGV0ZVRocmVhZGAgY2FzY2FkZXMgdG8gdGhlIG1lc3NhZ2UgdGFibGUsIHNvIHdlXG4gIC8vIGNhbid0IGFzayBmb3IgYSBjb3VudCBhZnRlciB0aGUgZmFjdC4gUmVhZCBpdCBwcmUtZGVsZXRlIHdpdGggYVxuICAvLyBvbmUtcGFnZSByZWNhbGwgc2l6ZWQgdG8gZml0IGNvbW1vbiB0aHJlYWRzIGluIGEgc2luZ2xlIHJvdW5kXG4gIC8vIHRyaXA7IHRoZSB2YWx1ZSBpcyBmb3IgdGVsZW1ldHJ5IC8gVUksIG5vdCBjb3JyZWN0bmVzcy5cbiAgbGV0IGNsZWFyZWQgPSAwO1xuICB0cnkge1xuICAgIGNvbnN0IHByb2JlID0gYXdhaXQgbWVtb3J5LnJlY2FsbCh7XG4gICAgICB0aHJlYWRJZDogb3B0cy50aHJlYWRJZCxcbiAgICAgIHBhZ2U6IDAsXG4gICAgICBwZXJQYWdlOiAxLFxuICAgIH0pO1xuICAgIGNsZWFyZWQgPSBwcm9iZS50b3RhbDtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgLy8gQSBtaXNzaW5nLXRocmVhZCBlcnJvciBpcyB0aGUgaGFwcHktcGF0aCBcIm5vdGhpbmcgdG8gY291bnRcIjtcbiAgICAvLyBldmVyeSBvdGhlciBlcnJvciBpcyBsb2dnZWQgYnV0IGRvZXNuJ3QgYmxvY2sgdGhlIGRlbGV0ZS5cbiAgICBsb2dnZXIuZGVidWcoXCJjbGVhcjpwcm9iZS1mYWlsZWRcIiwge1xuICAgICAgYWdlbnRJZDogb3B0cy5hZ2VudC5pZCxcbiAgICAgIHRocmVhZElkOiBvcHRzLnRocmVhZElkLFxuICAgICAgZXJyb3I6IGVycm9yLmVycm9yTWVzc2FnZShlcnIpLFxuICAgIH0pO1xuICB9XG5cbiAgY29uc3Qgc3RhcnRlZEF0ID0gRGF0ZS5ub3coKTtcbiAgdHJ5IHtcbiAgICBhd2FpdCBtZW1vcnkuZGVsZXRlVGhyZWFkKG9wdHMudGhyZWFkSWQpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICAvLyBNYXN0cmEncyBgZGVsZXRlVGhyZWFkYCByYWlzZXMgd2hlbiB0aGUgdGhyZWFkIHJvdyB3YXMgbmV2ZXJcbiAgICAvLyBjcmVhdGVkIChlLmcuIGNsZWFyaW5nIGFuIGVtcHR5IHNlc3Npb24pLiBTdXJmYWNlIGFzIGEgc29mdFxuICAgIC8vIHdhcm4gYW5kIHRyZWF0IGFzIHN1Y2Nlc3MgLSB0aGUgdXNlci1mYWNpbmcgc2VtYW50aWMgaXNcbiAgICAvLyBcImhpc3RvcnkgaXMgbm93IGVtcHR5XCIgd2hpY2ggaXMgYWxyZWFkeSB0cnVlLlxuICAgIGxvZ2dlci53YXJuKFwiY2xlYXI6ZGVsZXRlLXNvZnQtZmFpbGVkXCIsIHtcbiAgICAgIGFnZW50SWQ6IG9wdHMuYWdlbnQuaWQsXG4gICAgICB0aHJlYWRJZDogb3B0cy50aHJlYWRJZCxcbiAgICAgIGVycm9yOiBlcnJvci5lcnJvck1lc3NhZ2UoZXJyKSxcbiAgICB9KTtcbiAgfVxuICBsb2dnZXIuaW5mbyhcImNsZWFyOmRvbmVcIiwge1xuICAgIGFnZW50SWQ6IG9wdHMuYWdlbnQuaWQsXG4gICAgdGhyZWFkSWQ6IG9wdHMudGhyZWFkSWQsXG4gICAgY2xlYXJlZCxcbiAgICBlbGFwc2VkTXM6IERhdGUubm93KCkgLSBzdGFydGVkQXQsXG4gIH0pO1xuICByZXR1cm4geyBjbGVhcmVkIH07XG59XG5cbi8qKiBPcHRpb25zIGFjY2VwdGVkIGJ5IHtAbGluayBoaXN0b3J5Um91dGV9LiAqL1xuZXhwb3J0IHR5cGUgSGlzdG9yeVJvdXRlT3B0aW9ucyA9XG4gIHsgcGF0aDogYCR7c3RyaW5nfTphZ2VudElkJHtzdHJpbmd9YDsgYWdlbnQ/OiBuZXZlciB9IHwgeyBwYXRoOiBzdHJpbmc7IGFnZW50OiBzdHJpbmcgfTtcblxuLyoqXG4gKiBSZWdpc3RlciB0aGUgYDxwYXRoPmAgTWFzdHJhIGN1c3RvbSBBUEkgcm91dGUuIEhhbmRsZXMgdHdvXG4gKiBtZXRob2RzIG9uIHRoZSBzYW1lIG1vdW50OlxuICpcbiAqICAgLSBgR0VUYDogcmV0dXJuIGEgcGFnZSBvZiBBSSBTREsgVjUgYFVJTWVzc2FnZWBzIGZvciB0aGVcbiAqICAgICBjYWxsZXIncyBjdXJyZW50IHRocmVhZCAoe0BsaW5rIGxvYWRIaXN0b3J5fSkuXG4gKiAgIC0gYERFTEVURWA6IHdpcGUgZXZlcnkgcGVyc2lzdGVkIG1lc3NhZ2Ugb24gdGhlIGNhbGxlcidzXG4gKiAgICAgdGhyZWFkICh7QGxpbmsgY2xlYXJIaXN0b3J5fSkuIFRoZSBzZXNzaW9uIGNvb2tpZSB0aGF0XG4gKiAgICAgYW5jaG9ycyB0aGUgdGhyZWFkIGlkIGlzIGxlZnQgYWxvbmUgc28gdGhlIHVzZXIga2VlcHMgdGhlXG4gKiAgICAgc2FtZSB0aHJlYWQgLSBvbmx5IHRoZSBjb250ZW50cyBnbyBhd2F5LlxuICpcbiAqIEZvbGxvd3MgdGhlIGBAbWFzdHJhL2FpLXNka2AgYGNoYXRSb3V0ZWAgY29udmVudGlvbiBmb3IgYWdlbnRcbiAqIGJpbmRpbmc6IHBhc3MgYGFnZW50YCBmb3IgYSBmaXhlZC1hZ2VudCBtb3VudCwgb3IgaW5jbHVkZVxuICogYDphZ2VudElkYCBpbiB0aGUgcGF0aCBmb3IgZHluYW1pYyByb3V0aW5nLiBUaGUgcGx1Z2luIHJlZ2lzdGVyc1xuICogYm90aCBgL3JvdXRlL2hpc3RvcnlgIChkZWZhdWx0IGFnZW50KSBhbmQgYC9yb3V0ZS9oaXN0b3J5LzphZ2VudElkYC5cbiAqXG4gKiBUaGUgaGFuZGxlciByZWFkcyBgdGhyZWFkSWRgIGFuZCBgcmVzb3VyY2VJZGAgZnJvbSBgUmVxdWVzdENvbnRleHRgXG4gKiAocG9wdWxhdGVkIHVwc3RyZWFtIGJ5IGBNYXN0cmFTZXJ2ZXIucmVnaXN0ZXJBdXRoTWlkZGxld2FyZWApLCBzb1xuICogbm8gY29va2llIG9yIHVzZXIgbG9va3VwcyBoYXBwZW4gaGVyZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGhpc3RvcnlSb3V0ZShvcHRpb25zOiBIaXN0b3J5Um91dGVPcHRpb25zKSB7XG4gIGNvbnN0IHsgcGF0aCB9ID0gb3B0aW9ucztcbiAgY29uc3QgZml4ZWRBZ2VudCA9IFwiYWdlbnRcIiBpbiBvcHRpb25zID8gb3B0aW9ucy5hZ2VudCA6IHVuZGVmaW5lZDtcbiAgaWYgKCFmaXhlZEFnZW50ICYmICFwYXRoLmluY2x1ZGVzKFwiOmFnZW50SWRcIikpIHtcbiAgICB0aHJvdyBWYWxpZGF0aW9uRXJyb3IuaW52YWxpZFZhbHVlKFxuICAgICAgXCJoaXN0b3J5Um91dGUucGF0aFwiLFxuICAgICAgcGF0aCxcbiAgICAgIFwiYSBwYXRoIGNvbnRhaW5pbmcgYDphZ2VudElkYCwgb3IgYW4gZXhwbGljaXQgYGFnZW50YFwiLFxuICAgICk7XG4gIH1cbiAgLy8gVGlueSByZXNvbHZlciBzaGFyZWQgYnkgR0VUIC8gREVMRVRFOiBkZXJpdmUgdGhlIGFjdGl2ZSBhZ2VudFxuICAvLyBhbmQgdGhyZWFkIGlkLCByZXR1cm5pbmcgYSBKU09OIGVycm9yIHJlc3BvbnNlIHdoZW4gZWl0aGVyIGlzXG4gIC8vIG1pc3NpbmcuIEtlZXBzIGJvdGggaGFuZGxlcnMgdGhpbiBhbmQgZ2l2ZXMgdGhlbSBpZGVudGljYWxcbiAgLy8gdmFsaWRhdGlvbiBiZWhhdmlvdXIuXG4gIGNvbnN0IHJlc29sdmVDb250ZXh0ID0gKGM6IENvbnRleHRXaXRoTWFzdHJhKSA9PiB7XG4gICAgY29uc3QgbWFzdHJhID0gYy5nZXQoXCJtYXN0cmFcIik7XG4gICAgY29uc3QgcmVxdWVzdENvbnRleHQgPSBjLmdldChcInJlcXVlc3RDb250ZXh0XCIpO1xuICAgIGNvbnN0IGFnZW50SWQgPSBmaXhlZEFnZW50ID8/IGMucmVxLnBhcmFtKFwiYWdlbnRJZFwiKTtcbiAgICBpZiAoIWFnZW50SWQpIHtcbiAgICAgIHJldHVybiB7IGVycm9yOiBjLmpzb24oeyBlcnJvcjogXCJhZ2VudElkIGlzIHJlcXVpcmVkXCIgfSwgNDAwKSB9IGFzIGNvbnN0O1xuICAgIH1cbiAgICBjb25zdCBhZ2VudCA9IG1hc3RyYS5nZXRBZ2VudEJ5SWQoYWdlbnRJZCk7XG4gICAgaWYgKCFhZ2VudCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZXJyb3I6IGMuanNvbih7IGVycm9yOiBgVW5rbm93biBhZ2VudCBcIiR7YWdlbnRJZH1cImAgfSwgNDA0KSxcbiAgICAgIH0gYXMgY29uc3Q7XG4gICAgfVxuICAgIGNvbnN0IHRocmVhZElkID0gcmVxdWVzdENvbnRleHQuZ2V0KE1BU1RSQV9USFJFQURfSURfS0VZKSBhcyBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgaWYgKCF0aHJlYWRJZCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZXJyb3I6IGMuanNvbih7IGVycm9yOiBcInRocmVhZCBpZCBtaXNzaW5nIGZyb20gcmVxdWVzdCBjb250ZXh0XCIgfSwgNDAwKSxcbiAgICAgIH0gYXMgY29uc3Q7XG4gICAgfVxuICAgIGNvbnN0IHJlc291cmNlSWQgPSByZXF1ZXN0Q29udGV4dC5nZXQoTUFTVFJBX1JFU09VUkNFX0lEX0tFWSkgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIHJldHVybiB7IGFnZW50SWQsIGFnZW50LCB0aHJlYWRJZCwgcmVzb3VyY2VJZCB9IGFzIGNvbnN0O1xuICB9O1xuXG4gIHJldHVybiBbXG4gICAgcmVnaXN0ZXJBcGlSb3V0ZShwYXRoLCB7XG4gICAgICBtZXRob2Q6IFwiR0VUXCIsXG4gICAgICBoYW5kbGVyOiBhc3luYyAoYzogQ29udGV4dFdpdGhNYXN0cmEpID0+IHtcbiAgICAgICAgY29uc3QgY3R4ID0gcmVzb2x2ZUNvbnRleHQoYyk7XG4gICAgICAgIGlmIChcImVycm9yXCIgaW4gY3R4KSByZXR1cm4gY3R4LmVycm9yO1xuICAgICAgICBjb25zdCBwYXlsb2FkID0gYXdhaXQgbG9hZEhpc3Rvcnkoe1xuICAgICAgICAgIGFnZW50OiBjdHguYWdlbnQsXG4gICAgICAgICAgdGhyZWFkSWQ6IGN0eC50aHJlYWRJZCxcbiAgICAgICAgICAuLi4oY3R4LnJlc291cmNlSWQgPyB7IHJlc291cmNlSWQ6IGN0eC5yZXNvdXJjZUlkIH0gOiB7fSksXG4gICAgICAgICAgcGFnZTogcGFyc2VJbnRQYXJhbShjLnJlcS5xdWVyeShcInBhZ2VcIikpLFxuICAgICAgICAgIHBlclBhZ2U6IHBhcnNlSW50UGFyYW0oYy5yZXEucXVlcnkoXCJwZXJQYWdlXCIpKSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBjLmpzb24ocGF5bG9hZCk7XG4gICAgICB9LFxuICAgIH0pLFxuICAgIHJlZ2lzdGVyQXBpUm91dGUocGF0aCwge1xuICAgICAgbWV0aG9kOiBcIkRFTEVURVwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKGM6IENvbnRleHRXaXRoTWFzdHJhKSA9PiB7XG4gICAgICAgIGNvbnN0IGN0eCA9IHJlc29sdmVDb250ZXh0KGMpO1xuICAgICAgICBpZiAoXCJlcnJvclwiIGluIGN0eCkgcmV0dXJuIGN0eC5lcnJvcjtcbiAgICAgICAgY29uc3QgeyBjbGVhcmVkIH0gPSBhd2FpdCBjbGVhckhpc3Rvcnkoe1xuICAgICAgICAgIGFnZW50OiBjdHguYWdlbnQsXG4gICAgICAgICAgdGhyZWFkSWQ6IGN0eC50aHJlYWRJZCxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHBheWxvYWQ6IE1hc3RyYUNsZWFySGlzdG9yeVJlc3BvbnNlID0ge1xuICAgICAgICAgIG9rOiB0cnVlLFxuICAgICAgICAgIGFnZW50SWQ6IGN0eC5hZ2VudElkLFxuICAgICAgICAgIHRocmVhZElkOiBjdHgudGhyZWFkSWQsXG4gICAgICAgICAgY2xlYXJlZCxcbiAgICAgICAgfTtcbiAgICAgICAgcmV0dXJuIGMuanNvbihwYXlsb2FkKTtcbiAgICAgIH0sXG4gICAgfSksXG4gIF07XG59XG5cbi8qKlxuICogU29ydCBtZXNzYWdlcyBvbGRlc3QtZmlyc3QgYnkgYGNyZWF0ZWRBdGAsIGZhbGxpbmcgYmFjayB0byB3aGF0ZXZlclxuICogb3JkZXIgdGhlIHN0b3JhZ2UgcmV0dXJuZWQgdGhlbSBpbi4gVGhlIG5hdGl2ZSBgcmVjYWxsYCBjYWxsIGhvbm9yc1xuICogYG9yZGVyQnlgIGJ1dCBkb2Vzbid0IGd1YXJhbnRlZSBhIHN0YWJsZSBzZWNvbmRhcnkgc29ydCwgc28gd2VcbiAqIG5vcm1hbGl6ZSBoZXJlIGJlZm9yZSBoYW5kaW5nIHRoZSBwYWdlIHRvIHRoZSBBSSBTREsgY29udmVydGVyLlxuICovXG5mdW5jdGlvbiBzb3J0Q2hyb25vbG9naWNhbChtZXNzYWdlczogTWFzdHJhREJNZXNzYWdlW10pOiBNYXN0cmFEQk1lc3NhZ2VbXSB7XG4gIHJldHVybiBbLi4ubWVzc2FnZXNdLnNvcnQoKGEsIGIpID0+IHtcbiAgICBjb25zdCB0YSA9IHRvRXBvY2goYS5jcmVhdGVkQXQpO1xuICAgIGNvbnN0IHRiID0gdG9FcG9jaChiLmNyZWF0ZWRBdCk7XG4gICAgcmV0dXJuIHRhIC0gdGI7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiB0b0Vwb2NoKHZhbHVlOiB1bmtub3duKTogbnVtYmVyIHtcbiAgaWYgKHZhbHVlIGluc3RhbmNlb2YgRGF0ZSkgcmV0dXJuIHZhbHVlLmdldFRpbWUoKTtcbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiB8fCB0eXBlb2YgdmFsdWUgPT09IFwibnVtYmVyXCIpIHtcbiAgICBjb25zdCBwYXJzZWQgPSBuZXcgRGF0ZSh2YWx1ZSkuZ2V0VGltZSgpO1xuICAgIHJldHVybiBOdW1iZXIuaXNOYU4ocGFyc2VkKSA/IDAgOiBwYXJzZWQ7XG4gIH1cbiAgcmV0dXJuIDA7XG59XG4iXX0=
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Optional Mastra MCP server exposure for the AppKit Mastra plugin.
3
+ *
4
+ * Turns the plugin's registered agents (and, opt-in, its ambient tools)
5
+ * into a Mastra `MCPServer` so external MCP clients - Claude Desktop,
6
+ * Cursor, the Mastra playground, or another agent - can call them over
7
+ * the standard MCP transports. The resulting server is handed to the
8
+ * `Mastra` instance via `mcpServers`, which makes `@mastra/express`
9
+ * serve the stock MCP routes under the plugin's base path; the plugin
10
+ * never registers a bespoke MCP route of its own.
11
+ *
12
+ * @module
13
+ */
14
+ import type { Agent } from "@mastra/core/agent";
15
+ import { MCPServer } from "@mastra/mcp";
16
+ import type { MastraTools } from "./agents.js";
17
+ import type { MastraPluginConfig } from "./config.js";
18
+ /**
19
+ * A built MCP server plus the request paths it answers on, relative to
20
+ * the plugin's base path (`/api/<plugin>`).
21
+ *
22
+ * The paths are the **clean public aliases** (`/mcp`, `/sse`,
23
+ * `/messages`) the plugin exposes. `@mastra/express` actually mounts the
24
+ * transports under `/mcp/<serverId>/<transport>` off the `Mastra`
25
+ * instance's `mcpServers`; the plugin rewrites the alias to that route
26
+ * before dispatch (see {@link ResolvedMcp.serverId}), so a client never
27
+ * sees the doubled `/mcp/<serverId>/mcp` segment.
28
+ */
29
+ export interface ResolvedMcp {
30
+ /**
31
+ * Registry id, used in the underlying `@mastra/express` route
32
+ * (`/mcp/<serverId>/...`) the plugin rewrites the clean alias to.
33
+ */
34
+ serverId: string;
35
+ /** The Mastra MCP server to hand to `new Mastra({ mcpServers })`. */
36
+ server: MCPServer;
37
+ /** Streamable-HTTP transport path, relative to the plugin base path. */
38
+ httpPath: string;
39
+ /** SSE transport path, relative to the plugin base path. */
40
+ ssePath: string;
41
+ /** SSE message path, relative to the plugin base path. */
42
+ messagePath: string;
43
+ }
44
+ /**
45
+ * Build the plugin's MCP server, or `null` only when `config.mcp` is
46
+ * explicitly `false`.
47
+ *
48
+ * Agent exposure is on by default because it's cheap: the already-
49
+ * registered agents are wrapped as `ask_<agentId>` tools and handed to
50
+ * `@mastra/express` (already mounted for the chat routes) - no extra
51
+ * process, dependency, or network cost, and requests run under the same
52
+ * OBO scope. So `undefined` (the default) and `true` both expose every
53
+ * registered agent under a server id equal to the plugin name; only
54
+ * `false` turns MCP off. The object form ({@link MastraMcpConfig}) tunes
55
+ * the id / advertised metadata and can additionally expose the plugin's
56
+ * ambient tools or a set of extra MCP-only tools. Ambient tools stay off
57
+ * unless explicitly enabled - they assume an in-process chat turn, so
58
+ * they aren't cheap / safe to expose to a standalone MCP client.
59
+ */
60
+ export declare function buildMcpServer(opts: {
61
+ config: MastraPluginConfig;
62
+ pluginName: string;
63
+ displayName: string;
64
+ agents: Record<string, Agent>;
65
+ ambientTools: MastraTools;
66
+ }): ResolvedMcp | null;
package/lib/src/mcp.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Optional Mastra MCP server exposure for the AppKit Mastra plugin.
3
+ *
4
+ * Turns the plugin's registered agents (and, opt-in, its ambient tools)
5
+ * into a Mastra `MCPServer` so external MCP clients - Claude Desktop,
6
+ * Cursor, the Mastra playground, or another agent - can call them over
7
+ * the standard MCP transports. The resulting server is handed to the
8
+ * `Mastra` instance via `mcpServers`, which makes `@mastra/express`
9
+ * serve the stock MCP routes under the plugin's base path; the plugin
10
+ * never registers a bespoke MCP route of its own.
11
+ *
12
+ * @module
13
+ */
14
+ import { MCPServer } from "@mastra/mcp";
15
+ /** MCP server version advertised when the caller doesn't pin one. */
16
+ const DEFAULT_MCP_VERSION = "1.0.0";
17
+ /**
18
+ * Build the plugin's MCP server, or `null` only when `config.mcp` is
19
+ * explicitly `false`.
20
+ *
21
+ * Agent exposure is on by default because it's cheap: the already-
22
+ * registered agents are wrapped as `ask_<agentId>` tools and handed to
23
+ * `@mastra/express` (already mounted for the chat routes) - no extra
24
+ * process, dependency, or network cost, and requests run under the same
25
+ * OBO scope. So `undefined` (the default) and `true` both expose every
26
+ * registered agent under a server id equal to the plugin name; only
27
+ * `false` turns MCP off. The object form ({@link MastraMcpConfig}) tunes
28
+ * the id / advertised metadata and can additionally expose the plugin's
29
+ * ambient tools or a set of extra MCP-only tools. Ambient tools stay off
30
+ * unless explicitly enabled - they assume an in-process chat turn, so
31
+ * they aren't cheap / safe to expose to a standalone MCP client.
32
+ */
33
+ export function buildMcpServer(opts) {
34
+ const { config, pluginName, displayName, agents, ambientTools } = opts;
35
+ if (config.mcp === false)
36
+ return null;
37
+ const settings = typeof config.mcp === "object" ? config.mcp : {};
38
+ const serverId = settings.serverId ?? pluginName;
39
+ const exposeAgents = settings.agents !== false;
40
+ const exposeTools = settings.tools === true;
41
+ // MCPServerConfig.tools is required, so always pass a record (empty
42
+ // when the caller exposes only agents).
43
+ const tools = {
44
+ ...(exposeTools ? ambientTools : {}),
45
+ ...(settings.extraTools ?? {}),
46
+ };
47
+ const server = new MCPServer({
48
+ id: serverId,
49
+ name: settings.name ?? `${displayName} MCP`,
50
+ version: settings.version ?? DEFAULT_MCP_VERSION,
51
+ ...(settings.description ? { description: settings.description } : {}),
52
+ ...(exposeAgents ? { agents } : {}),
53
+ tools,
54
+ });
55
+ // Advertise the clean aliases; the plugin rewrites these to the stock
56
+ // `/mcp/<serverId>/<transport>` routes `@mastra/express` mounts.
57
+ return {
58
+ serverId,
59
+ server,
60
+ httpPath: "/mcp",
61
+ ssePath: "/sse",
62
+ messagePath: "/messages",
63
+ };
64
+ }
65
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWNwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21jcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7O0dBWUc7QUFHSCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBS3hDLHFFQUFxRTtBQUNyRSxNQUFNLG1CQUFtQixHQUFHLE9BQU8sQ0FBQztBQTZCcEM7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLGNBQWMsQ0FBQyxJQU05QjtJQUNDLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLEdBQUcsSUFBSSxDQUFDO0lBQ3ZFLElBQUksTUFBTSxDQUFDLEdBQUcsS0FBSyxLQUFLO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDdEMsTUFBTSxRQUFRLEdBQW9CLE9BQU8sTUFBTSxDQUFDLEdBQUcsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUVuRixNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsUUFBUSxJQUFJLFVBQVUsQ0FBQztJQUNqRCxNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsTUFBTSxLQUFLLEtBQUssQ0FBQztJQUMvQyxNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsS0FBSyxLQUFLLElBQUksQ0FBQztJQUU1QyxvRUFBb0U7SUFDcEUsd0NBQXdDO0lBQ3hDLE1BQU0sS0FBSyxHQUFnQjtRQUN6QixHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNwQyxHQUFHLENBQUMsUUFBUSxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUM7S0FDL0IsQ0FBQztJQUVGLE1BQU0sTUFBTSxHQUFHLElBQUksU0FBUyxDQUFDO1FBQzNCLEVBQUUsRUFBRSxRQUFRO1FBQ1osSUFBSSxFQUFFLFFBQVEsQ0FBQyxJQUFJLElBQUksR0FBRyxXQUFXLE1BQU07UUFDM0MsT0FBTyxFQUFFLFFBQVEsQ0FBQyxPQUFPLElBQUksbUJBQW1CO1FBQ2hELEdBQUcsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUN0RSxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDbkMsS0FBSztLQUNOLENBQUMsQ0FBQztJQUVILHNFQUFzRTtJQUN0RSxpRUFBaUU7SUFDakUsT0FBTztRQUNMLFFBQVE7UUFDUixNQUFNO1FBQ04sUUFBUSxFQUFFLE1BQU07UUFDaEIsT0FBTyxFQUFFLE1BQU07UUFDZixXQUFXLEVBQUUsV0FBVztLQUN6QixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT3B0aW9uYWwgTWFzdHJhIE1DUCBzZXJ2ZXIgZXhwb3N1cmUgZm9yIHRoZSBBcHBLaXQgTWFzdHJhIHBsdWdpbi5cbiAqXG4gKiBUdXJucyB0aGUgcGx1Z2luJ3MgcmVnaXN0ZXJlZCBhZ2VudHMgKGFuZCwgb3B0LWluLCBpdHMgYW1iaWVudCB0b29scylcbiAqIGludG8gYSBNYXN0cmEgYE1DUFNlcnZlcmAgc28gZXh0ZXJuYWwgTUNQIGNsaWVudHMgLSBDbGF1ZGUgRGVza3RvcCxcbiAqIEN1cnNvciwgdGhlIE1hc3RyYSBwbGF5Z3JvdW5kLCBvciBhbm90aGVyIGFnZW50IC0gY2FuIGNhbGwgdGhlbSBvdmVyXG4gKiB0aGUgc3RhbmRhcmQgTUNQIHRyYW5zcG9ydHMuIFRoZSByZXN1bHRpbmcgc2VydmVyIGlzIGhhbmRlZCB0byB0aGVcbiAqIGBNYXN0cmFgIGluc3RhbmNlIHZpYSBgbWNwU2VydmVyc2AsIHdoaWNoIG1ha2VzIGBAbWFzdHJhL2V4cHJlc3NgXG4gKiBzZXJ2ZSB0aGUgc3RvY2sgTUNQIHJvdXRlcyB1bmRlciB0aGUgcGx1Z2luJ3MgYmFzZSBwYXRoOyB0aGUgcGx1Z2luXG4gKiBuZXZlciByZWdpc3RlcnMgYSBiZXNwb2tlIE1DUCByb3V0ZSBvZiBpdHMgb3duLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IEFnZW50IH0gZnJvbSBcIkBtYXN0cmEvY29yZS9hZ2VudFwiO1xuaW1wb3J0IHsgTUNQU2VydmVyIH0gZnJvbSBcIkBtYXN0cmEvbWNwXCI7XG5cbmltcG9ydCB0eXBlIHsgTWFzdHJhVG9vbHMgfSBmcm9tIFwiLi9hZ2VudHNcIjtcbmltcG9ydCB0eXBlIHsgTWFzdHJhTWNwQ29uZmlnLCBNYXN0cmFQbHVnaW5Db25maWcgfSBmcm9tIFwiLi9jb25maWdcIjtcblxuLyoqIE1DUCBzZXJ2ZXIgdmVyc2lvbiBhZHZlcnRpc2VkIHdoZW4gdGhlIGNhbGxlciBkb2Vzbid0IHBpbiBvbmUuICovXG5jb25zdCBERUZBVUxUX01DUF9WRVJTSU9OID0gXCIxLjAuMFwiO1xuXG4vKipcbiAqIEEgYnVpbHQgTUNQIHNlcnZlciBwbHVzIHRoZSByZXF1ZXN0IHBhdGhzIGl0IGFuc3dlcnMgb24sIHJlbGF0aXZlIHRvXG4gKiB0aGUgcGx1Z2luJ3MgYmFzZSBwYXRoIChgL2FwaS88cGx1Z2luPmApLlxuICpcbiAqIFRoZSBwYXRocyBhcmUgdGhlICoqY2xlYW4gcHVibGljIGFsaWFzZXMqKiAoYC9tY3BgLCBgL3NzZWAsXG4gKiBgL21lc3NhZ2VzYCkgdGhlIHBsdWdpbiBleHBvc2VzLiBgQG1hc3RyYS9leHByZXNzYCBhY3R1YWxseSBtb3VudHMgdGhlXG4gKiB0cmFuc3BvcnRzIHVuZGVyIGAvbWNwLzxzZXJ2ZXJJZD4vPHRyYW5zcG9ydD5gIG9mZiB0aGUgYE1hc3RyYWBcbiAqIGluc3RhbmNlJ3MgYG1jcFNlcnZlcnNgOyB0aGUgcGx1Z2luIHJld3JpdGVzIHRoZSBhbGlhcyB0byB0aGF0IHJvdXRlXG4gKiBiZWZvcmUgZGlzcGF0Y2ggKHNlZSB7QGxpbmsgUmVzb2x2ZWRNY3Auc2VydmVySWR9KSwgc28gYSBjbGllbnQgbmV2ZXJcbiAqIHNlZXMgdGhlIGRvdWJsZWQgYC9tY3AvPHNlcnZlcklkPi9tY3BgIHNlZ21lbnQuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZWRNY3Age1xuICAvKipcbiAgICogUmVnaXN0cnkgaWQsIHVzZWQgaW4gdGhlIHVuZGVybHlpbmcgYEBtYXN0cmEvZXhwcmVzc2Agcm91dGVcbiAgICogKGAvbWNwLzxzZXJ2ZXJJZD4vLi4uYCkgdGhlIHBsdWdpbiByZXdyaXRlcyB0aGUgY2xlYW4gYWxpYXMgdG8uXG4gICAqL1xuICBzZXJ2ZXJJZDogc3RyaW5nO1xuICAvKiogVGhlIE1hc3RyYSBNQ1Agc2VydmVyIHRvIGhhbmQgdG8gYG5ldyBNYXN0cmEoeyBtY3BTZXJ2ZXJzIH0pYC4gKi9cbiAgc2VydmVyOiBNQ1BTZXJ2ZXI7XG4gIC8qKiBTdHJlYW1hYmxlLUhUVFAgdHJhbnNwb3J0IHBhdGgsIHJlbGF0aXZlIHRvIHRoZSBwbHVnaW4gYmFzZSBwYXRoLiAqL1xuICBodHRwUGF0aDogc3RyaW5nO1xuICAvKiogU1NFIHRyYW5zcG9ydCBwYXRoLCByZWxhdGl2ZSB0byB0aGUgcGx1Z2luIGJhc2UgcGF0aC4gKi9cbiAgc3NlUGF0aDogc3RyaW5nO1xuICAvKiogU1NFIG1lc3NhZ2UgcGF0aCwgcmVsYXRpdmUgdG8gdGhlIHBsdWdpbiBiYXNlIHBhdGguICovXG4gIG1lc3NhZ2VQYXRoOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIHBsdWdpbidzIE1DUCBzZXJ2ZXIsIG9yIGBudWxsYCBvbmx5IHdoZW4gYGNvbmZpZy5tY3BgIGlzXG4gKiBleHBsaWNpdGx5IGBmYWxzZWAuXG4gKlxuICogQWdlbnQgZXhwb3N1cmUgaXMgb24gYnkgZGVmYXVsdCBiZWNhdXNlIGl0J3MgY2hlYXA6IHRoZSBhbHJlYWR5LVxuICogcmVnaXN0ZXJlZCBhZ2VudHMgYXJlIHdyYXBwZWQgYXMgYGFza188YWdlbnRJZD5gIHRvb2xzIGFuZCBoYW5kZWQgdG9cbiAqIGBAbWFzdHJhL2V4cHJlc3NgIChhbHJlYWR5IG1vdW50ZWQgZm9yIHRoZSBjaGF0IHJvdXRlcykgLSBubyBleHRyYVxuICogcHJvY2VzcywgZGVwZW5kZW5jeSwgb3IgbmV0d29yayBjb3N0LCBhbmQgcmVxdWVzdHMgcnVuIHVuZGVyIHRoZSBzYW1lXG4gKiBPQk8gc2NvcGUuIFNvIGB1bmRlZmluZWRgICh0aGUgZGVmYXVsdCkgYW5kIGB0cnVlYCBib3RoIGV4cG9zZSBldmVyeVxuICogcmVnaXN0ZXJlZCBhZ2VudCB1bmRlciBhIHNlcnZlciBpZCBlcXVhbCB0byB0aGUgcGx1Z2luIG5hbWU7IG9ubHlcbiAqIGBmYWxzZWAgdHVybnMgTUNQIG9mZi4gVGhlIG9iamVjdCBmb3JtICh7QGxpbmsgTWFzdHJhTWNwQ29uZmlnfSkgdHVuZXNcbiAqIHRoZSBpZCAvIGFkdmVydGlzZWQgbWV0YWRhdGEgYW5kIGNhbiBhZGRpdGlvbmFsbHkgZXhwb3NlIHRoZSBwbHVnaW4nc1xuICogYW1iaWVudCB0b29scyBvciBhIHNldCBvZiBleHRyYSBNQ1Atb25seSB0b29scy4gQW1iaWVudCB0b29scyBzdGF5IG9mZlxuICogdW5sZXNzIGV4cGxpY2l0bHkgZW5hYmxlZCAtIHRoZXkgYXNzdW1lIGFuIGluLXByb2Nlc3MgY2hhdCB0dXJuLCBzb1xuICogdGhleSBhcmVuJ3QgY2hlYXAgLyBzYWZlIHRvIGV4cG9zZSB0byBhIHN0YW5kYWxvbmUgTUNQIGNsaWVudC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkTWNwU2VydmVyKG9wdHM6IHtcbiAgY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWc7XG4gIHBsdWdpbk5hbWU6IHN0cmluZztcbiAgZGlzcGxheU5hbWU6IHN0cmluZztcbiAgYWdlbnRzOiBSZWNvcmQ8c3RyaW5nLCBBZ2VudD47XG4gIGFtYmllbnRUb29sczogTWFzdHJhVG9vbHM7XG59KTogUmVzb2x2ZWRNY3AgfCBudWxsIHtcbiAgY29uc3QgeyBjb25maWcsIHBsdWdpbk5hbWUsIGRpc3BsYXlOYW1lLCBhZ2VudHMsIGFtYmllbnRUb29scyB9ID0gb3B0cztcbiAgaWYgKGNvbmZpZy5tY3AgPT09IGZhbHNlKSByZXR1cm4gbnVsbDtcbiAgY29uc3Qgc2V0dGluZ3M6IE1hc3RyYU1jcENvbmZpZyA9IHR5cGVvZiBjb25maWcubWNwID09PSBcIm9iamVjdFwiID8gY29uZmlnLm1jcCA6IHt9O1xuXG4gIGNvbnN0IHNlcnZlcklkID0gc2V0dGluZ3Muc2VydmVySWQgPz8gcGx1Z2luTmFtZTtcbiAgY29uc3QgZXhwb3NlQWdlbnRzID0gc2V0dGluZ3MuYWdlbnRzICE9PSBmYWxzZTtcbiAgY29uc3QgZXhwb3NlVG9vbHMgPSBzZXR0aW5ncy50b29scyA9PT0gdHJ1ZTtcblxuICAvLyBNQ1BTZXJ2ZXJDb25maWcudG9vbHMgaXMgcmVxdWlyZWQsIHNvIGFsd2F5cyBwYXNzIGEgcmVjb3JkIChlbXB0eVxuICAvLyB3aGVuIHRoZSBjYWxsZXIgZXhwb3NlcyBvbmx5IGFnZW50cykuXG4gIGNvbnN0IHRvb2xzOiBNYXN0cmFUb29scyA9IHtcbiAgICAuLi4oZXhwb3NlVG9vbHMgPyBhbWJpZW50VG9vbHMgOiB7fSksXG4gICAgLi4uKHNldHRpbmdzLmV4dHJhVG9vbHMgPz8ge30pLFxuICB9O1xuXG4gIGNvbnN0IHNlcnZlciA9IG5ldyBNQ1BTZXJ2ZXIoe1xuICAgIGlkOiBzZXJ2ZXJJZCxcbiAgICBuYW1lOiBzZXR0aW5ncy5uYW1lID8/IGAke2Rpc3BsYXlOYW1lfSBNQ1BgLFxuICAgIHZlcnNpb246IHNldHRpbmdzLnZlcnNpb24gPz8gREVGQVVMVF9NQ1BfVkVSU0lPTixcbiAgICAuLi4oc2V0dGluZ3MuZGVzY3JpcHRpb24gPyB7IGRlc2NyaXB0aW9uOiBzZXR0aW5ncy5kZXNjcmlwdGlvbiB9IDoge30pLFxuICAgIC4uLihleHBvc2VBZ2VudHMgPyB7IGFnZW50cyB9IDoge30pLFxuICAgIHRvb2xzLFxuICB9KTtcblxuICAvLyBBZHZlcnRpc2UgdGhlIGNsZWFuIGFsaWFzZXM7IHRoZSBwbHVnaW4gcmV3cml0ZXMgdGhlc2UgdG8gdGhlIHN0b2NrXG4gIC8vIGAvbWNwLzxzZXJ2ZXJJZD4vPHRyYW5zcG9ydD5gIHJvdXRlcyBgQG1hc3RyYS9leHByZXNzYCBtb3VudHMuXG4gIHJldHVybiB7XG4gICAgc2VydmVySWQsXG4gICAgc2VydmVyLFxuICAgIGh0dHBQYXRoOiBcIi9tY3BcIixcbiAgICBzc2VQYXRoOiBcIi9zc2VcIixcbiAgICBtZXNzYWdlUGF0aDogXCIvbWVzc2FnZXNcIixcbiAgfTtcbn1cbiJdfQ==
@@ -0,0 +1,111 @@
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 {@link agentStorageSchemaName} 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
+ * Additionally, {@link MemoryBuilder.instanceStorage} returns a
18
+ * **Mastra-instance-level** `PostgresStore` (schema `mastra_instance`)
19
+ * used for workflow snapshots - the persistence layer
20
+ * `agent.resumeStream()` reads from when waking a suspended
21
+ * `requireApproval` tool call. Per-agent stores are not enough for
22
+ * this: workflow runs are scoped to the Mastra instance, not an
23
+ * individual agent's `Memory`.
24
+ *
25
+ * Plugin-level `config.storage` / `config.memory` act as the baseline
26
+ * (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
27
+ * is registered); per-agent settings cascade on top of that.
28
+ *
29
+ * @module
30
+ */
31
+ import { Memory } from "@mastra/memory";
32
+ import { PostgresStore } from "@mastra/pg";
33
+ import { Pool, type PoolConfig } from "pg";
34
+ import type { MastraAgentDefinition } from "./agents.js";
35
+ import type { MastraPluginConfig } from "./config.js";
36
+ /**
37
+ * Build a dedicated **service-principal** Lakebase pool for Mastra
38
+ * memory from the lakebase plugin's resolved SP pg config.
39
+ *
40
+ * The plugin's `exports().pool` is a `RoutingPool` that switches to
41
+ * the per-user (OBO) pool whenever a query runs inside an `asUser`
42
+ * scope - exactly the context the mastra plugin establishes around
43
+ * every chat turn. Memory (threads / messages + semantic recall) must
44
+ * instead always act as the app service principal: it owns the
45
+ * auto-created `mastra_*` schemas (a per-user role usually can't
46
+ * `CREATE SCHEMA`) and is shared across users, so it cannot inherit a
47
+ * request's OBO identity.
48
+ *
49
+ * `pgConfig` must be the plugin's `exports().getPgConfig()` evaluated
50
+ * **outside** any `asUser` scope (i.e. during setup), so it carries
51
+ * the SP connection target, OAuth token-refresh `password` callback,
52
+ * and any `lakebase({ pool })` tuning overrides - all of which this
53
+ * pool inherits. See the call site in `plugin.ts`.
54
+ */
55
+ export declare function createServicePrincipalPool(pgConfig: PoolConfig): Promise<Pool>;
56
+ /**
57
+ * True when any plugin-level or per-agent setting could need the
58
+ * Lakebase pool. Used by `plugin.ts` to gate creation of the
59
+ * service-principal pool and the {@link MemoryBuilder} that consumes
60
+ * it; when false neither is built.
61
+ */
62
+ export declare function needsLakebase(config: MastraPluginConfig): boolean;
63
+ /**
64
+ * Construct a per-agent {@link Memory} factory bound to the supplied
65
+ * service-principal pool (see {@link createServicePrincipalPool}).
66
+ * Caches the shared `PgVector` singleton (built on first need) so each
67
+ * agent build is O(1) after the first.
68
+ */
69
+ export declare function createMemoryBuilder(config: MastraPluginConfig, servicePrincipalPool: Pool): MemoryBuilder;
70
+ /**
71
+ * Builds one `Memory` per agent against a shared service-principal
72
+ * Lakebase pool. Per-instance state keeps the shared `PgVector` alive
73
+ * across calls so registering N agents stays cheap.
74
+ */
75
+ export declare class MemoryBuilder {
76
+ private readonly config;
77
+ private readonly servicePrincipalPool;
78
+ private sharedVector;
79
+ constructor(config: MastraPluginConfig, servicePrincipalPool: Pool);
80
+ /**
81
+ * Build the Mastra-instance-level storage used for workflow
82
+ * snapshots. Returns `undefined` when plugin-level `storage` is
83
+ * disabled, in which case `agent.resumeStream()` (and therefore
84
+ * the `requireApproval` flow) will not be available.
85
+ *
86
+ * The store lives in a dedicated `mastra_instance` schema so it
87
+ * never collides with per-agent {@link agentStorageSchemaName} namespaces.
88
+ * Workflow snapshots are not per-agent state; they belong to the
89
+ * `Mastra` instance that owns the workflow execution.
90
+ */
91
+ instanceStorage(): PostgresStore | undefined;
92
+ /**
93
+ * Build a `Memory` for `agentId` after the plugin/agent cascade.
94
+ * Returns `undefined` when the agent has neither storage nor a
95
+ * vector store enabled - Mastra accepts a missing `memory` field
96
+ * and treats the agent as stateless.
97
+ */
98
+ forAgent(agentId: string, def: MastraAgentDefinition): Memory | undefined;
99
+ private buildStorage;
100
+ /**
101
+ * Resolve the agent's vector store. Cascade:
102
+ *
103
+ * - falsy: no vector.
104
+ * - `boolean` / `undefined-inheriting-true`: return the shared
105
+ * singleton (built lazily on first call). All agents that
106
+ * default-enable memory write into and recall from one index.
107
+ * - object: build a dedicated `PgVector` for this agent.
108
+ */
109
+ private buildVector;
110
+ private getSharedVector;
111
+ }