@rei-standard/amsg-server 2.1.1 → 2.3.1

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 CHANGED
@@ -65,6 +65,38 @@ const rei = await createReiServer({
65
65
 
66
66
  如果上游返回 `405 Method Not Allowed`,通常表示 `apiUrl` 指向了基础域名而非聊天端点,请优先检查配置值。
67
67
 
68
+ ## 提示词字段(`completePrompt` vs `messages`,2.2.0+)
69
+
70
+ AI 配置消息的提示词可以用两种形态之一,**互斥二选一**:
71
+
72
+ - `completePrompt: string` —— 简单场景:内部包成单条 `{role:'user', content}` 后发给 LLM。
73
+ - `messages: Array<{ role: 'system'|'user'|'assistant'|'tool', content: string | unknown[] }>` —— 多轮 / 带 system role:**原样**转发给 LLM,不做任何 role 注入或重排。和上游主聊天路径调用 LLM 的 body 字节级一致。
74
+
75
+ 两个字段同时给 → `400 INVALID_PARAMETERS`;都不给且没 `userMessage`(仅 instant 类型允许 fallback)也是 400。`messages` 数组必须非空,role 必须是上面四种之一。
76
+
77
+ 可选 `temperature?: number` 透传给 LLM:`completePrompt` 路径未传时默认 0.8(保持旧行为);`messages` 路径未传时**不发**,让上游主路径自己决定。
78
+
79
+ ## 自定义分句正则 `splitPattern`(2.3.0+)
80
+
81
+ `processSingleMessage` 默认按 `/([。!?!?]+)/` 把 LLM 返回的整段文本切成多条推送(每条间隔 1.5s)。`splitPattern` 让调用方覆盖这个正则:
82
+
83
+ - `splitPattern: string` —— 单个正则 source(不带 flags)。例:`"([\\n]+)"` 按换行切。
84
+ - `splitPattern: string[]` —— **级联**应用:第一个正则切完,每段再用第二个切。例:`["(\\n\\n+)", "([。!?!?]+)"]` 先按段落、再按句号。
85
+ - 不传 / `null` / `[]` → 走默认正则,行为字节级与 2.2.x 一致;老库存任务(无此字段)零迁移。
86
+
87
+ **约定**:
88
+
89
+ - 传**正则 source**,不要带两边的 `/.../` 也不要带尾部 flag(`/foo/i` 会被当字面量斜杠 + 字面量 `i` 匹配)。需要大小写不敏感请用 `[Aa]` 这种字符类替代。
90
+ - 想让分隔符回贴到前一段(与默认行为一致),把分隔符包进 `(...)` 捕获组。库不会自动包——传 `"\\n+"` 而不是 `"(\\n+)"` 会得到首尾相连、分隔符丢失的奇怪结果。
91
+ - 数组语义是级联,不是"任一匹配就切"。后者请自己用 `|` 合一条正则。
92
+ - 限制:每项 ≤ 200 字符,数组 ≤ 10 项;非法或无法 `new RegExp(...)` 通过 → `400 INVALID_PARAMETERS`(schedule)/ `400 INVALID_UPDATE_DATA`(update)。
93
+ - `update-message` 显式传 `splitPattern: null` 可重置回默认;不传则保留原值。
94
+
95
+ ## 导出(新增)
96
+
97
+ - `validateLlmMessagesArray(messages)` — 同步预校验 messages 数组,返回 `string | null`(错误信息 / 通过)。和 `@rei-standard/amsg-instant` 的校验规则字节级一致。
98
+ - `validateSplitPattern(value)` — 同步预校验 splitPattern(string / string[] / null),返回 `string | null`。
99
+
68
100
  ## 一体化初始化流程
69
101
 
70
102
  1. 管理员配置环境变量(VAPID + tenant secrets)
package/dist/index.cjs CHANGED
@@ -47,6 +47,71 @@ function isValidUUIDv4(uuid) {
47
47
  const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
48
48
  return uuidV4Regex.test(uuid);
49
49
  }
50
+ var VALID_LLM_MESSAGE_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool"]);
51
+ var AVATAR_URL_MAX_LENGTH = 2048;
52
+ function validateAvatarUrl(value) {
53
+ if (value === void 0 || value === null) return null;
54
+ if (typeof value !== "string") {
55
+ return "avatarUrl \u5FC5\u987B\u662F\u5B57\u7B26\u4E32";
56
+ }
57
+ if (/^data:/i.test(value)) {
58
+ return "\u5934\u50CF\u4E0D\u652F\u6301\u4F20\u5165 data: URI\uFF0C\u8BF7\u6539\u4E3A\u516C\u7F51\u53EF\u8BBF\u95EE\u7684 https:// \u56FE\u7247 URL";
59
+ }
60
+ if (value.length > AVATAR_URL_MAX_LENGTH) {
61
+ return `\u5934\u50CF URL \u957F\u5EA6 ${value.length} \u5B57\u7B26\u8D85\u8FC7 ${AVATAR_URL_MAX_LENGTH} \u4E0A\u9650\uFF0C\u8BF7\u6539\u4E3A\u66F4\u77ED\u7684\u56FE\u7247 URL`;
62
+ }
63
+ if (!isValidUrl(value)) {
64
+ return "avatarUrl \u4E0D\u662F\u5408\u6CD5 URL";
65
+ }
66
+ return null;
67
+ }
68
+ var SPLIT_PATTERN_MAX_LENGTH = 200;
69
+ var SPLIT_PATTERN_MAX_ITEMS = 10;
70
+ function validateSplitPattern(value) {
71
+ if (value === void 0 || value === null) return null;
72
+ const isArray = Array.isArray(value);
73
+ const items = isArray ? value : [value];
74
+ if (isArray && items.length === 0) return null;
75
+ if (items.length > SPLIT_PATTERN_MAX_ITEMS) {
76
+ return `splitPattern \u6570\u7EC4\u6700\u591A ${SPLIT_PATTERN_MAX_ITEMS} \u9879`;
77
+ }
78
+ for (let i = 0; i < items.length; i++) {
79
+ const s = items[i];
80
+ const label = isArray ? `splitPattern[${i}]` : "splitPattern";
81
+ if (typeof s !== "string") return `${label} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`;
82
+ if (s.length > SPLIT_PATTERN_MAX_LENGTH) {
83
+ return `${label} \u4E0D\u80FD\u8D85\u8FC7 ${SPLIT_PATTERN_MAX_LENGTH} \u5B57\u7B26`;
84
+ }
85
+ try {
86
+ new RegExp(s);
87
+ } catch (_) {
88
+ return `${label} \u4E0D\u662F\u6709\u6548\u6B63\u5219\u8868\u8FBE\u5F0F`;
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+ function validateLlmMessagesArray(messages) {
94
+ if (!Array.isArray(messages) || messages.length === 0) {
95
+ return "messages must be a non-empty array";
96
+ }
97
+ for (let i = 0; i < messages.length; i++) {
98
+ const m = messages[i];
99
+ if (!m || typeof m !== "object" || Array.isArray(m)) {
100
+ return `messages[${i}] must be an object`;
101
+ }
102
+ if (!VALID_LLM_MESSAGE_ROLES.has(m.role)) {
103
+ return `messages[${i}].role must be one of system / user / assistant / tool`;
104
+ }
105
+ if (typeof m.content === "string") {
106
+ if (!m.content) return `messages[${i}].content must be a non-empty string`;
107
+ } else if (Array.isArray(m.content)) {
108
+ if (m.content.length === 0) return `messages[${i}].content array must be non-empty`;
109
+ } else {
110
+ return `messages[${i}].content must be a non-empty string or a non-empty array`;
111
+ }
112
+ }
113
+ return null;
114
+ }
50
115
  function validateScheduleMessagePayload(payload) {
51
116
  if (!payload.contactName || typeof payload.contactName !== "string") {
52
117
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["contactName"] } };
@@ -74,9 +139,35 @@ function validateScheduleMessagePayload(payload) {
74
139
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["userMessage (required for fixed type)"] } };
75
140
  }
76
141
  }
142
+ const promptCheck = (() => {
143
+ const hasCompletePrompt = payload.completePrompt !== void 0 && payload.completePrompt !== null && payload.completePrompt !== "";
144
+ const hasMessages = payload.messages !== void 0 && payload.messages !== null;
145
+ if (hasCompletePrompt && hasMessages) {
146
+ return {
147
+ error: { code: "INVALID_PARAMETERS", message: "exactly one of `completePrompt` or `messages` must be provided\uFF08\u4E24\u8005\u4E0D\u80FD\u540C\u65F6\u51FA\u73B0\uFF09", details: { invalidFields: ["completePrompt", "messages"] } },
148
+ hasCompletePrompt: true,
149
+ hasMessages: true
150
+ };
151
+ }
152
+ if (hasMessages) {
153
+ const err = validateLlmMessagesArray(payload.messages);
154
+ if (err) {
155
+ return {
156
+ error: { code: "INVALID_PARAMETERS", message: err, details: { invalidFields: ["messages"] } },
157
+ hasCompletePrompt: false,
158
+ hasMessages: true
159
+ };
160
+ }
161
+ }
162
+ return { error: null, hasCompletePrompt, hasMessages };
163
+ })();
164
+ if (promptCheck.error) {
165
+ return { valid: false, errorCode: promptCheck.error.code, errorMessage: promptCheck.error.message, details: promptCheck.error.details };
166
+ }
167
+ const hasPrompt = promptCheck.hasCompletePrompt || promptCheck.hasMessages;
77
168
  if (payload.messageType === "prompted" || payload.messageType === "auto") {
78
169
  const missingAiFields = [];
79
- if (!payload.completePrompt) missingAiFields.push("completePrompt");
170
+ if (!hasPrompt) missingAiFields.push("completePrompt or messages");
80
171
  if (!payload.apiUrl) missingAiFields.push("apiUrl");
81
172
  if (!payload.apiKey) missingAiFields.push("apiKey");
82
173
  if (!payload.primaryModel) missingAiFields.push("primaryModel");
@@ -88,14 +179,18 @@ function validateScheduleMessagePayload(payload) {
88
179
  if (payload.recurrenceType && payload.recurrenceType !== "none") {
89
180
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "instant \u7C7B\u578B\u7684 recurrenceType \u5FC5\u987B\u4E3A none", details: { invalidFields: ['recurrenceType (must be "none" for instant type)'] } };
90
181
  }
91
- const hasAiConfig = payload.completePrompt && payload.apiUrl && payload.apiKey && payload.primaryModel;
182
+ const hasAiConfig = hasPrompt && payload.apiUrl && payload.apiKey && payload.primaryModel;
92
183
  const hasUserMessage = payload.userMessage;
93
184
  if (!hasAiConfig && !hasUserMessage) {
94
- return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "instant \u7C7B\u578B\u5FC5\u987B\u63D0\u4F9B userMessage \u6216\u5B8C\u6574\u7684 AI \u914D\u7F6E", details: { missingFields: ["userMessage or (completePrompt + apiUrl + apiKey + primaryModel)"] } };
185
+ return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "instant \u7C7B\u578B\u5FC5\u987B\u63D0\u4F9B userMessage \u6216\u5B8C\u6574\u7684 AI \u914D\u7F6E", details: { missingFields: ["userMessage or ((completePrompt | messages) + apiUrl + apiKey + primaryModel)"] } };
95
186
  }
96
187
  }
