@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,168 @@
1
+ /**
2
+ * Databricks Model Serving resolver for Mastra agents.
3
+ *
4
+ * Each agent step calls {@link buildModel} with the active
5
+ * `RequestContext`. The user stamped by `MastraServer` carries an
6
+ * AppKit `WorkspaceClient`; we ask it for the workspace host and a
7
+ * fresh bearer header, then point Mastra's OpenAI-compatible provider
8
+ * at `/serving-endpoints` on that host.
9
+ *
10
+ * This module only adds the Mastra-specific glue. The actual model
11
+ * selection - listing the workspace catalogue and resolving an
12
+ * explicit name / class / fallback chain to a real endpoint id - lives
13
+ * in `@dbx-tools/model` ({@link selectModel}) so non-Mastra consumers
14
+ * (e.g. a job that just needs a model name) can reuse it. Here we
15
+ * assemble the explicit ask from Mastra's request context (the
16
+ * per-request override under {@link MASTRA_MODEL_OVERRIDE_KEY}, the
17
+ * agent / plugin `modelId`, or `DATABRICKS_SERVING_ENDPOINT_NAME`),
18
+ * pass the plugin's fuzzy / class / fallback knobs through, and wrap
19
+ * the resolved id in the OpenAI-compatible provider config Mastra
20
+ * expects. Catalogue fetches fail loud: network / auth errors
21
+ * propagate so callers see the real SDK message.
22
+ *
23
+ * @module
24
+ */
25
+ import { getExecutionContext } from "@databricks/appkit";
26
+ import { classes, resolve } from "@dbx-tools/model";
27
+ import { functionModule, json, log, net } from "@dbx-tools/shared-core";
28
+ import { MASTRA_USER_KEY } from "./config.js";
29
+ import { MASTRA_MODEL_OVERRIDE_KEY, resolveServingConfig } from "./serving.js";
30
+ import { rewriteServingBody, rewriteServingResponseBody } from "./serving-sanitize.js";
31
+ const { parseModelClass } = classes;
32
+ const { selectModel } = resolve;
33
+ /**
34
+ * Resolve a `MastraModelConfig` for the current agent step. Runs
35
+ * while `agent.stream` is inside the `asUser(req)` scope so tokens
36
+ * are user-scoped; outside an active user context the workspace
37
+ * client falls back to the service principal.
38
+ *
39
+ * Endpoint precedence: the per-request override
40
+ * ({@link MASTRA_MODEL_OVERRIDE_KEY}, only when `config.modelOverride` allows
41
+ * it), then {@link BuildModelOverrides.modelId} from the agent / plugin
42
+ * config, then `DATABRICKS_SERVING_ENDPOINT_NAME`. With none of those set the
43
+ * capability class and fallback ladder in `@dbx-tools/model` choose the
44
+ * endpoint.
45
+ */
46
+ export async function buildModel(config, requestContext, overrides = {}) {
47
+ void setupFetchInterceptor();
48
+ // The chat path stamps the AppKit user on the request context via
49
+ // `MastraServer`. The MCP transport routes don't thread that context
50
+ // into tool execution, so fall back to the ambient execution context
51
+ // (the active OBO scope, or the service principal) when it's absent.
52
+ const user = requestContext.get(MASTRA_USER_KEY);
53
+ const executionContext = user?.executionContext ?? getExecutionContext();
54
+ const clientConfig = executionContext.client.config;
55
+ const host = (await clientConfig.getHost()).toString();
56
+ const headers = new Headers();
57
+ await clientConfig.authenticate(headers);
58
+ // The OpenAI Node SDK appends paths like `/chat/completions` to whatever
59
+ // URL we hand it. Drop the trailing slash so the resulting URL stays
60
+ // well-formed (`/serving-endpoints/chat/completions`).
61
+ const url = new URL("/serving-endpoints", host).toString().replace(/\/$/, "");
62
+ const logger = log.logger(config);
63
+ const serving = resolveServingConfig(config);
64
+ const override = serving.allowOverride
65
+ ? requestContext.get(MASTRA_MODEL_OVERRIDE_KEY)
66
+ : undefined;
67
+ // The override / agent default / env value can be either a concrete
68
+ // endpoint name or a model class slug ("chat-thinking" /
69
+ // "chat-balanced" / "chat-fast"). A class slug becomes a class intent
70
+ // (let the live catalogue pick the best model in that band); anything
71
+ // else is an explicit name fuzzy-matched against the catalogue. An
72
+ // internal `overrides.modelClass` (e.g. the chart planner) is the
73
+ // floor when nothing was requested.
74
+ const requested = override ?? overrides.modelId ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;
75
+ const requestedClass = requested !== undefined ? parseModelClass(requested) : null;
76
+ const explicit = requestedClass === null ? requested : undefined;
77
+ const modelClass = requestedClass ?? overrides.modelClass;
78
+ const { modelId, source } = await selectModel(executionContext.client, host, {
79
+ ...(explicit !== undefined ? { explicit } : {}),
80
+ fuzzy: serving.fuzzy,
81
+ threshold: serving.threshold,
82
+ ...(modelClass !== undefined ? { modelClass } : {}),
83
+ fallbacks: serving.fallbacks,
84
+ ttlMs: serving.ttlMs,
85
+ });
86
+ logger.debug("model selected", { modelId, source, requested });
87
+ return {
88
+ providerId: config.providerId ?? "databricks",
89
+ modelId,
90
+ url,
91
+ headers: Object.fromEntries(headers.entries()),
92
+ };
93
+ }
94
+ /** Path prefix that identifies a Databricks Model Serving REST call. */
95
+ const SERVING_ENDPOINTS_PATH_PREFIX = "/serving-endpoints/";
96
+ /**
97
+ * Install a single shared `globalThis.fetch` wrapper for every POST to
98
+ * `/serving-endpoints/...`. The wrapper does two things:
99
+ *
100
+ * 1. Rewrites the outgoing `messages` array to repair Mastra/AI SDK
101
+ * stream-replay quirks that Databricks-hosted Claude rejects (see
102
+ * {@link rewriteServingBody} in `./serving-sanitize.js`).
103
+ * 2. At `LOG_LEVEL=debug`, dumps the (post-sanitize) JSON body so
104
+ * 4xx debugging doesn't have to fight AI SDK's `[Array]`
105
+ * formatter.
106
+ * 3. Repairs the non-streaming JSON response, where Databricks-hosted
107
+ * Gemini returns `choices[].message.content` as a parts array that
108
+ * the AI SDK's OpenAI schema rejects (see
109
+ * {@link rewriteServingResponseBody}).
110
+ *
111
+ * Safe to call from any hot path: {@link functionModule.memoize} ensures
112
+ * the wrapper is installed at most once per process, so subsequent
113
+ * calls are a no-op even when {@link buildModel} fires on every agent
114
+ * step.
115
+ */
116
+ const setupFetchInterceptor = functionModule.memoize(() => {
117
+ const logger = log.logger("mastra/llm");
118
+ const original = globalThis.fetch.bind(globalThis);
119
+ globalThis.fetch = (async (input, init) => {
120
+ const url = net.urlBuilder(input);
121
+ if (!url ||
122
+ !url.pathname.startsWith(SERVING_ENDPOINTS_PATH_PREFIX) ||
123
+ typeof init?.body !== "string") {
124
+ return original(input, init);
125
+ }
126
+ const rewritten = rewriteServingBody(init.body);
127
+ if (rewritten !== init.body) {
128
+ init = { ...init, body: rewritten };
129
+ }
130
+ const parsed = json.parse(rewritten);
131
+ logger.debug("POST", parsed === undefined
132
+ ? { url: url.toString(), bodyType: "non-JSON" }
133
+ : { url: url.toString(), body: parsed });
134
+ const response = await original(input, init);
135
+ return repairServingResponse(response);
136
+ });
137
+ });
138
+ /**
139
+ * Rewrite a non-streaming serving response whose body needs repair, leaving
140
+ * everything else byte-identical.
141
+ *
142
+ * Streaming turns are passed straight through: an SSE body must stay a live
143
+ * stream (buffering it to a string would defeat streaming and break
144
+ * `text/event-stream` parsing), and the delta frames already carry string
145
+ * content, so they never hit the array-shaped `content` bug. Likewise a
146
+ * non-JSON or error body is returned untouched, so the caller still sees the
147
+ * original status and headers.
148
+ */
149
+ async function repairServingResponse(response) {
150
+ const contentType = response.headers.get("content-type") ?? "";
151
+ if (!contentType.includes("application/json"))
152
+ return response;
153
+ const body = await response.clone().text();
154
+ const rewritten = rewriteServingResponseBody(body);
155
+ if (rewritten === body)
156
+ return response;
157
+ // `content-length` / `content-encoding` describe the ORIGINAL bytes, so they
158
+ // are dropped: the rewritten body is a different length and already decoded.
159
+ const headers = new Headers(response.headers);
160
+ headers.delete("content-length");
161
+ headers.delete("content-encoding");
162
+ return new Response(rewritten, {
163
+ status: response.status,
164
+ statusText: response.statusText,
165
+ headers,
166
+ });
167
+ }
168
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUJHO0FBRUgsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDekQsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFLeEUsT0FBTyxFQUFFLGVBQWUsRUFBc0MsTUFBTSxVQUFVLENBQUM7QUFDL0UsT0FBTyxFQUFFLHlCQUF5QixFQUFFLG9CQUFvQixFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzVFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSwwQkFBMEIsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBR3BGLE1BQU0sRUFBRSxlQUFlLEVBQUUsR0FBRyxPQUFPLENBQUM7QUFDcEMsTUFBTSxFQUFFLFdBQVcsRUFBRSxHQUFHLE9BQU8sQ0FBQztBQXFCaEM7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxVQUFVLENBQzlCLE1BQTBCLEVBQzFCLGNBQThCLEVBQzlCLFlBQWlDLEVBQUU7SUFFbkMsS0FBSyxxQkFBcUIsRUFBRSxDQUFDO0lBQzdCLGtFQUFrRTtJQUNsRSxxRUFBcUU7SUFDckUscUVBQXFFO0lBQ3JFLHFFQUFxRTtJQUNyRSxNQUFNLElBQUksR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBcUIsQ0FBQztJQUNyRSxNQUFNLGdCQUFnQixHQUFHLElBQUksRUFBRSxnQkFBZ0IsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO0lBQ3pFLE1BQU0sWUFBWSxHQUFHLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDcEQsTUFBTSxJ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OztHQVVHO0FBQ0gsS0FBSyxVQUFVLHFCQUFxQixDQUFDLFFBQWtCO0lBQ3JELE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUMvRCxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQztRQUFFLE9BQU8sUUFBUSxDQUFDO0lBRS9ELE1BQU0sSUFBSSxHQUFHLE1BQU0sUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzNDLE1BQU0sU0FBUyxHQUFHLDBCQUEwQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ25ELElBQUksU0FBUyxLQUFLLElBQUk7UUFBRSxPQUFPLFFBQVEsQ0FBQztJQUV4Qyw2RUFBNkU7SUFDN0UsNkVBQTZFO0lBQzdFLE1BQU0sT0FBTyxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM5QyxPQUFPLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDakMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBQ25DLE9BQU8sSUFBSSxRQUFRLENBQUMsU0FBUyxFQUFFO1FBQzdCLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTTtRQUN2QixVQUFVLEVBQUUsUUFBUSxDQUFDLFVBQVU7UUFDL0IsT0FBTztLQUNSLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIERhdGFicmlja3MgTW9kZWwgU2VydmluZyByZXNvbHZlciBmb3IgTWFzdHJhIGFnZW50cy5cbiAqXG4gKiBFYWNoIGFnZW50IHN0ZXAgY2FsbHMge0BsaW5rIGJ1aWxkTW9kZWx9IHdpdGggdGhlIGFjdGl2ZVxuICogYFJlcXVlc3RDb250ZXh0YC4gVGhlIHVzZXIgc3RhbXBlZCBieSBgTWFzdHJhU2VydmVyYCBjYXJyaWVzIGFuXG4gKiBBcHBLaXQgYFdvcmtzcGFjZUNsaWVudGA7IHdlIGFzayBpdCBmb3IgdGhlIHdvcmtzcGFjZSBob3N0IGFuZCBhXG4gKiBmcmVzaCBiZWFyZXIgaGVhZGVyLCB0aGVuIHBvaW50IE1hc3RyYSdzIE9wZW5BSS1jb21wYXRpYmxlIHByb3ZpZGVyXG4gKiBhdCBgL3NlcnZpbmctZW5kcG9pbnRzYCBvbiB0aGF0IGhvc3QuXG4gKlxuICogVGhpcyBtb2R1bGUgb25seSBhZGRzIHRoZSBNYXN0cmEtc3BlY2lmaWMgZ2x1ZS4gVGhlIGFjdHVhbCBtb2RlbFxuICogc2VsZWN0aW9uIC0gbGlzdGluZyB0aGUgd29ya3NwYWNlIGNhdGFsb2d1ZSBhbmQgcmVzb2x2aW5nIGFuXG4gKiBleHBsaWNpdCBuYW1lIC8gY2xhc3MgLyBmYWxsYmFjayBjaGFpbiB0byBhIHJlYWwgZW5kcG9pbnQgaWQgLSBsaXZlc1xuICogaW4gYEBkYngtdG9vbHMvbW9kZWxgICh7QGxpbmsgc2VsZWN0TW9kZWx9KSBzbyBub24tTWFzdHJhIGNvbnN1bWVyc1xuICogKGUuZy4gYSBqb2IgdGhhdCBqdXN0IG5lZWRzIGEgbW9kZWwgbmFtZSkgY2FuIHJldXNlIGl0LiBIZXJlIHdlXG4gKiBhc3NlbWJsZSB0aGUgZXhwbGljaXQgYXNrIGZyb20gTWFzdHJhJ3MgcmVxdWVzdCBjb250ZXh0ICh0aGVcbiAqIHBlci1yZXF1ZXN0IG92ZXJyaWRlIHVuZGVyIHtAbGluayBNQVNUUkFfTU9ERUxfT1ZFUlJJREVfS0VZfSwgdGhlXG4gKiBhZ2VudCAvIHBsdWdpbiBgbW9kZWxJZGAsIG9yIGBEQVRBQlJJQ0tTX1NFUlZJTkdfRU5EUE9JTlRfTkFNRWApLFxuICogcGFzcyB0aGUgcGx1Z2luJ3MgZnV6enkgLyBjbGFzcyAvIGZhbGxiYWNrIGtub2JzIHRocm91Z2gsIGFuZCB3cmFwXG4gKiB0aGUgcmVzb2x2ZWQgaWQgaW4gdGhlIE9wZW5BSS1jb21wYXRpYmxlIHByb3ZpZGVyIGNvbmZpZyBNYXN0cmFcbiAqIGV4cGVjdHMuIENhdGFsb2d1ZSBmZXRjaGVzIGZhaWwgbG91ZDogbmV0d29yayAvIGF1dGggZXJyb3JzXG4gKiBwcm9wYWdhdGUgc28gY2FsbGVycyBzZWUgdGhlIHJlYWwgU0RLIG1lc3NhZ2UuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGdldEV4ZWN1dGlvbkNvbnRleHQgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBjbGFzc2VzLCByZXNvbHZlIH0gZnJvbSBcIkBkYngtdG9vbHMvbW9kZWxcIjtcbmltcG9ydCB7IGZ1bmN0aW9uTW9kdWxlLCBqc29uLCBsb2csIG5ldCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBtb2RlbCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1tb2RlbFwiO1xuaW1wb3J0IHR5cGUgeyBNYXN0cmFNb2RlbENvbmZpZyB9IGZyb20gXCJAbWFzdHJhL2NvcmUvbGxtXCI7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3RDb250ZXh0IH0gZnJvbSBcIkBtYXN0cmEvY29yZS9yZXF1ZXN0LWNvbnRleHRcIjtcblxuaW1wb3J0IHsgTUFTVFJBX1VTRVJfS0VZLCB0eXBlIE1hc3RyYVBsdWdpbkNvbmZpZywgdHlwZSBVc2VyIH0gZnJvbSBcIi4vY29uZmlnXCI7XG5pbXBvcnQgeyBNQVNUUkFfTU9ERUxfT1ZFUlJJREVfS0VZLCByZXNvbHZlU2VydmluZ0NvbmZpZyB9IGZyb20gXCIuL3NlcnZpbmdcIjtcbmltcG9ydCB7IHJld3JpdGVTZXJ2aW5nQm9keSwgcmV3cml0ZVNlcnZpbmdSZXNwb25zZUJvZHkgfSBmcm9tIFwiLi9zZXJ2aW5nLXNhbml0aXplXCI7XG5cbnR5cGUgTW9kZWxDbGFzcyA9IG1vZGVsLk1vZGVsQ2xhc3M7XG5jb25zdCB7IHBhcnNlTW9kZWxDbGFzcyB9ID0gY2xhc3NlcztcbmNvbnN0IHsgc2VsZWN0TW9kZWwgfSA9IHJlc29sdmU7XG5cbi8qKiBPcHRpb25hbCBvdmVycmlkZXMgYWNjZXB0ZWQgYnkge0BsaW5rIGJ1aWxkTW9kZWx9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWlsZE1vZGVsT3ZlcnJpZGVzIHtcbiAgLyoqXG4gICAqIFN0YXRpYyBtb2RlbCBpZCBmcm9tIHRoZSBhZ2VudCAvIHBsdWdpbiBjb25maWcgKHN0cmluZyBzdWdhciBvblxuICAgKiBgZGVmLm1vZGVsYCBvciBgY29uZmlnLmRlZmF1bHRNb2RlbGApLiBMb3NlcyB0byB0aGUgcGVyLXJlcXVlc3RcbiAgICogb3ZlcnJpZGUgYnV0IHdpbnMgb3ZlciBlbnYgLyBjbGFzcyAvIGZhbGxiYWNrLlxuICAgKi9cbiAgbW9kZWxJZD86IHN0cmluZztcbiAgLyoqXG4gICAqIENoYXQgY2FwYWJpbGl0eSBjbGFzcyB0byByZXNvbHZlIHdoZW4gbm8gZXhwbGljaXQgbW9kZWwgaWQgaXNcbiAgICogc3VwcGxpZWQuIFVzZWQgYnkgaW50ZXJuYWwgYWdlbnRzIChlLmcuIHRoZSBjaGFydCBwbGFubmVyIGFza3MgZm9yXG4gICAqIHtAbGluayBtb2RlbC5Nb2RlbENsYXNzLkNoYXRGYXN0fSkgdG8gZXhwcmVzcyBpbnRlbnQgd2l0aG91dCBwaW5uaW5nIGFuXG4gICAqIGVuZHBvaW50IG5hbWU7IHRoZSBsaXZlIGNhdGFsb2d1ZSBpcyBjbGFzc2lmaWVkIGFuZCB0aGUgdG9wXG4gICAqIGF2YWlsYWJsZSBtb2RlbCBpbiB0aGUgY2xhc3MgaXMgY2hvc2VuLCBmYWxsaW5nIGJhY2sgdG8gdGhlXG4gICAqIGNsYXNzJ3Mgc3RhdGljIGxpc3Qgd2hlbiB0aGUgd29ya3NwYWNlIGhhcyBub25lLlxuICAgKi9cbiAgbW9kZWxDbGFzcz86IE1vZGVsQ2xhc3M7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIGBNYXN0cmFNb2RlbENvbmZpZ2AgZm9yIHRoZSBjdXJyZW50IGFnZW50IHN0ZXAuIFJ1bnNcbiAqIHdoaWxlIGBhZ2VudC5zdHJlYW1gIGlzIGluc2lkZSB0aGUgYGFzVXNlcihyZXEpYCBzY29wZSBzbyB0b2tlbnNcbiAqIGFyZSB1c2VyLXNjb3BlZDsgb3V0c2lkZSBhbiBhY3RpdmUgdXNlciBjb250ZXh0IHRoZSB3b3Jrc3BhY2VcbiAqIGNsaWVudCBmYWxscyBiYWNrIHRvIHRoZSBzZXJ2aWNlIHByaW5jaXBhbC5cbiAqXG4gKiBFbmRwb2ludCBwcmVjZWRlbmNlOiB0aGUgcGVyLXJlcXVlc3Qgb3ZlcnJpZGVcbiAqICh7QGxpbmsgTUFTVFJBX01PREVMX09WRVJSSURFX0tFWX0sIG9ubHkgd2hlbiBgY29uZmlnLm1vZGVsT3ZlcnJpZGVgIGFsbG93c1xuICogaXQpLCB0aGVuIHtAbGluayBCdWlsZE1vZGVsT3ZlcnJpZGVzLm1vZGVsSWR9IGZyb20gdGhlIGFnZW50IC8gcGx1Z2luXG4gKiBjb25maWcsIHRoZW4gYERBVEFCUklDS1NfU0VSVklOR19FTkRQT0lOVF9OQU1FYC4gV2l0aCBub25lIG9mIHRob3NlIHNldCB0aGVcbiAqIGNhcGFiaWxpdHkgY2xhc3MgYW5kIGZhbGxiYWNrIGxhZGRlciBpbiBgQGRieC10b29scy9tb2RlbGAgY2hvb3NlIHRoZVxuICogZW5kcG9pbnQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBidWlsZE1vZGVsKFxuICBjb25maWc6IE1hc3RyYVBsdWdpbkNvbmZpZyxcbiAgcmVxdWVzdENvbnRleHQ6IFJlcXVlc3RDb250ZXh0LFxuICBvdmVycmlkZXM6IEJ1aWxkTW9kZWxPdmVycmlkZXMgPSB7fSxcbik6IFByb21pc2U8TWFzdHJhTW9kZWxDb25maWc+IHtcbiAgdm9pZCBzZXR1cEZldGNoSW50ZXJjZXB0b3IoKTtcbiAgLy8gVGhlIGNoYXQgcGF0aCBzdGFtcHMgdGhlIEFwcEtpdCB1c2VyIG9uIHRoZSByZXF1ZXN0IGNvbnRleHQgdmlhXG4gIC8vIGBNYXN0cmFTZXJ2ZXJgLiBUaGUgTUNQIHRyYW5zcG9ydCByb3V0ZXMgZG9uJ3QgdGhyZWFkIHRoYXQgY29udGV4dFxuICAvLyBpbnRvIHRvb2wgZXhlY3V0aW9uLCBzbyBmYWxsIGJhY2sgdG8gdGhlIGFtYmllbnQgZXhlY3V0aW9uIGNvbnRleHRcbiAgLy8gKHRoZSBhY3RpdmUgT0JPIHNjb3BlLCBvciB0aGUgc2VydmljZSBwcmluY2lwYWwpIHdoZW4gaXQncyBhYnNlbnQuXG4gIGNvbnN0IHVzZXIgPSByZXF1ZXN0Q29udGV4dC5nZXQoTUFTVFJBX1VTRVJfS0VZKSBhcyBVc2VyIHwgdW5kZWZpbmVkO1xuICBjb25zdCBleGVjdXRpb25Db250ZXh0ID0gdXNlcj8uZXhlY3V0aW9uQ29udGV4dCA/PyBnZXRFeGVjdXRpb25Db250ZXh0KCk7XG4gIGNvbnN0IGNsaWVudENvbmZpZyA9IGV4ZWN1dGlvbkNvbnRleHQuY2xpZW50LmNvbmZpZztcbiAgY29uc3QgaG9zdCA9IChhd2FpdCBjbGllbnRDb25maWcuZ2V0SG9zdCgpKS50b1N0cmluZygpO1xuICBjb25zdCBoZWFkZXJzID0gbmV3IEhlYWRlcnMoKTtcbiAgYXdhaXQgY2xpZW50Q29uZmlnLmF1dGhlbnRpY2F0ZShoZWFkZXJzKTtcbiAgLy8gVGhlIE9wZW5BSSBOb2RlIFNESyBhcHBlbmRzIHBhdGhzIGxpa2UgYC9jaGF0L2NvbXBsZXRpb25zYCB0byB3aGF0ZXZlclxuICAvLyBVUkwgd2UgaGFuZCBpdC4gRHJvcCB0aGUgdHJhaWxpbmcgc2xhc2ggc28gdGhlIHJlc3VsdGluZyBVUkwgc3RheXNcbiAgLy8gd2VsbC1mb3JtZWQgKGAvc2VydmluZy1lbmRwb2ludHMvY2hhdC9jb21wbGV0aW9uc2ApLlxuICBjb25zdCB1cmwgPSBuZXcgVVJMKFwiL3NlcnZpbmctZW5kcG9pbnRzXCIsIGhvc3QpLnRvU3RyaW5nKCkucmVwbGFjZSgvXFwvJC8sIFwiXCIpO1xuXG4gIGNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoY29uZmlnKTtcbiAgY29uc3Qgc2VydmluZyA9IHJlc29sdmVTZXJ2aW5nQ29uZmlnKGNvbmZpZyk7XG4gIGNvbnN0IG92ZXJyaWRlID0gc2VydmluZy5hbGxvd092ZXJyaWRlXG4gICAgPyAocmVxdWVzdENvbnRleHQuZ2V0KE1BU1RSQV9NT0RFTF9PVkVSUklERV9LRVkpIGFzIHN0cmluZyB8IHVuZGVmaW5lZClcbiAgICA6IHVuZGVmaW5lZDtcblxuICAvLyBUaGUgb3ZlcnJpZGUgLyBhZ2VudCBkZWZhdWx0IC8gZW52IHZhbHVlIGNhbiBiZSBlaXRoZXIgYSBjb25jcmV0ZVxuICAvLyBlbmRwb2ludCBuYW1lIG9yIGEgbW9kZWwgY2xhc3Mgc2x1ZyAoXCJjaGF0LXRoaW5raW5nXCIgL1xuICAvLyBcImNoYXQtYmFsYW5jZWRcIiAvIFwiY2hhdC1mYXN0XCIpLiBBIGNsYXNzIHNsdWcgYmVjb21lcyBhIGNsYXNzIGludGVudFxuICAvLyAobGV0IHRoZSBsaXZlIGNhdGFsb2d1ZSBwaWNrIHRoZSBiZXN0IG1vZGVsIGluIHRoYXQgYmFuZCk7IGFueXRoaW5nXG4gIC8vIGVsc2UgaXMgYW4gZXhwbGljaXQgbmFtZSBmdXp6eS1tYXRjaGVkIGFnYWluc3QgdGhlIGNhdGFsb2d1ZS4gQW5cbiAgLy8gaW50ZXJuYWwgYG92ZXJyaWRlcy5tb2RlbENsYXNzYCAoZS5nLiB0aGUgY2hhcnQgcGxhbm5lcikgaXMgdGhlXG4gIC8vIGZsb29yIHdoZW4gbm90aGluZyB3YXMgcmVxdWVzdGVkLlxuICBjb25zdCByZXF1ZXN0ZWQgPSBvdmVycmlkZSA/PyBvdmVycmlkZXMubW9kZWxJZCA/PyBwcm9jZXNzLmVudi5EQVRBQlJJQ0tTX1NFUlZJTkdfRU5EUE9JTlRfTkFNRTtcbiAgY29uc3QgcmVxdWVzdGVkQ2xhc3MgPSByZXF1ZXN0ZWQgIT09IHVuZGVmaW5lZCA/IHBhcnNlTW9kZWxDbGFzcyhyZXF1ZXN0ZWQpIDogbnVsbDtcbiAgY29uc3QgZXhwbGljaXQgPSByZXF1ZXN0ZWRDbGFzcyA9PT0gbnVsbCA/IHJlcXVlc3RlZCA6IHVuZGVmaW5lZDtcbiAgY29uc3QgbW9kZWxDbGFzcyA9IHJlcXVlc3RlZENsYXNzID8/IG92ZXJyaWRlcy5tb2RlbENsYXNzO1xuXG4gIGNvbnN0IHsgbW9kZWxJZCwgc291cmNlIH0gPSBhd2FpdCBzZWxlY3RNb2RlbChleGVjdXRpb25Db250ZXh0LmNsaWVudCwgaG9zdCwge1xuICAgIC4uLihleHBsaWNpdCAhPT0gdW5kZWZpbmVkID8geyBleHBsaWNpdCB9IDoge30pLFxuICAgIGZ1enp5OiBzZXJ2aW5nLmZ1enp5LFxuICAgIHRocmVzaG9sZDogc2VydmluZy50aHJlc2hvbGQsXG4gICAgLi4uKG1vZGVsQ2xhc3MgIT09IHVuZGVmaW5lZCA/IHsgbW9kZWxDbGFzcyB9IDoge30pLFxuICAgIGZhbGxiYWNrczogc2VydmluZy5mYWxsYmFja3MsXG4gICAgdHRsTXM6IHNlcnZpbmcudHRsTXMsXG4gIH0pO1xuICBsb2dnZXIuZGVidWcoXCJtb2RlbCBzZWxlY3RlZFwiLCB7IG1vZGVsSWQsIHNvdXJjZSwgcmVxdWVzdGVkIH0pO1xuXG4gIHJldHVybiB7XG4gICAgcHJvdmlkZXJJZDogY29uZmlnLnByb3ZpZGVySWQgPz8gXCJkYXRhYnJpY2tzXCIsXG4gICAgbW9kZWxJZCxcbiAgICB1cmwsXG4gICAgaGVhZGVyczogT2JqZWN0LmZyb21FbnRyaWVzKGhlYWRlcnMuZW50cmllcygpKSxcbiAgfTtcbn1cblxuLyoqIFBhdGggcHJlZml4IHRoYXQgaWRlbnRpZmllcyBhIERhdGFicmlja3MgTW9kZWwgU2VydmluZyBSRVNUIGNhbGwuICovXG5jb25zdCBTRVJWSU5HX0VORFBPSU5UU19QQVRIX1BSRUZJWCA9IFwiL3NlcnZpbmctZW5kcG9pbnRzL1wiO1xuXG4vKipcbiAqIEluc3RhbGwgYSBzaW5nbGUgc2hhcmVkIGBnbG9iYWxUaGlzLmZldGNoYCB3cmFwcGVyIGZvciBldmVyeSBQT1NUIHRvXG4gKiBgL3NlcnZpbmctZW5kcG9pbnRzLy4uLmAuIFRoZSB3cmFwcGVyIGRvZXMgdHdvIHRoaW5nczpcbiAqXG4gKiAgIDEuIFJld3JpdGVzIHRoZSBvdXRnb2luZyBgbWVzc2FnZXNgIGFycmF5IHRvIHJlcGFpciBNYXN0cmEvQUkgU0RLXG4gKiAgICAgIHN0cmVhbS1yZXBsYXkgcXVpcmtzIHRoYXQgRGF0YWJyaWNrcy1ob3N0ZWQgQ2xhdWRlIHJlamVjdHMgKHNlZVxuICogICAgICB7QGxpbmsgcmV3cml0ZVNlcnZpbmdCb2R5fSBpbiBgLi9zZXJ2aW5nLXNhbml0aXplLmpzYCkuXG4gKiAgIDIuIEF0IGBMT0dfTEVWRUw9ZGVidWdgLCBkdW1wcyB0aGUgKHBvc3Qtc2FuaXRpemUpIEpTT04gYm9keSBzb1xuICogICAgICA0eHggZGVidWdnaW5nIGRvZXNuJ3QgaGF2ZSB0byBmaWdodCBBSSBTREsncyBgW0FycmF5XWBcbiAqICAgICAgZm9ybWF0dGVyLlxuICogICAzLiBSZXBhaXJzIHRoZSBub24tc3RyZWFtaW5nIEpTT04gcmVzcG9uc2UsIHdoZXJlIERhdGFicmlja3MtaG9zdGVkXG4gKiAgICAgIEdlbWluaSByZXR1cm5zIGBjaG9pY2VzW10ubWVzc2FnZS5jb250ZW50YCBhcyBhIHBhcnRzIGFycmF5IHRoYXRcbiAqICAgICAgdGhlIEFJIFNESydzIE9wZW5BSSBzY2hlbWEgcmVqZWN0cyAoc2VlXG4gKiAgICAgIHtAbGluayByZXdyaXRlU2VydmluZ1Jlc3BvbnNlQm9keX0pLlxuICpcbiAqIFNhZmUgdG8gY2FsbCBmcm9tIGFueSBob3QgcGF0aDoge0BsaW5rIGZ1bmN0aW9uTW9kdWxlLm1lbW9pemV9IGVuc3VyZXNcbiAqIHRoZSB3cmFwcGVyIGlzIGluc3RhbGxlZCBhdCBtb3N0IG9uY2UgcGVyIHByb2Nlc3MsIHNvIHN1YnNlcXVlbnRcbiAqIGNhbGxzIGFyZSBhIG5vLW9wIGV2ZW4gd2hlbiB7QGxpbmsgYnVpbGRNb2RlbH0gZmlyZXMgb24gZXZlcnkgYWdlbnRcbiAqIHN0ZXAuXG4gKi9cbmNvbnN0IHNldHVwRmV0Y2hJbnRlcmNlcHRvciA9IGZ1bmN0aW9uTW9kdWxlLm1lbW9pemUoKCk6IHZvaWQgPT4ge1xuICBjb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwibWFzdHJhL2xsbVwiKTtcbiAgY29uc3Qgb3JpZ2luYWwgPSBnbG9iYWxUaGlzLmZldGNoLmJpbmQoZ2xvYmFsVGhpcyk7XG4gIGdsb2JhbFRoaXMuZmV0Y2ggPSAoYXN5bmMgKGlucHV0LCBpbml0KSA9PiB7XG4gICAgY29uc3QgdXJsID0gbmV0LnVybEJ1aWxkZXIoaW5wdXQpO1xuICAgIGlmIChcbiAgICAgICF1cmwgfHxcbiAgICAgICF1cmwucGF0aG5hbWUuc3RhcnRzV2l0aChTRVJWSU5HX0VORFBPSU5UU19QQVRIX1BSRUZJWCkgfHxcbiAgICAgIHR5cGVvZiBpbml0Py5ib2R5ICE9PSBcInN0cmluZ1wiXG4gICAgKSB7XG4gICAgICByZXR1cm4gb3JpZ2luYWwoaW5wdXQsIGluaXQpO1xuICAgIH1cbiAgICBjb25zdCByZXdyaXR0ZW4gPSByZXdyaXRlU2VydmluZ0JvZHkoaW5pdC5ib2R5KTtcbiAgICBpZiAocmV3cml0dGVuICE9PSBpbml0LmJvZHkpIHtcbiAgICAgIGluaXQgPSB7IC4uLmluaXQsIGJvZHk6IHJld3JpdHRlbiB9O1xuICAgIH1cbiAgICBjb25zdCBwYXJzZWQgPSBqc29uLnBhcnNlPHVua25vd24+KHJld3JpdHRlbik7XG4gICAgbG9nZ2VyLmRlYnVnKFxuICAgICAgXCJQT1NUXCIsXG4gICAgICBwYXJzZWQgPT09IHVuZGVmaW5lZFxuICAgICAgICA/IHsgdXJsOiB1cmwudG9TdHJpbmcoKSwgYm9keVR5cGU6IFwibm9uLUpTT05cIiB9XG4gICAgICAgIDogeyB1cmw6IHVybC50b1N0cmluZygpLCBib2R5OiBwYXJzZWQgfSxcbiAgICApO1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgb3JpZ2luYWwoaW5wdXQsIGluaXQpO1xuICAgIHJldHVybiByZXBhaXJTZXJ2aW5nUmVzcG9uc2UocmVzcG9uc2UpO1xuICB9KSBhcyB0eXBlb2YgZ2xvYmFsVGhpcy5mZXRjaDtcbn0pO1xuXG4vKipcbiAqIFJld3JpdGUgYSBub24tc3RyZWFtaW5nIHNlcnZpbmcgcmVzcG9uc2Ugd2hvc2UgYm9keSBuZWVkcyByZXBhaXIsIGxlYXZpbmdcbiAqIGV2ZXJ5dGhpbmcgZWxzZSBieXRlLWlkZW50aWNhbC5cbiAqXG4gKiBTdHJlYW1pbmcgdHVybnMgYXJlIHBhc3NlZCBzdHJhaWdodCB0aHJvdWdoOiBhbiBTU0UgYm9keSBtdXN0IHN0YXkgYSBsaXZlXG4gKiBzdHJlYW0gKGJ1ZmZlcmluZyBpdCB0byBhIHN0cmluZyB3b3VsZCBkZWZlYXQgc3RyZWFtaW5nIGFuZCBicmVha1xuICogYHRleHQvZXZlbnQtc3RyZWFtYCBwYXJzaW5nKSwgYW5kIHRoZSBkZWx0YSBmcmFtZXMgYWxyZWFkeSBjYXJyeSBzdHJpbmdcbiAqIGNvbnRlbnQsIHNvIHRoZXkgbmV2ZXIgaGl0IHRoZSBhcnJheS1zaGFwZWQgYGNvbnRlbnRgIGJ1Zy4gTGlrZXdpc2UgYVxuICogbm9uLUpTT04gb3IgZXJyb3IgYm9keSBpcyByZXR1cm5lZCB1bnRvdWNoZWQsIHNvIHRoZSBjYWxsZXIgc3RpbGwgc2VlcyB0aGVcbiAqIG9yaWdpbmFsIHN0YXR1cyBhbmQgaGVhZGVycy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVwYWlyU2VydmluZ1Jlc3BvbnNlKHJlc3BvbnNlOiBSZXNwb25zZSk6IFByb21pc2U8UmVzcG9uc2U+IHtcbiAgY29uc3QgY29udGVudFR5cGUgPSByZXNwb25zZS5oZWFkZXJzLmdldChcImNvbnRlbnQtdHlwZVwiKSA/PyBcIlwiO1xuICBpZiAoIWNvbnRlbnRUeXBlLmluY2x1ZGVzKFwiYXBwbGljYXRpb24vanNvblwiKSkgcmV0dXJuIHJlc3BvbnNlO1xuXG4gIGNvbnN0IGJvZHkgPSBhd2FpdCByZXNwb25zZS5jbG9uZSgpLnRleHQoKTtcbiAgY29uc3QgcmV3cml0dGVuID0gcmV3cml0ZVNlcnZpbmdSZXNwb25zZUJvZHkoYm9keSk7XG4gIGlmIChyZXdyaXR0ZW4gPT09IGJvZHkpIHJldHVybiByZXNwb25zZTtcblxuICAvLyBgY29udGVudC1sZW5ndGhgIC8gYGNvbnRlbnQtZW5jb2RpbmdgIGRlc2NyaWJlIHRoZSBPUklHSU5BTCBieXRlcywgc28gdGhleVxuICAvLyBhcmUgZHJvcHBlZDogdGhlIHJld3JpdHRlbiBib2R5IGlzIGEgZGlmZmVyZW50IGxlbmd0aCBhbmQgYWxyZWFkeSBkZWNvZGVkLlxuICBjb25zdCBoZWFkZXJzID0gbmV3IEhlYWRlcnMocmVzcG9uc2UuaGVhZGVycyk7XG4gIGhlYWRlcnMuZGVsZXRlKFwiY29udGVudC1sZW5ndGhcIik7XG4gIGhlYWRlcnMuZGVsZXRlKFwiY29udGVudC1lbmNvZGluZ1wiKTtcbiAgcmV0dXJuIG5ldyBSZXNwb25zZShyZXdyaXR0ZW4sIHtcbiAgICBzdGF0dXM6IHJlc3BvbnNlLnN0YXR1cyxcbiAgICBzdGF0dXNUZXh0OiByZXNwb25zZS5zdGF0dXNUZXh0LFxuICAgIGhlYWRlcnMsXG4gIH0pO1xufVxuIl19
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Mastra observability wired through the same OTel pipeline AppKit's
3
+ * built-in plugins (e.g. `agents`) use, via `@mastra/otel-bridge`.
4
+ *
5
+ * How traces flow:
6
+ *
7
+ * 1. `@databricks/appkit` boots a global `NodeSDK` in
8
+ * `TelemetryManager.initialize()` (during `createApp`) when
9
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` is set in the process env.
10
+ * 2. Every AppKit plugin span (e.g. the `agents` plugin's
11
+ * `executeStream`) is created via the global OTel tracer
12
+ * (`trace.getTracer(<plugin>)`), so it lands on that NodeSDK and
13
+ * is shipped through its OTLP exporter.
14
+ * 3. The Mastra `OtelBridge` ALSO creates real OTel spans on the same
15
+ * global tracer for every Mastra operation (agent runs, model
16
+ * calls, tool invocations, workflow steps). They inherit the
17
+ * ambient OTel context, so when Mastra is invoked from inside an
18
+ * AppKit HTTP span the trace stays connected.
19
+ *
20
+ * Net effect: Mastra spans get exactly the treatment AppKit's
21
+ * `agents` plugin gets. No custom OTLP pipeline lives in this
22
+ * package; the OTLP endpoint, headers, and resource attributes are
23
+ * driven by the standard OTel env vars
24
+ * (`OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_HEADERS`,
25
+ * `OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`, ...) and consumed
26
+ * by AppKit's `TelemetryManager`. Set those once and both AppKit and
27
+ * Mastra spans end up at the same backend.
28
+ *
29
+ * When `OTEL_EXPORTER_OTLP_ENDPOINT` is unset the bridge is not
30
+ * registered at all (unless `observability: true` forces it on), so
31
+ * Mastra does not emit `[OtelBridge] No OTEL span found` warnings.
32
+ *
33
+ * @module
34
+ */
35
+ import { Observability } from "@mastra/observability";
36
+ export interface BuildObservabilityOptions {
37
+ /**
38
+ * Whether to wire the Mastra `OtelBridge`. Mirrors
39
+ * {@link MastraPluginConfig.observability}:
40
+ *
41
+ * - `undefined` (auto): on only when `OTEL_EXPORTER_OTLP_ENDPOINT` or
42
+ * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is set (same signal AppKit's
43
+ * `TelemetryManager` uses to register a real tracer). When unset,
44
+ * skip the bridge entirely so Mastra does not emit
45
+ * `[OtelBridge] No OTEL span found` warnings on the noop tracer.
46
+ * - `true`: force on even without an OTLP endpoint.
47
+ * - `false`: force off.
48
+ */
49
+ enabled?: boolean;
50
+ /**
51
+ * Service name attached to the Mastra `Observability` config. Used
52
+ * as the tracer scope name on bridged OTel spans (the `service.name`
53
+ * resource attribute is owned by AppKit's `TelemetryManager` instead
54
+ * - it reads `OTEL_SERVICE_NAME` / `DATABRICKS_APP_NAME` at
55
+ * `createApp` time).
56
+ *
57
+ * Defaults to project name then `"mastra"`.
58
+ */
59
+ serviceName?: string;
60
+ /**
61
+ * `RequestContext` keys to extract as span metadata on every Mastra
62
+ * trace. Defaults to {@link TRACE_REQUEST_CONTEXT_KEYS} (user id,
63
+ * thread id, request id, environment, model override, ...).
64
+ *
65
+ * Supports dot notation for nested values per the Mastra docs.
66
+ */
67
+ requestContextKeys?: readonly string[];
68
+ }
69
+ /** True when AppKit's global OTel pipeline is configured to export traces. */
70
+ export declare function isOtlpTracingConfigured(): boolean;
71
+ /**
72
+ * Build a Mastra `Observability` whose spans ride AppKit's global
73
+ * OTel pipeline via `@mastra/otel-bridge`.
74
+ *
75
+ * Returns `undefined` when tracing is off: either the caller passed
76
+ * `enabled: false`, or auto mode finds no OTLP endpoint (the default).
77
+ * Skipping the bridge in that case avoids `[OtelBridge] No OTEL span
78
+ * found` log spam from Mastra spans that have no parent on the noop
79
+ * tracer.
80
+ */
81
+ export declare function buildObservability(options?: BuildObservabilityOptions): Promise<Observability | undefined>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Mastra observability wired through the same OTel pipeline AppKit's
3
+ * built-in plugins (e.g. `agents`) use, via `@mastra/otel-bridge`.
4
+ *
5
+ * How traces flow:
6
+ *
7
+ * 1. `@databricks/appkit` boots a global `NodeSDK` in
8
+ * `TelemetryManager.initialize()` (during `createApp`) when
9
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` is set in the process env.
10
+ * 2. Every AppKit plugin span (e.g. the `agents` plugin's
11
+ * `executeStream`) is created via the global OTel tracer
12
+ * (`trace.getTracer(<plugin>)`), so it lands on that NodeSDK and
13
+ * is shipped through its OTLP exporter.
14
+ * 3. The Mastra `OtelBridge` ALSO creates real OTel spans on the same
15
+ * global tracer for every Mastra operation (agent runs, model
16
+ * calls, tool invocations, workflow steps). They inherit the
17
+ * ambient OTel context, so when Mastra is invoked from inside an
18
+ * AppKit HTTP span the trace stays connected.
19
+ *
20
+ * Net effect: Mastra spans get exactly the treatment AppKit's
21
+ * `agents` plugin gets. No custom OTLP pipeline lives in this
22
+ * package; the OTLP endpoint, headers, and resource attributes are
23
+ * driven by the standard OTel env vars
24
+ * (`OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_HEADERS`,
25
+ * `OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`, ...) and consumed
26
+ * by AppKit's `TelemetryManager`. Set those once and both AppKit and
27
+ * Mastra spans end up at the same backend.
28
+ *
29
+ * When `OTEL_EXPORTER_OTLP_ENDPOINT` is unset the bridge is not
30
+ * registered at all (unless `observability: true` forces it on), so
31
+ * Mastra does not emit `[OtelBridge] No OTEL span found` warnings.
32
+ *
33
+ * @module
34
+ */
35
+ import { project } from "@dbx-tools/core";
36
+ import { log } from "@dbx-tools/shared-core";
37
+ import { Observability } from "@mastra/observability";
38
+ import { OtelBridge } from "@mastra/otel-bridge";
39
+ import { TRACE_REQUEST_CONTEXT_KEYS } from "./config.js";
40
+ const logger = log.logger("mastra/observability");
41
+ const DEFAULT_SERVICE_NAME = "mastra";
42
+ /** True when AppKit's global OTel pipeline is configured to export traces. */
43
+ export function isOtlpTracingConfigured() {
44
+ return Boolean(process.env.OTEL_EXPORTER_OTLP_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);
45
+ }
46
+ /**
47
+ * Build a Mastra `Observability` whose spans ride AppKit's global
48
+ * OTel pipeline via `@mastra/otel-bridge`.
49
+ *
50
+ * Returns `undefined` when tracing is off: either the caller passed
51
+ * `enabled: false`, or auto mode finds no OTLP endpoint (the default).
52
+ * Skipping the bridge in that case avoids `[OtelBridge] No OTEL span
53
+ * found` log spam from Mastra spans that have no parent on the noop
54
+ * tracer.
55
+ */
56
+ export async function buildObservability(options) {
57
+ const otelBase = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
58
+ const otelTracesOverride = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
59
+ const otlpConfigured = isOtlpTracingConfigured();
60
+ if (options?.enabled === false || (options?.enabled !== true && !otlpConfigured)) {
61
+ logger.info("Mastra observability off", {
62
+ reason: options?.enabled === false
63
+ ? "disabled in plugin config"
64
+ : "OTEL_EXPORTER_OTLP_ENDPOINT unset",
65
+ });
66
+ return undefined;
67
+ }
68
+ const serviceName = options?.serviceName ?? (await project.name()) ?? DEFAULT_SERVICE_NAME;
69
+ const requestContextKeys = [...(options?.requestContextKeys ?? TRACE_REQUEST_CONTEXT_KEYS)];
70
+ // The OTel HTTP exporter treats `OTEL_EXPORTER_OTLP_ENDPOINT` as a
71
+ // *base* URL and appends the signal path itself (e.g.
72
+ // `http://localhost:6006` -> `http://localhost:6006/v1/traces`). Log
73
+ // the resolved POST URL so misconfigurations (e.g. accidentally
74
+ // setting the base var to a `/v1/traces`-suffixed URL, which makes
75
+ // the SDK POST to `.../v1/traces/v1/traces` and Phoenix 404s) are
76
+ // obvious in startup output.
77
+ const resolvedTracesUrl = otelTracesOverride
78
+ ? otelTracesOverride
79
+ : otelBase
80
+ ? `${otelBase.replace(/\/+$/, "")}/v1/traces`
81
+ : undefined;
82
+ logger.info("Mastra observability wired through OTel bridge", {
83
+ serviceName,
84
+ requestContextKeys,
85
+ otelBase: otelBase ?? "<unset>",
86
+ resolvedTracesUrl: resolvedTracesUrl ?? "<unset>",
87
+ });
88
+ return new Observability({
89
+ configs: {
90
+ serviceName: {
91
+ serviceName,
92
+ bridge: new OtelBridge(),
93
+ requestContextKeys,
94
+ },
95
+ },
96
+ });
97
+ }
98
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JzZXJ2YWJpbGl0eS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vYnNlcnZhYmlsaXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQ0c7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzdDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFakQsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sVUFBVSxDQUFDO0FBRXRELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsc0JBQXNCLENBQUMsQ0FBQztBQUVsRCxNQUFNLG9CQUFvQixHQUFHLFFBQVEsQ0FBQztBQW9DdEMsOEVBQThFO0FBQzlFLE1BQU0sVUFBVSx1QkFBdUI7SUFDckMsT0FBTyxPQUFPLENBQ1osT0FBTyxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLGtDQUFrQyxDQUMxRixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQ3RDLE9BQW1DO0lBRW5DLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUM7SUFDekQsTUFBTSxrQkFBa0IsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGtDQUFrQyxDQUFDO0lBQzFFLE1BQU0sY0FBYyxHQUFHLHVCQUF1QixFQUFFLENBQUM7SUFFakQsSUFBSSxPQUFPLEVBQUUsT0FBTyxLQUFLLEtBQUssSUFBSSxDQUFDLE9BQU8sRUFBRSxPQUFPLEtBQUssSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztRQUNqRixNQUFNLENBQUMsSUFBSSxDQUFDLDBCQUEwQixFQUFFO1lBQ3RDLE1BQU0sRUFDSixPQUFPLEVBQUUsT0FBTyxLQUFLLEtBQUs7Z0JBQ3hCLENBQUMsQ0FBQywyQkFBMkI7Z0JBQzdCLENBQUMsQ0FBQyxtQ0FBbUM7U0FDMUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE1BQU0sV0FBVyxHQUFHLE9BQU8sRUFBRSxXQUFXLElBQUksQ0FBQyxNQUFNLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLG9CQUFvQixDQUFDO0lBQzNGLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGtCQUFrQixJQUFJLDBCQUEwQixDQUFDLENBQUMsQ0FBQztJQUU1RixtRUFBbUU7SUFDbkUsc0RBQXNEO0lBQ3RELHFFQUFxRTtJQUNyRSxnRUFBZ0U7SUFDaEUsbUVBQW1FO0lBQ25FLGtFQUFrRTtJQUNsRSw2QkFBNkI7SUFDN0IsTUFBTSxpQkFBaUIsR0FBRyxrQkFBa0I7UUFDMUMsQ0FBQyxDQUFDLGtCQUFrQjtRQUNwQixDQUFDLENBQUMsUUFBUTtZQUNSLENBQUMsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxZQUFZO1lBQzdDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDaEIsTUFBTSxDQUFDLElBQUksQ0FBQyxnREFBZ0QsRUFBRTtRQUM1RCxXQUFXO1FBQ1gsa0JBQWtCO1FBQ2xCLFFBQVEsRUFBRSxRQUFRLElBQUksU0FBUztRQUMvQixpQkFBaUIsRUFBRSxpQkFBaUIsSUFBSSxTQUFTO0tBQ2xELENBQUMsQ0FBQztJQUVILE9BQU8sSUFBSSxhQUFhLENBQUM7UUFDdkIsT0FBTyxFQUFFO1lBQ1AsV0FBVyxFQUFFO2dCQUNYLFdBQVc7Z0JBQ1gsTUFBTSxFQUFFLElBQUksVUFBVSxFQUFFO2dCQUN4QixrQkFBa0I7YUFDbkI7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE1hc3RyYSBvYnNlcnZhYmlsaXR5IHdpcmVkIHRocm91Z2ggdGhlIHNhbWUgT1RlbCBwaXBlbGluZSBBcHBLaXQnc1xuICogYnVpbHQtaW4gcGx1Z2lucyAoZS5nLiBgYWdlbnRzYCkgdXNlLCB2aWEgYEBtYXN0cmEvb3RlbC1icmlkZ2VgLlxuICpcbiAqIEhvdyB0cmFjZXMgZmxvdzpcbiAqXG4gKiAxLiBgQGRhdGFicmlja3MvYXBwa2l0YCBib290cyBhIGdsb2JhbCBgTm9kZVNES2AgaW5cbiAqICAgIGBUZWxlbWV0cnlNYW5hZ2VyLmluaXRpYWxpemUoKWAgKGR1cmluZyBgY3JlYXRlQXBwYCkgd2hlblxuICogICAgYE9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVGAgaXMgc2V0IGluIHRoZSBwcm9jZXNzIGVudi5cbiAqIDIuIEV2ZXJ5IEFwcEtpdCBwbHVnaW4gc3BhbiAoZS5nLiB0aGUgYGFnZW50c2AgcGx1Z2luJ3NcbiAqICAgIGBleGVjdXRlU3RyZWFtYCkgaXMgY3JlYXRlZCB2aWEgdGhlIGdsb2JhbCBPVGVsIHRyYWNlclxuICogICAgKGB0cmFjZS5nZXRUcmFjZXIoPHBsdWdpbj4pYCksIHNvIGl0IGxhbmRzIG9uIHRoYXQgTm9kZVNESyBhbmRcbiAqICAgIGlzIHNoaXBwZWQgdGhyb3VnaCBpdHMgT1RMUCBleHBvcnRlci5cbiAqIDMuIFRoZSBNYXN0cmEgYE90ZWxCcmlkZ2VgIEFMU08gY3JlYXRlcyByZWFsIE9UZWwgc3BhbnMgb24gdGhlIHNhbWVcbiAqICAgIGdsb2JhbCB0cmFjZXIgZm9yIGV2ZXJ5IE1hc3RyYSBvcGVyYXRpb24gKGFnZW50IHJ1bnMsIG1vZGVsXG4gKiAgICBjYWxscywgdG9vbCBpbnZvY2F0aW9ucywgd29ya2Zsb3cgc3RlcHMpLiBUaGV5IGluaGVyaXQgdGhlXG4gKiAgICBhbWJpZW50IE9UZWwgY29udGV4dCwgc28gd2hlbiBNYXN0cmEgaXMgaW52b2tlZCBmcm9tIGluc2lkZSBhblxuICogICAgQXBwS2l0IEhUVFAgc3BhbiB0aGUgdHJhY2Ugc3RheXMgY29ubmVjdGVkLlxuICpcbiAqIE5ldCBlZmZlY3Q6IE1hc3RyYSBzcGFucyBnZXQgZXhhY3RseSB0aGUgdHJlYXRtZW50IEFwcEtpdCdzXG4gKiBgYWdlbnRzYCBwbHVnaW4gZ2V0cy4gTm8gY3VzdG9tIE9UTFAgcGlwZWxpbmUgbGl2ZXMgaW4gdGhpc1xuICogcGFja2FnZTsgdGhlIE9UTFAgZW5kcG9pbnQsIGhlYWRlcnMsIGFuZCByZXNvdXJjZSBhdHRyaWJ1dGVzIGFyZVxuICogZHJpdmVuIGJ5IHRoZSBzdGFuZGFyZCBPVGVsIGVudiB2YXJzXG4gKiAoYE9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVGAsIGBPVEVMX0VYUE9SVEVSX09UTFBfSEVBREVSU2AsXG4gKiBgT1RFTF9TRVJWSUNFX05BTUVgLCBgT1RFTF9SRVNPVVJDRV9BVFRSSUJVVEVTYCwgLi4uKSBhbmQgY29uc3VtZWRcbiAqIGJ5IEFwcEtpdCdzIGBUZWxlbWV0cnlNYW5hZ2VyYC4gU2V0IHRob3NlIG9uY2UgYW5kIGJvdGggQXBwS2l0IGFuZFxuICogTWFzdHJhIHNwYW5zIGVuZCB1cCBhdCB0aGUgc2FtZSBiYWNrZW5kLlxuICpcbiAqIFdoZW4gYE9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVGAgaXMgdW5zZXQgdGhlIGJyaWRnZSBpcyBub3RcbiAqIHJlZ2lzdGVyZWQgYXQgYWxsICh1bmxlc3MgYG9ic2VydmFiaWxpdHk6IHRydWVgIGZvcmNlcyBpdCBvbiksIHNvXG4gKiBNYXN0cmEgZG9lcyBub3QgZW1pdCBgW090ZWxCcmlkZ2VdIE5vIE9URUwgc3BhbiBmb3VuZGAgd2FybmluZ3MuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHByb2plY3QgfSBmcm9tIFwiQGRieC10b29scy9jb3JlXCI7XG5pbXBvcnQgeyBsb2cgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgT2JzZXJ2YWJpbGl0eSB9IGZyb20gXCJAbWFzdHJhL29ic2VydmFiaWxpdHlcIjtcbmltcG9ydCB7IE90ZWxCcmlkZ2UgfSBmcm9tIFwiQG1hc3RyYS9vdGVsLWJyaWRnZVwiO1xuXG5pbXBvcnQgeyBUUkFDRV9SRVFVRVNUX0NPTlRFWFRfS0VZUyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwibWFzdHJhL29ic2VydmFiaWxpdHlcIik7XG5cbmNvbnN0IERFRkFVTFRfU0VSVklDRV9OQU1FID0gXCJtYXN0cmFcIjtcblxuZXhwb3J0IGludGVyZmFjZSBCdWlsZE9ic2VydmFiaWxpdHlPcHRpb25zIHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gd2lyZSB0aGUgTWFzdHJhIGBPdGVsQnJpZGdlYC4gTWlycm9yc1xuICAgKiB7QGxpbmsgTWFzdHJhUGx1Z2luQ29uZmlnLm9ic2VydmFiaWxpdHl9OlxuICAgKlxuICAgKiAtIGB1bmRlZmluZWRgIChhdXRvKTogb24gb25seSB3aGVuIGBPVEVMX0VYUE9SVEVSX09UTFBfRU5EUE9JTlRgIG9yXG4gICAqICAgYE9URUxfRVhQT1JURVJfT1RMUF9UUkFDRVNfRU5EUE9JTlRgIGlzIHNldCAoc2FtZSBzaWduYWwgQXBwS2l0J3NcbiAgICogICBgVGVsZW1ldHJ5TWFuYWdlcmAgdXNlcyB0byByZWdpc3RlciBhIHJlYWwgdHJhY2VyKS4gV2hlbiB1bnNldCxcbiAgICogICBza2lwIHRoZSBicmlkZ2UgZW50aXJlbHkgc28gTWFzdHJhIGRvZXMgbm90IGVtaXRcbiAgICogICBgW090ZWxCcmlkZ2VdIE5vIE9URUwgc3BhbiBmb3VuZGAgd2FybmluZ3Mgb24gdGhlIG5vb3AgdHJhY2VyLlxuICAgKiAtIGB0cnVlYDogZm9yY2Ugb24gZXZlbiB3aXRob3V0IGFuIE9UTFAgZW5kcG9pbnQuXG4gICAqIC0gYGZhbHNlYDogZm9yY2Ugb2ZmLlxuICAgKi9cbiAgZW5hYmxlZD86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBTZXJ2aWNlIG5hbWUgYXR0YWNoZWQgdG8gdGhlIE1hc3RyYSBgT2JzZXJ2YWJpbGl0eWAgY29uZmlnLiBVc2VkXG4gICAqIGFzIHRoZSB0cmFjZXIgc2NvcGUgbmFtZSBvbiBicmlkZ2VkIE9UZWwgc3BhbnMgKHRoZSBgc2VydmljZS5uYW1lYFxuICAgKiByZXNvdXJjZSBhdHRyaWJ1dGUgaXMgb3duZWQgYnkgQXBwS2l0J3MgYFRlbGVtZXRyeU1hbmFnZXJgIGluc3RlYWRcbiAgICogLSBpdCByZWFkcyBgT1RFTF9TRVJWSUNFX05BTUVgIC8gYERBVEFCUklDS1NfQVBQX05BTUVgIGF0XG4gICAqIGBjcmVhdGVBcHBgIHRpbWUpLlxuICAgKlxuICAgKiBEZWZhdWx0cyB0byBwcm9qZWN0IG5hbWUgdGhlbiBgXCJtYXN0cmFcImAuXG4gICAqL1xuICBzZXJ2aWNlTmFtZT86IHN0cmluZztcbiAgLyoqXG4gICAqIGBSZXF1ZXN0Q29udGV4dGAga2V5cyB0byBleHRyYWN0IGFzIHNwYW4gbWV0YWRhdGEgb24gZXZlcnkgTWFzdHJhXG4gICAqIHRyYWNlLiBEZWZhdWx0cyB0byB7QGxpbmsgVFJBQ0VfUkVRVUVTVF9DT05URVhUX0tFWVN9ICh1c2VyIGlkLFxuICAgKiB0aHJlYWQgaWQsIHJlcXVlc3QgaWQsIGVudmlyb25tZW50LCBtb2RlbCBvdmVycmlkZSwgLi4uKS5cbiAgICpcbiAgICogU3VwcG9ydHMgZG90IG5vdGF0aW9uIGZvciBuZXN0ZWQgdmFsdWVzIHBlciB0aGUgTWFzdHJhIGRvY3MuXG4gICAqL1xuICByZXF1ZXN0Q29udGV4dEtleXM/OiByZWFkb25seSBzdHJpbmdbXTtcbn1cblxuLyoqIFRydWUgd2hlbiBBcHBLaXQncyBnbG9iYWwgT1RlbCBwaXBlbGluZSBpcyBjb25maWd1cmVkIHRvIGV4cG9ydCB0cmFjZXMuICovXG5leHBvcnQgZnVuY3Rpb24gaXNPdGxwVHJhY2luZ0NvbmZpZ3VyZWQoKTogYm9vbGVhbiB7XG4gIHJldHVybiBCb29sZWFuKFxuICAgIHByb2Nlc3MuZW52Lk9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVCB8fCBwcm9jZXNzLmVudi5PVEVMX0VYUE9SVEVSX09UTFBfVFJBQ0VTX0VORFBPSU5ULFxuICApO1xufVxuXG4vKipcbiAqIEJ1aWxkIGEgTWFzdHJhIGBPYnNlcnZhYmlsaXR5YCB3aG9zZSBzcGFucyByaWRlIEFwcEtpdCdzIGdsb2JhbFxuICogT1RlbCBwaXBlbGluZSB2aWEgYEBtYXN0cmEvb3RlbC1icmlkZ2VgLlxuICpcbiAqIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiB0cmFjaW5nIGlzIG9mZjogZWl0aGVyIHRoZSBjYWxsZXIgcGFzc2VkXG4gKiBgZW5hYmxlZDogZmFsc2VgLCBvciBhdXRvIG1vZGUgZmluZHMgbm8gT1RMUCBlbmRwb2ludCAodGhlIGRlZmF1bHQpLlxuICogU2tpcHBpbmcgdGhlIGJyaWRnZSBpbiB0aGF0IGNhc2UgYXZvaWRzIGBbT3RlbEJyaWRnZV0gTm8gT1RFTCBzcGFuXG4gKiBmb3VuZGAgbG9nIHNwYW0gZnJvbSBNYXN0cmEgc3BhbnMgdGhhdCBoYXZlIG5vIHBhcmVudCBvbiB0aGUgbm9vcFxuICogdHJhY2VyLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYnVpbGRPYnNlcnZhYmlsaXR5KFxuICBvcHRpb25zPzogQnVpbGRPYnNlcnZhYmlsaXR5T3B0aW9ucyxcbik6IFByb21pc2U8T2JzZXJ2YWJpbGl0eSB8IHVuZGVmaW5lZD4ge1xuICBjb25zdCBvdGVsQmFzZSA9IHByb2Nlc3MuZW52Lk9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVDtcbiAgY29uc3Qgb3RlbFRyYWNlc092ZXJyaWRlID0gcHJvY2Vzcy5lbnYuT1RFTF9FWFBPUlRFUl9PVExQX1RSQUNFU19FTkRQT0lOVDtcbiAgY29uc3Qgb3RscENvbmZpZ3VyZWQgPSBpc090bHBUcmFjaW5nQ29uZmlndXJlZCgpO1xuXG4gIGlmIChvcHRpb25zPy5lbmFibGVkID09PSBmYWxzZSB8fCAob3B0aW9ucz8uZW5hYmxlZCAhPT0gdHJ1ZSAmJiAhb3RscENvbmZpZ3VyZWQpKSB7XG4gICAgbG9nZ2VyLmluZm8oXCJNYXN0cmEgb2JzZXJ2YWJpbGl0eSBvZmZcIiwge1xuICAgICAgcmVhc29uOlxuICAgICAgICBvcHRpb25zPy5lbmFibGVkID09PSBmYWxzZVxuICAgICAgICAgID8gXCJkaXNhYmxlZCBpbiBwbHVnaW4gY29uZmlnXCJcbiAgICAgICAgICA6IFwiT1RFTF9FWFBPUlRFUl9PVExQX0VORFBPSU5UIHVuc2V0XCIsXG4gICAgfSk7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIGNvbnN0IHNlcnZpY2VOYW1lID0gb3B0aW9ucz8uc2VydmljZU5hbWUgPz8gKGF3YWl0IHByb2plY3QubmFtZSgpKSA/PyBERUZBVUxUX1NFUlZJQ0VfTkFNRTtcbiAgY29uc3QgcmVxdWVzdENvbnRleHRLZXlzID0gWy4uLihvcHRpb25zPy5yZXF1ZXN0Q29udGV4dEtleXMgPz8gVFJBQ0VfUkVRVUVTVF9DT05URVhUX0tFWVMpXTtcblxuICAvLyBUaGUgT1RlbCBIVFRQIGV4cG9ydGVyIHRyZWF0cyBgT1RFTF9FWFBPUlRFUl9PVExQX0VORFBPSU5UYCBhcyBhXG4gIC8vICpiYXNlKiBVUkwgYW5kIGFwcGVuZHMgdGhlIHNpZ25hbCBwYXRoIGl0c2VsZiAoZS5nLlxuICAvLyBgaHR0cDovL2xvY2FsaG9zdDo2MDA2YCAtPiBgaHR0cDovL2xvY2FsaG9zdDo2MDA2L3YxL3RyYWNlc2ApLiBMb2dcbiAgLy8gdGhlIHJlc29sdmVkIFBPU1QgVVJMIHNvIG1pc2NvbmZpZ3VyYXRpb25zIChlLmcuIGFjY2lkZW50YWxseVxuICAvLyBzZXR0aW5nIHRoZSBiYXNlIHZhciB0byBhIGAvdjEvdHJhY2VzYC1zdWZmaXhlZCBVUkwsIHdoaWNoIG1ha2VzXG4gIC8vIHRoZSBTREsgUE9TVCB0byBgLi4uL3YxL3RyYWNlcy92MS90cmFjZXNgIGFuZCBQaG9lbml4IDQwNHMpIGFyZVxuICAvLyBvYnZpb3VzIGluIHN0YXJ0dXAgb3V0cHV0LlxuICBjb25zdCByZXNvbHZlZFRyYWNlc1VybCA9IG90ZWxUcmFjZXNPdmVycmlkZVxuICAgID8gb3RlbFRyYWNlc092ZXJyaWRlXG4gICAgOiBvdGVsQmFzZVxuICAgICAgPyBgJHtvdGVsQmFzZS5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpfS92MS90cmFjZXNgXG4gICAgICA6IHVuZGVmaW5lZDtcbiAgbG9nZ2VyLmluZm8oXCJNYXN0cmEgb2JzZXJ2YWJpbGl0eSB3aXJlZCB0aHJvdWdoIE9UZWwgYnJpZGdlXCIsIHtcbiAgICBzZXJ2aWNlTmFtZSxcbiAgICByZXF1ZXN0Q29udGV4dEtleXMsXG4gICAgb3RlbEJhc2U6IG90ZWxCYXNlID8/IFwiPHVuc2V0PlwiLFxuICAgIHJlc29sdmVkVHJhY2VzVXJsOiByZXNvbHZlZFRyYWNlc1VybCA/PyBcIjx1bnNldD5cIixcbiAgfSk7XG5cbiAgcmV0dXJuIG5ldyBPYnNlcnZhYmlsaXR5KHtcbiAgICBjb25maWdzOiB7XG4gICAgICBzZXJ2aWNlTmFtZToge1xuICAgICAgICBzZXJ2aWNlTmFtZSxcbiAgICAgICAgYnJpZGdlOiBuZXcgT3RlbEJyaWRnZSgpLFxuICAgICAgICByZXF1ZXN0Q29udGV4dEtleXMsXG4gICAgICB9LFxuICAgIH0sXG4gIH0pO1xufVxuIl19
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Query-parameter coercion shared by the paginated custom routes
3
+ * (`history` / `threads`). Each route keeps its own page-size default
4
+ * and hard cap and passes them in, so the coercion rules stay in one
5
+ * place without collapsing the routes' distinct sizing.
6
+ *
7
+ * @module
8
+ */
9
+ /** Bounds for {@link clampPerPage}. */
10
+ export interface PerPageBounds {
11
+ /** Page size used for empty / non-positive / non-numeric inputs. */
12
+ fallback: number;
13
+ /** Hard cap so a misbehaving client can't fetch everything at once. */
14
+ max: number;
15
+ }
16
+ /** Coerce / clamp a `perPage` value, falling back to `bounds.fallback`. */
17
+ export declare function clampPerPage(value: number | undefined, bounds: PerPageBounds): number;
18
+ /**
19
+ * Coerce a Hono query value into a non-negative integer. Returns
20
+ * `undefined` for empty / non-numeric / negative inputs so the caller
21
+ * can apply its built-in defaults.
22
+ */
23
+ export declare function parseIntParam(value: string | undefined): number | undefined;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Query-parameter coercion shared by the paginated custom routes
3
+ * (`history` / `threads`). Each route keeps its own page-size default
4
+ * and hard cap and passes them in, so the coercion rules stay in one
5
+ * place without collapsing the routes' distinct sizing.
6
+ *
7
+ * @module
8
+ */
9
+ /** Coerce / clamp a `perPage` value, falling back to `bounds.fallback`. */
10
+ export function clampPerPage(value, bounds) {
11
+ if (value === undefined || Number.isNaN(value))
12
+ return bounds.fallback;
13
+ const n = Math.trunc(value);
14
+ if (n <= 0)
15
+ return bounds.fallback;
16
+ return Math.min(n, bounds.max);
17
+ }
18
+ /**
19
+ * Coerce a Hono query value into a non-negative integer. Returns
20
+ * `undefined` for empty / non-numeric / negative inputs so the caller
21
+ * can apply its built-in defaults.
22
+ */
23
+ export function parseIntParam(value) {
24
+ if (!value)
25
+ return undefined;
26
+ const n = Number(value);
27
+ if (!Number.isFinite(n) || n < 0)
28
+ return undefined;
29
+ return Math.trunc(n);
30
+ }
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYWdpbmF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFVSCwyRUFBMkU7QUFDM0UsTUFBTSxVQUFVLFlBQVksQ0FBQyxLQUF5QixFQUFFLE1BQXFCO0lBQzNFLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sTUFBTSxDQUFDLFFBQVEsQ0FBQztJQUN2RSxNQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVCLElBQUksQ0FBQyxJQUFJLENBQUM7UUFBRSxPQUFPLE1BQU0sQ0FBQyxRQUFRLENBQUM7SUFDbkMsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDakMsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLEtBQXlCO0lBQ3JELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDN0IsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3hCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDbkQsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3ZCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFF1ZXJ5LXBhcmFtZXRlciBjb2VyY2lvbiBzaGFyZWQgYnkgdGhlIHBhZ2luYXRlZCBjdXN0b20gcm91dGVzXG4gKiAoYGhpc3RvcnlgIC8gYHRocmVhZHNgKS4gRWFjaCByb3V0ZSBrZWVwcyBpdHMgb3duIHBhZ2Utc2l6ZSBkZWZhdWx0XG4gKiBhbmQgaGFyZCBjYXAgYW5kIHBhc3NlcyB0aGVtIGluLCBzbyB0aGUgY29lcmNpb24gcnVsZXMgc3RheSBpbiBvbmVcbiAqIHBsYWNlIHdpdGhvdXQgY29sbGFwc2luZyB0aGUgcm91dGVzJyBkaXN0aW5jdCBzaXppbmcuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbi8qKiBCb3VuZHMgZm9yIHtAbGluayBjbGFtcFBlclBhZ2V9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBQZXJQYWdlQm91bmRzIHtcbiAgLyoqIFBhZ2Ugc2l6ZSB1c2VkIGZvciBlbXB0eSAvIG5vbi1wb3NpdGl2ZSAvIG5vbi1udW1lcmljIGlucHV0cy4gKi9cbiAgZmFsbGJhY2s6IG51bWJlcjtcbiAgLyoqIEhhcmQgY2FwIHNvIGEgbWlzYmVoYXZpbmcgY2xpZW50IGNhbid0IGZldGNoIGV2ZXJ5dGhpbmcgYXQgb25jZS4gKi9cbiAgbWF4OiBudW1iZXI7XG59XG5cbi8qKiBDb2VyY2UgLyBjbGFtcCBhIGBwZXJQYWdlYCB2YWx1ZSwgZmFsbGluZyBiYWNrIHRvIGBib3VuZHMuZmFsbGJhY2tgLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNsYW1wUGVyUGFnZSh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkLCBib3VuZHM6IFBlclBhZ2VCb3VuZHMpOiBudW1iZXIge1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCBOdW1iZXIuaXNOYU4odmFsdWUpKSByZXR1cm4gYm91bmRzLmZhbGxiYWNrO1xuICBjb25zdCBuID0gTWF0aC50cnVuYyh2YWx1ZSk7XG4gIGlmIChuIDw9IDApIHJldHVybiBib3VuZHMuZmFsbGJhY2s7XG4gIHJldHVybiBNYXRoLm1pbihuLCBib3VuZHMubWF4KTtcbn1cblxuLyoqXG4gKiBDb2VyY2UgYSBIb25vIHF1ZXJ5IHZhbHVlIGludG8gYSBub24tbmVnYXRpdmUgaW50ZWdlci4gUmV0dXJuc1xuICogYHVuZGVmaW5lZGAgZm9yIGVtcHR5IC8gbm9uLW51bWVyaWMgLyBuZWdhdGl2ZSBpbnB1dHMgc28gdGhlIGNhbGxlclxuICogY2FuIGFwcGx5IGl0cyBidWlsdC1pbiBkZWZhdWx0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlSW50UGFyYW0odmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZCk6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gIGlmICghdmFsdWUpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IG4gPSBOdW1iZXIodmFsdWUpO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShuKSB8fCBuIDwgMCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIE1hdGgudHJ1bmMobik7XG59XG4iXX0=