@dbx-tools/appkit-mastra 0.1.78 → 0.1.80
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.
- package/README.md +23 -54
- package/dist/index.d.ts +50 -326
- package/dist/index.js +191 -617
- package/package.json +11 -8
package/dist/index.js
CHANGED
|
@@ -338,7 +338,7 @@ function sanitizeServingMessages(messages) {
|
|
|
338
338
|
* the demo client and any other UI consumer share the exact same
|
|
339
339
|
* shape this module reads and writes.
|
|
340
340
|
*/
|
|
341
|
-
const log$
|
|
341
|
+
const log$7 = logUtils.logger("mastra/chart");
|
|
342
342
|
/**
|
|
343
343
|
* TTL for cached chart entries. One hour balances "long enough for
|
|
344
344
|
* the host UI to fetch the chart well after the model finished
|
|
@@ -594,7 +594,7 @@ async function writeChart(entry) {
|
|
|
594
594
|
const key = await chartCacheKey(entry.chartId);
|
|
595
595
|
await CacheManager.getInstanceSync().set(key, entry, { ttl: CHART_CACHE_TTL_SEC });
|
|
596
596
|
} catch (err) {
|
|
597
|
-
log$
|
|
597
|
+
log$7.warn("write-error", {
|
|
598
598
|
chartId: entry.chartId,
|
|
599
599
|
error: commonUtils.errorMessage(err)
|
|
600
600
|
});
|
|
@@ -609,7 +609,7 @@ async function readChart(chartId) {
|
|
|
609
609
|
const key = await chartCacheKey(chartId);
|
|
610
610
|
return await CacheManager.getInstanceSync().get(key) ?? void 0;
|
|
611
611
|
} catch (err) {
|
|
612
|
-
log$
|
|
612
|
+
log$7.warn("read-error", {
|
|
613
613
|
chartId,
|
|
614
614
|
error: commonUtils.errorMessage(err)
|
|
615
615
|
});
|
|
@@ -634,7 +634,7 @@ async function readChart(chartId) {
|
|
|
634
634
|
async function prepareChart(opts) {
|
|
635
635
|
const chartId = commonUtils.id();
|
|
636
636
|
await writeChart({ chartId });
|
|
637
|
-
log$
|
|
637
|
+
log$7.debug("queued", { chartId });
|
|
638
638
|
runPrepareChart(chartId, opts);
|
|
639
639
|
return { chartId };
|
|
640
640
|
}
|
|
@@ -655,14 +655,14 @@ async function runPrepareChart(chartId, opts) {
|
|
|
655
655
|
chartId,
|
|
656
656
|
result
|
|
657
657
|
});
|
|
658
|
-
log$
|
|
658
|
+
log$7.info("done", {
|
|
659
659
|
chartId,
|
|
660
660
|
chartType: result.chartType,
|
|
661
661
|
elapsedMs: Date.now() - startedAt
|
|
662
662
|
});
|
|
663
663
|
} catch (err) {
|
|
664
664
|
const error = commonUtils.errorMessage(err);
|
|
665
|
-
log$
|
|
665
|
+
log$7.warn("error", {
|
|
666
666
|
chartId,
|
|
667
667
|
error
|
|
668
668
|
});
|
|
@@ -849,338 +849,6 @@ function buildRenderDataTool(config) {
|
|
|
849
849
|
});
|
|
850
850
|
}
|
|
851
851
|
|
|
852
|
-
//#endregion
|
|
853
|
-
//#region packages/appkit-mastra/src/rest.ts
|
|
854
|
-
/**
|
|
855
|
-
* Resolve the workspace host + an authenticated header set off the
|
|
856
|
-
* client and issue a `fetch` against `path` (mounted on the host).
|
|
857
|
-
* Runs as whatever identity the client carries - the per-request OBO
|
|
858
|
-
* user when called from a request scope, the service principal
|
|
859
|
-
* otherwise.
|
|
860
|
-
*/
|
|
861
|
-
async function databricksFetch(client, path, init) {
|
|
862
|
-
const host = (await client.config.getHost()).toString();
|
|
863
|
-
const headers = new Headers({
|
|
864
|
-
"Content-Type": "application/json",
|
|
865
|
-
...init.headers
|
|
866
|
-
});
|
|
867
|
-
await client.config.authenticate(headers);
|
|
868
|
-
const url = new URL(path, host).toString();
|
|
869
|
-
return fetch(url, {
|
|
870
|
-
method: init.method,
|
|
871
|
-
headers,
|
|
872
|
-
...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {},
|
|
873
|
-
...init.signal ? { signal: init.signal } : {}
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
/** Read a response body as text, swallowing read errors (returns `""`). */
|
|
877
|
-
async function readResponseText(res) {
|
|
878
|
-
try {
|
|
879
|
-
return await res.text();
|
|
880
|
-
} catch {
|
|
881
|
-
return "";
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
/** Parse a response body as JSON, returning `{}` on empty / invalid bodies. */
|
|
885
|
-
async function readResponseJson(res) {
|
|
886
|
-
const text = await readResponseText(res);
|
|
887
|
-
if (!text) return {};
|
|
888
|
-
try {
|
|
889
|
-
return JSON.parse(text);
|
|
890
|
-
} catch {
|
|
891
|
-
return {};
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
//#endregion
|
|
896
|
-
//#region packages/appkit-mastra/src/connectors/managed-memory/client.ts
|
|
897
|
-
/**
|
|
898
|
-
* Thin REST client over the Databricks Unity Catalog Managed Agent
|
|
899
|
-
* Memory API (Beta). There is no TS SDK for memory-stores yet, so this
|
|
900
|
-
* issues authenticated requests through the shared {@link databricksFetch}
|
|
901
|
-
* primitive (`rest.ts`) as whatever identity the caller's client carries
|
|
902
|
-
* - the per-request OBO user for tools / recall, the service principal
|
|
903
|
-
* for setup-time probes.
|
|
904
|
-
*
|
|
905
|
-
* Responses are parsed defensively: the Beta wire shape may still drift,
|
|
906
|
-
* so the search parser tolerates missing / renamed entry fields and the
|
|
907
|
-
* store probe maps a 404 to `null` rather than throwing.
|
|
908
|
-
*/
|
|
909
|
-
/** Base path for the Unity Catalog memory-stores REST surface. */
|
|
910
|
-
const MEMORY_STORES_PATH = "/api/2.1/unity-catalog/memory-stores";
|
|
911
|
-
/**
|
|
912
|
-
* Error thrown for a non-2xx memory-store response. Carries the HTTP
|
|
913
|
-
* status so callers can distinguish "feature unavailable / store
|
|
914
|
-
* missing" (probe falls back) from genuine failures.
|
|
915
|
-
*/
|
|
916
|
-
var ManagedMemoryError = class extends Error {
|
|
917
|
-
constructor(status, statusText, body) {
|
|
918
|
-
super(`managed-memory: ${status} ${statusText}${body ? ` - ${body}` : ""}`);
|
|
919
|
-
this.status = status;
|
|
920
|
-
this.statusText = statusText;
|
|
921
|
-
this.name = "ManagedMemoryError";
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
/**
|
|
925
|
-
* Split a three-level `catalog.schema.name` into its parts. Throws when
|
|
926
|
-
* the input isn't exactly three dot-separated, non-empty segments so a
|
|
927
|
-
* misconfigured `MEMORY_STORE` fails loudly at setup rather than
|
|
928
|
-
* silently hitting a malformed URL.
|
|
929
|
-
*/
|
|
930
|
-
function parseStoreName(fullName) {
|
|
931
|
-
const parts = fullName.split(".");
|
|
932
|
-
if (parts.length !== 3 || parts.some((p) => p.trim() === "")) throw new Error(`managed-memory: store name must be three-level "catalog.schema.name", got "${fullName}"`);
|
|
933
|
-
const [catalog, schema, name] = parts;
|
|
934
|
-
return {
|
|
935
|
-
catalog,
|
|
936
|
-
schema,
|
|
937
|
-
name
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Issue a request and parse the JSON body, throwing
|
|
942
|
-
* {@link ManagedMemoryError} on any non-2xx status. Used by the calls
|
|
943
|
-
* where anything but success is a real error (create / write / search).
|
|
944
|
-
*/
|
|
945
|
-
async function requestJson(client, path, init) {
|
|
946
|
-
const res = await databricksFetch(client, path, init);
|
|
947
|
-
if (!res.ok) throw new ManagedMemoryError(res.status, res.statusText, await readResponseText(res));
|
|
948
|
-
return readResponseJson(res);
|
|
949
|
-
}
|
|
950
|
-
/**
|
|
951
|
-
* Fetch a store by full name. Returns the raw store payload when it
|
|
952
|
-
* exists, or `null` on 404 so callers can treat "missing" as a normal
|
|
953
|
-
* condition (the capability probe and `ensureStore` both rely on this).
|
|
954
|
-
* Any other non-2xx status throws.
|
|
955
|
-
*/
|
|
956
|
-
async function getStore(client, fullName, signal) {
|
|
957
|
-
const res = await databricksFetch(client, `${MEMORY_STORES_PATH}/${fullName}`, {
|
|
958
|
-
method: "GET",
|
|
959
|
-
...signal ? { signal } : {}
|
|
960
|
-
});
|
|
961
|
-
if (res.status === 404) return null;
|
|
962
|
-
if (!res.ok) throw new ManagedMemoryError(res.status, res.statusText, await readResponseText(res));
|
|
963
|
-
return readResponseJson(res);
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Probe for the store and create it when missing. Returns `true` when
|
|
967
|
-
* the store exists (found or created). The create body splits the
|
|
968
|
-
* three-level name into the catalog / schema / name fields the API
|
|
969
|
-
* expects. A `getStore` 404 followed by a successful create is the
|
|
970
|
-
* common first-run path.
|
|
971
|
-
*/
|
|
972
|
-
async function ensureStore(client, fullName, description, signal) {
|
|
973
|
-
if (await getStore(client, fullName, signal) !== null) return true;
|
|
974
|
-
const { catalog, schema, name } = parseStoreName(fullName);
|
|
975
|
-
await requestJson(client, MEMORY_STORES_PATH, {
|
|
976
|
-
method: "POST",
|
|
977
|
-
body: {
|
|
978
|
-
name,
|
|
979
|
-
catalog_name: catalog,
|
|
980
|
-
schema_name: schema,
|
|
981
|
-
description
|
|
982
|
-
},
|
|
983
|
-
...signal ? { signal } : {}
|
|
984
|
-
});
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Write (upsert) an entry into the store under `scope`. Scope is the
|
|
989
|
-
* OBO user id resolved in trusted code; it travels as a query parameter
|
|
990
|
-
* so the API isolates one user's memories from another's.
|
|
991
|
-
*/
|
|
992
|
-
async function writeEntry(client, fullName, scope, entry, signal) {
|
|
993
|
-
await requestJson(client, `${MEMORY_STORES_PATH}/${fullName}/entries?scope=${encodeURIComponent(scope)}`, {
|
|
994
|
-
method: "POST",
|
|
995
|
-
body: {
|
|
996
|
-
path: entry.path,
|
|
997
|
-
contents: entry.contents,
|
|
998
|
-
...entry.description ? { description: entry.description } : {}
|
|
999
|
-
},
|
|
1000
|
-
...signal ? { signal } : {}
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
/**
|
|
1004
|
-
* Semantic search over a user's entries within `scope`. Returns up to
|
|
1005
|
-
* `topK` entries, parsed defensively so a renamed / missing Beta field
|
|
1006
|
-
* degrades to an empty list rather than crashing the turn.
|
|
1007
|
-
*/
|
|
1008
|
-
async function search(client, fullName, scope, query, topK, signal) {
|
|
1009
|
-
return parseEntries(await requestJson(client, `${MEMORY_STORES_PATH}/${fullName}/entries:search?scope=${encodeURIComponent(scope)}`, {
|
|
1010
|
-
method: "POST",
|
|
1011
|
-
body: {
|
|
1012
|
-
query,
|
|
1013
|
-
top_k: topK
|
|
1014
|
-
},
|
|
1015
|
-
...signal ? { signal } : {}
|
|
1016
|
-
})).slice(0, topK);
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Defensively pull a `MemoryEntry[]` out of a search response. Accepts
|
|
1020
|
-
* the documented `{ entries: [...] }` envelope plus a bare array, and
|
|
1021
|
-
* tolerates snake_case / camelCase field drift. Entries without any
|
|
1022
|
-
* textual content are dropped.
|
|
1023
|
-
*/
|
|
1024
|
-
function parseEntries(body) {
|
|
1025
|
-
const list = Array.isArray(body) ? body : Array.isArray(body?.entries) ? body.entries : Array.isArray(body?.results) ? body.results : [];
|
|
1026
|
-
const out = [];
|
|
1027
|
-
for (const raw of list) {
|
|
1028
|
-
if (!raw || typeof raw !== "object") continue;
|
|
1029
|
-
const obj = raw;
|
|
1030
|
-
const contents = stringUtils.firstNonEmpty([
|
|
1031
|
-
obj["contents"],
|
|
1032
|
-
obj["content"],
|
|
1033
|
-
obj["text"]
|
|
1034
|
-
]);
|
|
1035
|
-
if (!contents) continue;
|
|
1036
|
-
const path = stringUtils.firstNonEmpty(obj["path"]);
|
|
1037
|
-
const description = stringUtils.firstNonEmpty([obj["description"], obj["summary"]]);
|
|
1038
|
-
const score = typeof obj["score"] === "number" ? obj["score"] : void 0;
|
|
1039
|
-
out.push({
|
|
1040
|
-
contents,
|
|
1041
|
-
...path ? { path } : {},
|
|
1042
|
-
...description ? { description } : {},
|
|
1043
|
-
...score !== void 0 ? { score } : {}
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
return out;
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
//#endregion
|
|
1050
|
-
//#region packages/appkit-mastra/src/connectors/managed-memory/context.ts
|
|
1051
|
-
/**
|
|
1052
|
-
* Per-request resolution of the OBO workspace client and the memory
|
|
1053
|
-
* scope (the user id memories are partitioned by). Shared by the
|
|
1054
|
-
* managed-memory tools and the recall processor so the "who is this
|
|
1055
|
-
* turn for, and as whom do we call Databricks" logic lives in one
|
|
1056
|
-
* place.
|
|
1057
|
-
*
|
|
1058
|
-
* Scope is always derived from trusted server state - the AppKit user
|
|
1059
|
-
* stamped on the request context, or the active execution context - and
|
|
1060
|
-
* never from anything the model controls. Returns `null` when there is
|
|
1061
|
-
* no resolvable user (a stateless turn, or an MCP call with no request
|
|
1062
|
-
* context), so callers cleanly no-op instead of leaking memories across
|
|
1063
|
-
* users.
|
|
1064
|
-
*/
|
|
1065
|
-
/**
|
|
1066
|
-
* Resolve the OBO-scoped workspace client and the memory scope for the
|
|
1067
|
-
* current turn. Mirrors `model.ts`: prefer the AppKit user stamped on
|
|
1068
|
-
* the request context, fall back to the ambient execution context (the
|
|
1069
|
-
* active OBO scope or the service principal). Scope is the Mastra
|
|
1070
|
-
* resource id (the per-user thread owner) when present, else the user
|
|
1071
|
-
* id. Returns `null` when neither yields a usable scope.
|
|
1072
|
-
*/
|
|
1073
|
-
function resolveMemoryContext(requestContext) {
|
|
1074
|
-
const user = requestContext?.get(MASTRA_USER_KEY);
|
|
1075
|
-
let executionContext;
|
|
1076
|
-
try {
|
|
1077
|
-
executionContext = user?.executionContext ?? getExecutionContext();
|
|
1078
|
-
} catch {
|
|
1079
|
-
return null;
|
|
1080
|
-
}
|
|
1081
|
-
const resourceId = requestContext?.get(MASTRA_RESOURCE_ID_KEY);
|
|
1082
|
-
const scope = stringUtils.trimToNull(resourceId) ?? stringUtils.trimToNull(user?.id);
|
|
1083
|
-
if (!scope) return null;
|
|
1084
|
-
return {
|
|
1085
|
-
client: executionContext.client,
|
|
1086
|
-
scope
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
//#endregion
|
|
1091
|
-
//#region packages/appkit-mastra/src/connectors/managed-memory/tools.ts
|
|
1092
|
-
/**
|
|
1093
|
-
* Ambient Mastra tools for Databricks Managed Agent Memory:
|
|
1094
|
-
* `save_memory` (persist a durable fact) and `search_memory` (look up
|
|
1095
|
-
* prior memories). Built only when managed memory is the active
|
|
1096
|
-
* long-term backend and merged into every agent's tool set by
|
|
1097
|
-
* `buildAgents`.
|
|
1098
|
-
*
|
|
1099
|
-
* Both tools resolve the OBO user scope from trusted request state via
|
|
1100
|
-
* {@link resolveMemoryContext} - the model passes only the content /
|
|
1101
|
-
* query, never the scope (honoring the Managed Memory security
|
|
1102
|
-
* guidance). On a stateless turn (no resolvable user) the tools no-op
|
|
1103
|
-
* with a clear message instead of writing cross-user data, and any REST
|
|
1104
|
-
* failure degrades to a short error string rather than aborting the
|
|
1105
|
-
* turn.
|
|
1106
|
-
*/
|
|
1107
|
-
const log$8 = logUtils.logger("mastra/managed-memory/tools");
|
|
1108
|
-
const saveMemoryInput = z.object({
|
|
1109
|
-
content: z.string().describe("The durable fact or preference to remember."),
|
|
1110
|
-
summary: z.string().optional().describe("Optional one-line summary used to label the memory.")
|
|
1111
|
-
});
|
|
1112
|
-
const searchMemoryInput = z.object({ query: z.string().describe("What to look up in the user's saved memories.") });
|
|
1113
|
-
/**
|
|
1114
|
-
* Build the `save_memory` / `search_memory` tool pair bound to a
|
|
1115
|
-
* resolved managed-memory runtime. The runtime supplies the store name,
|
|
1116
|
-
* recall size, and default entry path; the per-request client and scope
|
|
1117
|
-
* are resolved inside each `execute`.
|
|
1118
|
-
*/
|
|
1119
|
-
function buildManagedMemoryTools(runtime) {
|
|
1120
|
-
return {
|
|
1121
|
-
save_memory: createTool$1({
|
|
1122
|
-
id: "save_memory",
|
|
1123
|
-
description: stringUtils.toDescription([`
|
|
1124
|
-
Save a durable fact or preference about the user to long-term
|
|
1125
|
-
memory so it can be recalled in future conversations. Use when
|
|
1126
|
-
the user states something worth remembering across sessions
|
|
1127
|
-
("I'm in the EU, use EUR", "always show the SQL"). Pass the
|
|
1128
|
-
fact as \`content\` and an optional one-line \`summary\`.
|
|
1129
|
-
`, `
|
|
1130
|
-
The memory is automatically scoped to the current user - never
|
|
1131
|
-
include another person's data.
|
|
1132
|
-
`]),
|
|
1133
|
-
inputSchema: saveMemoryInput,
|
|
1134
|
-
execute: async (input, ctxRaw) => {
|
|
1135
|
-
const { content, summary } = input;
|
|
1136
|
-
const ctx = resolveMemoryContext(requestContextOf(ctxRaw));
|
|
1137
|
-
if (!ctx) return "Memory is not available for this conversation.";
|
|
1138
|
-
try {
|
|
1139
|
-
await writeEntry(ctx.client, runtime.storeName, ctx.scope, {
|
|
1140
|
-
path: runtime.entryPath,
|
|
1141
|
-
contents: content,
|
|
1142
|
-
...summary ? { description: summary } : {}
|
|
1143
|
-
});
|
|
1144
|
-
log$8.debug("saved", { scope: ctx.scope });
|
|
1145
|
-
return "Saved to memory.";
|
|
1146
|
-
} catch (err) {
|
|
1147
|
-
log$8.warn("save:error", { error: commonUtils.errorMessage(err) });
|
|
1148
|
-
return "Could not save to memory right now.";
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}),
|
|
1152
|
-
search_memory: createTool$1({
|
|
1153
|
-
id: "search_memory",
|
|
1154
|
-
description: stringUtils.toDescription([`
|
|
1155
|
-
Search the user's long-term memory for relevant saved facts or
|
|
1156
|
-
preferences. Use before answering when prior context might
|
|
1157
|
-
help. Pass what you're looking for as \`query\`; returns the
|
|
1158
|
-
most relevant saved memories (scoped to the current user).
|
|
1159
|
-
`]),
|
|
1160
|
-
inputSchema: searchMemoryInput,
|
|
1161
|
-
execute: async (input, ctxRaw) => {
|
|
1162
|
-
const { query } = input;
|
|
1163
|
-
const ctx = resolveMemoryContext(requestContextOf(ctxRaw));
|
|
1164
|
-
if (!ctx) return { memories: [] };
|
|
1165
|
-
try {
|
|
1166
|
-
return { memories: (await search(ctx.client, runtime.storeName, ctx.scope, query, runtime.topK)).map(renderEntry) };
|
|
1167
|
-
} catch (err) {
|
|
1168
|
-
log$8.warn("search:error", { error: commonUtils.errorMessage(err) });
|
|
1169
|
-
return { memories: [] };
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
})
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
/** Render an entry to a single recall line (summary prefix when present). */
|
|
1176
|
-
function renderEntry(entry) {
|
|
1177
|
-
return entry.description ? `${entry.description}: ${entry.contents}` : entry.contents;
|
|
1178
|
-
}
|
|
1179
|
-
/** Pull the Mastra `RequestContext` out of a tool's execution context arg. */
|
|
1180
|
-
function requestContextOf(ctxRaw) {
|
|
1181
|
-
return ctxRaw?.requestContext;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
852
|
//#endregion
|
|
1185
853
|
//#region packages/appkit-mastra/src/statement.ts
|
|
1186
854
|
/**
|
|
@@ -1303,7 +971,7 @@ async function safeWrite(log, writer, chunk, context = {}) {
|
|
|
1303
971
|
* `instructions` when you want the canonical "how to drive the
|
|
1304
972
|
* Genie tools" guidance.
|
|
1305
973
|
*/
|
|
1306
|
-
const log$
|
|
974
|
+
const log$6 = logUtils.logger("mastra/genie");
|
|
1307
975
|
/** Default alias used when a single unnamed Genie space is wired up. */
|
|
1308
976
|
const DEFAULT_GENIE_ALIAS = "default";
|
|
1309
977
|
/**
|
|
@@ -1418,7 +1086,7 @@ async function readCachedConversationId(cacheKey) {
|
|
|
1418
1086
|
try {
|
|
1419
1087
|
return await CacheManager.getInstanceSync().get(cacheKey) ?? void 0;
|
|
1420
1088
|
} catch (err) {
|
|
1421
|
-
log$
|
|
1089
|
+
log$6.warn("conversation-cache:read-error", { error: commonUtils.errorMessage(err) });
|
|
1422
1090
|
return;
|
|
1423
1091
|
}
|
|
1424
1092
|
}
|
|
@@ -1434,7 +1102,7 @@ async function saveCachedConversationId(cacheKey, conversationId) {
|
|
|
1434
1102
|
try {
|
|
1435
1103
|
await CacheManager.getInstanceSync().set(cacheKey, conversationId, { ttl: CONVERSATION_TTL_SEC });
|
|
1436
1104
|
} catch (err) {
|
|
1437
|
-
log$
|
|
1105
|
+
log$6.warn("conversation-cache:write-error", { error: commonUtils.errorMessage(err) });
|
|
1438
1106
|
}
|
|
1439
1107
|
}
|
|
1440
1108
|
/** Force-evict a cached conversation id. Used on the stale-id recovery path. */
|
|
@@ -1443,7 +1111,7 @@ async function evictCachedConversationId(cacheKey) {
|
|
|
1443
1111
|
try {
|
|
1444
1112
|
await CacheManager.getInstanceSync().delete(cacheKey);
|
|
1445
1113
|
} catch (err) {
|
|
1446
|
-
log$
|
|
1114
|
+
log$6.warn("conversation-cache:delete-error", { error: commonUtils.errorMessage(err) });
|
|
1447
1115
|
}
|
|
1448
1116
|
}
|
|
1449
1117
|
/**
|
|
@@ -1557,7 +1225,7 @@ function buildAskGenieTool(opts) {
|
|
|
1557
1225
|
if (trimmed.length === 0 || PLACEHOLDER_QUESTIONS.has(trimmed.toLowerCase())) throw new Error(`${toolId}: refusing placeholder question "${question}" - call ${toolId} only with a real natural-language question, or skip the call entirely`);
|
|
1558
1226
|
const cacheKey = await conversationCacheKey(spaceId, threadId);
|
|
1559
1227
|
await ensureConversationSeeded(requestContext, spaceId, cacheKey);
|
|
1560
|
-
await safeWrite(log$
|
|
1228
|
+
await safeWrite(log$6, writer, {
|
|
1561
1229
|
type: "started",
|
|
1562
1230
|
spaceId,
|
|
1563
1231
|
content: question
|
|
@@ -1570,7 +1238,7 @@ function buildAskGenieTool(opts) {
|
|
|
1570
1238
|
...seedConversationId ? { conversationId: seedConversationId } : {},
|
|
1571
1239
|
...signal ? { context: signal } : {}
|
|
1572
1240
|
})) {
|
|
1573
|
-
if (event.type !== "message") await safeWrite(log$
|
|
1241
|
+
if (event.type !== "message") await safeWrite(log$6, writer, event);
|
|
1574
1242
|
const eventConversationId = event.conversation_id;
|
|
1575
1243
|
if (eventConversationId) writeContextConversationId(requestContext, spaceId, eventConversationId);
|
|
1576
1244
|
if (event.type === "result") finalMessage = event.message;
|
|
@@ -1584,7 +1252,7 @@ function buildAskGenieTool(opts) {
|
|
|
1584
1252
|
} catch (err) {
|
|
1585
1253
|
const seeded = readContextConversationId(requestContext, spaceId);
|
|
1586
1254
|
if (seeded && apiUtils.isNotFoundError(err)) {
|
|
1587
|
-
log$
|
|
1255
|
+
log$6.warn("conversation-cache:stale, resetting", {
|
|
1588
1256
|
spaceId,
|
|
1589
1257
|
conversationId: seeded,
|
|
1590
1258
|
error: commonUtils.errorMessage(err)
|
|
@@ -2069,7 +1737,7 @@ async function collectSpaceSuggestions(opts) {
|
|
|
2069
1737
|
}));
|
|
2070
1738
|
writeSuggestionCache(spaceId, questions);
|
|
2071
1739
|
} catch (err) {
|
|
2072
|
-
log$
|
|
1740
|
+
log$6.warn("suggestions:fetch-error", {
|
|
2073
1741
|
spaceId,
|
|
2074
1742
|
error: commonUtils.errorMessage(err)
|
|
2075
1743
|
});
|
|
@@ -2086,107 +1754,41 @@ async function collectSpaceSuggestions(opts) {
|
|
|
2086
1754
|
}
|
|
2087
1755
|
|
|
2088
1756
|
//#endregion
|
|
2089
|
-
//#region packages/appkit-mastra/src/
|
|
2090
|
-
var ResultProcessor = class {
|
|
2091
|
-
id = "result-processor";
|
|
2092
|
-
processDataParts = true;
|
|
2093
|
-
async processOutputStream({ part }) {
|
|
2094
|
-
if (!part || typeof part !== "object") return part;
|
|
2095
|
-
if (![
|
|
2096
|
-
"step-finish",
|
|
2097
|
-
"finish",
|
|
2098
|
-
"tool-result",
|
|
2099
|
-
"data-tool-agent"
|
|
2100
|
-
].includes(part.type)) return part;
|
|
2101
|
-
const payload = part.payload;
|
|
2102
|
-
if (!payload || typeof payload !== "object") return part;
|
|
2103
|
-
for (const key of [
|
|
2104
|
-
"output",
|
|
2105
|
-
"messages",
|
|
2106
|
-
"response",
|
|
2107
|
-
"result"
|
|
2108
|
-
]) if (key in payload) {
|
|
2109
|
-
const value = payload[key];
|
|
2110
|
-
if (typeof value === "object") payload[key] = {};
|
|
2111
|
-
else if (Array.isArray(value)) payload[key] = [];
|
|
2112
|
-
else delete payload[key];
|
|
2113
|
-
}
|
|
2114
|
-
return part;
|
|
2115
|
-
}
|
|
2116
|
-
};
|
|
2117
|
-
|
|
2118
|
-
//#endregion
|
|
2119
|
-
//#region packages/appkit-mastra/src/processors/managed-memory-recall.ts
|
|
1757
|
+
//#region packages/appkit-mastra/src/processors.ts
|
|
2120
1758
|
/**
|
|
2121
|
-
* Mastra
|
|
2122
|
-
*
|
|
2123
|
-
*
|
|
2124
|
-
* vector index, this processor searches the user's memory-store scope
|
|
2125
|
-
* with the latest user message and injects the top entries as an
|
|
2126
|
-
* appended system message so the model has the user's durable context.
|
|
1759
|
+
* Mastra stream processors wired onto the plugin's agents.
|
|
1760
|
+
*
|
|
1761
|
+
* Two complementary processors live here:
|
|
2127
1762
|
*
|
|
2128
|
-
*
|
|
2129
|
-
*
|
|
2130
|
-
*
|
|
2131
|
-
*
|
|
1763
|
+
* - An **input** processor ({@link stripStaleChartsProcessor}) that
|
|
1764
|
+
* scrubs turn-scoped `chartId` fields out of prior tool results
|
|
1765
|
+
* replayed from Memory, so the model can't copy a stale id into the
|
|
1766
|
+
* new turn's `[chart:<id>]` markers.
|
|
1767
|
+
* - An **output** processor ({@link ResultProcessor}) that trims the
|
|
1768
|
+
* bulky, redundant payload fields off targeted outbound stream frames
|
|
1769
|
+
* before they're serialized to the SSE client.
|
|
2132
1770
|
*/
|
|
2133
|
-
const log$
|
|
1771
|
+
const log$5 = logUtils.logger("mastra/processors");
|
|
2134
1772
|
/**
|
|
2135
|
-
*
|
|
2136
|
-
*
|
|
2137
|
-
*
|
|
1773
|
+
* Recursively clone `value`, omitting any property whose key is
|
|
1774
|
+
* `chartId`. Arrays are mapped element-wise; primitives are
|
|
1775
|
+
* returned as-is. The result is structurally identical to the
|
|
1776
|
+
* input minus chartIds, so downstream message-shape consumers
|
|
1777
|
+
* keep working.
|
|
2138
1778
|
*/
|
|
2139
|
-
function
|
|
2140
|
-
return
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
if (
|
|
2146
|
-
|
|
2147
|
-
if (!query) return args.messages;
|
|
2148
|
-
try {
|
|
2149
|
-
const entries = await search(ctx.client, runtime.storeName, ctx.scope, query, runtime.topK, args.abortSignal);
|
|
2150
|
-
if (entries.length === 0) return args.messages;
|
|
2151
|
-
const block = ["Relevant memories about the user (from long-term memory):", ...entries.map((e) => `- ${renderEntry(e)}`)].join("\n");
|
|
2152
|
-
log$6.debug("recalled", {
|
|
2153
|
-
scope: ctx.scope,
|
|
2154
|
-
entries: entries.length
|
|
2155
|
-
});
|
|
2156
|
-
return {
|
|
2157
|
-
messages: args.messages,
|
|
2158
|
-
systemMessages: [...args.systemMessages, {
|
|
2159
|
-
role: "system",
|
|
2160
|
-
content: block
|
|
2161
|
-
}]
|
|
2162
|
-
};
|
|
2163
|
-
} catch (err) {
|
|
2164
|
-
log$6.warn("recall:error", { error: commonUtils.errorMessage(err) });
|
|
2165
|
-
return args.messages;
|
|
2166
|
-
}
|
|
1779
|
+
function stripChartIds(value) {
|
|
1780
|
+
if (Array.isArray(value)) return value.map(stripChartIds);
|
|
1781
|
+
if (value && typeof value === "object") {
|
|
1782
|
+
const obj = value;
|
|
1783
|
+
const out = {};
|
|
1784
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
1785
|
+
if (key === "chartId") continue;
|
|
1786
|
+
out[key] = stripChartIds(val);
|
|
2167
1787
|
}
|
|
2168
|
-
|
|
2169
|
-
}
|
|
2170
|
-
/**
|
|
2171
|
-
* Extract the most recent user message's text. Walks the message list
|
|
2172
|
-
* from the end, collecting `text` parts off the first `user` message.
|
|
2173
|
-
* Returns `null` when there is no user text to search with.
|
|
2174
|
-
*/
|
|
2175
|
-
function latestUserText(messages) {
|
|
2176
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2177
|
-
const message = messages[i];
|
|
2178
|
-
if (!message || message.role !== "user") continue;
|
|
2179
|
-
const content = message.content;
|
|
2180
|
-
const parts = content?.parts;
|
|
2181
|
-
if (!Array.isArray(parts)) return stringUtils.trimToNull(content?.content);
|
|
2182
|
-
const text = parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(" ");
|
|
2183
|
-
return stringUtils.trimToNull(text);
|
|
1788
|
+
return out;
|
|
2184
1789
|
}
|
|
2185
|
-
return
|
|
1790
|
+
return value;
|
|
2186
1791
|
}
|
|
2187
|
-
|
|
2188
|
-
//#endregion
|
|
2189
|
-
//#region packages/appkit-mastra/src/processors/strip-stale-charts.ts
|
|
2190
1792
|
/**
|
|
2191
1793
|
* Mastra input processor that strips `chartId` fields from every
|
|
2192
1794
|
* tool-invocation result in prior assistant messages before they
|
|
@@ -2210,32 +1812,8 @@ function latestUserText(messages) {
|
|
|
2210
1812
|
* `prepare_chart` / `render_data` top-level chartIds and any
|
|
2211
1813
|
* legacy `datasets[].chartId` payloads uniformly without coupling
|
|
2212
1814
|
* to specific tool ids.
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
/**
|
|
2216
|
-
* Recursively clone `value`, omitting any property whose key is
|
|
2217
|
-
* `chartId`. Arrays are mapped element-wise; primitives are
|
|
2218
|
-
* returned as-is. The result is structurally identical to the
|
|
2219
|
-
* input minus chartIds, so downstream message-shape consumers
|
|
2220
|
-
* keep working.
|
|
2221
|
-
*/
|
|
2222
|
-
function stripChartIds(value) {
|
|
2223
|
-
if (Array.isArray(value)) return value.map(stripChartIds);
|
|
2224
|
-
if (value && typeof value === "object") {
|
|
2225
|
-
const obj = value;
|
|
2226
|
-
const out = {};
|
|
2227
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
2228
|
-
if (key === "chartId") continue;
|
|
2229
|
-
out[key] = stripChartIds(val);
|
|
2230
|
-
}
|
|
2231
|
-
return out;
|
|
2232
|
-
}
|
|
2233
|
-
return value;
|
|
2234
|
-
}
|
|
2235
|
-
/**
|
|
2236
|
-
* Input processor that scrubs `chartId` from every tool-invocation
|
|
2237
|
-
* result in the message list. Wired onto every agent by default
|
|
2238
|
-
* via {@link buildAgents}; opt out with
|
|
1815
|
+
*
|
|
1816
|
+
* Wired onto every agent by default via `buildAgents`; opt out with
|
|
2239
1817
|
* `MastraPluginConfig.stripStaleCharts: false`.
|
|
2240
1818
|
*/
|
|
2241
1819
|
const stripStaleChartsProcessor = {
|
|
@@ -2263,6 +1841,44 @@ const stripStaleChartsProcessor = {
|
|
|
2263
1841
|
return args.messages;
|
|
2264
1842
|
}
|
|
2265
1843
|
};
|
|
1844
|
+
/**
|
|
1845
|
+
* Mastra output processor that trims the bulky, redundant payload
|
|
1846
|
+
* fields off targeted outbound stream frames.
|
|
1847
|
+
*
|
|
1848
|
+
* Mastra emits step / finish / tool-result frames whose payloads
|
|
1849
|
+
* echo the full tool `output` / `result` / assistant `messages` /
|
|
1850
|
+
* `response` - data the chat client already received inline and
|
|
1851
|
+
* doesn't need re-sent. This processor empties those keys on the
|
|
1852
|
+
* targeted frame types so the outbound SSE stream stays lean;
|
|
1853
|
+
* every other frame passes through untouched.
|
|
1854
|
+
*/
|
|
1855
|
+
var ResultProcessor = class {
|
|
1856
|
+
id = "result-processor";
|
|
1857
|
+
processDataParts = true;
|
|
1858
|
+
async processOutputStream({ part }) {
|
|
1859
|
+
if (!part || typeof part !== "object") return part;
|
|
1860
|
+
if (![
|
|
1861
|
+
"step-finish",
|
|
1862
|
+
"finish",
|
|
1863
|
+
"tool-result",
|
|
1864
|
+
"data-tool-agent"
|
|
1865
|
+
].includes(part.type)) return part;
|
|
1866
|
+
const payload = part.payload;
|
|
1867
|
+
if (!payload || typeof payload !== "object") return part;
|
|
1868
|
+
for (const key of [
|
|
1869
|
+
"output",
|
|
1870
|
+
"messages",
|
|
1871
|
+
"response",
|
|
1872
|
+
"result"
|
|
1873
|
+
]) if (key in payload) {
|
|
1874
|
+
const value = payload[key];
|
|
1875
|
+
if (typeof value === "object") payload[key] = {};
|
|
1876
|
+
else if (Array.isArray(value)) payload[key] = [];
|
|
1877
|
+
else delete payload[key];
|
|
1878
|
+
}
|
|
1879
|
+
return part;
|
|
1880
|
+
}
|
|
1881
|
+
};
|
|
2266
1882
|
|
|
2267
1883
|
//#endregion
|
|
2268
1884
|
//#region packages/appkit-mastra/src/summarize.ts
|
|
@@ -2536,6 +2152,15 @@ function composeInstructions(agentInstructions, style) {
|
|
|
2536
2152
|
return `${agentInstructions.trimEnd()}\n\n${style}`;
|
|
2537
2153
|
}
|
|
2538
2154
|
/**
|
|
2155
|
+
* Generated description for an agent whose definition omits one. Kept
|
|
2156
|
+
* generic (the agent's own name is the only reliable signal at build
|
|
2157
|
+
* time) so it reads sensibly as the `ask_<id>` MCP tool's description
|
|
2158
|
+
* and in agent metadata.
|
|
2159
|
+
*/
|
|
2160
|
+
function defaultAgentDescription(name) {
|
|
2161
|
+
return `Conversational AI agent "${name}".`;
|
|
2162
|
+
}
|
|
2163
|
+
/**
|
|
2539
2164
|
* Resolve every entry in `config.agents` into a Mastra `Agent`
|
|
2540
2165
|
* instance. When `config.agents` is omitted the plugin registers a
|
|
2541
2166
|
* single built-in `default` analyst so the bare `mastra()` call still
|
|
@@ -2549,24 +2174,19 @@ function composeInstructions(agentInstructions, style) {
|
|
|
2549
2174
|
* resolved registry; this is a wiring bug, not a runtime condition.
|
|
2550
2175
|
*/
|
|
2551
2176
|
async function buildAgents(opts) {
|
|
2552
|
-
const { config, context, memoryBuilder,
|
|
2177
|
+
const { config, context, memoryBuilder, log } = opts;
|
|
2553
2178
|
const definitions = resolveDefinitions(config);
|
|
2554
2179
|
const ids = Object.keys(definitions);
|
|
2555
2180
|
const defaultAgentId = config.defaultAgent ?? ids[0] ?? FALLBACK_AGENT_ID;
|
|
2556
2181
|
const plugins = buildPluginsMap(config, context);
|
|
2557
|
-
const systemTools = {
|
|
2558
|
-
render_data: buildRenderDataTool(config),
|
|
2559
|
-
summarize: buildSummarizeTool(config)
|
|
2560
|
-
};
|
|
2561
|
-
const managedMemoryTools = managedMemory?.tools ? buildManagedMemoryTools(managedMemory) : {};
|
|
2562
2182
|
const ambientTools = {
|
|
2563
|
-
|
|
2564
|
-
|
|
2183
|
+
render_data: buildRenderDataTool(config),
|
|
2184
|
+
summarize: buildSummarizeTool(config),
|
|
2565
2185
|
...config.tools ?? {}
|
|
2566
2186
|
};
|
|
2567
2187
|
const style = resolveStyleInstructions(config);
|
|
2568
2188
|
const outputProcessors = [new ResultProcessor()];
|
|
2569
|
-
const inputProcessors = [...config.stripStaleCharts === false ? [] : [stripStaleChartsProcessor]
|
|
2189
|
+
const inputProcessors = [...config.stripStaleCharts === false ? [] : [stripStaleChartsProcessor]];
|
|
2570
2190
|
const agents = {};
|
|
2571
2191
|
for (const [id, def] of Object.entries(definitions)) {
|
|
2572
2192
|
const tools = await resolveTools(def.tools, plugins, ambientTools);
|
|
@@ -2574,7 +2194,7 @@ async function buildAgents(opts) {
|
|
|
2574
2194
|
agents[id] = new Agent({
|
|
2575
2195
|
id,
|
|
2576
2196
|
name: def.name ?? id,
|
|
2577
|
-
|
|
2197
|
+
description: def.description?.trim() || defaultAgentDescription(def.name ?? id),
|
|
2578
2198
|
instructions: composeInstructions(def.instructions, style),
|
|
2579
2199
|
model: resolveModel(config, def.model),
|
|
2580
2200
|
defaultOptions: { maxSteps: config.agentMaxSteps ?? DEFAULT_AGENT_MAX_STEPS },
|
|
@@ -2811,90 +2431,30 @@ function toolkitEntryToMastraTool(entry, plugin) {
|
|
|
2811
2431
|
});
|
|
2812
2432
|
}
|
|
2813
2433
|
|
|
2814
|
-
//#endregion
|
|
2815
|
-
//#region packages/appkit-mastra/src/connectors/managed-memory/defaults.ts
|
|
2816
|
-
/**
|
|
2817
|
-
* Default values and environment variable names for the managed-memory
|
|
2818
|
-
* connector. Kept in a leaf module so the client, tools, recall
|
|
2819
|
-
* processor, and plugin setup can share them without a cycle.
|
|
2820
|
-
*/
|
|
2821
|
-
/**
|
|
2822
|
-
* Environment variable holding the three-level Unity Catalog store name
|
|
2823
|
-
* (`catalog.schema.name`). Read when {@link ManagedMemoryConfig.store}
|
|
2824
|
-
* is omitted; also the sole enable signal in prefer-if-available mode
|
|
2825
|
-
* (managed memory stays off when it is unset).
|
|
2826
|
-
*/
|
|
2827
|
-
const MEMORY_STORE_ENV = "MEMORY_STORE";
|
|
2828
|
-
/**
|
|
2829
|
-
* Default number of entries the recall processor injects and
|
|
2830
|
-
* `search_memory` returns. Matches the prior `PgVector`
|
|
2831
|
-
* `semanticRecall.topK` so behavior is unchanged after the swap.
|
|
2832
|
-
*/
|
|
2833
|
-
const DEFAULT_TOPK = 3;
|
|
2834
|
-
/**
|
|
2835
|
-
* Default path `save_memory` writes land under. Managed Memory keys
|
|
2836
|
-
* entries by path within a scope, so a single stable path accumulates a
|
|
2837
|
-
* user's notes rather than scattering them.
|
|
2838
|
-
*/
|
|
2839
|
-
const DEFAULT_ENTRY_PATH = "/memories/notes.md";
|
|
2840
|
-
/** Description stamped on the store when the connector auto-creates it. */
|
|
2841
|
-
const DEFAULT_STORE_DESCRIPTION = "Long-term agent memory managed by @dbx-tools/appkit-mastra.";
|
|
2842
|
-
|
|
2843
|
-
//#endregion
|
|
2844
|
-
//#region packages/appkit-mastra/src/connectors/managed-memory/resolve.ts
|
|
2845
|
-
/**
|
|
2846
|
-
* Resolve the managed-memory target from config + env, or `null` when
|
|
2847
|
-
* managed memory is disabled. Throws when explicitly enabled but no
|
|
2848
|
-
* store name can be resolved (config or `MEMORY_STORE`), so a
|
|
2849
|
-
* misconfiguration surfaces at setup rather than silently falling back.
|
|
2850
|
-
*/
|
|
2851
|
-
function resolveManagedMemoryTarget(config) {
|
|
2852
|
-
const setting = config.managedMemory;
|
|
2853
|
-
if (setting === false) return null;
|
|
2854
|
-
const envStore = trimmedEnv(MEMORY_STORE_ENV);
|
|
2855
|
-
const obj = typeof setting === "object" ? setting : {};
|
|
2856
|
-
if (!(setting === true || typeof setting === "object") && !envStore) return null;
|
|
2857
|
-
const storeName = trimmed(obj.store) ?? envStore;
|
|
2858
|
-
if (!storeName) throw new Error(`mastra: managedMemory is enabled but no store name is set. Set managedMemory.store or the ${MEMORY_STORE_ENV} env var to a three-level Unity Catalog name (catalog.schema.name).`);
|
|
2859
|
-
return {
|
|
2860
|
-
storeName,
|
|
2861
|
-
description: trimmed(obj.description) ?? DEFAULT_STORE_DESCRIPTION,
|
|
2862
|
-
topK: obj.topK ?? DEFAULT_TOPK,
|
|
2863
|
-
entryPath: trimmed(obj.entryPath) ?? DEFAULT_ENTRY_PATH,
|
|
2864
|
-
autoCreate: obj.autoCreate ?? true,
|
|
2865
|
-
recall: obj.recall ?? true,
|
|
2866
|
-
tools: obj.tools ?? true
|
|
2867
|
-
};
|
|
2868
|
-
}
|
|
2869
|
-
/** Trimmed env var value, or undefined when unset / blank. */
|
|
2870
|
-
function trimmedEnv(name) {
|
|
2871
|
-
return trimmed(process.env[name]);
|
|
2872
|
-
}
|
|
2873
|
-
/** Trimmed string, or undefined when undefined / blank. */
|
|
2874
|
-
function trimmed(value) {
|
|
2875
|
-
if (typeof value !== "string") return void 0;
|
|
2876
|
-
const t = value.trim();
|
|
2877
|
-
return t === "" ? void 0 : t;
|
|
2878
|
-
}
|
|
2879
|
-
|
|
2880
2434
|
//#endregion
|
|
2881
2435
|
//#region packages/appkit-mastra/src/mcp.ts
|
|
2882
2436
|
/** MCP server version advertised when the caller doesn't pin one. */
|
|
2883
2437
|
const DEFAULT_MCP_VERSION = "1.0.0";
|
|
2884
2438
|
/**
|
|
2885
|
-
* Build the plugin's MCP server, or `null` when `config.mcp` is
|
|
2886
|
-
*
|
|
2439
|
+
* Build the plugin's MCP server, or `null` only when `config.mcp` is
|
|
2440
|
+
* explicitly `false`.
|
|
2887
2441
|
*
|
|
2888
|
-
*
|
|
2889
|
-
*
|
|
2890
|
-
* (
|
|
2891
|
-
*
|
|
2892
|
-
*
|
|
2442
|
+
* Agent exposure is on by default because it's cheap: the already-
|
|
2443
|
+
* registered agents are wrapped as `ask_<agentId>` tools and handed to
|
|
2444
|
+
* `@mastra/express` (already mounted for the chat routes) - no extra
|
|
2445
|
+
* process, dependency, or network cost, and requests run under the same
|
|
2446
|
+
* OBO scope. So `undefined` (the default) and `true` both expose every
|
|
2447
|
+
* registered agent under a server id equal to the plugin name; only
|
|
2448
|
+
* `false` turns MCP off. The object form ({@link MastraMcpConfig}) tunes
|
|
2449
|
+
* the id / advertised metadata and can additionally expose the plugin's
|
|
2450
|
+
* ambient tools or a set of extra MCP-only tools. Ambient tools stay off
|
|
2451
|
+
* unless explicitly enabled - they assume an in-process chat turn, so
|
|
2452
|
+
* they aren't cheap / safe to expose to a standalone MCP client.
|
|
2893
2453
|
*/
|
|
2894
2454
|
function buildMcpServer(opts) {
|
|
2895
2455
|
const { config, pluginName, displayName, agents, ambientTools } = opts;
|
|
2896
|
-
if (
|
|
2897
|
-
const settings = config.mcp ===
|
|
2456
|
+
if (config.mcp === false) return null;
|
|
2457
|
+
const settings = typeof config.mcp === "object" ? config.mcp : {};
|
|
2898
2458
|
const serverId = settings.serverId ?? pluginName;
|
|
2899
2459
|
const exposeAgents = settings.agents !== false;
|
|
2900
2460
|
const tools = {
|
|
@@ -2911,9 +2471,9 @@ function buildMcpServer(opts) {
|
|
|
2911
2471
|
...exposeAgents ? { agents } : {},
|
|
2912
2472
|
tools
|
|
2913
2473
|
}),
|
|
2914
|
-
httpPath:
|
|
2915
|
-
ssePath:
|
|
2916
|
-
messagePath:
|
|
2474
|
+
httpPath: "/mcp",
|
|
2475
|
+
ssePath: "/sse",
|
|
2476
|
+
messagePath: "/messages"
|
|
2917
2477
|
};
|
|
2918
2478
|
}
|
|
2919
2479
|
|
|
@@ -3231,8 +2791,8 @@ function needsLakebase(config) {
|
|
|
3231
2791
|
* Caches the shared `PgVector` singleton (built on first need) so each
|
|
3232
2792
|
* agent build is O(1) after the first.
|
|
3233
2793
|
*/
|
|
3234
|
-
function createMemoryBuilder(config, servicePrincipalPool
|
|
3235
|
-
return new MemoryBuilder(config, servicePrincipalPool
|
|
2794
|
+
function createMemoryBuilder(config, servicePrincipalPool) {
|
|
2795
|
+
return new MemoryBuilder(config, servicePrincipalPool);
|
|
3236
2796
|
}
|
|
3237
2797
|
/**
|
|
3238
2798
|
* Builds one `Memory` per agent against a shared service-principal
|
|
@@ -3241,18 +2801,11 @@ function createMemoryBuilder(config, servicePrincipalPool, options = {}) {
|
|
|
3241
2801
|
*/
|
|
3242
2802
|
var MemoryBuilder = class {
|
|
3243
2803
|
sharedVector;
|
|
3244
|
-
constructor(config, servicePrincipalPool
|
|
2804
|
+
constructor(config, servicePrincipalPool) {
|
|
3245
2805
|
this.config = config;
|
|
3246
2806
|
this.servicePrincipalPool = servicePrincipalPool;
|
|
3247
|
-
this.options = options;
|
|
3248
2807
|
}
|
|
3249
2808
|
/**
|
|
3250
|
-
* Build a `Memory` for `agentId` after the plugin/agent cascade.
|
|
3251
|
-
* Returns `undefined` when the agent has neither storage nor a
|
|
3252
|
-
* vector store enabled - Mastra accepts a missing `memory` field
|
|
3253
|
-
* and treats the agent as stateless.
|
|
3254
|
-
*/
|
|
3255
|
-
/**
|
|
3256
2809
|
* Build the Mastra-instance-level storage used for workflow
|
|
3257
2810
|
* snapshots. Returns `undefined` when plugin-level `storage` is
|
|
3258
2811
|
* disabled, in which case `agent.resumeStream()` (and therefore
|
|
@@ -3273,6 +2826,12 @@ var MemoryBuilder = class {
|
|
|
3273
2826
|
pool: this.servicePrincipalPool
|
|
3274
2827
|
});
|
|
3275
2828
|
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Build a `Memory` for `agentId` after the plugin/agent cascade.
|
|
2831
|
+
* Returns `undefined` when the agent has neither storage nor a
|
|
2832
|
+
* vector store enabled - Mastra accepts a missing `memory` field
|
|
2833
|
+
* and treats the agent as stateless.
|
|
2834
|
+
*/
|
|
3276
2835
|
forAgent(agentId, def) {
|
|
3277
2836
|
const storageSetting = def.storage ?? this.config.storage;
|
|
3278
2837
|
const memorySetting = def.memory ?? this.config.memory;
|
|
@@ -3326,7 +2885,6 @@ var MemoryBuilder = class {
|
|
|
3326
2885
|
* - object: build a dedicated `PgVector` for this agent.
|
|
3327
2886
|
*/
|
|
3328
2887
|
buildVector(setting) {
|
|
3329
|
-
if (this.options.suppressVector) return void 0;
|
|
3330
2888
|
if (!setting) return void 0;
|
|
3331
2889
|
if (typeof setting === "boolean") return this.getSharedVector();
|
|
3332
2890
|
return buildPgVector(setting);
|
|
@@ -3394,6 +2952,49 @@ function withId(value, fallback) {
|
|
|
3394
2952
|
};
|
|
3395
2953
|
}
|
|
3396
2954
|
|
|
2955
|
+
//#endregion
|
|
2956
|
+
//#region packages/appkit-mastra/src/rest.ts
|
|
2957
|
+
/**
|
|
2958
|
+
* Resolve the workspace host + an authenticated header set off the
|
|
2959
|
+
* client and issue a `fetch` against `path` (mounted on the host).
|
|
2960
|
+
* Runs as whatever identity the client carries - the per-request OBO
|
|
2961
|
+
* user when called from a request scope, the service principal
|
|
2962
|
+
* otherwise.
|
|
2963
|
+
*/
|
|
2964
|
+
async function databricksFetch(client, path, init) {
|
|
2965
|
+
const host = (await client.config.getHost()).toString();
|
|
2966
|
+
const headers = new Headers({
|
|
2967
|
+
"Content-Type": "application/json",
|
|
2968
|
+
...init.headers
|
|
2969
|
+
});
|
|
2970
|
+
await client.config.authenticate(headers);
|
|
2971
|
+
const url = new URL(path, host).toString();
|
|
2972
|
+
return fetch(url, {
|
|
2973
|
+
method: init.method,
|
|
2974
|
+
headers,
|
|
2975
|
+
...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {},
|
|
2976
|
+
...init.signal ? { signal: init.signal } : {}
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2979
|
+
/** Read a response body as text, swallowing read errors (returns `""`). */
|
|
2980
|
+
async function readResponseText(res) {
|
|
2981
|
+
try {
|
|
2982
|
+
return await res.text();
|
|
2983
|
+
} catch {
|
|
2984
|
+
return "";
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
/** Parse a response body as JSON, returning `{}` on empty / invalid bodies. */
|
|
2988
|
+
async function readResponseJson(res) {
|
|
2989
|
+
const text = await readResponseText(res);
|
|
2990
|
+
if (!text) return {};
|
|
2991
|
+
try {
|
|
2992
|
+
return JSON.parse(text);
|
|
2993
|
+
} catch {
|
|
2994
|
+
return {};
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
|
|
3397
2998
|
//#endregion
|
|
3398
2999
|
//#region packages/appkit-mastra/src/mlflow.ts
|
|
3399
3000
|
/**
|
|
@@ -4226,6 +3827,14 @@ var MastraPlugin = class MastraPlugin extends Plugin {
|
|
|
4226
3827
|
return resolveFeedbackEnabled(this.config.feedback);
|
|
4227
3828
|
}
|
|
4228
3829
|
injectRoutes(router) {
|
|
3830
|
+
router.use((req, _res, next) => {
|
|
3831
|
+
const target = this.mcpRouteAlias(req.path);
|
|
3832
|
+
if (target) {
|
|
3833
|
+
const q = req.url.indexOf("?");
|
|
3834
|
+
req.url = q >= 0 ? target + req.url.slice(q) : target;
|
|
3835
|
+
}
|
|
3836
|
+
next();
|
|
3837
|
+
});
|
|
4229
3838
|
router.get(MASTRA_ROUTES.models, (req, res, next) => {
|
|
4230
3839
|
this.userScopedSelf(req).listModels().then((endpoints) => res.json({ endpoints })).catch(next);
|
|
4231
3840
|
});
|
|
@@ -4299,7 +3908,7 @@ var MastraPlugin = class MastraPlugin extends Plugin {
|
|
|
4299
3908
|
if (!this.mastraApp) return res.status(503).end();
|
|
4300
3909
|
if (!isMastraRequestAllowed(req.method, req.path, {
|
|
4301
3910
|
access: this.config.apiAccess ?? "scoped",
|
|
4302
|
-
mcpEnabled:
|
|
3911
|
+
mcpEnabled: this.mcp !== null
|
|
4303
3912
|
})) {
|
|
4304
3913
|
res.status(403).json({ error: "Endpoint not exposed to the client (apiAccess=scoped)" });
|
|
4305
3914
|
return;
|
|
@@ -4320,6 +3929,21 @@ var MastraPlugin = class MastraPlugin extends Plugin {
|
|
|
4320
3929
|
this.mastraApp(req, res, next);
|
|
4321
3930
|
}
|
|
4322
3931
|
/**
|
|
3932
|
+
* Map a clean, mount-relative MCP alias path to the underlying
|
|
3933
|
+
* `@mastra/express` route. Returns `null` when MCP is off or the path
|
|
3934
|
+
* isn't an alias. Collapses the stock `/mcp/<serverId>/<transport>`
|
|
3935
|
+
* layout (serverId defaults to the plugin name) down to `/mcp`,
|
|
3936
|
+
* `/sse`, and `/messages`.
|
|
3937
|
+
*/
|
|
3938
|
+
mcpRouteAlias(path) {
|
|
3939
|
+
if (!this.mcp) return null;
|
|
3940
|
+
const id = this.mcp.serverId;
|
|
3941
|
+
if (path === "/mcp") return `/mcp/${id}/mcp`;
|
|
3942
|
+
if (path === "/sse") return `/mcp/${id}/sse`;
|
|
3943
|
+
if (path === "/messages") return `/mcp/${id}/messages`;
|
|
3944
|
+
return null;
|
|
3945
|
+
}
|
|
3946
|
+
/**
|
|
4323
3947
|
* Implementation backing the `/suggestions` route. Runs inside the
|
|
4324
3948
|
* AppKit user-context proxy so `getExecutionContext()` returns the
|
|
4325
3949
|
* OBO-scoped client. Resolves the plugin's Genie spaces and merges
|
|
@@ -4404,67 +4028,17 @@ var MastraPlugin = class MastraPlugin extends Plugin {
|
|
|
4404
4028
|
const client = getExecutionContext().client;
|
|
4405
4029
|
return listServingEndpoints(client, (await client.config.getHost()).toString(), { ttlMs: resolveServingConfig(this.config).ttlMs });
|
|
4406
4030
|
}
|
|
4407
|
-
/**
|
|
4408
|
-
* Resolve and probe the Databricks Managed Memory backend at setup.
|
|
4409
|
-
*
|
|
4410
|
-
* Returns the runtime (store + recall sizing + tool / recall toggles)
|
|
4411
|
-
* when managed memory is enabled and the workspace memory-stores API
|
|
4412
|
-
* is reachable, else `undefined` so the caller uses the PgVector path.
|
|
4413
|
-
*
|
|
4414
|
-
* The probe runs as the service principal (setup is outside any
|
|
4415
|
-
* `asUser` scope, so `getExecutionContext()` yields the SP client):
|
|
4416
|
-
* the SP owns the store and is the identity that can `CREATE MEMORY
|
|
4417
|
-
* STORE`. When `autoCreate` is on the store is created if missing.
|
|
4418
|
-
*
|
|
4419
|
-
* A misconfiguration where managed memory is explicitly enabled but no
|
|
4420
|
-
* store name is resolvable throws ({@link resolveManagedMemoryTarget})
|
|
4421
|
-
* - that is a wiring bug, not a runtime condition. Every other failure
|
|
4422
|
-
* (feature unavailable, permission denied, network) is caught, logged,
|
|
4423
|
-
* and degraded to a PgVector fallback so chat stays available.
|
|
4424
|
-
*/
|
|
4425
|
-
async resolveManagedMemory() {
|
|
4426
|
-
const target = resolveManagedMemoryTarget(this.config);
|
|
4427
|
-
if (!target) return void 0;
|
|
4428
|
-
try {
|
|
4429
|
-
const client = getExecutionContext().client;
|
|
4430
|
-
if (!(target.autoCreate ? await ensureStore(client, target.storeName, target.description) : await getStore(client, target.storeName) !== null)) {
|
|
4431
|
-
this.log.warn("managed-memory:unavailable; using PgVector fallback", { store: target.storeName });
|
|
4432
|
-
return;
|
|
4433
|
-
}
|
|
4434
|
-
this.log.info("managed-memory:active", {
|
|
4435
|
-
store: target.storeName,
|
|
4436
|
-
topK: target.topK,
|
|
4437
|
-
autoCreate: target.autoCreate
|
|
4438
|
-
});
|
|
4439
|
-
return {
|
|
4440
|
-
storeName: target.storeName,
|
|
4441
|
-
topK: target.topK,
|
|
4442
|
-
entryPath: target.entryPath,
|
|
4443
|
-
tools: target.tools,
|
|
4444
|
-
recall: target.recall
|
|
4445
|
-
};
|
|
4446
|
-
} catch (err) {
|
|
4447
|
-
this.log.warn("managed-memory:probe failed; using PgVector fallback", {
|
|
4448
|
-
store: target.storeName,
|
|
4449
|
-
error: commonUtils.errorMessage(err)
|
|
4450
|
-
});
|
|
4451
|
-
return;
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
4031
|
async buildAgentAndServer() {
|
|
4455
4032
|
if (needsLakebase(this.config)) this.servicePrincipalPool = await createServicePrincipalPool(appkitUtils.require(this.context, lakebase, this.config).exports().getPgConfig());
|
|
4456
|
-
const
|
|
4457
|
-
const memoryBuilder = this.servicePrincipalPool ? createMemoryBuilder(this.config, this.servicePrincipalPool, { suppressVector: managedMemory !== void 0 }) : void 0;
|
|
4033
|
+
const memoryBuilder = this.servicePrincipalPool ? createMemoryBuilder(this.config, this.servicePrincipalPool) : void 0;
|
|
4458
4034
|
this.log.debug("build:start", {
|
|
4459
4035
|
lakebase: memoryBuilder !== void 0,
|
|
4460
|
-
managedMemory: managedMemory !== void 0,
|
|
4461
4036
|
stripStaleCharts: this.config.stripStaleCharts !== false
|
|
4462
4037
|
});
|
|
4463
4038
|
this.built = await buildAgents({
|
|
4464
4039
|
config: this.config,
|
|
4465
4040
|
context: this.context,
|
|
4466
4041
|
memoryBuilder,
|
|
4467
|
-
...managedMemory ? { managedMemory } : {},
|
|
4468
4042
|
log: this.log
|
|
4469
4043
|
});
|
|
4470
4044
|
const instanceStorage = memoryBuilder?.instanceStorage();
|
|
@@ -4551,4 +4125,4 @@ function parseStatementLimit(raw) {
|
|
|
4551
4125
|
const mastra = toPlugin(MastraPlugin);
|
|
4552
4126
|
|
|
4553
4127
|
//#endregion
|
|
4554
|
-
export { DEFAULT_AGENT_MAX_STEPS,
|
|
4128
|
+
export { DEFAULT_AGENT_MAX_STEPS, DEFAULT_GENIE_ALIAS, DEFAULT_STYLE_INSTRUCTIONS, FALLBACK_AGENT_ID, GENIE_INSTRUCTIONS, MASTRA_MODEL_OVERRIDE_KEY, MASTRA_REQUEST_ID_KEY, MASTRA_USER_EMAIL_KEY, MASTRA_USER_KEY, MASTRA_USER_NAME_KEY, MastraPlugin, TRACE_REQUEST_CONTEXT_KEYS, buildAgents, buildGenieToolkitProvider, buildGenieTools, buildMcpServer, buildRenderDataTool, buildSummarizeTool, chartPlannerRequestSchema, collectSpaceSuggestions, createAgent, createTool, extractModelOverride, fetchChart, mastra, normalizeGenieSpaces, prepareChart, resolveGenieSpaces, summarizeText, tool };
|