97
- if (payload.avatarUrl && !isValidUrl(payload.avatarUrl)) {
98
- return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["avatarUrl (invalid URL format)"] } };
188
+ if (payload.temperature !== void 0 && payload.temperature !== null && (typeof payload.temperature !== "number" || !Number.isFinite(payload.temperature))) {
189
+ return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["temperature (must be a finite number)"] } };
190
+ }
191
+ const avatarErr = validateAvatarUrl(payload.avatarUrl);
192
+ if (avatarErr) {
193
+ return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: avatarErr, details: { invalidFields: ["avatarUrl"] } };
99
194
  }
100
195
  if (payload.uuid && !isValidUUID(payload.uuid)) {
101
196
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["uuid (invalid UUID format)"] } };
@@ -103,6 +198,10 @@ function validateScheduleMessagePayload(payload) {
103
198
  if (payload.messageSubtype !== void 0 && payload.messageSubtype !== null && typeof payload.messageSubtype !== "string") {
104
199
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["messageSubtype"] } };
105
200
  }
201
+ const splitErr = validateSplitPattern(payload.splitPattern);
202
+ if (splitErr) {
203
+ return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: splitErr, details: { invalidFields: ["splitPattern"] } };
204
+ }
106
205
  return { valid: true };
107
206
  }
