@case-framework/survey-assistant 0.1.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 +212 -0
- package/dist/authoring-references-Cy8J-DWt.d.mts +48 -0
- package/dist/authoring-references-Cy8J-DWt.d.mts.map +1 -0
- package/dist/authoring-references-X6uRaWuf.mjs +68 -0
- package/dist/authoring-references-X6uRaWuf.mjs.map +1 -0
- package/dist/capabilities-B6tvE7fK.mjs +66 -0
- package/dist/capabilities-B6tvE7fK.mjs.map +1 -0
- package/dist/capabilities-DvSKtjAw.d.mts +182 -0
- package/dist/capabilities-DvSKtjAw.d.mts.map +1 -0
- package/dist/capabilities-default.d.mts +15 -0
- package/dist/capabilities-default.d.mts.map +1 -0
- package/dist/capabilities-default.mjs +347 -0
- package/dist/capabilities-default.mjs.map +1 -0
- package/dist/constants-CFUWrRtl.mjs +7 -0
- package/dist/constants-CFUWrRtl.mjs.map +1 -0
- package/dist/constants-Jw0k8ojS.d.mts +67 -0
- package/dist/constants-Jw0k8ojS.d.mts.map +1 -0
- package/dist/controller-proxy-BRDusFiO.d.mts +57 -0
- package/dist/controller-proxy-BRDusFiO.d.mts.map +1 -0
- package/dist/controller-proxy-Duxo2IFE.mjs +92 -0
- package/dist/controller-proxy-Duxo2IFE.mjs.map +1 -0
- package/dist/digest-BsoVq49o.d.mts +9 -0
- package/dist/digest-BsoVq49o.d.mts.map +1 -0
- package/dist/digest.d.mts +2 -0
- package/dist/digest.mjs +20 -0
- package/dist/digest.mjs.map +1 -0
- package/dist/engine-sPvuixWg.mjs +4857 -0
- package/dist/engine-sPvuixWg.mjs.map +1 -0
- package/dist/engine.d.mts +3 -0
- package/dist/engine.mjs +4 -0
- package/dist/index-BWNHkpkh.d.mts +187 -0
- package/dist/index-BWNHkpkh.d.mts.map +1 -0
- package/dist/index-CUqbBjkc.d.mts +641 -0
- package/dist/index-CUqbBjkc.d.mts.map +1 -0
- package/dist/index-MpEMQzTm.d.mts +123 -0
- package/dist/index-MpEMQzTm.d.mts.map +1 -0
- package/dist/index-TFtvBiSQ.d.mts +1149 -0
- package/dist/index-TFtvBiSQ.d.mts.map +1 -0
- package/dist/memory-thread-repository-CJkkVE2-.mjs +718 -0
- package/dist/memory-thread-repository-CJkkVE2-.mjs.map +1 -0
- package/dist/protocol-DV0ka9WT.mjs +1032 -0
- package/dist/protocol-DV0ka9WT.mjs.map +1 -0
- package/dist/protocol.d.mts +3 -0
- package/dist/protocol.mjs +2 -0
- package/dist/react-VuYe-cPh.mjs +521 -0
- package/dist/react-VuYe-cPh.mjs.map +1 -0
- package/dist/react-integration.d.mts +2 -0
- package/dist/react-integration.mjs +3 -0
- package/dist/react.d.mts +3 -0
- package/dist/react.mjs +4 -0
- package/dist/references/assistant-operations.md +287 -0
- package/dist/references/core-rules.md +11 -0
- package/dist/references/embedded-forms.md +80 -0
- package/dist/references/expressions.md +301 -0
- package/dist/references/item-types.md +364 -0
- package/dist/references/localization.md +25 -0
- package/dist/references/rich-text-content.md +193 -0
- package/dist/references/source-material-surveys.md +60 -0
- package/dist/references/survey-data-model.md +109 -0
- package/dist/references/survey-quality.md +23 -0
- package/dist/request-context-jcmXT_Q2.mjs +44 -0
- package/dist/request-context-jcmXT_Q2.mjs.map +1 -0
- package/dist/request-guardrails-C59IWnH8.mjs +73 -0
- package/dist/request-guardrails-C59IWnH8.mjs.map +1 -0
- package/dist/server-agent.d.mts +131 -0
- package/dist/server-agent.d.mts.map +1 -0
- package/dist/server-agent.mjs +229 -0
- package/dist/server-agent.mjs.map +1 -0
- package/dist/server-runtime.d.mts +123 -0
- package/dist/server-runtime.d.mts.map +1 -0
- package/dist/server-runtime.mjs +296 -0
- package/dist/server-runtime.mjs.map +1 -0
- package/dist/server-tasks.d.mts +78 -0
- package/dist/server-tasks.d.mts.map +1 -0
- package/dist/server-tasks.mjs +253 -0
- package/dist/server-tasks.mjs.map +1 -0
- package/dist/server-tools.d.mts +2 -0
- package/dist/server-tools.mjs +2 -0
- package/dist/server.d.mts +52 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +6 -0
- package/dist/storage-postgres.d.mts +37 -0
- package/dist/storage-postgres.d.mts.map +1 -0
- package/dist/storage-postgres.mjs +167 -0
- package/dist/storage-postgres.mjs.map +1 -0
- package/dist/thread-documents-DamIq9Od.d.mts +91 -0
- package/dist/thread-documents-DamIq9Od.d.mts.map +1 -0
- package/dist/thread-handlers-DpynUc64.d.mts +112 -0
- package/dist/thread-handlers-DpynUc64.d.mts.map +1 -0
- package/dist/tools-Hz2p7vyv.mjs +1879 -0
- package/dist/tools-Hz2p7vyv.mjs.map +1 -0
- package/dist/ui.css +2 -0
- package/dist/ui.d.mts +63 -0
- package/dist/ui.d.mts.map +1 -0
- package/dist/ui.mjs +2238 -0
- package/dist/ui.mjs.map +1 -0
- package/package.json +166 -0
- package/ui.css.d.ts +3 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import { D as createSurveyAssistantThreadInputSchema, Ot as serializeSurveyAssistantError, j as updateSurveyAssistantThreadInputSchema, n as SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE } from "./protocol-DV0ka9WT.mjs";
|
|
2
|
+
import { t as SurveyAssistantCapabilityMismatchError } from "./capabilities-B6tvE7fK.mjs";
|
|
3
|
+
import { o as TurnChangeCollector } from "./engine-sPvuixWg.mjs";
|
|
4
|
+
import "./constants-CFUWrRtl.mjs";
|
|
5
|
+
import { n as parseSurveyAssistantSnapshot, t as buildSurveyAssistantRequestContext } from "./request-context-jcmXT_Q2.mjs";
|
|
6
|
+
import { c as SurveyAssistantRequestError, l as readSurveyAssistantRequestJson, u as validateSurveyAssistantMessages } from "./request-guardrails-C59IWnH8.mjs";
|
|
7
|
+
import { createUIMessageStreamResponse } from "ai";
|
|
8
|
+
import { handleChatStream } from "@mastra/ai-sdk";
|
|
9
|
+
import { toAISdkMessages } from "@mastra/ai-sdk/ui";
|
|
10
|
+
//#region src/server/turn-stream.ts
|
|
11
|
+
const createTurnFinalChunk = (turnFinal) => ({
|
|
12
|
+
type: SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE,
|
|
13
|
+
id: turnFinal.turnId,
|
|
14
|
+
data: turnFinal
|
|
15
|
+
});
|
|
16
|
+
const createSurveyAssistantTurnStream = ({ changeCollector, onSettled, onUnexpectedError = () => "The survey assistant stream ended unexpectedly.", signal, stream }) => {
|
|
17
|
+
const reader = stream.getReader();
|
|
18
|
+
let settled = false;
|
|
19
|
+
const settle = () => {
|
|
20
|
+
if (settled) return;
|
|
21
|
+
settled = true;
|
|
22
|
+
onSettled?.();
|
|
23
|
+
};
|
|
24
|
+
return new ReadableStream({
|
|
25
|
+
async start(controller) {
|
|
26
|
+
let emittedTurnFinal = false;
|
|
27
|
+
let sawFinish = false;
|
|
28
|
+
let finishReason;
|
|
29
|
+
const emitTurnFinal = (status) => {
|
|
30
|
+
if (emittedTurnFinal) return;
|
|
31
|
+
emittedTurnFinal = true;
|
|
32
|
+
controller.enqueue(createTurnFinalChunk(changeCollector.finalize(status)));
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
while (true) {
|
|
36
|
+
const { done, value } = await reader.read();
|
|
37
|
+
if (done) break;
|
|
38
|
+
if (value.type === SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE) {
|
|
39
|
+
emitTurnFinal("failed");
|
|
40
|
+
controller.enqueue({
|
|
41
|
+
type: "error",
|
|
42
|
+
errorText: "The survey assistant returned an invalid duplicate turn final."
|
|
43
|
+
});
|
|
44
|
+
await reader.cancel("duplicate turn final");
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
if (value.type === "error") {
|
|
48
|
+
emitTurnFinal("failed");
|
|
49
|
+
controller.enqueue(value);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (value.type === "abort") {
|
|
53
|
+
emitTurnFinal("aborted");
|
|
54
|
+
controller.enqueue(value);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (value.type === "finish") {
|
|
58
|
+
sawFinish = true;
|
|
59
|
+
finishReason = value.finishReason;
|
|
60
|
+
}
|
|
61
|
+
controller.enqueue(value);
|
|
62
|
+
}
|
|
63
|
+
if (!emittedTurnFinal) emitTurnFinal(signal?.aborted ? "aborted" : sawFinish && (finishReason === void 0 || finishReason === "stop") ? "completed" : "failed");
|
|
64
|
+
controller.close();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
try {
|
|
67
|
+
emitTurnFinal(signal?.aborted ? "aborted" : "failed");
|
|
68
|
+
if (!signal?.aborted) controller.enqueue({
|
|
69
|
+
type: "error",
|
|
70
|
+
errorText: onUnexpectedError(error)
|
|
71
|
+
});
|
|
72
|
+
controller.close();
|
|
73
|
+
} catch {}
|
|
74
|
+
} finally {
|
|
75
|
+
reader.releaseLock();
|
|
76
|
+
settle();
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
async cancel(reason) {
|
|
80
|
+
changeCollector.finalize("aborted");
|
|
81
|
+
settle();
|
|
82
|
+
await reader.cancel(reason);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/server/thread-documents.ts
|
|
88
|
+
const parseFileDataUrl = (url) => {
|
|
89
|
+
const match = /^data:([^;,]+);base64,([A-Za-z0-9+/]*={0,2})$/.exec(url);
|
|
90
|
+
return match ? {
|
|
91
|
+
mediaType: match[1],
|
|
92
|
+
data: match[2]
|
|
93
|
+
} : null;
|
|
94
|
+
};
|
|
95
|
+
const getBase64Bytes = (data) => {
|
|
96
|
+
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
97
|
+
return Math.max(0, Math.floor(data.length * 3 / 4) - padding);
|
|
98
|
+
};
|
|
99
|
+
const registerSurveyAssistantThreadAttachments = async ({ messages, repository, resourceId, threadId }) => {
|
|
100
|
+
const registered = [];
|
|
101
|
+
const currentMessage = getCurrentUserMessage(messages);
|
|
102
|
+
for (const message of currentMessage ? [currentMessage] : []) for (const part of message.parts) {
|
|
103
|
+
if (part.type !== "file" || typeof part.url !== "string") continue;
|
|
104
|
+
const parsed = parseFileDataUrl(part.url);
|
|
105
|
+
if (!parsed) continue;
|
|
106
|
+
registered.push(await repository.put({
|
|
107
|
+
resourceId,
|
|
108
|
+
threadId,
|
|
109
|
+
name: part.filename?.trim() || "Attached document",
|
|
110
|
+
mediaType: parsed.mediaType,
|
|
111
|
+
data: parsed.data,
|
|
112
|
+
bytes: getBase64Bytes(parsed.data)
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
return registered;
|
|
116
|
+
};
|
|
117
|
+
const hasMatchingCurrentFile = (message, document) => message.parts.some((part) => part.type === "file" && part.filename === document.name && part.mediaType === document.mediaType);
|
|
118
|
+
const getCurrentUserMessage = (messages) => {
|
|
119
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
120
|
+
const message = messages[index];
|
|
121
|
+
if (message?.role === "user") return message;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const removeHistoricalSurveyAssistantAttachments = (messages) => {
|
|
125
|
+
const current = getCurrentUserMessage(messages);
|
|
126
|
+
return messages.map((message) => {
|
|
127
|
+
if (message === current) return message;
|
|
128
|
+
const parts = message.parts.filter((part) => part.type !== "file");
|
|
129
|
+
return parts.length === message.parts.length ? message : {
|
|
130
|
+
...message,
|
|
131
|
+
parts
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const getSurveyAssistantReplayableDocuments = (documents, messages) => {
|
|
136
|
+
const current = getCurrentUserMessage(messages);
|
|
137
|
+
return current ? documents.filter((document) => !hasMatchingCurrentFile(current, document)) : [];
|
|
138
|
+
};
|
|
139
|
+
const loadSurveyAssistantThreadAttachments = async ({ documents, messages, repository, resourceId, threadId }) => {
|
|
140
|
+
return loadSurveyAssistantDocuments({
|
|
141
|
+
documents: getSurveyAssistantReplayableDocuments(documents, messages),
|
|
142
|
+
repository,
|
|
143
|
+
resourceId,
|
|
144
|
+
threadId
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const loadSurveyAssistantDocuments = async ({ documents, repository, resourceId, threadId }) => {
|
|
148
|
+
const attachments = [];
|
|
149
|
+
for (const document of documents) {
|
|
150
|
+
const content = await repository.read({
|
|
151
|
+
resourceId,
|
|
152
|
+
threadId,
|
|
153
|
+
documentId: document.id
|
|
154
|
+
});
|
|
155
|
+
if (!content) continue;
|
|
156
|
+
attachments.push({
|
|
157
|
+
filename: content.name,
|
|
158
|
+
mediaType: content.mediaType,
|
|
159
|
+
data: content.data,
|
|
160
|
+
contentHash: content.contentHash
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return attachments;
|
|
164
|
+
};
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/server/handler.ts
|
|
167
|
+
const DEFAULT_MAX_STEPS = 48;
|
|
168
|
+
const DEFAULT_MAX_CONCURRENT_REQUESTS = 4;
|
|
169
|
+
const MAX_USER_ERROR_MESSAGE_LENGTH = 700;
|
|
170
|
+
const MAX_ERROR_CAUSE_DEPTH = 5;
|
|
171
|
+
var SurveyAssistantAuthorizationError = class extends Error {
|
|
172
|
+
status;
|
|
173
|
+
constructor(message = "The survey assistant request is not authorized.", status = 403) {
|
|
174
|
+
super(message);
|
|
175
|
+
this.name = "SurveyAssistantAuthorizationError";
|
|
176
|
+
this.status = status;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
180
|
+
const optionalRequestedId = (value) => {
|
|
181
|
+
if (typeof value !== "string") return void 0;
|
|
182
|
+
const normalized = value.trim();
|
|
183
|
+
return normalized.length > 0 && normalized.length <= 240 ? normalized : void 0;
|
|
184
|
+
};
|
|
185
|
+
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);
|
|
187
|
+
return {
|
|
188
|
+
authority: identity.authority,
|
|
189
|
+
resourceId: identity.resourceId.trim(),
|
|
190
|
+
threadId: identity.threadId.trim()
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const validateModelSelection = (selection) => {
|
|
194
|
+
if (!selection) return null;
|
|
195
|
+
const id = optionalRequestedId(selection.id);
|
|
196
|
+
const model = optionalRequestedId(selection.model);
|
|
197
|
+
return id && model ? {
|
|
198
|
+
id,
|
|
199
|
+
model
|
|
200
|
+
} : null;
|
|
201
|
+
};
|
|
202
|
+
const readNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
203
|
+
const recordTurnStreamPart = (metrics, part) => {
|
|
204
|
+
if (!isRecord(part)) return;
|
|
205
|
+
if (part.type === "tool-call") metrics.toolCalls += 1;
|
|
206
|
+
if (part.type !== "finish") return;
|
|
207
|
+
const payload = isRecord(part.payload) ? part.payload : void 0;
|
|
208
|
+
const output = isRecord(payload?.output) ? payload.output : void 0;
|
|
209
|
+
const usage = isRecord(output?.usage) ? output.usage : void 0;
|
|
210
|
+
if (!usage) return;
|
|
211
|
+
metrics.inputTokens = readNumber(usage.inputTokens);
|
|
212
|
+
metrics.outputTokens = readNumber(usage.outputTokens);
|
|
213
|
+
metrics.totalTokens = readNumber(usage.totalTokens);
|
|
214
|
+
metrics.reasoningTokens = readNumber(usage.reasoningTokens);
|
|
215
|
+
metrics.cachedInputTokens = readNumber(usage.cachedInputTokens);
|
|
216
|
+
};
|
|
217
|
+
const unwrapStreamError = (error) => {
|
|
218
|
+
if (isRecord(error) && error.error !== void 0) return error.error;
|
|
219
|
+
return error;
|
|
220
|
+
};
|
|
221
|
+
const describeErrorLevel = (value) => {
|
|
222
|
+
if (value instanceof Error) {
|
|
223
|
+
const properties = Object.fromEntries(Object.getOwnPropertyNames(value).filter((key) => key !== "stack").map((key) => [key, value[key]]));
|
|
224
|
+
return {
|
|
225
|
+
name: value.name,
|
|
226
|
+
message: value.message,
|
|
227
|
+
stack: value.stack,
|
|
228
|
+
...properties
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
if (isRecord(value)) return {
|
|
232
|
+
constructorName: value.constructor?.name,
|
|
233
|
+
...Object.fromEntries(Object.getOwnPropertyNames(value).map((key) => [key, value[key]]))
|
|
234
|
+
};
|
|
235
|
+
return { value };
|
|
236
|
+
};
|
|
237
|
+
const describeSurveyAssistantStreamError = (error) => {
|
|
238
|
+
const levels = [];
|
|
239
|
+
let current = unwrapStreamError(error);
|
|
240
|
+
for (let depth = 0; depth < MAX_ERROR_CAUSE_DEPTH && current !== void 0 && current !== null; depth += 1) {
|
|
241
|
+
const level = describeErrorLevel(current);
|
|
242
|
+
levels.push(level);
|
|
243
|
+
current = level.cause;
|
|
244
|
+
}
|
|
245
|
+
return levels.length === 1 ? levels[0] : { levels };
|
|
246
|
+
};
|
|
247
|
+
const clampUserErrorMessage = (message) => {
|
|
248
|
+
const normalized = message.replace(/\s+/g, " ").trim();
|
|
249
|
+
return normalized.length <= MAX_USER_ERROR_MESSAGE_LENGTH ? normalized : `${normalized.slice(0, MAX_USER_ERROR_MESSAGE_LENGTH - 1)}…`;
|
|
250
|
+
};
|
|
251
|
+
const getProviderModelName = (message) => message.match(/\bmodel\s+[`'"]([^`'"]+)[`'"]/i)?.[1]?.trim();
|
|
252
|
+
const classifyStreamError = (message, statusCode) => {
|
|
253
|
+
if (statusCode === 401 || statusCode === 403) return "authorization_failed";
|
|
254
|
+
if (statusCode === 429) return "rate_limited";
|
|
255
|
+
if (/\bmodel\b[\s\S]{0,160}\b(?:limited preview|not available)\b/i.test(message) || /\blimited preview\b[\s\S]{0,160}\bmodel\b/i.test(message)) return "model_unavailable";
|
|
256
|
+
if (/\b(?:rate limit|too many requests|request limit)\b/i.test(message)) return "rate_limited";
|
|
257
|
+
if (/\b(?:invalid api key|incorrect api key|authentication|unauthorized|forbidden)\b/i.test(message)) return "authorization_failed";
|
|
258
|
+
if (/\b(?:attachment|document|file)\b[\s\S]{0,120}\b(?:missing|unavailable|not found|failed)\b/i.test(message)) return "attachment_unavailable";
|
|
259
|
+
return "request_failed";
|
|
260
|
+
};
|
|
261
|
+
const formatKnownProviderError = (message) => {
|
|
262
|
+
const normalized = clampUserErrorMessage(message);
|
|
263
|
+
if (/\bmodel\b[\s\S]{0,160}\b(?:limited preview|not available)\b/i.test(normalized) || /\blimited preview\b[\s\S]{0,160}\bmodel\b/i.test(normalized)) {
|
|
264
|
+
const modelName = getProviderModelName(normalized);
|
|
265
|
+
return `The configured model${modelName ? ` \`${modelName}\`` : ""} is not available on this account.`;
|
|
266
|
+
}
|
|
267
|
+
if (/\b(?:rate limit|too many requests|request limit)\b/i.test(normalized)) return "The AI provider is temporarily limiting requests.";
|
|
268
|
+
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 normalized;
|
|
270
|
+
};
|
|
271
|
+
const parseProviderResponseBodyMessage = (responseBody) => {
|
|
272
|
+
if (typeof responseBody !== "string" || responseBody.trim().length === 0) return void 0;
|
|
273
|
+
try {
|
|
274
|
+
const parsed = JSON.parse(responseBody);
|
|
275
|
+
if (isRecord(parsed) && isRecord(parsed.error) && typeof parsed.error.message === "string") return parsed.error.message;
|
|
276
|
+
} catch {
|
|
277
|
+
return responseBody;
|
|
278
|
+
}
|
|
279
|
+
return responseBody;
|
|
280
|
+
};
|
|
281
|
+
const extractUserErrorMessageFromLevel = (level) => {
|
|
282
|
+
if (isRecord(level.data) && isRecord(level.data.error) && typeof level.data.error.message === "string") return level.data.error.message;
|
|
283
|
+
return parseProviderResponseBodyMessage(level.responseBody) ?? (typeof level.message === "string" ? level.message : void 0);
|
|
284
|
+
};
|
|
285
|
+
const formatSurveyAssistantStreamError = (error) => {
|
|
286
|
+
const describedError = describeSurveyAssistantStreamError(error);
|
|
287
|
+
const levels = Array.isArray(describedError.levels) ? describedError.levels : [describedError];
|
|
288
|
+
const messageLevel = levels.find((level) => isRecord(level) && Boolean(extractUserErrorMessageFromLevel(level)));
|
|
289
|
+
const statusLevel = levels.find((level) => isRecord(level) && level.statusCode !== void 0);
|
|
290
|
+
const statusCode = Number(statusLevel?.statusCode);
|
|
291
|
+
const detail = messageLevel ? extractUserErrorMessageFromLevel(messageLevel) : void 0;
|
|
292
|
+
const formattedDetail = detail ? formatKnownProviderError(detail) : void 0;
|
|
293
|
+
return serializeSurveyAssistantError({
|
|
294
|
+
code: classifyStreamError(formattedDetail ?? "", Number.isFinite(statusCode) ? statusCode : void 0),
|
|
295
|
+
...formattedDetail ? { detail: formattedDetail } : {}
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
const requestErrorResponse = (error, fallback) => {
|
|
299
|
+
const requestError = error instanceof SurveyAssistantRequestError ? error : new SurveyAssistantRequestError(fallback);
|
|
300
|
+
return Response.json({
|
|
301
|
+
code: "request_failed",
|
|
302
|
+
error: requestError.message
|
|
303
|
+
}, { status: requestError.status });
|
|
304
|
+
};
|
|
305
|
+
const createSurveyAssistantChatHandler = (options) => {
|
|
306
|
+
const maxConcurrentRequests = Number.isInteger(options.maxConcurrentRequests) && (options.maxConcurrentRequests ?? 0) > 0 ? options.maxConcurrentRequests : DEFAULT_MAX_CONCURRENT_REQUESTS;
|
|
307
|
+
const maxSteps = Number.isInteger(options.maxSteps) && (options.maxSteps ?? 0) > 0 ? options.maxSteps : DEFAULT_MAX_STEPS;
|
|
308
|
+
let activeRequestCount = 0;
|
|
309
|
+
const acquireRequestSlot = () => {
|
|
310
|
+
if (activeRequestCount >= maxConcurrentRequests) return null;
|
|
311
|
+
activeRequestCount += 1;
|
|
312
|
+
let released = false;
|
|
313
|
+
return () => {
|
|
314
|
+
if (released) return;
|
|
315
|
+
released = true;
|
|
316
|
+
activeRequestCount = Math.max(0, activeRequestCount - 1);
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
return async (request) => {
|
|
320
|
+
if (options.isEnabled && !await options.isEnabled(request)) return Response.json({
|
|
321
|
+
code: "authorization_failed",
|
|
322
|
+
error: "Survey assistant is disabled."
|
|
323
|
+
}, { status: 404 });
|
|
324
|
+
let rawParams;
|
|
325
|
+
try {
|
|
326
|
+
rawParams = await readSurveyAssistantRequestJson(request);
|
|
327
|
+
} catch (error) {
|
|
328
|
+
return requestErrorResponse(error, "The survey assistant request could not be read.");
|
|
329
|
+
}
|
|
330
|
+
const params = isRecord(rawParams) ? rawParams : {};
|
|
331
|
+
let identity;
|
|
332
|
+
try {
|
|
333
|
+
identity = validateAuthorizedIdentity(await options.authorize({
|
|
334
|
+
request,
|
|
335
|
+
requestedResourceId: optionalRequestedId(params.resourceId),
|
|
336
|
+
requestedThreadId: optionalRequestedId(params.threadId ?? params.id),
|
|
337
|
+
requestedSurveyId: optionalRequestedId(params.surveyId)
|
|
338
|
+
}));
|
|
339
|
+
} catch (error) {
|
|
340
|
+
const authorizationError = error instanceof SurveyAssistantAuthorizationError ? error : new SurveyAssistantAuthorizationError();
|
|
341
|
+
return Response.json({
|
|
342
|
+
code: "authorization_failed",
|
|
343
|
+
error: authorizationError.message
|
|
344
|
+
}, { status: authorizationError.status });
|
|
345
|
+
}
|
|
346
|
+
let messages;
|
|
347
|
+
try {
|
|
348
|
+
messages = validateSurveyAssistantMessages(params.messages);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
return requestErrorResponse(error, "The survey assistant messages are invalid.");
|
|
351
|
+
}
|
|
352
|
+
let modelSelection;
|
|
353
|
+
if (options.resolveModel) {
|
|
354
|
+
const requestedModelId = optionalRequestedId(params.modelId);
|
|
355
|
+
if (params.modelId !== void 0 && !requestedModelId) return Response.json({
|
|
356
|
+
code: "model_unavailable",
|
|
357
|
+
error: "Invalid survey assistant model selection."
|
|
358
|
+
}, { status: 400 });
|
|
359
|
+
try {
|
|
360
|
+
modelSelection = validateModelSelection(await options.resolveModel({
|
|
361
|
+
identity,
|
|
362
|
+
request,
|
|
363
|
+
requestedModelId
|
|
364
|
+
})) ?? void 0;
|
|
365
|
+
} catch (error) {
|
|
366
|
+
options.onStreamError?.(error, {
|
|
367
|
+
resourceId: identity.resourceId,
|
|
368
|
+
threadId: identity.threadId
|
|
369
|
+
});
|
|
370
|
+
return Response.json({
|
|
371
|
+
code: "model_unavailable",
|
|
372
|
+
error: "The survey assistant model catalog is unavailable."
|
|
373
|
+
}, { status: 503 });
|
|
374
|
+
}
|
|
375
|
+
if (!modelSelection) return Response.json({
|
|
376
|
+
code: "model_unavailable",
|
|
377
|
+
error: "The selected survey assistant model is not available."
|
|
378
|
+
}, { status: 400 });
|
|
379
|
+
}
|
|
380
|
+
let snapshot;
|
|
381
|
+
try {
|
|
382
|
+
snapshot = parseSurveyAssistantSnapshot(params.snapshot);
|
|
383
|
+
} catch {
|
|
384
|
+
return Response.json({
|
|
385
|
+
code: "request_failed",
|
|
386
|
+
error: "Invalid survey assistant snapshot."
|
|
387
|
+
}, { status: 400 });
|
|
388
|
+
}
|
|
389
|
+
const readinessError = await options.getReadinessError?.(request);
|
|
390
|
+
if (readinessError) return Response.json({
|
|
391
|
+
code: "request_failed",
|
|
392
|
+
error: readinessError
|
|
393
|
+
}, { status: 503 });
|
|
394
|
+
const releaseRequestSlot = acquireRequestSlot();
|
|
395
|
+
if (!releaseRequestSlot) return Response.json({
|
|
396
|
+
code: "rate_limited",
|
|
397
|
+
error: "The survey assistant is handling too many requests. Try again shortly."
|
|
398
|
+
}, { status: 429 });
|
|
399
|
+
const changeCollector = new TurnChangeCollector();
|
|
400
|
+
let requestContext;
|
|
401
|
+
try {
|
|
402
|
+
let attachmentAccess;
|
|
403
|
+
let modelAttachments = [];
|
|
404
|
+
if (options.threadDocuments) {
|
|
405
|
+
const repository = options.threadDocuments;
|
|
406
|
+
const registered = await registerSurveyAssistantThreadAttachments({
|
|
407
|
+
messages,
|
|
408
|
+
repository,
|
|
409
|
+
resourceId: identity.resourceId,
|
|
410
|
+
threadId: identity.threadId
|
|
411
|
+
});
|
|
412
|
+
const manifest = await repository.list({
|
|
413
|
+
resourceId: identity.resourceId,
|
|
414
|
+
threadId: identity.threadId
|
|
415
|
+
});
|
|
416
|
+
modelAttachments = await loadSurveyAssistantDocuments({
|
|
417
|
+
documents: registered,
|
|
418
|
+
repository,
|
|
419
|
+
resourceId: identity.resourceId,
|
|
420
|
+
threadId: identity.threadId
|
|
421
|
+
});
|
|
422
|
+
messages = removeHistoricalSurveyAssistantAttachments(messages);
|
|
423
|
+
const registeredIds = new Set(registered.map((document) => document.id));
|
|
424
|
+
const replayableDocuments = getSurveyAssistantReplayableDocuments(manifest.filter((document) => !registeredIds.has(document.id)), messages);
|
|
425
|
+
const documents = [...registered, ...replayableDocuments];
|
|
426
|
+
if (documents.length > 0) attachmentAccess = {
|
|
427
|
+
documents,
|
|
428
|
+
currentDocumentIds: registered.map((document) => document.id),
|
|
429
|
+
selectedAttachments: [],
|
|
430
|
+
load: (documentIds) => loadSurveyAssistantDocuments({
|
|
431
|
+
documents: documents.filter((document) => documentIds.includes(document.id)),
|
|
432
|
+
repository,
|
|
433
|
+
resourceId: identity.resourceId,
|
|
434
|
+
threadId: identity.threadId
|
|
435
|
+
})
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
requestContext = buildSurveyAssistantRequestContext({
|
|
439
|
+
changeCollector,
|
|
440
|
+
identity,
|
|
441
|
+
snapshot,
|
|
442
|
+
snapshotSource: snapshot ? "full" : "missing",
|
|
443
|
+
trustedCapabilities: options.trustedCapabilities,
|
|
444
|
+
attachmentAccess,
|
|
445
|
+
modelAttachments,
|
|
446
|
+
modelSelection
|
|
447
|
+
});
|
|
448
|
+
} catch (error) {
|
|
449
|
+
releaseRequestSlot();
|
|
450
|
+
if (error instanceof SurveyAssistantRequestError) return requestErrorResponse(error, "The thread attachments could not be prepared.");
|
|
451
|
+
if (error instanceof SurveyAssistantCapabilityMismatchError) return Response.json({
|
|
452
|
+
error: error.message,
|
|
453
|
+
code: error.code
|
|
454
|
+
}, { status: 409 });
|
|
455
|
+
options.onStreamError?.(error, {
|
|
456
|
+
resourceId: identity.resourceId,
|
|
457
|
+
threadId: identity.threadId
|
|
458
|
+
});
|
|
459
|
+
return Response.json({
|
|
460
|
+
code: "attachment_unavailable",
|
|
461
|
+
error: "The survey assistant could not prepare the thread's reference documents."
|
|
462
|
+
}, { status: 503 });
|
|
463
|
+
}
|
|
464
|
+
const metrics = {
|
|
465
|
+
snapshotBytes: snapshot ? new TextEncoder().encode(JSON.stringify(snapshot)).byteLength : 0,
|
|
466
|
+
toolCalls: 0
|
|
467
|
+
};
|
|
468
|
+
const streamErrorContext = {
|
|
469
|
+
resourceId: identity.resourceId,
|
|
470
|
+
threadId: identity.threadId
|
|
471
|
+
};
|
|
472
|
+
const handleStreamError = (error) => {
|
|
473
|
+
options.onStreamError?.(error, streamErrorContext);
|
|
474
|
+
return options.formatStreamError?.(error, streamErrorContext) ?? formatSurveyAssistantStreamError(error);
|
|
475
|
+
};
|
|
476
|
+
let stream;
|
|
477
|
+
try {
|
|
478
|
+
stream = await handleChatStream({
|
|
479
|
+
mastra: typeof options.mastra === "function" ? await options.mastra() : options.mastra,
|
|
480
|
+
agentId: options.agentId ?? "survey-assistant-agent",
|
|
481
|
+
version: "v6",
|
|
482
|
+
params: {
|
|
483
|
+
messages,
|
|
484
|
+
...params.trigger ? { trigger: params.trigger } : {},
|
|
485
|
+
requestContext,
|
|
486
|
+
runId: changeCollector.turnId,
|
|
487
|
+
abortSignal: request.signal,
|
|
488
|
+
onAbort: () => {
|
|
489
|
+
changeCollector.finalize("aborted");
|
|
490
|
+
},
|
|
491
|
+
onError: () => {
|
|
492
|
+
changeCollector.finalize("failed");
|
|
493
|
+
},
|
|
494
|
+
memory: {
|
|
495
|
+
thread: identity.threadId,
|
|
496
|
+
resource: identity.resourceId
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
defaultOptions: {
|
|
500
|
+
activeTools: [...options.activeTools],
|
|
501
|
+
maxSteps,
|
|
502
|
+
...options.providerOptions ? { providerOptions: options.providerOptions } : {}
|
|
503
|
+
},
|
|
504
|
+
sendReasoning: options.sendReasoning ?? true,
|
|
505
|
+
messageMetadata: ({ part }) => {
|
|
506
|
+
recordTurnStreamPart(metrics, part);
|
|
507
|
+
},
|
|
508
|
+
onError: (error) => {
|
|
509
|
+
changeCollector.finalize("failed");
|
|
510
|
+
return handleStreamError(error);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
} catch (error) {
|
|
514
|
+
changeCollector.finalize(request.signal.aborted ? "aborted" : "failed");
|
|
515
|
+
releaseRequestSlot();
|
|
516
|
+
return Response.json({ error: handleStreamError(error) }, { status: 503 });
|
|
517
|
+
}
|
|
518
|
+
return createUIMessageStreamResponse({ stream: createSurveyAssistantTurnStream({
|
|
519
|
+
stream,
|
|
520
|
+
changeCollector,
|
|
521
|
+
signal: request.signal,
|
|
522
|
+
onUnexpectedError: handleStreamError,
|
|
523
|
+
onSettled: () => {
|
|
524
|
+
releaseRequestSlot();
|
|
525
|
+
options.onTurnMetrics?.({
|
|
526
|
+
metrics,
|
|
527
|
+
provider: options.providerName,
|
|
528
|
+
turnId: changeCollector.turnId
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}) });
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
//#endregion
|
|
535
|
+
//#region src/server/thread-handlers.ts
|
|
536
|
+
const DEFAULT_TITLE = "New chat";
|
|
537
|
+
const MANUAL_TITLE_METADATA_KEY = "surveyAssistant.manualTitle";
|
|
538
|
+
const AUTOMATIC_TITLE_METADATA_KEY = "surveyAssistant.automaticTitle";
|
|
539
|
+
const getMetadataString = (metadata, key) => {
|
|
540
|
+
const value = metadata?.[key];
|
|
541
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
542
|
+
};
|
|
543
|
+
const getRawTitle = (thread) => thread.title?.trim() || DEFAULT_TITLE;
|
|
544
|
+
const getAutomaticTitle = (thread, manualTitle) => {
|
|
545
|
+
const rawTitle = thread.title?.trim();
|
|
546
|
+
if (rawTitle && rawTitle !== manualTitle) return rawTitle;
|
|
547
|
+
return getMetadataString(thread.metadata, AUTOMATIC_TITLE_METADATA_KEY) ?? DEFAULT_TITLE;
|
|
548
|
+
};
|
|
549
|
+
const asThread = (thread) => {
|
|
550
|
+
const manualTitle = getMetadataString(thread.metadata, MANUAL_TITLE_METADATA_KEY);
|
|
551
|
+
return {
|
|
552
|
+
id: thread.id,
|
|
553
|
+
title: manualTitle ?? getRawTitle(thread),
|
|
554
|
+
titleMode: manualTitle ? "manual" : "automatic",
|
|
555
|
+
createdAt: thread.createdAt.toISOString(),
|
|
556
|
+
updatedAt: thread.updatedAt.toISOString()
|
|
557
|
+
};
|
|
558
|
+
};
|
|
559
|
+
const json = (body, status = 200) => Response.json(body, { status });
|
|
560
|
+
const errorResponse = (error) => {
|
|
561
|
+
const message = error instanceof Error ? error.message : "The chat request could not be completed.";
|
|
562
|
+
const status = typeof error === "object" && error !== null && "status" in error && typeof error.status === "number" ? error.status : 400;
|
|
563
|
+
return json({
|
|
564
|
+
code: status === 401 || status === 403 ? "authorization_failed" : "request_failed",
|
|
565
|
+
error: message
|
|
566
|
+
}, status);
|
|
567
|
+
};
|
|
568
|
+
const ownsThread = (thread, identity) => thread.resourceId === identity.resourceId && Object.entries(identity.metadata).every(([key, value]) => thread.metadata?.[key] === value);
|
|
569
|
+
const getOwnedThread = async (repository, identity, threadId) => {
|
|
570
|
+
const thread = await repository.get(threadId);
|
|
571
|
+
return thread && ownsThread(thread, identity) ? thread : null;
|
|
572
|
+
};
|
|
573
|
+
const createSurveyAssistantThreadHandlers = ({ authorize, repository }) => {
|
|
574
|
+
const list = async (request) => {
|
|
575
|
+
try {
|
|
576
|
+
const identity = await authorize(request);
|
|
577
|
+
return json({ threads: (await repository.list(identity.resourceId, identity.metadata)).filter((thread) => ownsThread(thread, identity) && typeof thread.metadata?.archivedAt !== "string").map(asThread) });
|
|
578
|
+
} catch (error) {
|
|
579
|
+
return errorResponse(error);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
const create = async (request) => {
|
|
583
|
+
try {
|
|
584
|
+
const [identity, input] = await Promise.all([authorize(request), request.json().catch(() => ({}))]);
|
|
585
|
+
const parsed = createSurveyAssistantThreadInputSchema.parse(input);
|
|
586
|
+
const metadata = { ...identity.metadata };
|
|
587
|
+
if (parsed.title) {
|
|
588
|
+
metadata[MANUAL_TITLE_METADATA_KEY] = parsed.title;
|
|
589
|
+
metadata[AUTOMATIC_TITLE_METADATA_KEY] = DEFAULT_TITLE;
|
|
590
|
+
}
|
|
591
|
+
return json({ thread: asThread(await repository.create({
|
|
592
|
+
resourceId: identity.resourceId,
|
|
593
|
+
title: parsed.title ?? DEFAULT_TITLE,
|
|
594
|
+
metadata
|
|
595
|
+
})) }, 201);
|
|
596
|
+
} catch (error) {
|
|
597
|
+
return errorResponse(error);
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
const read = async (request, threadId) => {
|
|
601
|
+
try {
|
|
602
|
+
const identity = await authorize(request);
|
|
603
|
+
const thread = await getOwnedThread(repository, identity, threadId);
|
|
604
|
+
if (!thread || typeof thread.metadata?.archivedAt === "string") return json({ error: "Chat not found." }, 404);
|
|
605
|
+
return json({
|
|
606
|
+
thread: asThread(thread),
|
|
607
|
+
messages: await repository.readMessages(threadId, identity.resourceId)
|
|
608
|
+
});
|
|
609
|
+
} catch (error) {
|
|
610
|
+
return errorResponse(error);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
const update = async (request, threadId) => {
|
|
614
|
+
try {
|
|
615
|
+
const [identity, input] = await Promise.all([authorize(request), request.json()]);
|
|
616
|
+
const thread = await getOwnedThread(repository, identity, threadId);
|
|
617
|
+
if (!thread || typeof thread.metadata?.archivedAt === "string") return json({ error: "Chat not found." }, 404);
|
|
618
|
+
const parsed = updateSurveyAssistantThreadInputSchema.parse(input);
|
|
619
|
+
const metadata = { ...thread.metadata ?? identity.metadata };
|
|
620
|
+
let title;
|
|
621
|
+
if (parsed.mode === "automatic") {
|
|
622
|
+
const manualTitle = getMetadataString(metadata, MANUAL_TITLE_METADATA_KEY);
|
|
623
|
+
title = parsed.title ?? (manualTitle ? getAutomaticTitle(thread, manualTitle) : getRawTitle(thread));
|
|
624
|
+
metadata[MANUAL_TITLE_METADATA_KEY] = null;
|
|
625
|
+
metadata[AUTOMATIC_TITLE_METADATA_KEY] = null;
|
|
626
|
+
} else {
|
|
627
|
+
const previousManualTitle = getMetadataString(metadata, MANUAL_TITLE_METADATA_KEY);
|
|
628
|
+
const automaticTitle = previousManualTitle ? getAutomaticTitle(thread, previousManualTitle) : getRawTitle(thread);
|
|
629
|
+
title = parsed.title;
|
|
630
|
+
metadata[MANUAL_TITLE_METADATA_KEY] = title;
|
|
631
|
+
metadata[AUTOMATIC_TITLE_METADATA_KEY] = automaticTitle;
|
|
632
|
+
}
|
|
633
|
+
return json({ thread: asThread(await repository.update({
|
|
634
|
+
id: thread.id,
|
|
635
|
+
title,
|
|
636
|
+
metadata
|
|
637
|
+
})) });
|
|
638
|
+
} catch (error) {
|
|
639
|
+
return errorResponse(error);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
const deleteThread = async (request, threadId) => {
|
|
643
|
+
try {
|
|
644
|
+
const thread = await getOwnedThread(repository, await authorize(request), threadId);
|
|
645
|
+
if (!thread) return json({ error: "Chat not found." }, 404);
|
|
646
|
+
await repository.delete(thread.id);
|
|
647
|
+
return new Response(null, { status: 204 });
|
|
648
|
+
} catch (error) {
|
|
649
|
+
return errorResponse(error);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
return {
|
|
653
|
+
create,
|
|
654
|
+
delete: deleteThread,
|
|
655
|
+
list,
|
|
656
|
+
read,
|
|
657
|
+
update
|
|
658
|
+
};
|
|
659
|
+
};
|
|
660
|
+
//#endregion
|
|
661
|
+
//#region src/server/memory-thread-repository.ts
|
|
662
|
+
const createMastraMemoryThreadRepository = ({ getMemory, threadDocuments }) => ({
|
|
663
|
+
async create({ metadata, resourceId, title }) {
|
|
664
|
+
return (await getMemory()).createThread({
|
|
665
|
+
metadata,
|
|
666
|
+
resourceId,
|
|
667
|
+
title,
|
|
668
|
+
saveThread: true
|
|
669
|
+
});
|
|
670
|
+
},
|
|
671
|
+
async get(threadId) {
|
|
672
|
+
return (await getMemory()).getThreadById({ threadId });
|
|
673
|
+
},
|
|
674
|
+
async list(resourceId, metadata) {
|
|
675
|
+
return (await (await getMemory()).listThreads({
|
|
676
|
+
filter: {
|
|
677
|
+
metadata,
|
|
678
|
+
resourceId
|
|
679
|
+
},
|
|
680
|
+
orderBy: {
|
|
681
|
+
field: "updatedAt",
|
|
682
|
+
direction: "DESC"
|
|
683
|
+
},
|
|
684
|
+
perPage: false
|
|
685
|
+
})).threads;
|
|
686
|
+
},
|
|
687
|
+
async readMessages(threadId, resourceId) {
|
|
688
|
+
return toAISdkMessages((await (await getMemory()).recall({
|
|
689
|
+
threadId,
|
|
690
|
+
resourceId,
|
|
691
|
+
perPage: false,
|
|
692
|
+
orderBy: {
|
|
693
|
+
field: "createdAt",
|
|
694
|
+
direction: "ASC"
|
|
695
|
+
}
|
|
696
|
+
})).messages, { version: "v6" });
|
|
697
|
+
},
|
|
698
|
+
async delete(threadId) {
|
|
699
|
+
const memory = await getMemory();
|
|
700
|
+
const thread = await memory.getThreadById({ threadId });
|
|
701
|
+
if (thread && threadDocuments) await threadDocuments.deleteThread({
|
|
702
|
+
resourceId: thread.resourceId,
|
|
703
|
+
threadId
|
|
704
|
+
});
|
|
705
|
+
await memory.deleteThread(threadId);
|
|
706
|
+
},
|
|
707
|
+
async update({ id, metadata, title }) {
|
|
708
|
+
return (await getMemory()).updateThread({
|
|
709
|
+
id,
|
|
710
|
+
metadata,
|
|
711
|
+
title
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
//#endregion
|
|
716
|
+
export { describeSurveyAssistantStreamError as a, loadSurveyAssistantDocuments as c, removeHistoricalSurveyAssistantAttachments as d, createSurveyAssistantTurnStream as f, createSurveyAssistantChatHandler as i, loadSurveyAssistantThreadAttachments as l, createSurveyAssistantThreadHandlers as n, formatSurveyAssistantStreamError as o, SurveyAssistantAuthorizationError as r, getSurveyAssistantReplayableDocuments as s, createMastraMemoryThreadRepository as t, registerSurveyAssistantThreadAttachments as u };
|
|
717
|
+
|
|
718
|
+
//# sourceMappingURL=memory-thread-repository-CJkkVE2-.mjs.map
|