@case-framework/survey-assistant 0.1.0 → 0.2.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.
- package/README.md +71 -11
- package/dist/{authoring-references-X6uRaWuf.mjs → authoring-references-DYuOAdVx.mjs} +26 -2
- package/dist/authoring-references-DYuOAdVx.mjs.map +1 -0
- package/dist/{capabilities-B6tvE7fK.mjs → capabilities-DZ0a_o2y.mjs} +2 -2
- package/dist/{capabilities-B6tvE7fK.mjs.map → capabilities-DZ0a_o2y.mjs.map} +1 -1
- package/dist/{capabilities-DvSKtjAw.d.mts → capabilities-DlC1l6qy.d.mts} +4 -1
- package/dist/capabilities-DlC1l6qy.d.mts.map +1 -0
- package/dist/capabilities-default.d.mts +1 -1
- package/dist/capabilities-default.d.mts.map +1 -1
- package/dist/capabilities-default.mjs +6 -3
- package/dist/capabilities-default.mjs.map +1 -1
- package/dist/{constants-Jw0k8ojS.d.mts → constants-56jf_4ls.d.mts} +36 -9
- package/dist/constants-56jf_4ls.d.mts.map +1 -0
- package/dist/{controller-proxy-BRDusFiO.d.mts → controller-proxy-2VH0b_5E.d.mts} +29 -4
- package/dist/controller-proxy-2VH0b_5E.d.mts.map +1 -0
- package/dist/{controller-proxy-Duxo2IFE.mjs → controller-proxy-ClsY4lFI.mjs} +74 -5
- package/dist/controller-proxy-ClsY4lFI.mjs.map +1 -0
- package/dist/{digest-BsoVq49o.d.mts → digest-BvXpeWcR.d.mts} +2 -2
- package/dist/{digest-BsoVq49o.d.mts.map → digest-BvXpeWcR.d.mts.map} +1 -1
- package/dist/digest.d.mts +1 -1
- package/dist/{engine-sPvuixWg.mjs → engine-DQrCO_ur.mjs} +392 -50
- package/dist/engine-DQrCO_ur.mjs.map +1 -0
- package/dist/engine.d.mts +2 -2
- package/dist/engine.mjs +2 -2
- package/dist/{index-MpEMQzTm.d.mts → index-B_x2IYi7.d.mts} +5 -1
- package/dist/index-B_x2IYi7.d.mts.map +1 -0
- package/dist/{index-BWNHkpkh.d.mts → index-C_x686M7.d.mts} +27 -5
- package/dist/index-C_x686M7.d.mts.map +1 -0
- package/dist/{index-TFtvBiSQ.d.mts → index-DVxVyhAu.d.mts} +31 -9
- package/dist/index-DVxVyhAu.d.mts.map +1 -0
- package/dist/{index-CUqbBjkc.d.mts → index-DZ6KBwVR.d.mts} +16 -3
- package/dist/index-DZ6KBwVR.d.mts.map +1 -0
- package/dist/{memory-thread-repository-CJkkVE2-.mjs → memory-thread-repository-Cm1BJyRw.mjs} +110 -41
- package/dist/memory-thread-repository-Cm1BJyRw.mjs.map +1 -0
- package/dist/{protocol-DV0ka9WT.mjs → protocol-7EEgcYja.mjs} +70 -97
- package/dist/protocol-7EEgcYja.mjs.map +1 -0
- package/dist/protocol.d.mts +3 -3
- package/dist/protocol.mjs +2 -2
- package/dist/{react-VuYe-cPh.mjs → react-B_5smfiK.mjs} +145 -38
- package/dist/react-B_5smfiK.mjs.map +1 -0
- package/dist/react-integration.d.mts +2 -2
- package/dist/react-integration.mjs +2 -2
- package/dist/react.d.mts +3 -3
- package/dist/react.mjs +3 -3
- package/dist/references/assistant-operations.md +6 -0
- package/dist/references/core-rules.md +6 -0
- package/dist/references/embedded-forms.md +21 -14
- package/dist/references/expressions.md +3 -0
- package/dist/references/item-types.md +37 -10
- package/dist/references/source-material-surveys.md +9 -1
- package/dist/references/survey-data-model.md +13 -0
- package/dist/request-context-BDBw4-Gw.mjs +79 -0
- package/dist/request-context-BDBw4-Gw.mjs.map +1 -0
- package/dist/request-guardrails-h4t6u8mP.mjs +129 -0
- package/dist/request-guardrails-h4t6u8mP.mjs.map +1 -0
- package/dist/server-agent.d.mts +6 -2
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +39 -24
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +21 -5
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +98 -32
- package/dist/server-runtime.mjs.map +1 -1
- package/dist/server-tasks.d.mts +2 -78
- package/dist/server-tasks.mjs +103 -46
- package/dist/server-tasks.mjs.map +1 -1
- package/dist/server-tools.d.mts +1 -1
- package/dist/server-tools.mjs +1 -1
- package/dist/server.d.mts +89 -21
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +136 -5
- package/dist/server.mjs.map +1 -0
- package/dist/storage-postgres.d.mts +12 -2
- package/dist/storage-postgres.d.mts.map +1 -1
- package/dist/storage-postgres.mjs +11 -6
- package/dist/storage-postgres.mjs.map +1 -1
- package/dist/tasks-COQnhSeT.d.mts +140 -0
- package/dist/tasks-COQnhSeT.d.mts.map +1 -0
- package/dist/{thread-documents-DamIq9Od.d.mts → thread-documents-BlxNjc7S.d.mts} +21 -5
- package/dist/thread-documents-BlxNjc7S.d.mts.map +1 -0
- package/dist/{thread-handlers-DpynUc64.d.mts → thread-handlers-l79JBl5m.d.mts} +38 -5
- package/dist/thread-handlers-l79JBl5m.d.mts.map +1 -0
- package/dist/{tools-Hz2p7vyv.mjs → tools-DDXaJ8Za.mjs} +257 -38
- package/dist/tools-DDXaJ8Za.mjs.map +1 -0
- package/dist/ui.d.mts +5 -3
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +91 -12
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +234 -0
- package/package.json +9 -7
- package/dist/authoring-references-Cy8J-DWt.d.mts +0 -48
- package/dist/authoring-references-Cy8J-DWt.d.mts.map +0 -1
- package/dist/authoring-references-X6uRaWuf.mjs.map +0 -1
- package/dist/capabilities-DvSKtjAw.d.mts.map +0 -1
- package/dist/constants-Jw0k8ojS.d.mts.map +0 -1
- package/dist/controller-proxy-BRDusFiO.d.mts.map +0 -1
- package/dist/controller-proxy-Duxo2IFE.mjs.map +0 -1
- package/dist/engine-sPvuixWg.mjs.map +0 -1
- package/dist/index-BWNHkpkh.d.mts.map +0 -1
- package/dist/index-CUqbBjkc.d.mts.map +0 -1
- package/dist/index-MpEMQzTm.d.mts.map +0 -1
- package/dist/index-TFtvBiSQ.d.mts.map +0 -1
- package/dist/memory-thread-repository-CJkkVE2-.mjs.map +0 -1
- package/dist/protocol-DV0ka9WT.mjs.map +0 -1
- package/dist/react-VuYe-cPh.mjs.map +0 -1
- package/dist/request-context-jcmXT_Q2.mjs +0 -44
- package/dist/request-context-jcmXT_Q2.mjs.map +0 -1
- package/dist/request-guardrails-C59IWnH8.mjs +0 -73
- package/dist/request-guardrails-C59IWnH8.mjs.map +0 -1
- package/dist/server-tasks.d.mts.map +0 -1
- package/dist/thread-documents-DamIq9Od.d.mts.map +0 -1
- package/dist/thread-handlers-DpynUc64.d.mts.map +0 -1
- package/dist/tools-Hz2p7vyv.mjs.map +0 -1
package/dist/{memory-thread-repository-CJkkVE2-.mjs → memory-thread-repository-Cm1BJyRw.mjs}
RENAMED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as SurveyAssistantCapabilityMismatchError } from "./capabilities-
|
|
3
|
-
import { o as TurnChangeCollector } from "./engine-
|
|
1
|
+
import { At as serializeSurveyAssistantError, D as SURVEY_ASSISTANT_ATTACHMENT_PART_TYPE, M as updateSurveyAssistantThreadInputSchema, O as createSurveyAssistantThreadInputSchema, n as SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE } from "./protocol-7EEgcYja.mjs";
|
|
2
|
+
import { t as SurveyAssistantCapabilityMismatchError } from "./capabilities-DZ0a_o2y.mjs";
|
|
3
|
+
import { o as TurnChangeCollector } from "./engine-DQrCO_ur.mjs";
|
|
4
4
|
import "./constants-CFUWrRtl.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { i as parseSurveyAssistantSnapshot, r as parseSurveyAssistantPreviousSettledVersion, t as buildSurveyAssistantRequestContext } from "./request-context-BDBw4-Gw.mjs";
|
|
6
|
+
import { d as readSurveyAssistantRequestJson, f as validateSurveyAssistantMessages, n as SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS, u as SurveyAssistantRequestError } from "./request-guardrails-h4t6u8mP.mjs";
|
|
7
7
|
import { createUIMessageStreamResponse } from "ai";
|
|
8
8
|
import { handleChatStream } from "@mastra/ai-sdk";
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
9
10
|
import { toAISdkMessages } from "@mastra/ai-sdk/ui";
|
|
10
11
|
//#region src/server/turn-stream.ts
|
|
11
12
|
const createTurnFinalChunk = (turnFinal) => ({
|
|
@@ -96,6 +97,47 @@ const getBase64Bytes = (data) => {
|
|
|
96
97
|
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
97
98
|
return Math.max(0, Math.floor(data.length * 3 / 4) - padding);
|
|
98
99
|
};
|
|
100
|
+
const getAttachmentContentHash = (data) => createHash("sha256").update(data).digest("hex");
|
|
101
|
+
const createAttachmentReferencePart = ({ document, filename, mediaType, parsedData }) => ({
|
|
102
|
+
type: SURVEY_ASSISTANT_ATTACHMENT_PART_TYPE,
|
|
103
|
+
data: {
|
|
104
|
+
...document ? { documentId: document.id } : {},
|
|
105
|
+
filename: filename?.trim() || document?.name || "Attached document",
|
|
106
|
+
mediaType: document?.mediaType || mediaType,
|
|
107
|
+
bytes: document?.bytes ?? (parsedData ? getBase64Bytes(parsedData) : 0)
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
const createDocumentIndex = (documents) => new Map(documents.map((document) => [`${document.mediaType}\u0000${document.contentHash}`, document]));
|
|
111
|
+
/**
|
|
112
|
+
* Replaces current-turn file data with small repository references before the
|
|
113
|
+
* message is handed to Mastra for persistence. The provider-bound attachment
|
|
114
|
+
* processor re-injects the authorized repository copy transiently.
|
|
115
|
+
*/
|
|
116
|
+
const replaceCurrentSurveyAssistantAttachmentsWithReferences = ({ documents, messages }) => {
|
|
117
|
+
const current = getCurrentUserMessage(messages);
|
|
118
|
+
if (!current) return messages;
|
|
119
|
+
const documentIndex = createDocumentIndex(documents);
|
|
120
|
+
return messages.map((message) => {
|
|
121
|
+
if (message !== current) return message;
|
|
122
|
+
let changed = false;
|
|
123
|
+
const parts = message.parts.map((part) => {
|
|
124
|
+
if (part.type !== "file") return part;
|
|
125
|
+
changed = true;
|
|
126
|
+
const parsed = typeof part.url === "string" ? parseFileDataUrl(part.url) : null;
|
|
127
|
+
const mediaType = parsed?.mediaType || part.mediaType;
|
|
128
|
+
return createAttachmentReferencePart({
|
|
129
|
+
document: parsed ? documentIndex.get(`${mediaType}\u0000${getAttachmentContentHash(parsed.data)}`) : void 0,
|
|
130
|
+
filename: part.filename,
|
|
131
|
+
mediaType,
|
|
132
|
+
parsedData: parsed?.data
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
return changed ? {
|
|
136
|
+
...message,
|
|
137
|
+
parts
|
|
138
|
+
} : message;
|
|
139
|
+
});
|
|
140
|
+
};
|
|
99
141
|
const registerSurveyAssistantThreadAttachments = async ({ messages, repository, resourceId, threadId }) => {
|
|
100
142
|
const registered = [];
|
|
101
143
|
const currentMessage = getCurrentUserMessage(messages);
|
|
@@ -114,7 +156,6 @@ const registerSurveyAssistantThreadAttachments = async ({ messages, repository,
|
|
|
114
156
|
}
|
|
115
157
|
return registered;
|
|
116
158
|
};
|
|
117
|
-
const hasMatchingCurrentFile = (message, document) => message.parts.some((part) => part.type === "file" && part.filename === document.name && part.mediaType === document.mediaType);
|
|
118
159
|
const getCurrentUserMessage = (messages) => {
|
|
119
160
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
120
161
|
const message = messages[index];
|
|
@@ -132,13 +173,14 @@ const removeHistoricalSurveyAssistantAttachments = (messages) => {
|
|
|
132
173
|
};
|
|
133
174
|
});
|
|
134
175
|
};
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
176
|
+
/**
|
|
177
|
+
* Excludes the exact repository documents already loaded for the current turn.
|
|
178
|
+
* Message parts are references by this stage and are not a deduplication source.
|
|
179
|
+
*/
|
|
180
|
+
const getSurveyAssistantReplayableDocuments = (documents, currentDocumentIds) => documents.filter((document) => !currentDocumentIds.has(document.id));
|
|
181
|
+
const loadSurveyAssistantThreadAttachments = async ({ currentDocumentIds = /* @__PURE__ */ new Set(), documents, repository, resourceId, threadId }) => {
|
|
140
182
|
return loadSurveyAssistantDocuments({
|
|
141
|
-
documents: getSurveyAssistantReplayableDocuments(documents,
|
|
183
|
+
documents: getSurveyAssistantReplayableDocuments(documents, currentDocumentIds),
|
|
142
184
|
repository,
|
|
143
185
|
resourceId,
|
|
144
186
|
threadId
|
|
@@ -166,6 +208,7 @@ const loadSurveyAssistantDocuments = async ({ documents, repository, resourceId,
|
|
|
166
208
|
//#region src/server/handler.ts
|
|
167
209
|
const DEFAULT_MAX_STEPS = 48;
|
|
168
210
|
const DEFAULT_MAX_CONCURRENT_REQUESTS = 4;
|
|
211
|
+
const DEFAULT_MAX_DURATION_MS = 180 * 1e3;
|
|
169
212
|
const MAX_USER_ERROR_MESSAGE_LENGTH = 700;
|
|
170
213
|
const MAX_ERROR_CAUSE_DEPTH = 5;
|
|
171
214
|
var SurveyAssistantAuthorizationError = class extends Error {
|
|
@@ -183,10 +226,11 @@ const optionalRequestedId = (value) => {
|
|
|
183
226
|
return normalized.length > 0 && normalized.length <= 240 ? normalized : void 0;
|
|
184
227
|
};
|
|
185
228
|
const validateAuthorizedIdentity = (identity) => {
|
|
186
|
-
if (!identity || identity.authority !== "authenticated-host" && identity.authority !== "experimental-client-request" || typeof identity.resourceId !== "string" || identity.resourceId.trim().length === 0 || identity.resourceId.length > 240 || typeof identity.threadId !== "string" || identity.threadId.trim().length === 0 || identity.threadId.length > 240) throw new SurveyAssistantAuthorizationError("The host returned an invalid survey assistant identity.", 500);
|
|
229
|
+
if (!identity || identity.authority !== "authenticated-host" && identity.authority !== "experimental-client-request" || typeof identity.resourceId !== "string" || identity.resourceId.trim().length === 0 || identity.resourceId.length > 240 || identity.surveyId !== void 0 && !optionalRequestedId(identity.surveyId) || typeof identity.threadId !== "string" || identity.threadId.trim().length === 0 || identity.threadId.length > 240) throw new SurveyAssistantAuthorizationError("The host returned an invalid survey assistant identity.", 500);
|
|
187
230
|
return {
|
|
188
231
|
authority: identity.authority,
|
|
189
232
|
resourceId: identity.resourceId.trim(),
|
|
233
|
+
...identity.surveyId ? { surveyId: optionalRequestedId(identity.surveyId) } : {},
|
|
190
234
|
threadId: identity.threadId.trim()
|
|
191
235
|
};
|
|
192
236
|
};
|
|
@@ -266,7 +310,7 @@ const formatKnownProviderError = (message) => {
|
|
|
266
310
|
}
|
|
267
311
|
if (/\b(?:rate limit|too many requests|request limit)\b/i.test(normalized)) return "The AI provider is temporarily limiting requests.";
|
|
268
312
|
if (/\b(?:invalid api key|incorrect api key|authentication|unauthorized|forbidden)\b/i.test(normalized)) return "The AI provider rejected the assistant credentials.";
|
|
269
|
-
return
|
|
313
|
+
return "The AI provider could not complete the request.";
|
|
270
314
|
};
|
|
271
315
|
const parseProviderResponseBodyMessage = (responseBody) => {
|
|
272
316
|
if (typeof responseBody !== "string" || responseBody.trim().length === 0) return void 0;
|
|
@@ -303,8 +347,15 @@ const requestErrorResponse = (error, fallback) => {
|
|
|
303
347
|
}, { status: requestError.status });
|
|
304
348
|
};
|
|
305
349
|
const createSurveyAssistantChatHandler = (options) => {
|
|
350
|
+
const requestLimits = {
|
|
351
|
+
...SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS,
|
|
352
|
+
...options.requestLimits
|
|
353
|
+
};
|
|
354
|
+
for (const [name, value] of Object.entries(requestLimits)) if (name !== "acceptedAttachmentTypes" && (!Number.isSafeInteger(value) || value <= 0)) throw new Error(`Survey assistant request limit "${name}" must be a positive integer.`);
|
|
355
|
+
if (typeof requestLimits.acceptedAttachmentTypes?.has !== "function") throw new Error("Survey assistant accepted attachment types must be a set.");
|
|
306
356
|
const maxConcurrentRequests = Number.isInteger(options.maxConcurrentRequests) && (options.maxConcurrentRequests ?? 0) > 0 ? options.maxConcurrentRequests : DEFAULT_MAX_CONCURRENT_REQUESTS;
|
|
307
357
|
const maxSteps = Number.isInteger(options.maxSteps) && (options.maxSteps ?? 0) > 0 ? options.maxSteps : DEFAULT_MAX_STEPS;
|
|
358
|
+
const maxDurationMs = Number.isFinite(options.maxDurationMs) && (options.maxDurationMs ?? 0) > 0 ? options.maxDurationMs : DEFAULT_MAX_DURATION_MS;
|
|
308
359
|
let activeRequestCount = 0;
|
|
309
360
|
const acquireRequestSlot = () => {
|
|
310
361
|
if (activeRequestCount >= maxConcurrentRequests) return null;
|
|
@@ -323,7 +374,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
323
374
|
}, { status: 404 });
|
|
324
375
|
let rawParams;
|
|
325
376
|
try {
|
|
326
|
-
rawParams = await readSurveyAssistantRequestJson(request);
|
|
377
|
+
rawParams = await readSurveyAssistantRequestJson(request, requestLimits.maxRequestBytes);
|
|
327
378
|
} catch (error) {
|
|
328
379
|
return requestErrorResponse(error, "The survey assistant request could not be read.");
|
|
329
380
|
}
|
|
@@ -345,7 +396,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
345
396
|
}
|
|
346
397
|
let messages;
|
|
347
398
|
try {
|
|
348
|
-
messages = validateSurveyAssistantMessages(params.messages);
|
|
399
|
+
messages = validateSurveyAssistantMessages(params.messages, requestLimits);
|
|
349
400
|
} catch (error) {
|
|
350
401
|
return requestErrorResponse(error, "The survey assistant messages are invalid.");
|
|
351
402
|
}
|
|
@@ -365,6 +416,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
365
416
|
} catch (error) {
|
|
366
417
|
options.onStreamError?.(error, {
|
|
367
418
|
resourceId: identity.resourceId,
|
|
419
|
+
surveyId: identity.surveyId,
|
|
368
420
|
threadId: identity.threadId
|
|
369
421
|
});
|
|
370
422
|
return Response.json({
|
|
@@ -378,12 +430,14 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
378
430
|
}, { status: 400 });
|
|
379
431
|
}
|
|
380
432
|
let snapshot;
|
|
433
|
+
let previousSettledVersion;
|
|
381
434
|
try {
|
|
382
435
|
snapshot = parseSurveyAssistantSnapshot(params.snapshot);
|
|
436
|
+
previousSettledVersion = parseSurveyAssistantPreviousSettledVersion(params.previousSettledVersion);
|
|
383
437
|
} catch {
|
|
384
438
|
return Response.json({
|
|
385
439
|
code: "request_failed",
|
|
386
|
-
error: "Invalid survey assistant
|
|
440
|
+
error: "Invalid survey assistant document context."
|
|
387
441
|
}, { status: 400 });
|
|
388
442
|
}
|
|
389
443
|
const readinessError = await options.getReadinessError?.(request);
|
|
@@ -419,9 +473,12 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
419
473
|
resourceId: identity.resourceId,
|
|
420
474
|
threadId: identity.threadId
|
|
421
475
|
});
|
|
476
|
+
messages = replaceCurrentSurveyAssistantAttachmentsWithReferences({
|
|
477
|
+
documents: registered,
|
|
478
|
+
messages
|
|
479
|
+
});
|
|
422
480
|
messages = removeHistoricalSurveyAssistantAttachments(messages);
|
|
423
|
-
const
|
|
424
|
-
const replayableDocuments = getSurveyAssistantReplayableDocuments(manifest.filter((document) => !registeredIds.has(document.id)), messages);
|
|
481
|
+
const replayableDocuments = getSurveyAssistantReplayableDocuments(manifest, new Set(registered.map((document) => document.id)));
|
|
425
482
|
const documents = [...registered, ...replayableDocuments];
|
|
426
483
|
if (documents.length > 0) attachmentAccess = {
|
|
427
484
|
documents,
|
|
@@ -439,7 +496,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
439
496
|
changeCollector,
|
|
440
497
|
identity,
|
|
441
498
|
snapshot,
|
|
442
|
-
|
|
499
|
+
previousSettledVersion,
|
|
443
500
|
trustedCapabilities: options.trustedCapabilities,
|
|
444
501
|
attachmentAccess,
|
|
445
502
|
modelAttachments,
|
|
@@ -454,6 +511,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
454
511
|
}, { status: 409 });
|
|
455
512
|
options.onStreamError?.(error, {
|
|
456
513
|
resourceId: identity.resourceId,
|
|
514
|
+
surveyId: identity.surveyId,
|
|
457
515
|
threadId: identity.threadId
|
|
458
516
|
});
|
|
459
517
|
return Response.json({
|
|
@@ -467,8 +525,11 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
467
525
|
};
|
|
468
526
|
const streamErrorContext = {
|
|
469
527
|
resourceId: identity.resourceId,
|
|
528
|
+
surveyId: identity.surveyId,
|
|
470
529
|
threadId: identity.threadId
|
|
471
530
|
};
|
|
531
|
+
const timeoutSignal = AbortSignal.timeout(maxDurationMs);
|
|
532
|
+
const executionSignal = AbortSignal.any([request.signal, timeoutSignal]);
|
|
472
533
|
const handleStreamError = (error) => {
|
|
473
534
|
options.onStreamError?.(error, streamErrorContext);
|
|
474
535
|
return options.formatStreamError?.(error, streamErrorContext) ?? formatSurveyAssistantStreamError(error);
|
|
@@ -484,7 +545,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
484
545
|
...params.trigger ? { trigger: params.trigger } : {},
|
|
485
546
|
requestContext,
|
|
486
547
|
runId: changeCollector.turnId,
|
|
487
|
-
abortSignal:
|
|
548
|
+
abortSignal: executionSignal,
|
|
488
549
|
onAbort: () => {
|
|
489
550
|
changeCollector.finalize("aborted");
|
|
490
551
|
},
|
|
@@ -511,22 +572,29 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
511
572
|
}
|
|
512
573
|
});
|
|
513
574
|
} catch (error) {
|
|
514
|
-
changeCollector.finalize(
|
|
575
|
+
changeCollector.finalize(executionSignal.aborted ? "aborted" : "failed");
|
|
515
576
|
releaseRequestSlot();
|
|
516
577
|
return Response.json({ error: handleStreamError(error) }, { status: 503 });
|
|
517
578
|
}
|
|
518
579
|
return createUIMessageStreamResponse({ stream: createSurveyAssistantTurnStream({
|
|
519
580
|
stream,
|
|
520
581
|
changeCollector,
|
|
521
|
-
signal:
|
|
582
|
+
signal: executionSignal,
|
|
522
583
|
onUnexpectedError: handleStreamError,
|
|
523
584
|
onSettled: () => {
|
|
524
585
|
releaseRequestSlot();
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
586
|
+
try {
|
|
587
|
+
options.onTurnMetrics?.({
|
|
588
|
+
identity,
|
|
589
|
+
metrics,
|
|
590
|
+
provider: options.providerName,
|
|
591
|
+
turnId: changeCollector.turnId
|
|
592
|
+
});
|
|
593
|
+
} catch (error) {
|
|
594
|
+
try {
|
|
595
|
+
options.onStreamError?.(error, streamErrorContext);
|
|
596
|
+
} catch {}
|
|
597
|
+
}
|
|
530
598
|
}
|
|
531
599
|
}) });
|
|
532
600
|
};
|
|
@@ -534,6 +602,7 @@ const createSurveyAssistantChatHandler = (options) => {
|
|
|
534
602
|
//#endregion
|
|
535
603
|
//#region src/server/thread-handlers.ts
|
|
536
604
|
const DEFAULT_TITLE = "New chat";
|
|
605
|
+
const MAX_THREAD_MUTATION_BYTES = 16 * 1024;
|
|
537
606
|
const MANUAL_TITLE_METADATA_KEY = "surveyAssistant.manualTitle";
|
|
538
607
|
const AUTOMATIC_TITLE_METADATA_KEY = "surveyAssistant.automaticTitle";
|
|
539
608
|
const getMetadataString = (metadata, key) => {
|
|
@@ -558,10 +627,10 @@ const asThread = (thread) => {
|
|
|
558
627
|
};
|
|
559
628
|
const json = (body, status = 200) => Response.json(body, { status });
|
|
560
629
|
const errorResponse = (error) => {
|
|
561
|
-
const
|
|
562
|
-
const
|
|
630
|
+
const status = typeof error === "object" && error !== null && "status" in error && typeof error.status === "number" ? error.status : error instanceof SurveyAssistantRequestError || error instanceof SyntaxError || error instanceof Error && error.name === "ZodError" ? 400 : 500;
|
|
631
|
+
const message = error instanceof SurveyAssistantRequestError ? error.message : status === 401 || status === 403 || status === 404 ? status === 404 ? "Chat not found." : "The chat request is not authorized." : status >= 500 ? "The chat request could not be completed." : "The chat request is invalid.";
|
|
563
632
|
return json({
|
|
564
|
-
code: status === 401 || status === 403 ? "authorization_failed" : "request_failed",
|
|
633
|
+
code: status === 401 || status === 403 || status === 404 ? "authorization_failed" : "request_failed",
|
|
565
634
|
error: message
|
|
566
635
|
}, status);
|
|
567
636
|
};
|
|
@@ -581,7 +650,7 @@ const createSurveyAssistantThreadHandlers = ({ authorize, repository }) => {
|
|
|
581
650
|
};
|
|
582
651
|
const create = async (request) => {
|
|
583
652
|
try {
|
|
584
|
-
const [identity, input] = await Promise.all([authorize(request), request
|
|
653
|
+
const [identity, input] = await Promise.all([authorize(request), readSurveyAssistantRequestJson(request, MAX_THREAD_MUTATION_BYTES)]);
|
|
585
654
|
const parsed = createSurveyAssistantThreadInputSchema.parse(input);
|
|
586
655
|
const metadata = { ...identity.metadata };
|
|
587
656
|
if (parsed.title) {
|
|
@@ -612,7 +681,7 @@ const createSurveyAssistantThreadHandlers = ({ authorize, repository }) => {
|
|
|
612
681
|
};
|
|
613
682
|
const update = async (request, threadId) => {
|
|
614
683
|
try {
|
|
615
|
-
const [identity, input] = await Promise.all([authorize(request), request
|
|
684
|
+
const [identity, input] = await Promise.all([authorize(request), readSurveyAssistantRequestJson(request, MAX_THREAD_MUTATION_BYTES)]);
|
|
616
685
|
const thread = await getOwnedThread(repository, identity, threadId);
|
|
617
686
|
if (!thread || typeof thread.metadata?.archivedAt === "string") return json({ error: "Chat not found." }, 404);
|
|
618
687
|
const parsed = updateSurveyAssistantThreadInputSchema.parse(input);
|
|
@@ -659,7 +728,7 @@ const createSurveyAssistantThreadHandlers = ({ authorize, repository }) => {
|
|
|
659
728
|
};
|
|
660
729
|
//#endregion
|
|
661
730
|
//#region src/server/memory-thread-repository.ts
|
|
662
|
-
const createMastraMemoryThreadRepository = ({ getMemory, threadDocuments }) => ({
|
|
731
|
+
const createMastraMemoryThreadRepository = ({ getMemory, maxMessagesPerTranscript = 1e3, maxThreadsPerList = 100, threadDocuments }) => ({
|
|
663
732
|
async create({ metadata, resourceId, title }) {
|
|
664
733
|
return (await getMemory()).createThread({
|
|
665
734
|
metadata,
|
|
@@ -681,19 +750,19 @@ const createMastraMemoryThreadRepository = ({ getMemory, threadDocuments }) => (
|
|
|
681
750
|
field: "updatedAt",
|
|
682
751
|
direction: "DESC"
|
|
683
752
|
},
|
|
684
|
-
perPage:
|
|
753
|
+
perPage: Math.max(1, Math.min(1e3, maxThreadsPerList))
|
|
685
754
|
})).threads;
|
|
686
755
|
},
|
|
687
756
|
async readMessages(threadId, resourceId) {
|
|
688
|
-
return toAISdkMessages((await (await getMemory()).recall({
|
|
757
|
+
return toAISdkMessages([...(await (await getMemory()).recall({
|
|
689
758
|
threadId,
|
|
690
759
|
resourceId,
|
|
691
|
-
perPage:
|
|
760
|
+
perPage: Math.max(1, Math.min(5e3, maxMessagesPerTranscript)),
|
|
692
761
|
orderBy: {
|
|
693
762
|
field: "createdAt",
|
|
694
|
-
direction: "
|
|
763
|
+
direction: "DESC"
|
|
695
764
|
}
|
|
696
|
-
})).messages, { version: "v6" });
|
|
765
|
+
})).messages].reverse(), { version: "v6" });
|
|
697
766
|
},
|
|
698
767
|
async delete(threadId) {
|
|
699
768
|
const memory = await getMemory();
|
|
@@ -713,6 +782,6 @@ const createMastraMemoryThreadRepository = ({ getMemory, threadDocuments }) => (
|
|
|
713
782
|
}
|
|
714
783
|
});
|
|
715
784
|
//#endregion
|
|
716
|
-
export { describeSurveyAssistantStreamError as a, loadSurveyAssistantDocuments as c, removeHistoricalSurveyAssistantAttachments as d,
|
|
785
|
+
export { describeSurveyAssistantStreamError as a, loadSurveyAssistantDocuments as c, removeHistoricalSurveyAssistantAttachments as d, replaceCurrentSurveyAssistantAttachmentsWithReferences as f, createSurveyAssistantChatHandler as i, loadSurveyAssistantThreadAttachments as l, createSurveyAssistantThreadHandlers as n, formatSurveyAssistantStreamError as o, createSurveyAssistantTurnStream as p, SurveyAssistantAuthorizationError as r, getSurveyAssistantReplayableDocuments as s, createMastraMemoryThreadRepository as t, registerSurveyAssistantThreadAttachments as u };
|
|
717
786
|
|
|
718
|
-
//# sourceMappingURL=memory-thread-repository-
|
|
787
|
+
//# sourceMappingURL=memory-thread-repository-Cm1BJyRw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-thread-repository-Cm1BJyRw.mjs","names":[],"sources":["../src/server/turn-stream.ts","../src/server/thread-documents.ts","../src/server/handler.ts","../src/server/thread-handlers.ts","../src/server/memory-thread-repository.ts"],"sourcesContent":["import {\n SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE,\n type SurveyAssistantTurnFinal,\n} from \"../protocol\";\nimport type { TurnChangeCollector } from \"../engine\";\nimport type { UIMessageChunk } from \"ai\";\n\nconst createTurnFinalChunk = (turnFinal: SurveyAssistantTurnFinal): UIMessageChunk => ({\n type: SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE,\n id: turnFinal.turnId,\n data: turnFinal,\n});\n\nexport interface SurveyAssistantTurnStreamOptions {\n stream: ReadableStream<UIMessageChunk>;\n changeCollector: TurnChangeCollector;\n signal?: AbortSignal;\n onUnexpectedError?: (error: unknown) => string;\n onSettled?: () => void;\n}\n\nexport const createSurveyAssistantTurnStream = ({\n changeCollector,\n onSettled,\n onUnexpectedError = () => \"The survey assistant stream ended unexpectedly.\",\n signal,\n stream,\n}: SurveyAssistantTurnStreamOptions): ReadableStream<UIMessageChunk> => {\n const reader = stream.getReader();\n let settled = false;\n\n const settle = () => {\n if (settled) {\n return;\n }\n settled = true;\n onSettled?.();\n };\n\n return new ReadableStream<UIMessageChunk>({\n async start(controller) {\n let emittedTurnFinal = false;\n let sawFinish = false;\n let finishReason: Extract<UIMessageChunk, { type: \"finish\" }>[\"finishReason\"];\n\n const emitTurnFinal = (status: SurveyAssistantTurnFinal[\"status\"]) => {\n if (emittedTurnFinal) {\n return;\n }\n emittedTurnFinal = true;\n controller.enqueue(createTurnFinalChunk(changeCollector.finalize(status)));\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n if (value.type === SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE) {\n emitTurnFinal(\"failed\");\n controller.enqueue({\n type: \"error\",\n errorText: \"The survey assistant returned an invalid duplicate turn final.\",\n });\n await reader.cancel(\"duplicate turn final\");\n break;\n }\n if (value.type === \"error\") {\n emitTurnFinal(\"failed\");\n controller.enqueue(value);\n continue;\n }\n if (value.type === \"abort\") {\n emitTurnFinal(\"aborted\");\n controller.enqueue(value);\n continue;\n }\n if (value.type === \"finish\") {\n sawFinish = true;\n finishReason = value.finishReason;\n }\n controller.enqueue(value);\n }\n\n if (!emittedTurnFinal) {\n emitTurnFinal(\n signal?.aborted\n ? \"aborted\"\n : sawFinish && (finishReason === undefined || finishReason === \"stop\")\n ? \"completed\"\n : \"failed\",\n );\n }\n controller.close();\n } catch (error) {\n try {\n emitTurnFinal(signal?.aborted ? \"aborted\" : \"failed\");\n if (!signal?.aborted) {\n controller.enqueue({ type: \"error\", errorText: onUnexpectedError(error) });\n }\n controller.close();\n } catch {\n // The client disconnected; finalization still occurred server-side.\n }\n } finally {\n reader.releaseLock();\n settle();\n }\n },\n async cancel(reason) {\n changeCollector.finalize(\"aborted\");\n settle();\n await reader.cancel(reason);\n },\n });\n};\n","import type { UIMessage } from \"ai\";\nimport { createHash } from \"node:crypto\";\nimport {\n SURVEY_ASSISTANT_ATTACHMENT_PART_TYPE,\n type SurveyAssistantAttachmentReference,\n} from \"../protocol\";\n\nexport interface SurveyAssistantThreadDocument {\n id: string;\n name: string;\n mediaType: string;\n bytes: number;\n contentHash: string;\n createdAt: string;\n}\n\nexport interface SurveyAssistantThreadDocumentContent extends SurveyAssistantThreadDocument {\n data: string;\n}\n\nexport interface SurveyAssistantModelAttachment {\n filename: string;\n mediaType: string;\n data: string;\n contentHash: string;\n}\n\nexport interface SurveyAssistantAttachmentAccess {\n documents: SurveyAssistantThreadDocument[];\n /** Attachments submitted in the current user message; already present as model context. */\n currentDocumentIds?: string[];\n selectedAttachments: SurveyAssistantModelAttachment[];\n load(documentIds: string[]): Promise<SurveyAssistantModelAttachment[]>;\n}\n\nexport interface SurveyAssistantThreadDocumentRepository {\n put(input: {\n resourceId: string;\n threadId: string;\n name: string;\n mediaType: string;\n data: string;\n bytes: number;\n }): Promise<SurveyAssistantThreadDocument>;\n list(input: { resourceId: string; threadId: string }): Promise<SurveyAssistantThreadDocument[]>;\n read(input: {\n resourceId: string;\n threadId: string;\n documentId: string;\n }): Promise<SurveyAssistantThreadDocumentContent | null>;\n deleteThread(input: { resourceId: string; threadId: string }): Promise<void>;\n}\n\nconst parseFileDataUrl = (url: string) => {\n const match = /^data:([^;,]+);base64,([A-Za-z0-9+/]*={0,2})$/.exec(url);\n return match ? { mediaType: match[1], data: match[2] } : null;\n};\n\nconst getBase64Bytes = (data: string) => {\n const padding = data.endsWith(\"==\") ? 2 : data.endsWith(\"=\") ? 1 : 0;\n return Math.max(0, Math.floor((data.length * 3) / 4) - padding);\n};\n\nconst getAttachmentContentHash = (data: string) =>\n createHash(\"sha256\").update(data).digest(\"hex\");\n\nconst createAttachmentReferencePart = ({\n document,\n filename,\n mediaType,\n parsedData,\n}: {\n document?: SurveyAssistantThreadDocument;\n filename?: string;\n mediaType: string;\n parsedData?: string;\n}) => ({\n type: SURVEY_ASSISTANT_ATTACHMENT_PART_TYPE,\n data: {\n ...(document ? { documentId: document.id } : {}),\n filename: filename?.trim() || document?.name || \"Attached document\",\n mediaType: document?.mediaType || mediaType,\n bytes: document?.bytes ?? (parsedData ? getBase64Bytes(parsedData) : 0),\n } satisfies SurveyAssistantAttachmentReference,\n});\n\nconst createDocumentIndex = (documents: SurveyAssistantThreadDocument[]) =>\n new Map(\n documents.map((document) => [\n `${document.mediaType}\\u0000${document.contentHash}`,\n document,\n ]),\n );\n\n/**\n * Replaces current-turn file data with small repository references before the\n * message is handed to Mastra for persistence. The provider-bound attachment\n * processor re-injects the authorized repository copy transiently.\n */\nexport const replaceCurrentSurveyAssistantAttachmentsWithReferences = ({\n documents,\n messages,\n}: {\n documents: SurveyAssistantThreadDocument[];\n messages: UIMessage[];\n}): UIMessage[] => {\n const current = getCurrentUserMessage(messages);\n if (!current) return messages;\n const documentIndex = createDocumentIndex(documents);\n\n return messages.map((message) => {\n if (message !== current) return message;\n let changed = false;\n const parts = message.parts.map((part) => {\n if (part.type !== \"file\") return part;\n changed = true;\n const parsed = typeof part.url === \"string\" ? parseFileDataUrl(part.url) : null;\n const mediaType = parsed?.mediaType || part.mediaType;\n const document = parsed\n ? documentIndex.get(`${mediaType}\\u0000${getAttachmentContentHash(parsed.data)}`)\n : undefined;\n return createAttachmentReferencePart({\n document,\n filename: part.filename,\n mediaType,\n parsedData: parsed?.data,\n });\n });\n return changed ? { ...message, parts } : message;\n }) as UIMessage[];\n};\n\nexport const registerSurveyAssistantThreadAttachments = async ({\n messages,\n repository,\n resourceId,\n threadId,\n}: {\n messages: UIMessage[];\n repository: SurveyAssistantThreadDocumentRepository;\n resourceId: string;\n threadId: string;\n}) => {\n const registered: SurveyAssistantThreadDocument[] = [];\n\n const currentMessage = getCurrentUserMessage(messages);\n for (const message of currentMessage ? [currentMessage] : []) {\n for (const part of message.parts) {\n if (part.type !== \"file\" || typeof part.url !== \"string\") continue;\n const parsed = parseFileDataUrl(part.url);\n if (!parsed) continue;\n\n registered.push(\n await repository.put({\n resourceId,\n threadId,\n name: part.filename?.trim() || \"Attached document\",\n mediaType: parsed.mediaType,\n data: parsed.data,\n bytes: getBase64Bytes(parsed.data),\n }),\n );\n }\n }\n\n return registered;\n};\n\nconst getCurrentUserMessage = (messages: UIMessage[]) => {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index];\n if (message?.role === \"user\") return message;\n }\n return undefined;\n};\n\nexport const removeHistoricalSurveyAssistantAttachments = (messages: UIMessage[]) => {\n const current = getCurrentUserMessage(messages);\n return messages.map((message) => {\n if (message === current) return message;\n const parts = message.parts.filter((part) => part.type !== \"file\");\n return parts.length === message.parts.length ? message : { ...message, parts };\n });\n};\n\n/**\n * Excludes the exact repository documents already loaded for the current turn.\n * Message parts are references by this stage and are not a deduplication source.\n */\nexport const getSurveyAssistantReplayableDocuments = (\n documents: SurveyAssistantThreadDocument[],\n currentDocumentIds: ReadonlySet<string>,\n) => documents.filter((document) => !currentDocumentIds.has(document.id));\n\nexport const loadSurveyAssistantThreadAttachments = async ({\n currentDocumentIds = new Set<string>(),\n documents,\n repository,\n resourceId,\n threadId,\n}: {\n currentDocumentIds?: ReadonlySet<string>;\n documents: SurveyAssistantThreadDocument[];\n repository: SurveyAssistantThreadDocumentRepository;\n resourceId: string;\n threadId: string;\n}): Promise<SurveyAssistantModelAttachment[]> => {\n const replayableDocuments = getSurveyAssistantReplayableDocuments(\n documents,\n currentDocumentIds,\n );\n\n return loadSurveyAssistantDocuments({\n documents: replayableDocuments,\n repository,\n resourceId,\n threadId,\n });\n};\n\nexport const loadSurveyAssistantDocuments = async ({\n documents,\n repository,\n resourceId,\n threadId,\n}: {\n documents: SurveyAssistantThreadDocument[];\n repository: SurveyAssistantThreadDocumentRepository;\n resourceId: string;\n threadId: string;\n}): Promise<SurveyAssistantModelAttachment[]> => {\n const attachments: SurveyAssistantModelAttachment[] = [];\n for (const document of documents) {\n const content = await repository.read({\n resourceId,\n threadId,\n documentId: document.id,\n });\n if (!content) continue;\n attachments.push({\n filename: content.name,\n mediaType: content.mediaType,\n data: content.data,\n contentHash: content.contentHash,\n });\n }\n\n return attachments;\n};\n","import { handleChatStream } from \"@mastra/ai-sdk\";\nimport type { AgentExecutionOptions } from \"@mastra/core/agent\";\nimport type { Mastra } from \"@mastra/core/mastra\";\nimport { createUIMessageStreamResponse, type UIMessage, type UIMessageChunk } from \"ai\";\nimport {\n SurveyAssistantCapabilityMismatchError,\n TurnChangeCollector,\n type SurveyAssistantCapabilitySet,\n} from \"../engine\";\nimport {\n serializeSurveyAssistantError,\n type SurveyAssistantErrorCode,\n type SurveyDocumentSnapshot,\n} from \"../protocol\";\nimport {\n buildSurveyAssistantRequestContext,\n parseSurveyAssistantPreviousSettledVersion,\n parseSurveyAssistantSnapshot,\n type SurveyAssistantModelSelection,\n type SurveyAssistantIdentityContext,\n} from \"./request-context\";\nimport { SURVEY_ASSISTANT_AGENT_ID } from \"./constants\";\nimport {\n readSurveyAssistantRequestJson,\n SurveyAssistantRequestError,\n SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS,\n type SurveyAssistantRequestLimits,\n validateSurveyAssistantMessages,\n} from \"./request-guardrails\";\nimport { createSurveyAssistantTurnStream } from \"./turn-stream\";\nimport {\n registerSurveyAssistantThreadAttachments,\n loadSurveyAssistantDocuments,\n getSurveyAssistantReplayableDocuments,\n removeHistoricalSurveyAssistantAttachments,\n replaceCurrentSurveyAssistantAttachmentsWithReferences,\n type SurveyAssistantAttachmentAccess,\n type SurveyAssistantModelAttachment,\n type SurveyAssistantThreadDocumentRepository,\n} from \"./thread-documents\";\n\nconst DEFAULT_MAX_STEPS = 48;\nconst DEFAULT_MAX_CONCURRENT_REQUESTS = 4;\nconst DEFAULT_MAX_DURATION_MS = 3 * 60 * 1_000;\nconst MAX_USER_ERROR_MESSAGE_LENGTH = 700;\nconst MAX_ERROR_CAUSE_DEPTH = 5;\n\ntype ProviderOptions = AgentExecutionOptions[\"providerOptions\"];\n\nexport interface SurveyAssistantAuthorizationInput {\n request: Request;\n requestedResourceId?: string;\n requestedThreadId?: string;\n requestedSurveyId?: string;\n}\n\nexport type SurveyAssistantAuthorizedIdentity = SurveyAssistantIdentityContext;\n\nexport type SurveyAssistantAuthorize = (\n input: SurveyAssistantAuthorizationInput,\n) => Promise<SurveyAssistantAuthorizedIdentity> | SurveyAssistantAuthorizedIdentity;\n\nexport interface SurveyAssistantModelResolutionInput {\n identity: SurveyAssistantAuthorizedIdentity;\n request: Request;\n requestedModelId?: string;\n}\n\nexport type SurveyAssistantResolveModel = (\n input: SurveyAssistantModelResolutionInput,\n) =>\n | Promise<SurveyAssistantModelSelection | null | undefined>\n | SurveyAssistantModelSelection\n | null\n | undefined;\n\nexport class SurveyAssistantAuthorizationError extends Error {\n readonly status: number;\n\n constructor(message = \"The survey assistant request is not authorized.\", status = 403) {\n super(message);\n this.name = \"SurveyAssistantAuthorizationError\";\n this.status = status;\n }\n}\n\nexport interface SurveyAssistantTurnMetrics {\n snapshotBytes: number;\n toolCalls: number;\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n}\n\nexport interface SurveyAssistantStreamErrorContext {\n resourceId: string;\n surveyId?: string;\n threadId: string;\n}\n\nexport interface SurveyAssistantChatHandlerOptions {\n mastra: Mastra | (() => Mastra | Promise<Mastra>);\n authorize: SurveyAssistantAuthorize;\n activeTools: readonly string[];\n agentId?: string;\n trustedCapabilities?: SurveyAssistantCapabilitySet;\n isEnabled?: (request: Request) => boolean | Promise<boolean>;\n getReadinessError?: (request: Request) => string | null | Promise<string | null>;\n maxConcurrentRequests?: number;\n maxDurationMs?: number;\n maxSteps?: number;\n providerName?: string;\n providerOptions?: ProviderOptions;\n sendReasoning?: boolean;\n formatStreamError?: (error: unknown, context: SurveyAssistantStreamErrorContext) => string;\n onStreamError?: (error: unknown, context: SurveyAssistantStreamErrorContext) => void;\n onTurnMetrics?: (input: {\n identity: SurveyAssistantAuthorizedIdentity;\n metrics: SurveyAssistantTurnMetrics;\n provider?: string;\n turnId: string;\n }) => void;\n /** Optional overrides; omitted values use the safe package defaults. */\n requestLimits?: Partial<SurveyAssistantRequestLimits>;\n resolveModel?: SurveyAssistantResolveModel;\n threadDocuments?: SurveyAssistantThreadDocumentRepository;\n}\n\ntype SurveyAssistantChatParams = {\n messages?: unknown;\n snapshot?: unknown;\n previousSettledVersion?: unknown;\n id?: unknown;\n resourceId?: unknown;\n modelId?: unknown;\n threadId?: unknown;\n surveyId?: unknown;\n trigger?: \"submit-message\" | \"regenerate-message\";\n} & Record<string, unknown>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst optionalRequestedId = (value: unknown) => {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.trim();\n return normalized.length > 0 && normalized.length <= 240 ? normalized : undefined;\n};\n\nconst validateAuthorizedIdentity = (\n identity: SurveyAssistantAuthorizedIdentity,\n): SurveyAssistantAuthorizedIdentity => {\n if (\n !identity ||\n (identity.authority !== \"authenticated-host\" &&\n identity.authority !== \"experimental-client-request\") ||\n typeof identity.resourceId !== \"string\" ||\n identity.resourceId.trim().length === 0 ||\n identity.resourceId.length > 240 ||\n (identity.surveyId !== undefined && !optionalRequestedId(identity.surveyId)) ||\n typeof identity.threadId !== \"string\" ||\n identity.threadId.trim().length === 0 ||\n identity.threadId.length > 240\n ) {\n throw new SurveyAssistantAuthorizationError(\n \"The host returned an invalid survey assistant identity.\",\n 500,\n );\n }\n\n return {\n authority: identity.authority,\n resourceId: identity.resourceId.trim(),\n ...(identity.surveyId ? { surveyId: optionalRequestedId(identity.surveyId) } : {}),\n threadId: identity.threadId.trim(),\n };\n};\n\nconst validateModelSelection = (\n selection: SurveyAssistantModelSelection | null | undefined,\n): SurveyAssistantModelSelection | null => {\n if (!selection) return null;\n const id = optionalRequestedId(selection.id);\n const model = optionalRequestedId(selection.model);\n return id && model ? { id, model } : null;\n};\n\nconst readNumber = (value: unknown): number | undefined =>\n typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n\nconst recordTurnStreamPart = (metrics: SurveyAssistantTurnMetrics, part: unknown) => {\n if (!isRecord(part)) return;\n if (part.type === \"tool-call\") metrics.toolCalls += 1;\n if (part.type !== \"finish\") return;\n\n const payload = isRecord(part.payload) ? part.payload : undefined;\n const output = isRecord(payload?.output) ? payload.output : undefined;\n const usage = isRecord(output?.usage) ? output.usage : undefined;\n if (!usage) return;\n\n metrics.inputTokens = readNumber(usage.inputTokens);\n metrics.outputTokens = readNumber(usage.outputTokens);\n metrics.totalTokens = readNumber(usage.totalTokens);\n metrics.reasoningTokens = readNumber(usage.reasoningTokens);\n metrics.cachedInputTokens = readNumber(usage.cachedInputTokens);\n};\n\nconst unwrapStreamError = (error: unknown) => {\n if (isRecord(error) && error.error !== undefined) return error.error;\n return error;\n};\n\nconst describeErrorLevel = (value: unknown): Record<string, unknown> => {\n if (value instanceof Error) {\n const properties = Object.fromEntries(\n Object.getOwnPropertyNames(value)\n .filter((key) => key !== \"stack\")\n .map((key) => [key, (value as unknown as Record<string, unknown>)[key]]),\n );\n return { name: value.name, message: value.message, stack: value.stack, ...properties };\n }\n\n if (isRecord(value)) {\n return {\n constructorName: value.constructor?.name,\n ...Object.fromEntries(Object.getOwnPropertyNames(value).map((key) => [key, value[key]])),\n };\n }\n\n return { value };\n};\n\nexport const describeSurveyAssistantStreamError = (error: unknown) => {\n const levels: Record<string, unknown>[] = [];\n let current: unknown = unwrapStreamError(error);\n\n for (\n let depth = 0;\n depth < MAX_ERROR_CAUSE_DEPTH && current !== undefined && current !== null;\n depth += 1\n ) {\n const level = describeErrorLevel(current);\n levels.push(level);\n current = level.cause;\n }\n\n return levels.length === 1 ? levels[0] : { levels };\n};\n\nconst clampUserErrorMessage = (message: string) => {\n const normalized = message.replace(/\\s+/g, \" \").trim();\n return normalized.length <= MAX_USER_ERROR_MESSAGE_LENGTH\n ? normalized\n : `${normalized.slice(0, MAX_USER_ERROR_MESSAGE_LENGTH - 1)}…`;\n};\n\nconst getProviderModelName = (message: string) =>\n message.match(/\\bmodel\\s+[`'\"]([^`'\"]+)[`'\"]/i)?.[1]?.trim();\n\nconst classifyStreamError = (message: string, statusCode?: number): SurveyAssistantErrorCode => {\n if (statusCode === 401 || statusCode === 403) return \"authorization_failed\";\n if (statusCode === 429) return \"rate_limited\";\n if (\n /\\bmodel\\b[\\s\\S]{0,160}\\b(?:limited preview|not available)\\b/i.test(message) ||\n /\\blimited preview\\b[\\s\\S]{0,160}\\bmodel\\b/i.test(message)\n ) {\n return \"model_unavailable\";\n }\n if (/\\b(?:rate limit|too many requests|request limit)\\b/i.test(message)) {\n return \"rate_limited\";\n }\n if (\n /\\b(?:invalid api key|incorrect api key|authentication|unauthorized|forbidden)\\b/i.test(message)\n ) {\n return \"authorization_failed\";\n }\n if (\n /\\b(?:attachment|document|file)\\b[\\s\\S]{0,120}\\b(?:missing|unavailable|not found|failed)\\b/i.test(\n message,\n )\n ) {\n return \"attachment_unavailable\";\n }\n return \"request_failed\";\n};\n\nconst formatKnownProviderError = (message: string) => {\n const normalized = clampUserErrorMessage(message);\n if (\n /\\bmodel\\b[\\s\\S]{0,160}\\b(?:limited preview|not available)\\b/i.test(normalized) ||\n /\\blimited preview\\b[\\s\\S]{0,160}\\bmodel\\b/i.test(normalized)\n ) {\n const modelName = getProviderModelName(normalized);\n return `The configured model${modelName ? ` \\`${modelName}\\`` : \"\"} is not available on this account.`;\n }\n if (/\\b(?:rate limit|too many requests|request limit)\\b/i.test(normalized)) {\n return \"The AI provider is temporarily limiting requests.\";\n }\n if (\n /\\b(?:invalid api key|incorrect api key|authentication|unauthorized|forbidden)\\b/i.test(\n normalized,\n )\n ) {\n return \"The AI provider rejected the assistant credentials.\";\n }\n return \"The AI provider could not complete the request.\";\n};\n\nconst parseProviderResponseBodyMessage = (responseBody: unknown) => {\n if (typeof responseBody !== \"string\" || responseBody.trim().length === 0) return undefined;\n try {\n const parsed = JSON.parse(responseBody) as unknown;\n if (isRecord(parsed) && isRecord(parsed.error) && typeof parsed.error.message === \"string\") {\n return parsed.error.message;\n }\n } catch {\n return responseBody;\n }\n return responseBody;\n};\n\nconst extractUserErrorMessageFromLevel = (level: Record<string, unknown>) => {\n if (\n isRecord(level.data) &&\n isRecord(level.data.error) &&\n typeof level.data.error.message === \"string\"\n ) {\n return level.data.error.message;\n }\n return (\n parseProviderResponseBodyMessage(level.responseBody) ??\n (typeof level.message === \"string\" ? level.message : undefined)\n );\n};\n\nexport const formatSurveyAssistantStreamError = (error: unknown) => {\n const describedError = describeSurveyAssistantStreamError(error);\n const levels = Array.isArray(describedError.levels) ? describedError.levels : [describedError];\n const messageLevel = levels.find(\n (level): level is Record<string, unknown> =>\n isRecord(level) && Boolean(extractUserErrorMessageFromLevel(level)),\n );\n const statusLevel = levels.find(\n (level): level is Record<string, unknown> => isRecord(level) && level.statusCode !== undefined,\n );\n const statusCode = Number(statusLevel?.statusCode);\n const detail = messageLevel ? extractUserErrorMessageFromLevel(messageLevel) : undefined;\n const formattedDetail = detail ? formatKnownProviderError(detail) : undefined;\n return serializeSurveyAssistantError({\n code: classifyStreamError(\n formattedDetail ?? \"\",\n Number.isFinite(statusCode) ? statusCode : undefined,\n ),\n ...(formattedDetail ? { detail: formattedDetail } : {}),\n });\n};\n\nconst requestErrorResponse = (error: unknown, fallback: string) => {\n const requestError =\n error instanceof SurveyAssistantRequestError\n ? error\n : new SurveyAssistantRequestError(fallback);\n return Response.json(\n { code: \"request_failed\", error: requestError.message },\n { status: requestError.status },\n );\n};\n\nexport const createSurveyAssistantChatHandler = (options: SurveyAssistantChatHandlerOptions) => {\n const requestLimits: SurveyAssistantRequestLimits = {\n ...SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS,\n ...options.requestLimits,\n };\n for (const [name, value] of Object.entries(requestLimits)) {\n if (\n name !== \"acceptedAttachmentTypes\" &&\n (!Number.isSafeInteger(value) || (value as number) <= 0)\n ) {\n throw new Error(`Survey assistant request limit \"${name}\" must be a positive integer.`);\n }\n }\n if (typeof requestLimits.acceptedAttachmentTypes?.has !== \"function\") {\n throw new Error(\"Survey assistant accepted attachment types must be a set.\");\n }\n const maxConcurrentRequests =\n Number.isInteger(options.maxConcurrentRequests) && (options.maxConcurrentRequests ?? 0) > 0\n ? options.maxConcurrentRequests!\n : DEFAULT_MAX_CONCURRENT_REQUESTS;\n const maxSteps =\n Number.isInteger(options.maxSteps) && (options.maxSteps ?? 0) > 0\n ? options.maxSteps!\n : DEFAULT_MAX_STEPS;\n const maxDurationMs =\n Number.isFinite(options.maxDurationMs) && (options.maxDurationMs ?? 0) > 0\n ? options.maxDurationMs!\n : DEFAULT_MAX_DURATION_MS;\n let activeRequestCount = 0;\n\n const acquireRequestSlot = () => {\n if (activeRequestCount >= maxConcurrentRequests) return null;\n activeRequestCount += 1;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n activeRequestCount = Math.max(0, activeRequestCount - 1);\n };\n };\n\n return async (request: Request): Promise<Response> => {\n if (options.isEnabled && !(await options.isEnabled(request))) {\n return Response.json(\n { code: \"authorization_failed\", error: \"Survey assistant is disabled.\" },\n { status: 404 },\n );\n }\n\n let rawParams: unknown;\n try {\n rawParams = await readSurveyAssistantRequestJson(request, requestLimits.maxRequestBytes);\n } catch (error) {\n return requestErrorResponse(error, \"The survey assistant request could not be read.\");\n }\n\n const params = isRecord(rawParams)\n ? (rawParams as SurveyAssistantChatParams)\n : ({} satisfies SurveyAssistantChatParams);\n\n let identity: SurveyAssistantAuthorizedIdentity;\n try {\n identity = validateAuthorizedIdentity(\n await options.authorize({\n request,\n requestedResourceId: optionalRequestedId(params.resourceId),\n requestedThreadId: optionalRequestedId(params.threadId ?? params.id),\n requestedSurveyId: optionalRequestedId(params.surveyId),\n }),\n );\n } catch (error) {\n const authorizationError =\n error instanceof SurveyAssistantAuthorizationError\n ? error\n : new SurveyAssistantAuthorizationError();\n return Response.json(\n { code: \"authorization_failed\", error: authorizationError.message },\n { status: authorizationError.status },\n );\n }\n\n let messages: UIMessage[];\n try {\n messages = validateSurveyAssistantMessages(params.messages, requestLimits);\n } catch (error) {\n return requestErrorResponse(error, \"The survey assistant messages are invalid.\");\n }\n\n let modelSelection: SurveyAssistantModelSelection | undefined;\n if (options.resolveModel) {\n const requestedModelId = optionalRequestedId(params.modelId);\n if (params.modelId !== undefined && !requestedModelId) {\n return Response.json(\n { code: \"model_unavailable\", error: \"Invalid survey assistant model selection.\" },\n { status: 400 },\n );\n }\n\n try {\n modelSelection =\n validateModelSelection(\n await options.resolveModel({ identity, request, requestedModelId }),\n ) ?? undefined;\n } catch (error) {\n options.onStreamError?.(error, {\n resourceId: identity.resourceId,\n surveyId: identity.surveyId,\n threadId: identity.threadId,\n });\n return Response.json(\n {\n code: \"model_unavailable\",\n error: \"The survey assistant model catalog is unavailable.\",\n },\n { status: 503 },\n );\n }\n if (!modelSelection) {\n return Response.json(\n {\n code: \"model_unavailable\",\n error: \"The selected survey assistant model is not available.\",\n },\n { status: 400 },\n );\n }\n }\n\n let snapshot: SurveyDocumentSnapshot | null;\n let previousSettledVersion;\n try {\n snapshot = parseSurveyAssistantSnapshot(params.snapshot);\n previousSettledVersion = parseSurveyAssistantPreviousSettledVersion(\n params.previousSettledVersion,\n );\n } catch {\n return Response.json(\n { code: \"request_failed\", error: \"Invalid survey assistant document context.\" },\n { status: 400 },\n );\n }\n\n const readinessError = await options.getReadinessError?.(request);\n if (readinessError) {\n return Response.json({ code: \"request_failed\", error: readinessError }, { status: 503 });\n }\n\n const releaseRequestSlot = acquireRequestSlot();\n if (!releaseRequestSlot) {\n return Response.json(\n {\n code: \"rate_limited\",\n error: \"The survey assistant is handling too many requests. Try again shortly.\",\n },\n { status: 429 },\n );\n }\n\n const changeCollector = new TurnChangeCollector();\n let requestContext;\n try {\n let attachmentAccess: SurveyAssistantAttachmentAccess | undefined;\n let modelAttachments: SurveyAssistantModelAttachment[] = [];\n if (options.threadDocuments) {\n const repository = options.threadDocuments;\n const registered = await registerSurveyAssistantThreadAttachments({\n messages,\n repository,\n resourceId: identity.resourceId,\n threadId: identity.threadId,\n });\n const manifest = await repository.list({\n resourceId: identity.resourceId,\n threadId: identity.threadId,\n });\n modelAttachments = await loadSurveyAssistantDocuments({\n documents: registered,\n repository,\n resourceId: identity.resourceId,\n threadId: identity.threadId,\n });\n messages = replaceCurrentSurveyAssistantAttachmentsWithReferences({\n documents: registered,\n messages,\n });\n messages = removeHistoricalSurveyAssistantAttachments(messages);\n const registeredIds = new Set(registered.map((document) => document.id));\n const replayableDocuments = getSurveyAssistantReplayableDocuments(\n manifest,\n registeredIds,\n );\n const documents = [...registered, ...replayableDocuments];\n if (documents.length > 0) {\n attachmentAccess = {\n documents,\n currentDocumentIds: registered.map((document) => document.id),\n selectedAttachments: [],\n load: (documentIds) =>\n loadSurveyAssistantDocuments({\n documents: documents.filter((document) => documentIds.includes(document.id)),\n repository,\n resourceId: identity.resourceId,\n threadId: identity.threadId,\n }),\n };\n }\n }\n requestContext = buildSurveyAssistantRequestContext({\n changeCollector,\n identity,\n snapshot,\n previousSettledVersion,\n trustedCapabilities: options.trustedCapabilities,\n attachmentAccess,\n modelAttachments,\n modelSelection,\n });\n } catch (error) {\n releaseRequestSlot();\n if (error instanceof SurveyAssistantRequestError) {\n return requestErrorResponse(error, \"The thread attachments could not be prepared.\");\n }\n if (error instanceof SurveyAssistantCapabilityMismatchError) {\n return Response.json({ error: error.message, code: error.code }, { status: 409 });\n }\n options.onStreamError?.(error, {\n resourceId: identity.resourceId,\n surveyId: identity.surveyId,\n threadId: identity.threadId,\n });\n return Response.json(\n {\n code: \"attachment_unavailable\",\n error: \"The survey assistant could not prepare the thread's reference documents.\",\n },\n { status: 503 },\n );\n }\n\n const metrics: SurveyAssistantTurnMetrics = {\n snapshotBytes: snapshot ? new TextEncoder().encode(JSON.stringify(snapshot)).byteLength : 0,\n toolCalls: 0,\n };\n const streamErrorContext = {\n resourceId: identity.resourceId,\n surveyId: identity.surveyId,\n threadId: identity.threadId,\n };\n const timeoutSignal = AbortSignal.timeout(maxDurationMs);\n const executionSignal = AbortSignal.any([request.signal, timeoutSignal]);\n const handleStreamError = (error: unknown) => {\n options.onStreamError?.(error, streamErrorContext);\n return (\n options.formatStreamError?.(error, streamErrorContext) ??\n formatSurveyAssistantStreamError(error)\n );\n };\n\n let stream: ReadableStream<UIMessageChunk>;\n try {\n const mastra = typeof options.mastra === \"function\" ? await options.mastra() : options.mastra;\n stream = (await handleChatStream({\n mastra,\n agentId: options.agentId ?? SURVEY_ASSISTANT_AGENT_ID,\n version: \"v6\",\n params: {\n messages,\n ...(params.trigger ? { trigger: params.trigger } : {}),\n requestContext,\n runId: changeCollector.turnId,\n abortSignal: executionSignal,\n onAbort: () => {\n changeCollector.finalize(\"aborted\");\n },\n onError: () => {\n changeCollector.finalize(\"failed\");\n },\n memory: {\n thread: identity.threadId,\n resource: identity.resourceId,\n },\n },\n defaultOptions: {\n activeTools: [...options.activeTools],\n maxSteps,\n ...(options.providerOptions ? { providerOptions: options.providerOptions } : {}),\n },\n sendReasoning: options.sendReasoning ?? true,\n messageMetadata: ({ part }: { part: unknown }) => {\n recordTurnStreamPart(metrics, part);\n return undefined;\n },\n onError: (error: unknown) => {\n changeCollector.finalize(\"failed\");\n return handleStreamError(error);\n },\n } as never)) as ReadableStream<UIMessageChunk>;\n } catch (error) {\n changeCollector.finalize(executionSignal.aborted ? \"aborted\" : \"failed\");\n releaseRequestSlot();\n return Response.json({ error: handleStreamError(error) }, { status: 503 });\n }\n\n const turnStream = createSurveyAssistantTurnStream({\n stream,\n changeCollector,\n signal: executionSignal,\n onUnexpectedError: handleStreamError,\n onSettled: () => {\n releaseRequestSlot();\n try {\n options.onTurnMetrics?.({\n identity,\n metrics,\n provider: options.providerName,\n turnId: changeCollector.turnId,\n });\n } catch (error) {\n try {\n options.onStreamError?.(error, streamErrorContext);\n } catch {\n // Host observability hooks must not break an already-settled stream.\n }\n }\n },\n });\n\n return createUIMessageStreamResponse({ stream: turnStream });\n };\n};\n","import {\n createSurveyAssistantThreadInputSchema,\n updateSurveyAssistantThreadInputSchema,\n type SurveyAssistantThread,\n} from \"../protocol\";\nimport { readSurveyAssistantRequestJson, SurveyAssistantRequestError } from \"./request-guardrails\";\n\nconst DEFAULT_TITLE = \"New chat\";\nconst MAX_THREAD_MUTATION_BYTES = 16 * 1024;\nconst MANUAL_TITLE_METADATA_KEY = \"surveyAssistant.manualTitle\";\nconst AUTOMATIC_TITLE_METADATA_KEY = \"surveyAssistant.automaticTitle\";\n\nexport interface SurveyAssistantThreadIdentity {\n resourceId: string;\n metadata: Record<string, string>;\n}\n\nexport interface CreateSurveyAssistantThreadHandlersOptions {\n authorize(\n request: Request,\n ): SurveyAssistantThreadIdentity | Promise<SurveyAssistantThreadIdentity>;\n repository: SurveyAssistantThreadRepository;\n}\n\nexport interface SurveyAssistantStoredThread {\n id: string;\n title?: string;\n resourceId: string;\n createdAt: Date;\n updatedAt: Date;\n metadata?: Record<string, unknown>;\n}\n\nexport interface SurveyAssistantThreadRepository {\n create(input: {\n resourceId: string;\n title: string;\n metadata: Record<string, unknown>;\n }): Promise<SurveyAssistantStoredThread>;\n get(threadId: string): Promise<SurveyAssistantStoredThread | null>;\n list(\n resourceId: string,\n metadata: Record<string, string>,\n ): Promise<SurveyAssistantStoredThread[]>;\n readMessages(threadId: string, resourceId: string): Promise<unknown[]>;\n delete(threadId: string): Promise<void>;\n update(input: {\n id: string;\n title: string;\n metadata: Record<string, unknown>;\n }): Promise<SurveyAssistantStoredThread>;\n}\n\nconst getMetadataString = (metadata: Record<string, unknown> | undefined, key: string) => {\n const value = metadata?.[key];\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n};\n\nconst getRawTitle = (thread: SurveyAssistantStoredThread) => thread.title?.trim() || DEFAULT_TITLE;\n\nconst getAutomaticTitle = (thread: SurveyAssistantStoredThread, manualTitle: string): string => {\n const rawTitle = thread.title?.trim();\n if (rawTitle && rawTitle !== manualTitle) return rawTitle;\n return getMetadataString(thread.metadata, AUTOMATIC_TITLE_METADATA_KEY) ?? DEFAULT_TITLE;\n};\n\nconst asThread = (thread: SurveyAssistantStoredThread): SurveyAssistantThread => {\n const manualTitle = getMetadataString(thread.metadata, MANUAL_TITLE_METADATA_KEY);\n\n return {\n id: thread.id,\n title: manualTitle ?? getRawTitle(thread),\n titleMode: manualTitle ? \"manual\" : \"automatic\",\n createdAt: thread.createdAt.toISOString(),\n updatedAt: thread.updatedAt.toISOString(),\n };\n};\n\nconst json = (body: unknown, status = 200) => Response.json(body, { status });\n\nconst errorResponse = (error: unknown) => {\n const status =\n typeof error === \"object\" &&\n error !== null &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ? error.status\n : error instanceof SurveyAssistantRequestError ||\n error instanceof SyntaxError ||\n (error instanceof Error && error.name === \"ZodError\")\n ? 400\n : 500;\n const message =\n error instanceof SurveyAssistantRequestError\n ? error.message\n : status === 401 || status === 403 || status === 404\n ? status === 404\n ? \"Chat not found.\"\n : \"The chat request is not authorized.\"\n : status >= 500\n ? \"The chat request could not be completed.\"\n : \"The chat request is invalid.\";\n return json(\n {\n code:\n status === 401 || status === 403 || status === 404\n ? \"authorization_failed\"\n : \"request_failed\",\n error: message,\n },\n status,\n );\n};\n\nconst ownsThread = (thread: SurveyAssistantStoredThread, identity: SurveyAssistantThreadIdentity) =>\n thread.resourceId === identity.resourceId &&\n Object.entries(identity.metadata).every(([key, value]) => thread.metadata?.[key] === value);\n\nconst getOwnedThread = async (\n repository: SurveyAssistantThreadRepository,\n identity: SurveyAssistantThreadIdentity,\n threadId: string,\n) => {\n const thread = await repository.get(threadId);\n return thread && ownsThread(thread, identity) ? thread : null;\n};\n\nexport const createSurveyAssistantThreadHandlers = ({\n authorize,\n repository,\n}: CreateSurveyAssistantThreadHandlersOptions) => {\n const list = async (request: Request) => {\n try {\n const identity = await authorize(request);\n const storedThreads = await repository.list(identity.resourceId, identity.metadata);\n const threads = storedThreads\n .filter(\n (thread) =>\n ownsThread(thread, identity) && typeof thread.metadata?.archivedAt !== \"string\",\n )\n .map(asThread);\n return json({ threads });\n } catch (error) {\n return errorResponse(error);\n }\n };\n\n const create = async (request: Request) => {\n try {\n const [identity, input] = await Promise.all([\n authorize(request),\n readSurveyAssistantRequestJson(request, MAX_THREAD_MUTATION_BYTES),\n ]);\n const parsed = createSurveyAssistantThreadInputSchema.parse(input);\n const metadata = { ...identity.metadata };\n if (parsed.title) {\n metadata[MANUAL_TITLE_METADATA_KEY] = parsed.title;\n metadata[AUTOMATIC_TITLE_METADATA_KEY] = DEFAULT_TITLE;\n }\n const thread = await repository.create({\n resourceId: identity.resourceId,\n title: parsed.title ?? DEFAULT_TITLE,\n metadata,\n });\n return json({ thread: asThread(thread) }, 201);\n } catch (error) {\n return errorResponse(error);\n }\n };\n\n const read = async (request: Request, threadId: string) => {\n try {\n const identity = await authorize(request);\n const thread = await getOwnedThread(repository, identity, threadId);\n if (!thread || typeof thread.metadata?.archivedAt === \"string\") {\n return json({ error: \"Chat not found.\" }, 404);\n }\n return json({\n thread: asThread(thread),\n messages: await repository.readMessages(threadId, identity.resourceId),\n });\n } catch (error) {\n return errorResponse(error);\n }\n };\n\n const update = async (request: Request, threadId: string) => {\n try {\n const [identity, input] = await Promise.all([\n authorize(request),\n readSurveyAssistantRequestJson(request, MAX_THREAD_MUTATION_BYTES),\n ]);\n const thread = await getOwnedThread(repository, identity, threadId);\n if (!thread || typeof thread.metadata?.archivedAt === \"string\") {\n return json({ error: \"Chat not found.\" }, 404);\n }\n const parsed = updateSurveyAssistantThreadInputSchema.parse(input);\n const metadata = { ...(thread.metadata ?? identity.metadata) };\n let title: string;\n\n if (parsed.mode === \"automatic\") {\n const manualTitle = getMetadataString(metadata, MANUAL_TITLE_METADATA_KEY);\n title =\n parsed.title ??\n (manualTitle ? getAutomaticTitle(thread, manualTitle) : getRawTitle(thread));\n metadata[MANUAL_TITLE_METADATA_KEY] = null;\n metadata[AUTOMATIC_TITLE_METADATA_KEY] = null;\n } else {\n const previousManualTitle = getMetadataString(metadata, MANUAL_TITLE_METADATA_KEY);\n const automaticTitle = previousManualTitle\n ? getAutomaticTitle(thread, previousManualTitle)\n : getRawTitle(thread);\n title = parsed.title;\n metadata[MANUAL_TITLE_METADATA_KEY] = title;\n metadata[AUTOMATIC_TITLE_METADATA_KEY] = automaticTitle;\n }\n\n const updated = await repository.update({\n id: thread.id,\n title,\n metadata,\n });\n return json({ thread: asThread(updated) });\n } catch (error) {\n return errorResponse(error);\n }\n };\n\n const deleteThread = async (request: Request, threadId: string) => {\n try {\n const identity = await authorize(request);\n const thread = await getOwnedThread(repository, identity, threadId);\n if (!thread) return json({ error: \"Chat not found.\" }, 404);\n await repository.delete(thread.id);\n return new Response(null, { status: 204 });\n } catch (error) {\n return errorResponse(error);\n }\n };\n\n return { create, delete: deleteThread, list, read, update };\n};\n","import { toAISdkMessages } from \"@mastra/ai-sdk/ui\";\nimport type { MastraMemory } from \"@mastra/core/memory\";\nimport type { SurveyAssistantThreadDocumentRepository } from \"./thread-documents\";\nimport type { SurveyAssistantThreadRepository } from \"./thread-handlers\";\n\ntype ThreadMemory = Pick<\n MastraMemory,\n \"createThread\" | \"deleteThread\" | \"getThreadById\" | \"listThreads\" | \"recall\" | \"updateThread\"\n>;\n\nexport interface CreateMastraMemoryThreadRepositoryOptions {\n getMemory(): ThreadMemory | Promise<ThreadMemory>;\n /** Maximum recent messages returned when hydrating a transcript. */\n maxMessagesPerTranscript?: number;\n /** Maximum recent threads returned by the history endpoint. */\n maxThreadsPerList?: number;\n threadDocuments?: SurveyAssistantThreadDocumentRepository;\n}\n\nexport const createMastraMemoryThreadRepository = ({\n getMemory,\n maxMessagesPerTranscript = 1_000,\n maxThreadsPerList = 100,\n threadDocuments,\n}: CreateMastraMemoryThreadRepositoryOptions): SurveyAssistantThreadRepository => ({\n async create({ metadata, resourceId, title }) {\n return (await getMemory()).createThread({ metadata, resourceId, title, saveThread: true });\n },\n async get(threadId) {\n return (await getMemory()).getThreadById({ threadId });\n },\n async list(resourceId, metadata) {\n const result = await (\n await getMemory()\n ).listThreads({\n filter: { metadata, resourceId },\n orderBy: { field: \"updatedAt\", direction: \"DESC\" },\n perPage: Math.max(1, Math.min(1_000, maxThreadsPerList)),\n });\n return result.threads;\n },\n async readMessages(threadId, resourceId) {\n const result = await (\n await getMemory()\n ).recall({\n threadId,\n resourceId,\n perPage: Math.max(1, Math.min(5_000, maxMessagesPerTranscript)),\n orderBy: { field: \"createdAt\", direction: \"DESC\" },\n });\n return toAISdkMessages([...result.messages].reverse(), { version: \"v6\" });\n },\n async delete(threadId) {\n const memory = await getMemory();\n const thread = await memory.getThreadById({ threadId });\n if (thread && threadDocuments) {\n await threadDocuments.deleteThread({ resourceId: thread.resourceId, threadId });\n }\n await memory.deleteThread(threadId);\n },\n async update({ id, metadata, title }) {\n return (await getMemory()).updateThread({ id, metadata, title });\n },\n});\n"],"mappings":"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"}
|