108
207
 
@@ -383,6 +482,26 @@ function isUniqueViolation(error) {
383
482
 
384
483
  // src/server/lib/message-processor.js
385
484
 
485
+ var DEFAULT_SPLIT_REGEX = /([。!?!?]+)/;
486
+ function splitOnceByRegex(chunk, regex) {
487
+ const out = chunk.split(regex).reduce((acc, part, i, arr) => {
488
+ if (i % 2 === 0 && part.trim()) {
489
+ const punctuation = arr[i + 1] || "";
490
+ acc.push(part.trim() + punctuation);
491
+ }
492
+ return acc;
493
+ }, []).filter((s) => s.length > 0);
494
+ return out.length > 0 ? out : [chunk];
495
+ }
496
+ function splitMessageIntoSentences(messageContent, splitPattern = null) {
497
+ const sources = splitPattern == null ? null : Array.isArray(splitPattern) ? splitPattern : [splitPattern];
498
+ const regexes = sources && sources.length > 0 ? sources.map((s) => new RegExp(s)) : [DEFAULT_SPLIT_REGEX];
499
+ let chunks = [messageContent];
500
+ for (const regex of regexes) {
501
+ chunks = chunks.flatMap((c) => splitOnceByRegex(c, regex));
502
+ }
503
+ return chunks.length > 0 ? chunks : [messageContent];
504
+ }
386
505
  async function processSingleMessage(task, ctx, providedMasterKey) {
387
506
  try {
388
507
  const masterKey = providedMasterKey || ctx.masterKey;
@@ -395,7 +514,8 @@ async function processSingleMessage(task, ctx, providedMasterKey) {
395
514
  if (decryptedPayload.messageType === "fixed") {
396
515
  messageContent = decryptedPayload.userMessage;
397
516
  } else if (decryptedPayload.messageType === "instant") {
398
- if (decryptedPayload.completePrompt && decryptedPayload.apiUrl && decryptedPayload.apiKey && decryptedPayload.primaryModel) {
517
+ const hasPrompt = !!decryptedPayload.completePrompt || Array.isArray(decryptedPayload.messages) && decryptedPayload.messages.length > 0;
518
+ if (hasPrompt && decryptedPayload.apiUrl && decryptedPayload.apiKey && decryptedPayload.primaryModel) {
399
519
  messageContent = await _callAI(decryptedPayload);
400
520
  } else if (decryptedPayload.userMessage) {
401
521
  messageContent = decryptedPayload.userMessage;
@@ -407,14 +527,7 @@ async function processSingleMessage(task, ctx, providedMasterKey) {
407
527
  } else {
408
528
  throw new Error("Invalid message configuration: no content source available");
409
529
  }
410
- const sentences = messageContent.split(/([。!?!?]+)/).reduce((acc, part, i, arr) => {
411
- if (i % 2 === 0 && part.trim()) {
412
- const punctuation = arr[i + 1] || "";
413
- acc.push(part.trim() + punctuation);
414
- }
415
- return acc;
416
- }, []).filter((s) => s.length > 0);
417
- const messages = sentences.length > 0 ? sentences : [messageContent];
530
+ const messages = splitMessageIntoSentences(messageContent, _nullishCoalesce(decryptedPayload.splitPattern, () => ( null)));
418
531
  if (!ctx.vapid.email || !ctx.vapid.publicKey || !ctx.vapid.privateKey) {
419
532
  throw new Error("VAPID configuration missing - push notifications cannot be sent");
420
533
  }
@@ -530,18 +643,23 @@ async function _callAI(payload) {
530
643
  );
531
644
  }
532
645
  const aiData = await aiResponse.json();
533
- const content = _optionalChain([aiData, 'optionalAccess', _ => _.choices, 'optionalAccess', _2 => _2[0], 'optionalAccess', _3 => _3.message, 'optionalAccess', _4 => _4.content]);
646
+ const content = _optionalChain([aiData, 'optionalAccess', _2 => _2.choices, 'optionalAccess', _3 => _3[0], 'optionalAccess', _4 => _4.message, 'optionalAccess', _5 => _5.content]);
534
647
  if (typeof content !== "string" || !content.trim()) {
535
648
  throw new Error("AI API error: response missing choices[0].message.content");
536
649
  }
537
650
  return content.trim();
538
651
  }
539
652
  function buildAiRequestBody(payload) {
653
+ const llmMessages = Array.isArray(payload.messages) && payload.messages.length > 0 ? payload.messages : [{ role: "user", content: payload.completePrompt }];
540
654
  const requestBody = {
541
655
  model: payload.primaryModel,
542
- messages: [{ role: "user", content: payload.completePrompt }],
543
- temperature: 0.8
656
+ messages: llmMessages
544
657
  };
658
+ if (payload.temperature !== void 0 && payload.temperature !== null) {
659
+ requestBody.temperature = payload.temperature;
660
+ } else if (!Array.isArray(payload.messages)) {
661
+ requestBody.temperature = 0.8;
662
+ }
545
663
  if (payload.maxTokens === void 0 || payload.maxTokens === null) {
546
664
  return requestBody;
547
665
  }
@@ -641,8 +759,17 @@ function createScheduleMessageHandler(ctx) {
641
759
  apiUrl: payload.apiUrl || null,
642
760
  apiKey: payload.apiKey || null,
643
761
  primaryModel: payload.primaryModel || null,
762
+ // Prompt is one-of: legacy completePrompt (string) OR messages (OpenAI-
763
+ // style array). Validation has already enforced exactly-one-of, so
764
+ // exactly one of these will be non-null when an AI config is provided.
644
765
  completePrompt: payload.completePrompt || null,
766
+ messages: Array.isArray(payload.messages) ? payload.messages : null,
645
767
  maxTokens: _nullishCoalesce(payload.maxTokens, () => ( null)),
768
+ temperature: _nullishCoalesce(payload.temperature, () => ( null)),
769
+ // 0.6.0+: optional caller-provided regex (string or string[]) used by
770
+ // the message processor to chunk LLM output into individual pushes.
771
+ // null → processor falls back to the default /([。!?!?]+)/ regex.
772
+ splitPattern: _nullishCoalesce(payload.splitPattern, () => ( null)),
646
773
  pushSubscription: payload.pushSubscription,
647
774
  metadata: payload.metadata || {}
648
775
  };
@@ -954,6 +1081,30 @@ function createUpdateMessageHandler(ctx) {
954
1081
  if (Object.prototype.hasOwnProperty.call(updates, "maxTokens") && updates.maxTokens !== null && (!Number.isInteger(updates.maxTokens) || updates.maxTokens <= 0)) {
955
1082
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["maxTokens"] } } } };
956
1083
  }
1084
+ if (updates.completePrompt && updates.messages !== void 0 && updates.messages !== null) {
1085
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "completePrompt \u4E0E messages \u4E0D\u80FD\u540C\u65F6\u66F4\u65B0\uFF08\u4E8C\u9009\u4E00\uFF09", details: { invalidFields: ["completePrompt", "messages"] } } } };
1086
+ }
1087
+ if (updates.messages !== void 0 && updates.messages !== null) {
1088
+ const msgErr = validateLlmMessagesArray(updates.messages);
1089
+ if (msgErr) {
1090
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: msgErr, details: { invalidFields: ["messages"] } } } };
1091
+ }
1092
+ }
1093
+ if (Object.prototype.hasOwnProperty.call(updates, "temperature") && updates.temperature !== null && (typeof updates.temperature !== "number" || !Number.isFinite(updates.temperature))) {
1094
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["temperature"] } } } };
1095
+ }
1096
+ if (Object.prototype.hasOwnProperty.call(updates, "splitPattern")) {
1097
+ const splitErr = validateSplitPattern(updates.splitPattern);
1098
+ if (splitErr) {
1099
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: splitErr, details: { invalidFields: ["splitPattern"] } } } };
1100
+ }
1101
+ }
1102
+ if (Object.prototype.hasOwnProperty.call(updates, "avatarUrl")) {
1103
+ const avatarErr = validateAvatarUrl(updates.avatarUrl);
1104
+ if (avatarErr) {
1105
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: avatarErr, details: { invalidFields: ["avatarUrl"] } } } };
1106
+ }
1107
+ }
957
1108
  const existingTask = await db.getTaskByUuid(taskUuid, userId);
958
1109
  if (!existingTask) {
959
1110
  const taskStatus = await db.getTaskStatus(taskUuid, userId);
@@ -963,14 +1114,27 @@ function createUpdateMessageHandler(ctx) {
963
1114
  return { status: 409, body: { success: false, error: { code: "TASK_ALREADY_COMPLETED", message: "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u6216\u5DF2\u5931\u8D25\uFF0C\u65E0\u6CD5\u66F4\u65B0" } } };
964
1115
  }
965
1116
  const existingData = JSON.parse(decryptFromStorage(existingTask.encrypted_payload, userKey));
1117
+ const promptUpdates = {};
1118
+ if (updates.completePrompt) {
1119
+ promptUpdates.completePrompt = updates.completePrompt;
1120
+ promptUpdates.messages = null;
1121
+ } else if (updates.messages !== void 0 && updates.messages !== null) {
1122
+ promptUpdates.messages = updates.messages;
1123
+ promptUpdates.completePrompt = null;
1124
+ }
966
1125
  const updatedData = {
967
1126
  ...existingData,
968
- ...updates.completePrompt && { completePrompt: updates.completePrompt },
1127
+ ...promptUpdates,
969
1128
  ...updates.userMessage && { userMessage: updates.userMessage },
970
1129
  ...updates.recurrenceType && { recurrenceType: updates.recurrenceType },
971
1130
  ...updates.avatarUrl && { avatarUrl: updates.avatarUrl },
972
1131
  ...updates.metadata && { metadata: updates.metadata },
973
- ...Object.prototype.hasOwnProperty.call(updates, "maxTokens") && { maxTokens: _nullishCoalesce(updates.maxTokens, () => ( null)) }
1132
+ ...Object.prototype.hasOwnProperty.call(updates, "maxTokens") && { maxTokens: _nullishCoalesce(updates.maxTokens, () => ( null)) },
1133
+ ...Object.prototype.hasOwnProperty.call(updates, "temperature") && { temperature: _nullishCoalesce(updates.temperature, () => ( null)) },
1134
+ // splitPattern: hasOwnProperty so that explicit `null` (= revert to
1135
+ // default) doesn't get swallowed by truthy-spread the way the optional
1136
+ // string fields above are.
1137
+ ...Object.prototype.hasOwnProperty.call(updates, "splitPattern") && { splitPattern: _nullishCoalesce(updates.splitPattern, () => ( null)) }
974
1138
  };
975
1139
  const encryptedPayload = encryptForStorage(JSON.stringify(updatedData), userKey);
976
1140
  const extraFields = updates.nextSendAt ? { next_send_at: updates.nextSendAt } : void 0;
@@ -1162,7 +1326,7 @@ async function resolveBlobStore(namespace) {
1162
1326
  }
1163
1327
  try {
1164
1328
  const blobModule = await Promise.resolve().then(() => _interopRequireWildcard(require("@netlify/blobs")));
1165
- const getStore = blobModule.getStore || _optionalChain([blobModule, 'access', _5 => _5.default, 'optionalAccess', _6 => _6.getStore]);
1329
+ const getStore = blobModule.getStore || _optionalChain([blobModule, 'access', _6 => _6.default, 'optionalAccess', _7 => _7.getStore]);
1166
1330
  if (typeof getStore === "function") {
1167
1331
  return getStore({ name: namespace });
1168
1332
  }
@@ -1493,4 +1657,7 @@ async function createReiServer(config) {
1493
1657
 
1494
1658
 
1495
1659
 
1496
- exports.createAdapter = createAdapter; exports.createReiServer = createReiServer; exports.createTenantToken = createTenantToken; exports.decryptFromStorage = decryptFromStorage; exports.decryptPayload = decryptPayload; exports.deriveUserEncryptionKey = deriveUserEncryptionKey; exports.encryptForStorage = encryptForStorage; exports.isValidISO8601 = isValidISO8601; exports.isValidUUID = isValidUUID; exports.isValidUUIDv4 = isValidUUIDv4; exports.isValidUrl = isValidUrl; exports.validateScheduleMessagePayload = validateScheduleMessagePayload; exports.verifyTenantToken = verifyTenantToken;
1660
+
1661
+
1662
+
1663
+ exports.createAdapter = createAdapter; exports.createReiServer = createReiServer; exports.createTenantToken = createTenantToken; exports.decryptFromStorage = decryptFromStorage; exports.decryptPayload = decryptPayload; exports.deriveUserEncryptionKey = deriveUserEncryptionKey; exports.encryptForStorage = encryptForStorage; exports.isValidISO8601 = isValidISO8601; exports.isValidUUID = isValidUUID; exports.isValidUUIDv4 = isValidUUIDv4; exports.isValidUrl = isValidUrl; exports.validateAvatarUrl = validateAvatarUrl; exports.validateLlmMessagesArray = validateLlmMessagesArray; exports.validateScheduleMessagePayload = validateScheduleMessagePayload; exports.validateSplitPattern = validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;