@rei-standard/amsg-server 3.0.0 → 3.1.0-next.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.
@@ -147,14 +147,33 @@ var _amsgshared = require('@rei-standard/amsg-shared');
147
147
  // src/server/lib/encryption.js
148
148
  var base64ToBytes = _amsgshared.base64UrlToBytes;
149
149
  var TAG_LENGTH_BYTES = 16;
150
+ var AES_KEY_CACHE_MAX = 128;
151
+ var aesKeyCache = /* @__PURE__ */ new Map();
150
152
  function importAesKey(hexKey, usage) {
151
- return globalThis.crypto.subtle.importKey(
152
- "raw",
153
- _amsgshared.hexToBytes.call(void 0, hexKey),
154
- { name: "AES-GCM" },
155
- false,
156
- [usage]
157
- );
153
+ const cacheKey = `${usage}:${hexKey}`;
154
+ const cached = aesKeyCache.get(cacheKey);
155
+ if (cached) return cached;
156
+ let promise;
157
+ try {
158
+ promise = globalThis.crypto.subtle.importKey(
159
+ "raw",
160
+ _amsgshared.hexToBytes.call(void 0, hexKey),
161
+ { name: "AES-GCM" },
162
+ false,
163
+ [usage]
164
+ ).catch((err6) => {
165
+ aesKeyCache.delete(cacheKey);
166
+ throw err6;
167
+ });
168
+ } catch (err6) {
169
+ return Promise.reject(err6);
170
+ }
171
+ if (aesKeyCache.size >= AES_KEY_CACHE_MAX) {
172
+ const oldest = aesKeyCache.keys().next().value;
173
+ if (oldest !== void 0) aesKeyCache.delete(oldest);
174
+ }
175
+ aesKeyCache.set(cacheKey, promise);
176
+ return promise;
158
177
  }
159
178
  async function aesGcmSeal(hexKey, iv, plaintextBytes) {
160
179
  const key = await importAesKey(hexKey, "encrypt");
@@ -428,6 +447,9 @@ function taskPayloadByteLength(serializedPayload) {
428
447
  return taskPayloadEncoder.encode(serializedPayload).length;
429
448
  }
430
449
  function validateScheduleMessagePayload(payload) {
450
+ if (payload.taskInput != null && (typeof payload.taskInput !== "object" || Array.isArray(payload.taskInput))) {
451
+ return { valid: false, errorCode: "INVALID_TASK_INPUT", errorMessage: "taskInput must be an object or null" };
452
+ }
431
453
  if (!payload.contactName || typeof payload.contactName !== "string") {
432
454
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["contactName"] } };
433
455
  }
@@ -982,6 +1004,40 @@ function createWebCryptoWebPush(vapid = {}, { ttl = SCHEDULED_DEFAULT_TTL } = {}
982
1004
  };
983
1005
  }
984
1006
 
1007
+ // src/server/lib/attachments.js
1008
+ var ATTACHMENT_NAMESPACE = "amsg:attachments";
1009
+ var ATTACHMENT_RETENTION_DAYS = 30;
1010
+ var MAX_ATTACHMENT_TTL_MS = ATTACHMENT_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
1011
+ function createAttachmentStore({ task, occurrenceMs, writeState, now }) {
1012
+ return async function storeAttachment(value, { ttlMs = MAX_ATTACHMENT_TTL_MS, owner, kind = "attachment", ownerGeneration } = {}) {
1013
+ if (typeof value !== "string") throw new TypeError("storeAttachment value must be a serialized string");
1014
+ if (!Number.isSafeInteger(ttlMs) || ttlMs <= 0 || ttlMs > MAX_ATTACHMENT_TTL_MS) {
1015
+ throw new RangeError("storeAttachment ttlMs must be between 1 millisecond and 30 days");
1016
+ }
1017
+ if (typeof task.uuid !== "string" || !task.uuid || !Number.isFinite(occurrenceMs)) {
1018
+ throw new TypeError("storeAttachment requires a task UUID and a valid occurrence");
1019
+ }
1020
+ const at = now();
1021
+ const ref = { version: (
1022
+ /** @type {1} */
1023
+ 1
1024
+ ), namespace: ATTACHMENT_NAMESPACE, key: _amsgshared.randomUUID.call(void 0, ), taskUuid: task.uuid, occurrenceMs, expiresAt: at + ttlMs };
1025
+ let result;
1026
+ try {
1027
+ result = await writeState(ATTACHMENT_NAMESPACE, [{ key: ref.key, value: JSON.stringify({ ...ref, owner, kind, ownerGeneration, value }), updatedAt: at, owner, kind, ownerGeneration }]);
1028
+ } catch (error) {
1029
+ if (_optionalChain([error, 'optionalAccess', _5 => _5.code]) === "AGENTIC_STATE_WRITE_UNSUPPORTED") {
1030
+ throw new DeploymentConfigError("ATTACHMENT_STORAGE_UNSUPPORTED: attachments require a client_state adapter (built-in support: D1)", { code: "ATTACHMENT_STORAGE_UNSUPPORTED" });
1031
+ }
1032
+ throw error;
1033
+ }
1034
+ if (result.upserted !== 1 || result.skipped !== 0) {
1035
+ throw new Error("ATTACHMENT_WRITE_INCOMPLETE: attachment was not stored");
1036
+ }
1037
+ return ref;
1038
+ };
1039
+ }
1040
+
985
1041
  // src/server/lib/agentic-fire.js
986
1042
 
987
1043
 
@@ -1192,6 +1248,7 @@ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1192
1248
  let upserted = 0;
1193
1249
  let skipped = 0;
1194
1250
  let deleted = 0;
1251
+ let unknown = 0;
1195
1252
  const skippedEntries = [];
1196
1253
  if (Array.isArray(result.outcomes) && result.outcomes.length === physicalRows.length) {
1197
1254
  for (let i = 0; i < rootRowIndexes.length; i++) {
@@ -1203,9 +1260,10 @@ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1203
1260
  skippedEntries.push({ namespace: entry.namespace, key: entry.key });
1204
1261
  }
1205
1262
  }
1263
+ } else if (result.upserted === physicalRows.length && result.skipped === 0) {
1264
+ upserted = rootRowIndexes.length;
1206
1265
  } else {
1207
- upserted = result.upserted;
1208
- skipped = result.skipped;
1266
+ unknown = rootRowIndexes.length;
1209
1267
  }
1210
1268
  const cleanupOutcomes = Array.isArray(result.cleanupOutcomes) && result.cleanupOutcomes.length === cleanups.length ? result.cleanupOutcomes : null;
1211
1269
  for (const { cleanupIndex, entry } of deletions) {
@@ -1216,22 +1274,26 @@ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1216
1274
  deleted++;
1217
1275
  }
1218
1276
  }
1219
- return { upserted, skipped, deleted, skippedEntries };
1277
+ return { upserted, skipped, deleted, skippedEntries, ...unknown > 0 ? { unknown } : {} };
1220
1278
  }
1221
1279
 
1222
1280
  // src/server/lib/state-accessors.js
1223
1281
  function createStateAccessors({ db, userId, userKey, maxStateValueBytes, now }) {
1224
1282
  const nowFn = typeof now === "function" ? now : Date.now;
1225
1283
  const valueCeiling = Number.isInteger(maxStateValueBytes) && maxStateValueBytes > 0 ? maxStateValueBytes : DEFAULT_MAX_STATE_VALUE_BYTES;
1226
- const readState = async (namespace) => {
1284
+ const readState = async (namespace, { key } = {}) => {
1227
1285
  if (typeof namespace !== "string" || !namespace.trim()) {
1228
1286
  throw new TypeError("readState(namespace) requires a non-empty string");
1229
1287
  }
1288
+ if (key !== void 0 && (typeof key !== "string" || !key.trim() || key.length > MAX_KEY_CHARS || INTERNAL_STATE_CHAR_RE.test(key))) {
1289
+ throw new TypeError(`readState key must be a non-empty string of at most ${MAX_KEY_CHARS} characters without control characters`);
1290
+ }
1230
1291
  if (!db || typeof db.getClientState !== "function") return [];
1231
- const rows = await db.getClientState(userId, namespace);
1292
+ const rows = await db.getClientState(userId, namespace, key === void 0 ? void 0 : { key });
1232
1293
  return resolveClientStateEntries(
1233
- rows,
1234
- () => db.getClientState(userId, chunkNamespaceFor(namespace)),
1294
+ // 老适配器可能忽略第三个参数,仍在解密前过滤根行,保持返回契约。
1295
+ key === void 0 ? rows : rows.filter((row) => row.key === key),
1296
+ () => db.getClientState(userId, chunkNamespaceFor(namespace), key === void 0 ? void 0 : { keyPrefix: chunkKeyPrefixFor(key) }),
1235
1297
  (value) => decryptFromStorage(value, userKey)
1236
1298
  );
1237
1299
  };
@@ -1287,6 +1349,9 @@ function createStateAccessors({ db, userId, userKey, maxStateValueBytes, now })
1287
1349
  namespace,
1288
1350
  key: entry.key,
1289
1351
  value: entry.value,
1352
+ owner: entry.owner,
1353
+ kind: entry.kind,
1354
+ ownerGeneration: entry.ownerGeneration,
1290
1355
  updatedAt: _nullishCoalesce(entry.updatedAt, () => ( at)),
1291
1356
  ...entry.version !== void 0 ? { version: entry.version } : {}
1292
1357
  };
@@ -1309,7 +1374,7 @@ async function appendPushesToOutbox({ db, userId, userKey, pushes }) {
1309
1374
  await db.appendOutboxMessages(userId, await toOutboxRows(pushes, userKey, Date.now()));
1310
1375
  return true;
1311
1376
  } catch (error) {
1312
- if (_optionalChain([error, 'optionalAccess', _5 => _5.code]) === "CLOUD_OWNER_RETIRED") throw error;
1377
+ if (_optionalChain([error, 'optionalAccess', _6 => _6.code]) === "CLOUD_OWNER_RETIRED") throw error;
1313
1378
  console.warn("[amsg-server] outbox \u843D\u884C\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u6295\u9012\uFF09:", error && error.message);
1314
1379
  return false;
1315
1380
  }
@@ -1583,7 +1648,7 @@ async function sendResultPush({ db, task, userKey, decryptedPayload, webpush, pu
1583
1648
  await sendTaggedPush(webpush, subscription, JSON.stringify(push));
1584
1649
  return true;
1585
1650
  } catch (error) {
1586
- if (isTaskCancelledError(error) || _optionalChain([error, 'optionalAccess', _6 => _6.code]) === "CLOUD_OWNER_RETIRED") throw error;
1651
+ if (isTaskCancelledError(error) || _optionalChain([error, 'optionalAccess', _7 => _7.code]) === "CLOUD_OWNER_RETIRED") throw error;
1587
1652
  console.warn(
1588
1653
  `[amsg-server] \u7ED3\u679C ${push.messageId} \u7684\u63A8\u9001\u6CA1\u53D1\u51FA\u53BB\uFF08\u5DF2\u843D\u8FDB\u6536\u4EF6\u7BB1\uFF0C\u7B49\u5BA2\u6237\u7AEF\u8865\u6536\uFF09:`,
1589
1654
  error && error.message
@@ -1618,7 +1683,7 @@ function projectTask(row, decryptedPayload, options = {}) {
1618
1683
  // 凭据引用只是名字(cred_id),本体在 llm_credentials 表里,凭据字段本身
1619
1684
  // 照旧被白名单挡在外面。
1620
1685
  credRefs: _nullishCoalesce(payload.credRefs, () => ( null)),
1621
- // 整份 metadata 只在单条查询里给(见上面的 includeMetadata)。
1686
+ // 整份 metadata 由 includeMetadata 控制,单条查询与 hook 回执会带。
1622
1687
  ...options.includeMetadata ? { metadata: _nullishCoalesce(payload.metadata, () => ( null)) } : {},
1623
1688
  // 上一次没发出去的原因。reason 'stale' 表示错过触发时刻太久被判定不再补
1624
1689
  // 发;其余是投递失败的错误信息。没有记录 → null。
@@ -1709,6 +1774,9 @@ function normalizeBeforeFireResult(result) {
1709
1774
  return { messages: result };
1710
1775
  }
1711
1776
  if (result && typeof result === "object" && Array.isArray(result.messages)) {
1777
+ if (result.allowedToolNames !== void 0 && (!Array.isArray(result.allowedToolNames) || result.allowedToolNames.some((name) => typeof name !== "string" || !name.trim()))) {
1778
+ throw markPermanent(new TypeError("allowedToolNames must be an array of non-empty strings"), "AGENTIC_BAD_BEFORE_FIRE");
1779
+ }
1712
1780
  return {
1713
1781
  messages: result.messages,
1714
1782
  maxToolIterations: result.maxToolIterations,
@@ -1717,7 +1785,8 @@ function normalizeBeforeFireResult(result) {
1717
1785
  // 用真值。真正决定请求体带不带 tools 的规则住在 llm.js 的
1718
1786
  // buildAiRequestBody(那边有测试钉着),这层只是不把空数组往下传。
1719
1787
  tools: Array.isArray(result.tools) && result.tools.length > 0 ? result.tools : void 0,
1720
- toolChoice: result.toolChoice
1788
+ toolChoice: result.toolChoice,
1789
+ allowedToolNames: result.allowedToolNames === void 0 ? void 0 : new Set(result.allowedToolNames)
1721
1790
  };
1722
1791
  }
1723
1792
  throw markPermanent(new TypeError(BAD_BEFORE_FIRE_MESSAGE), "AGENTIC_BAD_BEFORE_FIRE");
@@ -1737,7 +1806,7 @@ function firstPositiveNumber(values, fallback) {
1737
1806
  async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1738
1807
  const hooks = ctx.hooks;
1739
1808
  const signal = ctx.signal || new AbortController().signal;
1740
- const isCancelled = () => signal.aborted || _optionalChain([ctx, 'access', _7 => _7.isTaskCancelled, 'optionalCall', _8 => _8()]) === true;
1809
+ const isCancelled = () => signal.aborted || _optionalChain([ctx, 'access', _8 => _8.isTaskCancelled, 'optionalCall', _9 => _9()]) === true;
1741
1810
  const throwIfCancelled = () => {
1742
1811
  if (!isCancelled()) return;
1743
1812
  if (isTaskCancelledError(signal.reason)) throw signal.reason;
@@ -1769,6 +1838,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1769
1838
  const sessionId = task.id != null ? `sess_task_${task.id}${occurrenceSuffix(task)}` : `sess_${_amsgshared.randomUUID.call(void 0, )}`;
1770
1839
  const messageIdBase = task.id != null ? `msg_task_${task.id}${occurrenceSuffix(task)}` : `msg_${_amsgshared.randomUUID.call(void 0, )}`;
1771
1840
  const occurrenceMs = occurrenceMsOf(task);
1841
+ const storeAttachment = createAttachmentStore({ task, occurrenceMs, writeState, now: nowFn });
1842
+ const storeSettledAttachment = createAttachmentStore({ task, occurrenceMs, writeState: writeSettledState, now: nowFn });
1772
1843
  const { emitResult } = createResultEmitter({
1773
1844
  db: ctx.db,
1774
1845
  task,
@@ -1791,17 +1862,37 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1791
1862
  const row = await ctx.db.getTaskByUuid(uuid, task.user_id);
1792
1863
  if (!row) return null;
1793
1864
  const payload = JSON.parse(await decryptFromStorage(row.encrypted_payload, userKey));
1794
- return projectTask(row, payload);
1865
+ return projectTask(row, payload, { includeMetadata: true });
1795
1866
  } catch (error) {
1796
1867
  console.warn("[amsg-server] scheduleTask: \u8BFB\u53D6\u649E\u8F66\u4EFB\u52A1\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09:", error && error.message);
1797
1868
  return null;
1798
1869
  }
1799
1870
  }
1871
+ const listTasks = async (options = {}) => {
1872
+ throwIfCancelled();
1873
+ const { status = "pending", limit = 100, offset = 0, includeMetadata = true } = options;
1874
+ if (!["pending", "sent", "failed", "all"].includes(status) || !Number.isInteger(limit) || limit < 1 || limit > 100 || !Number.isInteger(offset) || offset < 0) {
1875
+ throw new TypeError("listTasks requires a valid status, limit 1-100, and non-negative offset");
1876
+ }
1877
+ if (typeof _optionalChain([ctx, 'access', _10 => _10.db, 'optionalAccess', _11 => _11.listTasks]) !== "function") {
1878
+ throw new DeploymentConfigError("AGENTIC_TASK_VIEW_UNSUPPORTED: adapter has no listTasks", { code: "AGENTIC_TASK_VIEW_UNSUPPORTED" });
1879
+ }
1880
+ const { tasks, total } = await ctx.db.listTasks(task.user_id, { status, limit, offset });
1881
+ const projected = await Promise.all(tasks.map(async (row) => projectTask(
1882
+ row,
1883
+ JSON.parse(await decryptFromStorage(row.encrypted_payload, userKey)),
1884
+ { includeMetadata }
1885
+ )));
1886
+ return { tasks: projected, pagination: { total, limit, offset, hasMore: offset + tasks.length < total } };
1887
+ };
1800
1888
  const scheduleTask = async (options) => {
1801
1889
  throwIfCancelled();
1802
1890
  if (!options || typeof options !== "object" || Array.isArray(options)) {
1803
1891
  throw new TypeError("scheduleTask(options) \u9700\u8981\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u81F3\u5C11\u5305\u542B { firstSendTime }");
1804
1892
  }
1893
+ if (options.taskInput != null && (typeof options.taskInput !== "object" || Array.isArray(options.taskInput))) {
1894
+ throw new TypeError("scheduleTask: taskInput must be an object or null");
1895
+ }
1805
1896
  if (typeof options.firstSendTime !== "string" || !options.firstSendTime.trim()) {
1806
1897
  throw new RangeError("scheduleTask: firstSendTime \u5FC5\u586B\uFF0C\u4E14\u5FC5\u987B\u662F ISO 8601 \u5B57\u7B26\u4E32");
1807
1898
  }
@@ -1896,7 +1987,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1896
1987
  // 与凭据同列的投递配置:中转的非标准参数(thinking 之类)跟着任务走,
1897
1988
  // 自排的后续任务不该突然掉档。
1898
1989
  llmExtraBody: _nullishCoalesce(decryptedPayload.llmExtraBody, () => ( null)),
1899
- metadata
1990
+ metadata,
1991
+ taskInput: _nullishCoalesce(options.taskInput, () => ( null))
1900
1992
  };
1901
1993
  const serializedTaskData = JSON.stringify(fullTaskData);
1902
1994
  const sizeError = validateTaskPayloadSize(serializedTaskData);
@@ -1950,7 +2042,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1950
2042
  created: true,
1951
2043
  id: _nullishCoalesce(created.id, () => ( null)),
1952
2044
  uuid: _nullishCoalesce(created.uuid, () => ( uuid)),
1953
- nextSendAt: _nullishCoalesce(created.next_send_at, () => ( nextSendAt))
2045
+ nextSendAt: _nullishCoalesce(created.next_send_at, () => ( nextSendAt)),
2046
+ task: projectTask({ status: "pending", next_send_at: nextSendAt, ...created }, fullTaskData, { includeMetadata: true })
1954
2047
  };
1955
2048
  };
1956
2049
  const cancelTask = async (uuid) => {
@@ -2018,7 +2111,11 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2018
2111
  const stillPending = await ctx.db.getTaskByUuid(uuid, task.user_id);
2019
2112
  return { renewed: false, reason: stillPending ? "in_flight" : "not_found" };
2020
2113
  }
2021
- return { renewed: true, uuid, nextSendAt: nextSendAtIso };
2114
+ return { renewed: true, uuid, nextSendAt: nextSendAtIso, task: projectTask(
2115
+ { ...row, next_send_at: nextSendAtIso, retry_count: 0 },
2116
+ payload,
2117
+ { includeMetadata: true }
2118
+ ) };
2022
2119
  };
2023
2120
  const resolveLlmCredential2 = async (credId) => {
2024
2121
  if (typeof credId !== "string" || !credId.trim()) {
@@ -2035,7 +2132,9 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2035
2132
  readState,
2036
2133
  writeState,
2037
2134
  emitResult,
2135
+ storeAttachment,
2038
2136
  scheduleTask,
2137
+ listTasks,
2039
2138
  cancelTask,
2040
2139
  renewTask,
2041
2140
  resolveLlmCredential: resolveLlmCredential2,
@@ -2068,8 +2167,10 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2068
2167
  readState,
2069
2168
  writeState,
2070
2169
  emitResult,
2170
+ storeAttachment,
2071
2171
  scratch,
2072
2172
  scheduleTask,
2173
+ listTasks,
2073
2174
  cancelTask,
2074
2175
  renewTask,
2075
2176
  resolveLlmCredential: resolveLlmCredential2,
@@ -2128,7 +2229,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2128
2229
  scratch,
2129
2230
  readState,
2130
2231
  writeState: writeSettledState,
2131
- emitResult
2232
+ emitResult,
2233
+ storeAttachment: storeSettledAttachment
2132
2234
  });
2133
2235
  }
2134
2236
  }
@@ -2143,8 +2245,10 @@ async function runFireChain({
2143
2245
  readState,
2144
2246
  writeState,
2145
2247
  emitResult,
2248
+ storeAttachment,
2146
2249
  scratch,
2147
2250
  scheduleTask,
2251
+ listTasks,
2148
2252
  cancelTask,
2149
2253
  renewTask,
2150
2254
  resolveLlmCredential: resolveLlmCredential2,
@@ -2243,15 +2347,35 @@ async function runFireChain({
2243
2347
  readState,
2244
2348
  writeState,
2245
2349
  emitResult,
2350
+ storeAttachment,
2246
2351
  scheduleTask,
2352
+ listTasks,
2247
2353
  cancelTask,
2248
2354
  renewTask,
2249
2355
  resolveLlmCredential: resolveLlmCredential2
2250
2356
  });
2251
2357
  const decision = await hooks.onLLMOutput(sessionCtx);
2252
2358
  ctx.throwIfCancelled();
2359
+ const rawCalls = _optionalChain([decision, 'optionalAccess', _12 => _12.decision]) === "tool-request" ? _amsgshared.extractToolCallsFromDecision.call(void 0, decision) : [];
2360
+ const validId = (call) => typeof _optionalChain([call, 'optionalAccess', _13 => _13.id]) === "string" && call.id.trim().length > 0;
2361
+ const nativeCalls = Array.isArray(assistantMessage.tool_calls) ? assistantMessage.tool_calls : [];
2362
+ const usedIds = new Set([...rawCalls, ...nativeCalls].filter(validId).map((call) => call.id));
2363
+ const malformedIds = /* @__PURE__ */ new Set();
2364
+ const toolCalls = rawCalls.map((call, index) => {
2365
+ let id = validId(call) ? call.id : `call_${iteration + 1}_${index + 1}`;
2366
+ if (!validId(call)) {
2367
+ while (usedIds.has(id)) id += "_";
2368
+ usedIds.add(id);
2369
+ }
2370
+ if (typeof _optionalChain([call, 'optionalAccess', _14 => _14.function, 'optionalAccess', _15 => _15.name]) !== "string" || !call.function.name.trim()) {
2371
+ malformedIds.add(id);
2372
+ return { id, type: "function", function: { name: "invalid_tool_call", arguments: "{}" } };
2373
+ }
2374
+ return { ...call, id };
2375
+ });
2376
+ const validationDecision = _optionalChain([decision, 'optionalAccess', _16 => _16.decision]) === "tool-request" && Array.isArray(decision.toolCalls) && decision.toolCalls.length > 0 ? { ...decision, toolCalls } : decision;
2253
2377
  try {
2254
- _amsgshared.assertValidDecision.call(void 0, decision, { inlineToolCalls: true });
2378
+ _amsgshared.assertValidDecision.call(void 0, validationDecision, { inlineToolCalls: true });
2255
2379
  } catch (error) {
2256
2380
  throw markPermanent(error, "AGENTIC_BAD_DECISION");
2257
2381
  }
@@ -2282,14 +2406,19 @@ async function runFireChain({
2282
2406
  });
2283
2407
  return { handled: true, result: { success: true, messagesSent, status: "finished", iterations: iteration + 1 } };
2284
2408
  }
2285
- const toolCalls = _amsgshared.extractToolCallsFromDecision.call(void 0, decision);
2286
2409
  if (toolCalls.length === 0) {
2287
2410
  throw taggedRetryableError(
2288
2411
  "AGENTIC_EMPTY_TOOL_REQUEST: tool-request decision carried no toolCalls (neither decision.toolCalls nor pushPayloads[].toolCalls)",
2289
2412
  "AGENTIC_EMPTY_TOOL_REQUEST"
2290
2413
  );
2291
2414
  }
2292
- if (typeof hooks.executeToolCalls !== "function") {
2415
+ const permittedCalls = toolCalls.filter((call) => !malformedIds.has(call.id) && (normalized.allowedToolNames === void 0 || normalized.allowedToolNames.has(call.function.name)));
2416
+ const deniedResults = toolCalls.filter((call) => !permittedCalls.includes(call)).map((call) => ({
2417
+ role: "tool",
2418
+ tool_call_id: call.id,
2419
+ content: JSON.stringify({ error: malformedIds.has(call.id) ? { code: "INVALID_TOOL_CALL", message: "Tool call requires a function name" } : { code: "TOOL_NOT_ALLOWED", message: "Tool execution is not allowed for this fire" } })
2420
+ }));
2421
+ if (permittedCalls.length > 0 && typeof hooks.executeToolCalls !== "function") {
2293
2422
  throw new DeploymentConfigError(
2294
2423
  "AGENTIC_CONFIG_ERROR: onLLMOutput returned tool-request but hooks.executeToolCalls is not configured",
2295
2424
  { code: "AGENTIC_CONFIG_ERROR" }
@@ -2301,7 +2430,7 @@ async function runFireChain({
2301
2430
  let toolResults;
2302
2431
  try {
2303
2432
  ctx.throwIfCancelled();
2304
- toolResults = await hooks.executeToolCalls(toolCalls, sessionCtx);
2433
+ toolResults = permittedCalls.length > 0 ? await hooks.executeToolCalls(permittedCalls, sessionCtx) : [];
2305
2434
  ctx.throwIfCancelled();
2306
2435
  if (!Array.isArray(toolResults)) {
2307
2436
  throw new TypeError('executeToolCalls must resolve to an array of { tool_call_id, role: "tool", content }');
@@ -2309,16 +2438,17 @@ async function runFireChain({
2309
2438
  } catch (error) {
2310
2439
  ctx.throwIfCancelled();
2311
2440
  if (isTaskCancelledError(error)) throw error;
2312
- toolResults = toolCalls.map((toolCall) => ({
2441
+ toolResults = permittedCalls.map((toolCall) => ({
2313
2442
  tool_call_id: toolCall && typeof toolCall === "object" && typeof toolCall.id === "string" ? toolCall.id : "",
2314
2443
  role: "tool",
2315
- content: `Tool execution failed: ${_nullishCoalesce(_optionalChain([error, 'optionalAccess', _9 => _9.message]), () => ( String(error)))}`
2444
+ content: `Tool execution failed: ${_nullishCoalesce(_optionalChain([error, 'optionalAccess', _17 => _17.message]), () => ( String(error)))}`
2316
2445
  }));
2317
2446
  }
2318
- const nativeCalls = Array.isArray(assistantMessage.tool_calls) ? assistantMessage.tool_calls : [];
2319
- const nativeIds = new Set(nativeCalls.map((tc) => tc && tc.id));
2320
- const synthesized = toolCalls.filter((tc) => !nativeIds.has(tc && tc.id));
2321
- const assistantWithTools = synthesized.length === 0 ? assistantMessage : { ...assistantMessage, tool_calls: [...nativeCalls, ...synthesized] };
2447
+ toolResults = [...toolResults, ...deniedResults];
2448
+ const declared = nativeCalls.filter(validId).map((call) => toolCalls.find((item) => item.id === call.id) || call);
2449
+ const nativeIds = new Set(declared.map((call) => call.id));
2450
+ const synthesized = toolCalls.filter((call) => !nativeIds.has(call.id));
2451
+ const assistantWithTools = { ...assistantMessage, tool_calls: [...declared, ...synthesized] };
2322
2452
  messages = [...messages.slice(0, -1), assistantWithTools, ...toolResults];
2323
2453
  }
2324
2454
  throw taggedRetryableError(
@@ -2497,7 +2627,7 @@ function validOwner(owner) {
2497
2627
  return owner && typeof owner.type === "string" && owner.type.trim().length > 0 && owner.type.length <= 100 && typeof owner.id === "string" && owner.id.trim().length > 0 && owner.id.length <= 300;
2498
2628
  }
2499
2629
  function validateGuard(guard) {
2500
- if (!validOwner(_optionalChain([guard, 'optionalAccess', _10 => _10.owner])))
2630
+ if (!validOwner(_optionalChain([guard, 'optionalAccess', _18 => _18.owner])))
2501
2631
  throw guardError("INVALID_CLOUD_OWNER", "Cloud owner is invalid");
2502
2632
  if (!Number.isSafeInteger(guard.generation) || guard.generation < 0) {
2503
2633
  throw guardError(
@@ -2525,14 +2655,14 @@ async function assertCloudGuard(db, userId, guard) {
2525
2655
  }
2526
2656
  async function resolveMetadata(resolveOwner, input, explicit) {
2527
2657
  const resolved = typeof resolveOwner === "function" ? await resolveOwner(input) : null;
2528
- const owner = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _11 => _11.owner]), () => ( _optionalChain([explicit, 'optionalAccess', _12 => _12.metadata, 'optionalAccess', _13 => _13.cloudOwner]))), () => ( _optionalChain([resolved, 'optionalAccess', _14 => _14.owner]))), () => ( (validOwner(resolved) ? resolved : null)));
2658
+ const owner = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _19 => _19.owner]), () => ( _optionalChain([explicit, 'optionalAccess', _20 => _20.metadata, 'optionalAccess', _21 => _21.cloudOwner]))), () => ( _optionalChain([resolved, 'optionalAccess', _22 => _22.owner]))), () => ( (validOwner(resolved) ? resolved : null)));
2529
2659
  if (owner !== null && !validOwner(owner))
2530
2660
  throw guardError("INVALID_CLOUD_OWNER", "Cloud owner is invalid");
2531
- return { owner, kind: _nullishCoalesce(_nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _15 => _15.kind]), () => ( _optionalChain([resolved, 'optionalAccess', _16 => _16.kind]))), () => ( null)) };
2661
+ return { owner, kind: _nullishCoalesce(_nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _23 => _23.kind]), () => ( _optionalChain([resolved, 'optionalAccess', _24 => _24.kind]))), () => ( null)) };
2532
2662
  }
2533
2663
  async function captureCloudTaskGuard(db, userId, payload, resolveOwner, task) {
2534
2664
  if (!db.cloudDataManagement) return null;
2535
- if (_optionalChain([payload, 'optionalAccess', _17 => _17.__cloudOwnerGuard])) {
2665
+ if (_optionalChain([payload, 'optionalAccess', _25 => _25.__cloudOwnerGuard])) {
2536
2666
  const guard2 = validateGuard(payload.__cloudOwnerGuard);
2537
2667
  await assertCloudGuard(db, userId, guard2);
2538
2668
  return guard2;
@@ -2545,7 +2675,7 @@ async function captureCloudTaskGuard(db, userId, payload, resolveOwner, task) {
2545
2675
  if (!owner) return null;
2546
2676
  const guard = validateGuard({
2547
2677
  owner,
2548
- generation: _nullishCoalesce(_optionalChain([payload, 'optionalAccess', _18 => _18.ownerGeneration]), () => ( 0))
2678
+ generation: _nullishCoalesce(_optionalChain([payload, 'optionalAccess', _26 => _26.ownerGeneration]), () => ( 0))
2549
2679
  });
2550
2680
  await assertCloudGuard(db, userId, guard);
2551
2681
  return guard;
@@ -2597,7 +2727,7 @@ function createCloudGuardedAdapter(db, {
2597
2727
  }
2598
2728
  guard = inheritedGuard || validateGuard({
2599
2729
  owner: resolved.owner,
2600
- generation: _nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _19 => _19.ownerGeneration]), () => ( 0))
2730
+ generation: _nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _27 => _27.ownerGeneration]), () => ( 0))
2601
2731
  });
2602
2732
  } else {
2603
2733
  guard = null;
@@ -2605,9 +2735,9 @@ function createCloudGuardedAdapter(db, {
2605
2735
  await assertCloudGuard(db, userId, inheritedGuard);
2606
2736
  await assertCloudGuard(db, userId, guard);
2607
2737
  const metadata = {
2608
- owner: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _20 => _20.owner]), () => ( resolved.owner)),
2738
+ owner: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _28 => _28.owner]), () => ( resolved.owner)),
2609
2739
  kind: resolved.kind,
2610
- generation: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _21 => _21.generation]), () => ( 0))
2740
+ generation: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _29 => _29.generation]), () => ( 0))
2611
2741
  };
2612
2742
  return {
2613
2743
  cloudGuard: guard,
@@ -2787,7 +2917,7 @@ function createCloudGuardedAdapter(db, {
2787
2917
  else delete payload[field];
2788
2918
  }
2789
2919
  if (payload.metadata && typeof payload.metadata === "object") {
2790
- if (_optionalChain([originalPayload, 'access', _22 => _22.metadata, 'optionalAccess', _23 => _23.cloudOwner]) !== void 0)
2920
+ if (_optionalChain([originalPayload, 'access', _30 => _30.metadata, 'optionalAccess', _31 => _31.cloudOwner]) !== void 0)
2791
2921
  payload.metadata.cloudOwner = originalPayload.metadata.cloudOwner;
2792
2922
  else delete payload.metadata.cloudOwner;
2793
2923
  }
@@ -2979,11 +3109,11 @@ async function processSingleMessage(task, ctx, providedMasterKey, predecrypted =
2979
3109
  }
2980
3110
  const userKey = predecrypted && predecrypted.userKey || await deriveUserEncryptionKey(task.user_id, masterKey);
2981
3111
  const decryptedPayload = predecrypted && predecrypted.payload || JSON.parse(await decryptFromStorage(task.encrypted_payload, userKey));
2982
- const cloudGuard = await captureCloudTaskGuard(ctx.db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _24 => _24.cloudData, 'optionalAccess', _25 => _25.resolveOwner]), task);
3112
+ const cloudGuard = await captureCloudTaskGuard(ctx.db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _32 => _32.cloudData, 'optionalAccess', _33 => _33.resolveOwner]), task);
2983
3113
  if (cloudGuard) {
2984
3114
  await assertCloudGuard(ctx.db, task.user_id, cloudGuard);
2985
3115
  const originalWebpush = ctx.webpush;
2986
- const guardedDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _26 => _26.cloudData, 'optionalAccess', _27 => _27.resolveOwner]), guard: cloudGuard, userId: task.user_id });
3116
+ const guardedDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _34 => _34.cloudData, 'optionalAccess', _35 => _35.resolveOwner]), guard: cloudGuard, userId: task.user_id });
2987
3117
  ctx = { ...ctx, db: guardedDb, webpush: originalWebpush && {
2988
3118
  ...originalWebpush,
2989
3119
  async sendNotification(...args) {
@@ -3179,8 +3309,8 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
3179
3309
  try {
3180
3310
  const userKey = await deriveUserEncryptionKey(task.user_id, masterKey);
3181
3311
  const payload = JSON.parse(await decryptFromStorage(task.encrypted_payload, userKey));
3182
- const guard = await captureCloudTaskGuard(ctx.db, task.user_id, payload, _optionalChain([ctx, 'access', _28 => _28.cloudData, 'optionalAccess', _29 => _29.resolveOwner]), task);
3183
- if (guard) attemptDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _30 => _30.cloudData, 'optionalAccess', _31 => _31.resolveOwner]), guard, userId: task.user_id });
3312
+ const guard = await captureCloudTaskGuard(ctx.db, task.user_id, payload, _optionalChain([ctx, 'access', _36 => _36.cloudData, 'optionalAccess', _37 => _37.resolveOwner]), task);
3313
+ if (guard) attemptDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _38 => _38.cloudData, 'optionalAccess', _39 => _39.resolveOwner]), guard, userId: task.user_id });
3184
3314
  } catch (error) {
3185
3315
  return { success: false, error: { code: error.code || "PROCESSING_ERROR", message: error.message, retriesAttempted: retryCount } };
3186
3316
  }
@@ -3216,7 +3346,7 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
3216
3346
  })
3217
3347
  });
3218
3348
  } catch (_updateError) {
3219
- if (_optionalChain([_updateError, 'optionalAccess', _32 => _32.code]) === "CLOUD_OWNER_RETIRED") {
3349
+ if (_optionalChain([_updateError, 'optionalAccess', _40 => _40.code]) === "CLOUD_OWNER_RETIRED") {
3220
3350
  return { success: false, error: { code: _updateError.code, message: _updateError.message, retriesAttempted: retryCount } };
3221
3351
  }
3222
3352
  try {
@@ -3392,6 +3522,9 @@ function createScheduleMessageHandler(ctx) {
3392
3522
  owner: payload.owner,
3393
3523
  kind: payload.kind,
3394
3524
  ownerGeneration: payload.ownerGeneration,
3525
+ // Input is persisted with the task in the same create/supersede transaction.
3526
+ // Shared credentials remain references and continue to resolve at fire time.
3527
+ taskInput: _nullishCoalesce(payload.taskInput, () => ( null)),
3395
3528
  metadata: payload.metadata || {}
3396
3529
  };
3397
3530
  const serializedTaskData = JSON.stringify(fullTaskData);
@@ -3599,7 +3732,7 @@ async function runScheduledTick(ctx) {
3599
3732
  }
3600
3733
  async function cleanupExpiredClientState(ctx) {
3601
3734
  if (typeof ctx.db.cleanupClientState !== "function") return;
3602
- const targets = planClientStateCleanup(ctx.clientStateTtl, Date.now());
3735
+ const targets = planClientStateCleanup({ [ATTACHMENT_NAMESPACE]: ATTACHMENT_RETENTION_DAYS, ...ctx.clientStateTtl }, Date.now());
3603
3736
  if (targets.length === 0) return;
3604
3737
  try {
3605
3738
  await ctx.db.cleanupClientState(targets);
@@ -3689,7 +3822,7 @@ async function deliverTasks(ctx, tasks) {
3689
3822
  const until = new Date(Date.now() + heartbeatLeaseTtlMs).toISOString();
3690
3823
  renewed = guard ? await db.renewTaskLease(task.id, until, guard) : await db.renewTaskLease(task.id, until);
3691
3824
  } catch (error) {
3692
- if (_optionalChain([error, 'optionalAccess', _33 => _33.code]) === "CLOUD_OWNER_RETIRED") renewed = false;
3825
+ if (_optionalChain([error, 'optionalAccess', _41 => _41.code]) === "CLOUD_OWNER_RETIRED") renewed = false;
3693
3826
  else console.warn("[amsg-server] \u79DF\u7EA6\u7EED\u79DF\u5931\u8D25\uFF08\u4E0B\u4E2A\u5FC3\u8DF3\u518D\u8BD5\uFF09:", error && error.message);
3694
3827
  }
3695
3828
  if (renewed === false) {
@@ -3732,7 +3865,7 @@ async function deliverTasks(ctx, tasks) {
3732
3865
  lastErrorColumnSuspicions.delete(db);
3733
3866
  return result2;
3734
3867
  } catch (error) {
3735
- if (_optionalChain([error, 'optionalAccess', _34 => _34.code]) === "CLOUD_OWNER_RETIRED") throw error;
3868
+ if (_optionalChain([error, 'optionalAccess', _42 => _42.code]) === "CLOUD_OWNER_RETIRED") throw error;
3736
3869
  combinedError = error;
3737
3870
  }
3738
3871
  const result = await updateLifecycleTask(taskId, stateFields);
@@ -3927,12 +4060,12 @@ async function deliverTasks(ctx, tasks) {
3927
4060
  let claimed;
3928
4061
  try {
3929
4062
  if (decrypted.ok) {
3930
- const guard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _35 => _35.cloudData, 'optionalAccess', _36 => _36.resolveOwner]), task);
4063
+ const guard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _43 => _43.cloudData, 'optionalAccess', _44 => _44.resolveOwner]), task);
3931
4064
  if (guard) taskCloudGuards.set(task.id, guard);
3932
4065
  }
3933
4066
  claimed = await claimForThisTick(task, serializeGroup);
3934
4067
  } catch (error) {
3935
- if (_optionalChain([error, 'optionalAccess', _37 => _37.code]) === "CLOUD_OWNER_RETIRED") {
4068
+ if (_optionalChain([error, 'optionalAccess', _45 => _45.code]) === "CLOUD_OWNER_RETIRED") {
3936
4069
  await recordCancelled(task, "owner_retired");
3937
4070
  return;
3938
4071
  }
@@ -3967,8 +4100,8 @@ async function deliverTasks(ctx, tasks) {
3967
4100
  const notOnFreshRetryChain = (task.retry_count || 0) === 0 || Number.isFinite(retryAfterMs) && Date.now() - retryAfterMs > staleAfterMs;
3968
4101
  const settleAsStale = async () => {
3969
4102
  try {
3970
- const cloudGuard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _38 => _38.cloudData, 'optionalAccess', _39 => _39.resolveOwner]), task);
3971
- const guardedDb = createCloudGuardedAdapter(db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _40 => _40.cloudData, 'optionalAccess', _41 => _41.resolveOwner]), guard: cloudGuard, userId: task.user_id });
4103
+ const cloudGuard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _46 => _46.cloudData, 'optionalAccess', _47 => _47.resolveOwner]), task);
4104
+ const guardedDb = createCloudGuardedAdapter(db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _48 => _48.cloudData, 'optionalAccess', _49 => _49.resolveOwner]), guard: cloudGuard, userId: task.user_id });
3972
4105
  const guardedWebpush = cloudGuard && ctx.webpush ? {
3973
4106
  ...ctx.webpush,
3974
4107
  async sendNotification(...args) {
@@ -4259,6 +4392,9 @@ function createUpdateMessageHandler(ctx) {
4259
4392
  if (!isPlainObject(updates)) {
4260
4393
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF" } } };
4261
4394
  }
4395
+ if (Object.prototype.hasOwnProperty.call(updates, "taskInput")) {
4396
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "taskInput \u53EA\u80FD\u5728\u521B\u5EFA\u6216\u66FF\u6362\u4EFB\u52A1\u65F6\u8BBE\u7F6E", details: { invalidFields: ["taskInput"] } } } };
4397
+ }
4262
4398
  if (updates.nextSendAt && !isValidISO8601(updates.nextSendAt)) {
4263
4399
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["nextSendAt"] } } } };
4264
4400
  }
@@ -5270,7 +5406,7 @@ var D1Adapter = class {
5270
5406
  return typeof this._db.batch === "function";
5271
5407
  }
5272
5408
  _cloudGuardPredicate(userId, guard) {
5273
- if (!_optionalChain([guard, 'optionalAccess', _42 => _42.owner]) || !Number.isSafeInteger(guard.generation) || guard.generation < 0) {
5409
+ if (!_optionalChain([guard, 'optionalAccess', _50 => _50.owner]) || !Number.isSafeInteger(guard.generation) || guard.generation < 0) {
5274
5410
  throw new Error("Invalid cloud owner guard");
5275
5411
  }
5276
5412
  return {
@@ -5297,7 +5433,7 @@ var D1Adapter = class {
5297
5433
  indexes.push(expanded.length);
5298
5434
  expanded.push(statements[i]);
5299
5435
  const item = metadata.get(i);
5300
- if (_optionalChain([item, 'optionalAccess', _43 => _43.value])) {
5436
+ if (_optionalChain([item, 'optionalAccess', _51 => _51.value])) {
5301
5437
  expanded.push(this._db.prepare(
5302
5438
  `INSERT INTO cloud_resource_metadata
5303
5439
  (user_id, resource_key, encrypted_value) SELECT ?, ?, ? WHERE changes() > 0
@@ -5332,14 +5468,39 @@ var D1Adapter = class {
5332
5468
  }
5333
5469
  async getCloudResourceMetadata(userId, resourceKey) {
5334
5470
  const row = await this._db.prepare("SELECT encrypted_value FROM cloud_resource_metadata WHERE user_id=? AND resource_key=?").bind(userId, resourceKey).first();
5335
- return _nullishCoalesce(_optionalChain([row, 'optionalAccess', _44 => _44.encrypted_value]), () => ( null));
5471
+ return _nullishCoalesce(_optionalChain([row, 'optionalAccess', _52 => _52.encrypted_value]), () => ( null));
5472
+ }
5473
+ async getOutboxPayload(userId, messageId) {
5474
+ const row = await this._db.prepare(
5475
+ "SELECT payload FROM message_outbox WHERE user_id = ? AND message_id = ? LIMIT 1"
5476
+ ).bind(userId, messageId).first();
5477
+ return _nullishCoalesce(_optionalChain([row, 'optionalAccess', _53 => _53.payload]), () => ( null));
5336
5478
  }
5337
5479
  async listCloudResourceRows(userId) {
5338
- const tables = { task: "scheduled_messages", state: "client_state", credential: "llm_credentials", outbox: "message_outbox", subscription: "push_subscriptions", metadata: "cloud_resource_metadata" };
5480
+ const queries = {
5481
+ task: "SELECT * FROM scheduled_messages WHERE user_id = ?",
5482
+ state: `SELECT user_id, namespace, key,
5483
+ CASE WHEN LENGTH(value) <= 1024 THEN value ELSE NULL END AS value,
5484
+ LENGTH(CAST(value AS BLOB)) AS byte_size,
5485
+ substr(value, 1, 128) AS value_prefix,
5486
+ CASE WHEN LENGTH(value) > 128 THEN substr(value, -64) ELSE '' END AS value_suffix,
5487
+ updated_at
5488
+ FROM client_state WHERE user_id = ?`,
5489
+ credential: "SELECT * FROM llm_credentials WHERE user_id = ?",
5490
+ outbox: `SELECT id, user_id, message_id, task_uuid, session_id, message_index, total_messages,
5491
+ CASE WHEN LENGTH(payload) <= 1024 THEN payload ELSE NULL END AS payload,
5492
+ LENGTH(CAST(payload AS BLOB)) AS byte_size,
5493
+ substr(payload, 1, 128) AS payload_prefix,
5494
+ CASE WHEN LENGTH(payload) > 128 THEN substr(payload, -64) ELSE '' END AS payload_suffix,
5495
+ created_at, delivered_at, acked_at
5496
+ FROM message_outbox WHERE user_id = ?`,
5497
+ subscription: "SELECT * FROM push_subscriptions WHERE user_id = ?",
5498
+ metadata: "SELECT * FROM cloud_resource_metadata WHERE user_id = ?"
5499
+ };
5339
5500
  const result = { gaps: [] };
5340
- await Promise.all(Object.entries(tables).map(async ([type, table]) => {
5501
+ await Promise.all(Object.entries(queries).map(async ([type, sql]) => {
5341
5502
  try {
5342
- const response = await this._db.prepare(`SELECT * FROM ${table} WHERE user_id = ?`).bind(userId).all();
5503
+ const response = await this._db.prepare(sql).bind(userId).all();
5343
5504
  result[type] = response.results || [];
5344
5505
  } catch (e4) {
5345
5506
  result[type] = [];
@@ -5404,7 +5565,10 @@ var D1Adapter = class {
5404
5565
  if (idempotencyKey) {
5405
5566
  return this._cloudRecord(await this._db.prepare(`SELECT * FROM cloud_data_records WHERE user_id=? AND kind=? AND idempotency_key=?`).bind(userId, kind, idempotencyKey).first());
5406
5567
  }
5407
- return this.getCloudDataRecord(userId, kind, id);
5568
+ if (createOnly) {
5569
+ return this.getCloudDataRecord(userId, kind, id);
5570
+ }
5571
+ return { id, userId, kind, data, updatedAt: now };
5408
5572
  }
5409
5573
  async getCloudDataRecord(userId, kind, id) {
5410
5574
  return this._cloudRecord(await this._db.prepare("SELECT * FROM cloud_data_records WHERE user_id=? AND kind=? AND id=?").bind(userId, kind, id).first());
@@ -5537,7 +5701,7 @@ var D1Adapter = class {
5537
5701
  const name = "metadata-sidecars-v1";
5538
5702
  const batch = await this.getCloudMaintenanceBatch(name, "cloud_resource_metadata", limit);
5539
5703
  if (batch.completed) return;
5540
- const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _45 => _45.rows, 'access', _46 => _46.at, 'call', _47 => _47(-1), 'optionalAccess', _48 => _48.maintenance_rowid]), () => ( batch.upperBound));
5704
+ const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _54 => _54.rows, 'access', _55 => _55.at, 'call', _56 => _56(-1), 'optionalAccess', _57 => _57.maintenance_rowid]), () => ( batch.upperBound));
5541
5705
  if (batch.rows.length) await this.cleanupCloudResourceMetadata(null, { after: batch.cursor, through: cursor });
5542
5706
  await this.finishCloudMaintenanceBatch(name, cursor, batch.rows.length < limit || cursor >= batch.upperBound);
5543
5707
  }
@@ -5611,7 +5775,30 @@ var D1Adapter = class {
5611
5775
  if (!guard) return true;
5612
5776
  const condition = this._cloudGuardPredicate(userId, guard);
5613
5777
  const row = await this._db.prepare(`SELECT ${condition.sql} AS valid`).bind(...condition.args).first();
5614
- return !!_optionalChain([row, 'optionalAccess', _49 => _49.valid]);
5778
+ return !!_optionalChain([row, 'optionalAccess', _58 => _58.valid]);
5779
+ }
5780
+ _rowSnapshotMatch(spec, row) {
5781
+ const clauses = [];
5782
+ const args = [];
5783
+ for (const column of spec.columns) {
5784
+ if (!Object.hasOwn(row, column)) continue;
5785
+ if ((column === "value" || column === "payload") && row[column] === null && typeof row.byte_size === "number") {
5786
+ clauses.push(
5787
+ `LENGTH(CAST(${column} AS BLOB)) IS ?`,
5788
+ `substr(${column}, 1, 128) IS ?`,
5789
+ `(CASE WHEN LENGTH(${column}) > 128 THEN substr(${column}, -64) ELSE '' END) IS ?`
5790
+ );
5791
+ args.push(
5792
+ row.byte_size,
5793
+ _nullishCoalesce(row[`${column}_prefix`], () => ( null)),
5794
+ _nullishCoalesce(row[`${column}_suffix`], () => ( ""))
5795
+ );
5796
+ } else {
5797
+ clauses.push(`${column} IS ?`);
5798
+ args.push(row[column]);
5799
+ }
5800
+ }
5801
+ return { predicate: clauses.join(" AND "), values: args };
5615
5802
  }
5616
5803
  /** Delete only the exact physical snapshot; no untrusted SQL identifiers. */
5617
5804
  async deleteCloudResourceRows(userId, locators, { operationId, leaseToken } = {}) {
@@ -5660,9 +5847,7 @@ var D1Adapter = class {
5660
5847
  ).bind(userId, namespace, key, "amsg-chunks" + namespace, key + "", key + " ", identities));
5661
5848
  }
5662
5849
  for (const row of locator.rows) {
5663
- const columns = spec.columns.filter((column) => Object.hasOwn(row, column));
5664
- const predicate = columns.map((column) => `${column} IS ?`).join(" AND ");
5665
- const values = columns.map((column) => row[column]);
5850
+ const { predicate, values } = this._rowSnapshotMatch(spec, row);
5666
5851
  const identity = spec.keys.map((column) => `${column} IS ?`).join(" AND ");
5667
5852
  statements.push(this._db.prepare(
5668
5853
  `INSERT INTO cloud_guard_assertions (allowed)
@@ -5671,8 +5856,8 @@ var D1Adapter = class {
5671
5856
  ).bind(...spec.keys.map((column) => row[column]), ...values));
5672
5857
  }
5673
5858
  for (const row of locator.rows) {
5674
- const columns = spec.columns.filter((column) => Object.hasOwn(row, column));
5675
- statements.push(this._db.prepare(`DELETE FROM ${spec.table} WHERE ${columns.map((column) => `${column} IS ?`).join(" AND ")}`).bind(...columns.map((column) => row[column])));
5859
+ const { predicate, values } = this._rowSnapshotMatch(spec, row);
5860
+ statements.push(this._db.prepare(`DELETE FROM ${spec.table} WHERE ${predicate}`).bind(...values));
5676
5861
  }
5677
5862
  if (locator.metadataKey) statements.push(this._db.prepare("DELETE FROM cloud_resource_metadata WHERE user_id=? AND resource_key=?").bind(userId, locator.metadataKey));
5678
5863
  try {
@@ -5864,7 +6049,7 @@ var D1Adapter = class {
5864
6049
  ).bind(...values);
5865
6050
  const ownerUser = cloudGuard ? await this._db.prepare("SELECT user_id FROM scheduled_messages WHERE id=?").bind(taskId).first() : null;
5866
6051
  if (cloudGuard && !ownerUser) return null;
5867
- await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _50 => _50.user_id]), [cloudGuard]);
6052
+ await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _59 => _59.user_id]), [cloudGuard]);
5868
6053
  return this._db.prepare("SELECT * FROM scheduled_messages WHERE id = ?").bind(taskId).first();
5869
6054
  }
5870
6055
  /**
@@ -5989,7 +6174,7 @@ var D1Adapter = class {
5989
6174
  ).bind(...values);
5990
6175
  const ownerUser = cloudGuard ? await this._db.prepare("SELECT user_id FROM scheduled_messages WHERE id=?").bind(taskId).first() : null;
5991
6176
  if (cloudGuard && !ownerUser) return false;
5992
- const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _51 => _51.user_id]), [cloudGuard]);
6177
+ const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _60 => _60.user_id]), [cloudGuard]);
5993
6178
  return (res.meta.changes || 0) > 0;
5994
6179
  }
5995
6180
  /**
@@ -6008,7 +6193,7 @@ var D1Adapter = class {
6008
6193
  ).bind(this._iso(leaseUntil), taskId);
6009
6194
  const ownerUser = cloudGuard ? await this._db.prepare("SELECT user_id FROM scheduled_messages WHERE id=?").bind(taskId).first() : null;
6010
6195
  if (cloudGuard && !ownerUser) return false;
6011
- const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _52 => _52.user_id]), [cloudGuard]);
6196
+ const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _61 => _61.user_id]), [cloudGuard]);
6012
6197
  return (res.meta.changes || 0) > 0;
6013
6198
  }
6014
6199
  async listTasks(userId, opts = {}) {
@@ -6169,30 +6354,39 @@ var D1Adapter = class {
6169
6354
  result.cleanupOutcomes = probeIndexes.map((probeAt) => {
6170
6355
  if (probeAt === null) return null;
6171
6356
  const row = results[probeAt] && Array.isArray(results[probeAt].results) ? results[probeAt].results[0] : null;
6172
- return Number(_nullishCoalesce(_optionalChain([row, 'optionalAccess', _53 => _53.n]), () => ( 0))) === 0;
6357
+ return Number(_nullishCoalesce(_optionalChain([row, 'optionalAccess', _62 => _62.n]), () => ( 0))) === 0;
6173
6358
  });
6174
6359
  }
6175
6360
  return result;
6176
6361
  }
6177
6362
  /**
6178
- * All entries of one namespace (values still encrypted).
6363
+ * Entries of one namespace (values still encrypted); optional exact/prefix selection.
6179
6364
  *
6180
6365
  * @param {string} userId
6181
6366
  * @param {string} namespace
6182
6367
  * @returns {Promise<Array<{ namespace: string, key: string, value: string, updated_at: number }>>}
6183
6368
  */
6184
- async getClientState(userId, namespace) {
6369
+ async getClientState(userId, namespace, options = {}) {
6370
+ const params = [userId, namespace];
6371
+ let selector = "";
6372
+ if (options.key !== void 0) {
6373
+ selector = " AND key = ?";
6374
+ params.push(options.key);
6375
+ } else if (options.keyPrefix !== void 0) {
6376
+ selector = " AND key >= ? AND key < ?";
6377
+ params.push(options.keyPrefix, prefixRangeEnd(options.keyPrefix));
6378
+ }
6185
6379
  const res = await this._db.prepare(
6186
6380
  `SELECT namespace, key, value, updated_at
6187
6381
  FROM client_state
6188
- WHERE user_id = ? AND namespace = ?
6382
+ WHERE user_id = ? AND namespace = ?${selector}
6189
6383
  ORDER BY key ASC`
6190
- ).bind(userId, namespace).all();
6384
+ ).bind(...params).all();
6191
6385
  return res.results || [];
6192
6386
  }
6193
6387
  /**
6194
6388
  * 例行清理:把指定命名空间下太久没更新的条目删掉(run-tick 每跳顺手调,
6195
- * 宿主配了 `clientStateTtl` 才会调)。
6389
+ * 附件默认保留 30 天,宿主 `clientStateTtl` 可覆盖或配置其他命名空间)。
6196
6390
  *
6197
6391
  * 不限用户——「这个命名空间只留最近 N 天」是命名空间级的约定,单用户部署
6198
6392
  * 下也就是这一个用户的行。指令由 lib/client-state-store.js 的
@@ -6924,8 +7118,9 @@ function createClientStateHandler(ctx) {
6924
7118
  if (typeof db.upsertClientState !== "function") {
6925
7119
  return err3(501, "CLIENT_STATE_NOT_SUPPORTED", "\u5F53\u524D\u6570\u636E\u5E93\u9002\u914D\u5668\u4E0D\u652F\u6301 client_state");
6926
7120
  }
6927
- const { upserted, skipped, deleted, skippedEntries } = await writeClientStateEntries({ db, userId, userKey, entries: accepted });
7121
+ const { upserted, skipped, deleted, skippedEntries, unknown } = await writeClientStateEntries({ db, userId, userKey, entries: accepted });
6928
7122
  const data = { upserted, skipped };
7123
+ if (unknown > 0) data.unknown = unknown;
6929
7124
  if (deleted > 0) data.deleted = deleted;
6930
7125
  if (skippedEntries.length > 0) data.skippedEntries = skippedEntries;
6931
7126
  if (rejected.length > 0) data.rejected = rejected;
@@ -6938,7 +7133,12 @@ function createClientStateHandler(ctx) {
6938
7133
  const gate = requireUserId(headers);
6939
7134
  if (gate.error) return gate.error;
6940
7135
  const { userId } = gate;
6941
- const namespace = new URL(url, "https://dummy").searchParams.get("namespace") || "";
7136
+ const query = new URL(url, "https://dummy").searchParams;
7137
+ const namespace = query.get("namespace") || "";
7138
+ const key = query.get("key");
7139
+ if (key !== null && (!key.trim() || key.length > MAX_KEY_CHARS || INTERNAL_STATE_CHAR_RE.test(key))) {
7140
+ return err3(400, "INVALID_STATE_KEY", "key must be non-empty, at most 256 characters, without control characters");
7141
+ }
6942
7142
  if (!namespace.trim()) return err3(400, "NAMESPACE_REQUIRED", "\u5FC5\u987B\u63D0\u4F9B namespace \u67E5\u8BE2\u53C2\u6570");
6943
7143
  if (INTERNAL_STATE_CHAR_RE.test(namespace)) {
6944
7144
  return err3(400, "INVALID_STATE_NAMESPACE", "namespace \u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26\uFF08\\u0000-\\u001f \u4E3A\u5E93\u5185\u90E8\u4FDD\u7559\uFF09");
@@ -6947,10 +7147,10 @@ function createClientStateHandler(ctx) {
6947
7147
  return err3(501, "CLIENT_STATE_NOT_SUPPORTED", "\u5F53\u524D\u6570\u636E\u5E93\u9002\u914D\u5668\u4E0D\u652F\u6301 client_state");
6948
7148
  }
6949
7149
  const userKey = await deriveUserEncryptionKey(userId, masterKey);
6950
- const rows = await db.getClientState(userId, namespace);
7150
+ const rows = await db.getClientState(userId, namespace, key === null ? void 0 : { key });
6951
7151
  const decrypted = await resolveClientStateEntries(
6952
- rows,
6953
- () => db.getClientState(userId, chunkNamespaceFor(namespace)),
7152
+ key === null ? rows : rows.filter((row) => row.key === key),
7153
+ () => db.getClientState(userId, chunkNamespaceFor(namespace), key === null ? void 0 : { keyPrefix: chunkKeyPrefixFor(key) }),
6954
7154
  (value) => decryptFromStorage(value, userKey)
6955
7155
  );
6956
7156
  const encryptedResponse = await encryptPayload({ namespace, entries: decrypted }, userKey);
@@ -7028,7 +7228,7 @@ function createClientStateNamespacesHandler(ctx) {
7028
7228
  }
7029
7229
 
7030
7230
  // src/server/lib/version.js
7031
- var SERVER_VERSION = true ? "3.0.0" : "0.0.0-dev";
7231
+ var SERVER_VERSION = true ? "3.1.0-next.1" : "0.0.0-dev";
7032
7232
 
7033
7233
  // src/server/handlers/capabilities.js
7034
7234
  var SERVER_FEATURES = Object.freeze([
@@ -7144,7 +7344,12 @@ var SERVER_FEATURES = Object.freeze([
7144
7344
  "max-generation-retries",
7145
7345
  // onBeforeFire 认 { defer: { afterMs } }:这次不生成,过一会儿再来问(不占重试
7146
7346
  // 次数);onFireSettled 的 status 多一种 'deferred',带 retryAfter。
7147
- "before-fire-defer"
7347
+ "before-fire-defer",
7348
+ "task-input",
7349
+ "client-state-key",
7350
+ "agentic-list-tasks",
7351
+ "agentic-tool-allowlist",
7352
+ "attachments"
7148
7353
  ]);
7149
7354
  function createCapabilitiesHandler(ctx) {
7150
7355
  async function GET(url, headers) {
@@ -7191,6 +7396,39 @@ function objectOrText(value) {
7191
7396
  return value;
7192
7397
  }
7193
7398
  }
7399
+ function utf8ByteLength(text) {
7400
+ if (typeof text !== "string" || text.length === 0) return 0;
7401
+ return /[^\x00-\x7f]/.test(text) ? encoder.encode(text).length : text.length;
7402
+ }
7403
+ function rowByteSize(row) {
7404
+ if (typeof row.byte_size === "number") return row.byte_size;
7405
+ return utf8ByteLength(
7406
+ String(
7407
+ _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(row.value, () => ( row.encrypted_payload)), () => ( row.encrypted_value)), () => ( row.payload)), () => ( row.subscription)), () => ( ""))
7408
+ )
7409
+ );
7410
+ }
7411
+ var STORAGE_PREFIX_RE = /^[0-9a-f]{32}:[0-9a-f]{24,32}:[0-9a-f]*$/i;
7412
+ var HEX_RE = /^[0-9a-f]*$/i;
7413
+ function isValidStorageCiphertext(fullText, prefix, suffix, byteSize) {
7414
+ if (typeof fullText === "string") {
7415
+ if (!STORAGE_PREFIX_RE.test(fullText)) return false;
7416
+ const secondColon = fullText.indexOf(":", 33);
7417
+ return secondColon > 32 && (fullText.length - secondColon - 1) % 2 === 0;
7418
+ }
7419
+ if (typeof prefix === "string" && typeof byteSize === "number") {
7420
+ if (!STORAGE_PREFIX_RE.test(prefix)) return false;
7421
+ if (typeof suffix === "string" && !HEX_RE.test(suffix)) return false;
7422
+ const secondColon = prefix.indexOf(":", 33);
7423
+ return secondColon > 32 && (byteSize - secondColon - 1) % 2 === 0;
7424
+ }
7425
+ return false;
7426
+ }
7427
+ function hasCompleteStoredMetadata(stored) {
7428
+ return Boolean(
7429
+ stored && safeOwner(stored.owner) !== null && typeof stored.kind === "string" && stored.kind.length > 0
7430
+ );
7431
+ }
7194
7432
  async function buildCloudDataInventory({
7195
7433
  db,
7196
7434
  userId,
@@ -7203,6 +7441,20 @@ async function buildCloudDataInventory({
7203
7441
  const metadata = new Map(
7204
7442
  (raw.metadata || []).map((row) => [row.resource_key, row.encrypted_value])
7205
7443
  );
7444
+ const readStoredMetadata = async (metadataKey) => {
7445
+ const encryptedMetadata = metadata.get(metadataKey) || null;
7446
+ if (!encryptedMetadata) {
7447
+ return { encryptedMetadata: null, stored: null, unreadable: false };
7448
+ }
7449
+ try {
7450
+ const stored = JSON.parse(
7451
+ await decryptFromStorage(encryptedMetadata, userKey)
7452
+ );
7453
+ return { encryptedMetadata, stored, unreadable: false };
7454
+ } catch (e6) {
7455
+ return { encryptedMetadata, stored: null, unreadable: true };
7456
+ }
7457
+ };
7206
7458
  const taskPayloads = /* @__PURE__ */ new Map();
7207
7459
  const taskByUuid = /* @__PURE__ */ new Map();
7208
7460
  for (const row of raw.task || []) {
@@ -7212,7 +7464,7 @@ async function buildCloudDataInventory({
7212
7464
  );
7213
7465
  taskPayloads.set(row.id, payload);
7214
7466
  if (row.uuid) taskByUuid.set(row.uuid, payload);
7215
- } catch (e6) {
7467
+ } catch (e7) {
7216
7468
  }
7217
7469
  }
7218
7470
  const tasks = (raw.task || []).filter((row) => taskPayloads.has(row.id)).map((row) => ({ ...taskPayloads.get(row.id), uuid: row.uuid }));
@@ -7227,23 +7479,19 @@ async function buildCloudDataInventory({
7227
7479
  status = null,
7228
7480
  namespace,
7229
7481
  key,
7230
- credId
7482
+ credId,
7483
+ metadataInfo
7231
7484
  }) => {
7232
7485
  const metadataKey = JSON.stringify([type, ...identity]);
7233
7486
  const id = await cloudDataHash(JSON.stringify([userId, metadataKey]));
7234
- let stored = null;
7235
- const encryptedMetadata = metadata.get(metadataKey);
7236
- if (encryptedMetadata) {
7237
- try {
7238
- stored = JSON.parse(
7239
- await decryptFromStorage(encryptedMetadata, userKey)
7240
- );
7241
- } catch (e7) {
7242
- unreadable = true;
7243
- }
7244
- }
7487
+ const {
7488
+ encryptedMetadata,
7489
+ stored,
7490
+ unreadable: metadataUnreadable
7491
+ } = await _asyncNullishCoalesce(metadataInfo, async () => ( await readStoredMetadata(metadataKey)));
7492
+ if (metadataUnreadable) unreadable = true;
7245
7493
  let resolved = null;
7246
- if (resolveOwner) {
7494
+ if (resolveOwner && !hasCompleteStoredMetadata(stored)) {
7247
7495
  try {
7248
7496
  resolved = await resolveOwner({
7249
7497
  type,
@@ -7263,17 +7511,10 @@ async function buildCloudDataInventory({
7263
7511
  });
7264
7512
  }
7265
7513
  }
7266
- const owner = safeOwner(_optionalChain([stored, 'optionalAccess', _54 => _54.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _55 => _55.__cloudOwnerGuard, 'optionalAccess', _56 => _56.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _57 => _57.metadata, 'optionalAccess', _58 => _58.cloudOwner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _59 => _59.metadata, 'optionalAccess', _60 => _60.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _61 => _61.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _62 => _62.__cloudOwnerGuard, 'optionalAccess', _63 => _63.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _64 => _64.metadata, 'optionalAccess', _65 => _65.cloudOwner])) || safeOwner(_optionalChain([task, 'optionalAccess', _66 => _66.metadata, 'optionalAccess', _67 => _67.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _68 => _68.owner])) || safeOwner(_optionalChain([resolved, 'optionalAccess', _69 => _69.owner]));
7267
- const kind = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([stored, 'optionalAccess', _70 => _70.kind]), () => ( _optionalChain([payload, 'optionalAccess', _71 => _71.metadata, 'optionalAccess', _72 => _72.kind]))), () => ( _optionalChain([payload, 'optionalAccess', _73 => _73.kind]))), () => ( _optionalChain([resolved, 'optionalAccess', _74 => _74.kind]))), () => ( null));
7514
+ const owner = safeOwner(_optionalChain([stored, 'optionalAccess', _63 => _63.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _64 => _64.__cloudOwnerGuard, 'optionalAccess', _65 => _65.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _66 => _66.metadata, 'optionalAccess', _67 => _67.cloudOwner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _68 => _68.metadata, 'optionalAccess', _69 => _69.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _70 => _70.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _71 => _71.__cloudOwnerGuard, 'optionalAccess', _72 => _72.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _73 => _73.metadata, 'optionalAccess', _74 => _74.cloudOwner])) || safeOwner(_optionalChain([task, 'optionalAccess', _75 => _75.metadata, 'optionalAccess', _76 => _76.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _77 => _77.owner])) || safeOwner(_optionalChain([resolved, 'optionalAccess', _78 => _78.owner]));
7515
+ const kind = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([stored, 'optionalAccess', _79 => _79.kind]), () => ( _optionalChain([payload, 'optionalAccess', _80 => _80.metadata, 'optionalAccess', _81 => _81.kind]))), () => ( _optionalChain([payload, 'optionalAccess', _82 => _82.kind]))), () => ( _optionalChain([resolved, 'optionalAccess', _83 => _83.kind]))), () => ( null));
7268
7516
  const times = rows.map((row) => timestamp(_nullishCoalesce(row.updated_at, () => ( row.created_at)))).filter((time) => time !== null);
7269
- const byteSize = rows.reduce(
7270
- (sum, row) => sum + encoder.encode(
7271
- String(
7272
- _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(row.value, () => ( row.encrypted_payload)), () => ( row.encrypted_value)), () => ( row.payload)), () => ( row.subscription)), () => ( ""))
7273
- )
7274
- ).length,
7275
- 0
7276
- );
7517
+ const byteSize = rows.reduce((sum, row) => sum + rowByteSize(row), 0);
7277
7518
  const label = type === "state" ? `${namespace}/${key}` : type === "credential" ? credId : type === "task" ? rows[0].uuid || `task row ${rows[0].id}` : type === "outbox" ? rows[0].message_id : "Web Push subscription";
7278
7519
  entries.push({
7279
7520
  resource: {
@@ -7328,22 +7569,75 @@ async function buildCloudDataInventory({
7328
7569
  const slices = chunks.get(group) || [];
7329
7570
  chunks.delete(group);
7330
7571
  const rows = [row, ...slices.sort((a, b) => a.key.localeCompare(b.key))];
7331
- let value, unreadable = false;
7572
+ const metadataKey = JSON.stringify(["state", row.namespace, row.key]);
7573
+ const metadataInfo = await readStoredMetadata(metadataKey);
7574
+ const skipFullDecrypt = !metadataInfo.unreadable && hasCompleteStoredMetadata(metadataInfo.stored);
7575
+ let value, unreadable = metadataInfo.unreadable;
7332
7576
  try {
7333
- const count = parseChunkedRootCount(row.value);
7334
- if (count === null) value = await decryptFromStorage(row.value, userKey);
7335
- else {
7577
+ const rootMarker = _nullishCoalesce(_nullishCoalesce(row.value, () => ( row.value_prefix)), () => ( ""));
7578
+ const count = parseChunkedRootCount(rootMarker);
7579
+ if (count === null) {
7580
+ if (typeof row.value === "string" && (!skipFullDecrypt || row.value.length <= 1024)) {
7581
+ value = await decryptFromStorage(row.value, userKey);
7582
+ } else if (skipFullDecrypt) {
7583
+ if (!isValidStorageCiphertext(
7584
+ row.value,
7585
+ row.value_prefix,
7586
+ row.value_suffix,
7587
+ row.byte_size
7588
+ )) {
7589
+ throw new Error("Invalid state ciphertext");
7590
+ }
7591
+ } else {
7592
+ const fetched = typeof db.getClientState === "function" ? await db.getClientState(userId, row.namespace, { key: row.key }) : [];
7593
+ const rawValue = _optionalChain([fetched, 'optionalAccess', _84 => _84[0], 'optionalAccess', _85 => _85.value]);
7594
+ if (typeof rawValue !== "string") {
7595
+ throw new Error("Missing state value");
7596
+ }
7597
+ value = await decryptFromStorage(rawValue, userKey);
7598
+ }
7599
+ } else {
7336
7600
  if (count > slices.length) throw new Error("Missing state chunks");
7337
- const parts = [];
7601
+ const sliceByKey = new Map(
7602
+ slices.map((candidate) => [candidate.key, candidate])
7603
+ );
7604
+ const orderedSlices = [];
7338
7605
  for (let i = 0; i < count; i++) {
7339
- const slice = slices.find(
7340
- (candidate) => candidate.key === chunkKeyFor(row.key, i)
7341
- );
7606
+ const slice = sliceByKey.get(chunkKeyFor(row.key, i));
7342
7607
  if (!slice || slice.updated_at !== row.updated_at)
7343
7608
  throw new Error("Mismatched state chunks");
7344
- parts.push(await decryptFromStorage(slice.value, userKey));
7609
+ if (!isValidStorageCiphertext(
7610
+ slice.value,
7611
+ slice.value_prefix,
7612
+ slice.value_suffix,
7613
+ slice.byte_size
7614
+ )) {
7615
+ throw new Error("Corrupt state chunk");
7616
+ }
7617
+ orderedSlices.push(slice);
7618
+ }
7619
+ if (!skipFullDecrypt) {
7620
+ let fetchedByKey = null;
7621
+ if (orderedSlices.some((slice) => typeof slice.value !== "string")) {
7622
+ const fetchedSlices = typeof db.getClientState === "function" ? await db.getClientState(
7623
+ userId,
7624
+ CHUNK_NAMESPACE_PREFIX + row.namespace,
7625
+ { keyPrefix: `${row.key}` }
7626
+ ) : [];
7627
+ fetchedByKey = new Map(
7628
+ fetchedSlices.map((candidate) => [candidate.key, candidate.value])
7629
+ );
7630
+ }
7631
+ const parts = [];
7632
+ for (const slice of orderedSlices) {
7633
+ const rawSliceValue = typeof slice.value === "string" ? slice.value : _optionalChain([fetchedByKey, 'optionalAccess', _86 => _86.get, 'call', _87 => _87(slice.key)]);
7634
+ if (typeof rawSliceValue !== "string") {
7635
+ throw new Error("Missing state chunk value");
7636
+ }
7637
+ parts.push(await decryptFromStorage(rawSliceValue, userKey));
7638
+ }
7639
+ value = parts.join("");
7345
7640
  }
7346
- value = parts.join("");
7347
7641
  }
7348
7642
  } catch (e9) {
7349
7643
  unreadable = true;
@@ -7356,7 +7650,8 @@ async function buildCloudDataInventory({
7356
7650
  rows,
7357
7651
  value,
7358
7652
  unreadable,
7359
- status: null
7653
+ status: null,
7654
+ metadataInfo
7360
7655
  });
7361
7656
  }
7362
7657
  for (const [group, rows] of chunks) {
@@ -7380,9 +7675,29 @@ async function buildCloudDataInventory({
7380
7675
  });
7381
7676
  }
7382
7677
  for (const row of raw.outbox || []) {
7383
- let payload, unreadable = false;
7678
+ const metadataKey = JSON.stringify(["outbox", row.message_id]);
7679
+ const metadataInfo = await readStoredMetadata(metadataKey);
7680
+ const skipFullDecrypt = !metadataInfo.unreadable && hasCompleteStoredMetadata(metadataInfo.stored);
7681
+ let payload, unreadable = metadataInfo.unreadable;
7384
7682
  try {
7385
- payload = objectOrText(await decryptFromStorage(row.payload, userKey));
7683
+ if (typeof row.payload === "string" && (!skipFullDecrypt || row.payload.length <= 1024)) {
7684
+ payload = objectOrText(await decryptFromStorage(row.payload, userKey));
7685
+ } else if (skipFullDecrypt) {
7686
+ if (!isValidStorageCiphertext(
7687
+ row.payload,
7688
+ row.payload_prefix,
7689
+ row.payload_suffix,
7690
+ row.byte_size
7691
+ )) {
7692
+ throw new Error("Invalid outbox ciphertext");
7693
+ }
7694
+ } else {
7695
+ const rawPayload = typeof db.getOutboxPayload === "function" ? await db.getOutboxPayload(userId, row.message_id) : null;
7696
+ if (typeof rawPayload !== "string") {
7697
+ throw new Error("Missing outbox payload");
7698
+ }
7699
+ payload = objectOrText(await decryptFromStorage(rawPayload, userKey));
7700
+ }
7386
7701
  } catch (e10) {
7387
7702
  unreadable = true;
7388
7703
  }
@@ -7393,7 +7708,8 @@ async function buildCloudDataInventory({
7393
7708
  payload,
7394
7709
  task: taskByUuid.get(row.task_uuid),
7395
7710
  unreadable,
7396
- status: row.acked_at != null ? "acked" : row.delivered_at != null ? "delivered" : "pending"
7711
+ status: row.acked_at != null ? "acked" : row.delivered_at != null ? "delivered" : "pending",
7712
+ metadataInfo
7397
7713
  });
7398
7714
  }
7399
7715
  for (const row of raw.subscription || []) {
@@ -7407,7 +7723,7 @@ async function buildCloudDataInventory({
7407
7723
  const ownerLabels = /* @__PURE__ */ new Map();
7408
7724
  for (const { resource } of entries) {
7409
7725
  const owner = resource.owner;
7410
- if (!_optionalChain([owner, 'optionalAccess', _75 => _75.label, 'optionalAccess', _76 => _76.trim, 'call', _77 => _77()])) continue;
7726
+ if (!_optionalChain([owner, 'optionalAccess', _88 => _88.label, 'optionalAccess', _89 => _89.trim, 'call', _90 => _90()])) continue;
7411
7727
  const identity = JSON.stringify([owner.type, owner.id]);
7412
7728
  const current = ownerLabels.get(identity);
7413
7729
  if (!current || (_nullishCoalesce(resource.updatedAt, () => ( 0))) > current.updatedAt) {
@@ -7419,7 +7735,7 @@ async function buildCloudDataInventory({
7419
7735
  }
7420
7736
  for (const { resource } of entries) {
7421
7737
  const owner = resource.owner;
7422
- if (!owner || _optionalChain([owner, 'access', _78 => _78.label, 'optionalAccess', _79 => _79.trim, 'call', _80 => _80()])) continue;
7738
+ if (!owner || _optionalChain([owner, 'access', _91 => _91.label, 'optionalAccess', _92 => _92.trim, 'call', _93 => _93()])) continue;
7423
7739
  const known = ownerLabels.get(JSON.stringify([owner.type, owner.id]));
7424
7740
  if (known) resource.owner = { ...owner, label: known.label };
7425
7741
  }
@@ -7447,7 +7763,7 @@ async function cloudInventory(ctx, db, userId, userKey) {
7447
7763
  db,
7448
7764
  userId,
7449
7765
  userKey,
7450
- resolveOwner: _optionalChain([ctx, 'access', _81 => _81.cloudData, 'optionalAccess', _82 => _82.resolveOwner])
7766
+ resolveOwner: _optionalChain([ctx, 'access', _94 => _94.cloudData, 'optionalAccess', _95 => _95.resolveOwner])
7451
7767
  });
7452
7768
  }
7453
7769
  function operationWork(operation) {
@@ -7504,7 +7820,7 @@ async function makeCleanupPlan(ctx, db, userId, key, input) {
7504
7820
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u8BF7\u9009\u62E9\u81F3\u5C11\u4E00\u9879\u8D44\u6E90");
7505
7821
  if (types !== void 0 && (!Array.isArray(types) || !types.length || types.some((type) => !CLOUD_RESOURCE_TYPES.includes(type))))
7506
7822
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u8D44\u6E90\u7C7B\u522B\u65E0\u6548");
7507
- if (mode === "purge" && !owner && !_optionalChain([resourceIds, 'optionalAccess', _83 => _83.length]))
7823
+ if (mode === "purge" && !owner && !_optionalChain([resourceIds, 'optionalAccess', _96 => _96.length]))
7508
7824
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u6E05\u7406\u9700\u8981\u660E\u786E\u8D44\u6E90\u6216\u5F52\u5C5E");
7509
7825
  if (mode === "retire-owner" && (resourceIds || types))
7510
7826
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u5F7B\u5E95\u79FB\u9664\u4E0D\u80FD\u9650\u5236\u8D44\u6E90\u7C7B\u522B");
@@ -7516,10 +7832,12 @@ async function makeCleanupPlan(ctx, db, userId, key, input) {
7516
7832
  "\u4E91\u7AEF\u6E05\u5355\u4E0D\u5B8C\u6574\uFF0C\u6682\u4E0D\u80FD\u751F\u6210\u6E05\u7406\u8BA1\u5212",
7517
7833
  409
7518
7834
  );
7835
+ const resourceIdSet = resourceIds ? new Set(resourceIds) : null;
7836
+ const typeSet = types ? new Set(types) : null;
7519
7837
  const entries = inventory.entries.filter(
7520
- (e) => (!owner || ownerEquals(e.resource.owner, owner)) && (!resourceIds || resourceIds.includes(e.resource.id)) && (!types || types.includes(e.resource.type))
7838
+ (e) => (!owner || ownerEquals(e.resource.owner, owner)) && (!resourceIdSet || resourceIdSet.has(e.resource.id)) && (!typeSet || typeSet.has(e.resource.type))
7521
7839
  );
7522
- if (resourceIds && new Set(resourceIds).size !== entries.length)
7840
+ if (resourceIdSet && resourceIdSet.size !== entries.length)
7523
7841
  throw cloudError(
7524
7842
  "CLOUD_RESOURCE_NOT_FOUND",
7525
7843
  "\u90E8\u5206\u9009\u4E2D\u8D44\u6E90\u4E0D\u5B58\u5728\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u8303\u56F4",
@@ -7677,6 +7995,7 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7677
7995
  if (operation.status === "completed" || operation.status === "failed")
7678
7996
  return operation;
7679
7997
  operation.status = "running";
7998
+ let retirementInventory = null;
7680
7999
  if (operation.mode === "retire-owner") {
7681
8000
  if (operation.fencedGeneration == null) {
7682
8001
  const state = await db.setCloudOwnerActive(
@@ -7704,6 +8023,7 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7704
8023
  "\u65E0\u6CD5\u786E\u8BA4\u5F85\u6E05\u7406\u8D44\u6E90",
7705
8024
  503
7706
8025
  );
8026
+ retirementInventory = inventory;
7707
8027
  const known = new Set(operation.knownResourceIds || []);
7708
8028
  operation.knownResourceVersions ||= {};
7709
8029
  operation.entries = inventory.entries.filter((entry) => {
@@ -7730,7 +8050,7 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7730
8050
  ).length;
7731
8051
  await save(db, userId, key, "operation", operation, { leaseToken });
7732
8052
  }
7733
- const beforeBatch = await cloudInventory(ctx, db, userId, key);
8053
+ const beforeBatch = await _asyncNullishCoalesce(retirementInventory, async () => ( await cloudInventory(ctx, db, userId, key)));
7734
8054
  if (!beforeBatch.complete)
7735
8055
  throw cloudError("CLOUD_INVENTORY_INCOMPLETE", "\u65E0\u6CD5\u786E\u8BA4\u5F85\u6E05\u7406\u8D44\u6E90", 503);
7736
8056
  const currentById = new Map(
@@ -7768,7 +8088,6 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7768
8088
  delete entry.locator;
7769
8089
  operation.cursor++;
7770
8090
  operation.updatedAt = Date.now();
7771
- await save(db, userId, key, "operation", operation, { leaseToken });
7772
8091
  }
7773
8092
  await renewLease();
7774
8093
  const current = await cloudInventory(ctx, db, userId, key);
@@ -7779,8 +8098,9 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7779
8098
  503
7780
8099
  );
7781
8100
  const selected = new Set(operation.entries.map((e) => e.resource.id));
8101
+ const knownIds = new Set(operation.knownResourceIds || []);
7782
8102
  const remaining = current.entries.filter(
7783
- (e) => operation.mode === "retire-owner" ? ownerEquals(e.resource.owner, operation.owner) || (operation.knownResourceIds || []).includes(e.resource.id) : selected.has(e.resource.id)
8103
+ (e) => operation.mode === "retire-owner" ? ownerEquals(e.resource.owner, operation.owner) || knownIds.has(e.resource.id) : selected.has(e.resource.id)
7784
8104
  );
7785
8105
  for (const count of operation.counts)
7786
8106
  count.remaining = remaining.filter(
@@ -7869,12 +8189,12 @@ async function repairLegacyWorkIndex(ctx) {
7869
8189
  entries.push({ userId: row.user_id, kind: row.kind, id: row.id, work, writeToken: row.write_token });
7870
8190
  }
7871
8191
  await db.indexCloudDataRecordWorkBatch(entries);
7872
- const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _84 => _84.rows, 'access', _85 => _85.at, 'call', _86 => _86(-1), 'optionalAccess', _87 => _87.maintenance_rowid]), () => ( batch.upperBound));
8192
+ const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _97 => _97.rows, 'access', _98 => _98.at, 'call', _99 => _99(-1), 'optionalAccess', _100 => _100.maintenance_rowid]), () => ( batch.upperBound));
7873
8193
  await db.finishCloudMaintenanceBatch(name, cursor, batch.rows.length < 100 || cursor >= batch.upperBound);
7874
8194
  }
7875
8195
  async function resumeCloudDataCleanups(ctx) {
7876
8196
  const db = ctx.db;
7877
- if (!_optionalChain([db, 'optionalAccess', _88 => _88.cloudDataManagement])) return;
8197
+ if (!_optionalChain([db, 'optionalAccess', _101 => _101.cloudDataManagement])) return;
7878
8198
  const indexed = WORK_INDEX_METHODS.every((method) => typeof db[method] === "function");
7879
8199
  let rows;
7880
8200
  if (indexed) {
@@ -7906,7 +8226,7 @@ async function resumeCloudDataCleanups(ctx) {
7906
8226
  }
7907
8227
  } catch (error) {
7908
8228
  console.warn("[amsg] A cloud cleanup operation could not be resumed");
7909
- if (!indexed || _optionalChain([error, 'optionalAccess', _89 => _89.code]) === "CLOUD_LEASE_LOST") continue;
8229
+ if (!indexed || _optionalChain([error, 'optionalAccess', _102 => _102.code]) === "CLOUD_LEASE_LOST") continue;
7910
8230
  await db.rescheduleCloudDataRecordWork(
7911
8231
  row.userId,
7912
8232
  "operation",
@@ -8014,7 +8334,7 @@ function createCloudDataHandler(ctx) {
8014
8334
  summary: inventorySummary(inventory),
8015
8335
  expiresAt: Date.now() + 15 * 60 * 1e3,
8016
8336
  resources: inventory.entries.map((e) => e.resource).filter(
8017
- (r) => (!type || r.type === type) && (!ownerId || _optionalChain([r, 'access', _90 => _90.owner, 'optionalAccess', _91 => _91.type]) === ownerType && r.owner.id === ownerId)
8337
+ (r) => (!type || r.type === type) && (!ownerId || _optionalChain([r, 'access', _103 => _103.owner, 'optionalAccess', _104 => _104.type]) === ownerType && r.owner.id === ownerId)
8018
8338
  ),
8019
8339
  complete: inventory.complete,
8020
8340
  gaps: inventory.gaps
@@ -8097,7 +8417,7 @@ function createCloudDataHandler(ctx) {
8097
8417
  const rows = await db.listCloudDataRecords(userId, "operation");
8098
8418
  for (const row of rows) {
8099
8419
  const op = JSON.parse(await decryptFromStorage(row.data, key));
8100
- if (op.mode === "retire-owner" && _optionalChain([op, 'access', _92 => _92.owner, 'optionalAccess', _93 => _93.type]) === owner.type && _optionalChain([op, 'access', _94 => _94.owner, 'optionalAccess', _95 => _95.id]) === owner.id && ["pending", "running"].includes(op.status))
8420
+ if (op.mode === "retire-owner" && _optionalChain([op, 'access', _105 => _105.owner, 'optionalAccess', _106 => _106.type]) === owner.type && _optionalChain([op, 'access', _107 => _107.owner, 'optionalAccess', _108 => _108.id]) === owner.id && ["pending", "running"].includes(op.status))
8101
8421
  throw cloudError(
8102
8422
  "CLOUD_CLEANUP_IN_PROGRESS",
8103
8423
  "\u6E05\u7406\u5B8C\u6210\u540E\u624D\u80FD\u91CD\u65B0\u542F\u7528",
@@ -8315,7 +8635,7 @@ function createSingleUserServer(config) {
8315
8635
  if (!config.masterKey) throw new Error("[amsg-server single-user] config.masterKey is required");
8316
8636
  const vapid = config.vapid || {};
8317
8637
  const tenantManager = createSingleUserContextManager({
8318
- db: createCloudGuardedAdapter(config.db, { masterKey: config.masterKey, resolveOwner: _optionalChain([config, 'access', _96 => _96.cloudData, 'optionalAccess', _97 => _97.resolveOwner]) }),
8638
+ db: createCloudGuardedAdapter(config.db, { masterKey: config.masterKey, resolveOwner: _optionalChain([config, 'access', _109 => _109.cloudData, 'optionalAccess', _110 => _110.resolveOwner]) }),
8319
8639
  masterKey: config.masterKey,
8320
8640
  serverToken: config.serverToken
8321
8641
  });
@@ -8401,7 +8721,7 @@ async function getSchemaVersion(db) {
8401
8721
  for (const index of SQLITE_REQUIRED_SCHEMA.indexes) {
8402
8722
  if (!liveIndexes.has(index)) missing.push(`index:${index}`);
8403
8723
  }
8404
- if (Array.isArray(_optionalChain([live, 'optionalAccess', _98 => _98.triggers]))) {
8724
+ if (Array.isArray(_optionalChain([live, 'optionalAccess', _111 => _111.triggers]))) {
8405
8725
  const liveTriggers = new Set(live.triggers);
8406
8726
  for (const name of SQLITE_REQUIRED_SCHEMA.triggers) {
8407
8727
  if (!liveTriggers.has(name)) missing.push(`trigger:${name}`);
@@ -8781,4 +9101,5 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
8781
9101
 
8782
9102
 
8783
9103
 
8784
- exports.isValidTimeZoneId = isValidTimeZoneId; exports.advanceOccurrence = advanceOccurrence; exports.planNextOccurrence = planNextOccurrence; exports.nextFutureOccurrence = nextFutureOccurrence; exports.base64UrlToBytes = _amsgshared.base64UrlToBytes; exports.utf8 = _amsgshared.utf8; exports.utf8Decode = _amsgshared.utf8Decode; exports.bytesToBase64Url = _amsgshared.bytesToBase64Url; exports.timingSafeEqualBytes = _amsgshared.timingSafeEqualBytes; exports.deriveUserEncryptionKey = deriveUserEncryptionKey; exports.decryptPayload = decryptPayload; exports.encryptForStorage = encryptForStorage; exports.decryptFromStorage = decryptFromStorage; exports.CRED_ID_MAX_LENGTH = CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = CRED_PUT_BATCH_MAX; exports.CRED_ROWS_PER_USER_MAX = CRED_ROWS_PER_USER_MAX; exports.CRED_REFS_MAX_ENTRIES = CRED_REFS_MAX_ENTRIES; exports.supportsLlmCredentialsStore = supportsLlmCredentialsStore; exports.isValidCredId = isValidCredId; exports.validateCredValue = validateCredValue; exports.validateCredRefs = validateCredRefs; exports.hasChatCredRef = hasChatCredRef; exports.resolveLlmCredential = resolveLlmCredential; exports.isValidUrl = _amsgshared.isValidUrl; exports.validateAvatarUrl = _amsgshared.validateAvatarUrl; exports.isValidISO8601 = isValidISO8601; exports.isValidUUID = isValidUUID; exports.isValidUUIDv4 = isValidUUIDv4; exports.validateSplitPattern = validateSplitPattern; exports.validateLlmMessagesArray = validateLlmMessagesArray; exports.MAX_TASK_PAYLOAD_BYTES = MAX_TASK_PAYLOAD_BYTES; exports.validateScheduleMessagePayload = validateScheduleMessagePayload; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = DEFAULT_MAX_REQUEST_BODY_BYTES; exports.readRequestBody = readRequestBody; exports.parseJsonBody = parseJsonBody; exports.getHeader = getHeader; exports.createGetUserKeyHandler = createGetUserKeyHandler; exports.NonRetryableError = NonRetryableError; exports.isNonRetryableError = isNonRetryableError; exports.DeploymentConfigError = DeploymentConfigError; exports.sanitizeErrorSummary = sanitizeErrorSummary; exports.summarizeErrorCause = summarizeErrorCause; exports.DEFAULT_MAX_DELIVERY_RETRIES = DEFAULT_MAX_DELIVERY_RETRIES; exports.WEB_PUSH_MAX_BODY_BYTES = WEB_PUSH_MAX_BODY_BYTES; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.MAX_PUSH_PAYLOAD_BYTES = MAX_PUSH_PAYLOAD_BYTES; exports.PUSH_ENVELOPE_RESERVED_BYTES = PUSH_ENVELOPE_RESERVED_BYTES; exports.measurePushPayload = measurePushPayload; exports.createWebCryptoWebPush = createWebCryptoWebPush; exports.DEFAULT_MAX_TOOL_ITERATIONS = DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = DEFAULT_TOTAL_TIMEOUT_MS; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.MIN_SCHEDULE_LEAD_MS = MIN_SCHEDULE_LEAD_MS; exports.MAX_DEFER_AFTER_MS = MAX_DEFER_AFTER_MS; exports.createScheduleMessageHandler = createScheduleMessageHandler; exports.DEFAULT_CLAIM_LEASE_MS = DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.runScheduledTick = runScheduledTick; exports.runTask = runTask; exports.createUpdateMessageHandler = createUpdateMessageHandler; exports.createCancelMessageHandler = createCancelMessageHandler; exports.createMessagesHandler = createMessagesHandler; exports.createGetMessageHandler = createGetMessageHandler; exports.createPushSubscriptionHandler = createPushSubscriptionHandler; exports.createLlmCredentialsHandler = createLlmCredentialsHandler; exports.createD1Adapter = createD1Adapter; exports.createSingleUserServer = createSingleUserServer; exports.SCHEMA_VERSION = SCHEMA_VERSION; exports.getSchemaVersion = getSchemaVersion; exports.ensureSchema = ensureSchema; exports.CORS_ALLOW_HEADERS = CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = CORS_ALLOW_METHODS; exports.createSingleUserCloudflareWorker = createSingleUserCloudflareWorker;
9104
+
9105
+ exports.isValidTimeZoneId = isValidTimeZoneId; exports.advanceOccurrence = advanceOccurrence; exports.planNextOccurrence = planNextOccurrence; exports.nextFutureOccurrence = nextFutureOccurrence; exports.base64UrlToBytes = _amsgshared.base64UrlToBytes; exports.utf8 = _amsgshared.utf8; exports.utf8Decode = _amsgshared.utf8Decode; exports.bytesToBase64Url = _amsgshared.bytesToBase64Url; exports.timingSafeEqualBytes = _amsgshared.timingSafeEqualBytes; exports.deriveUserEncryptionKey = deriveUserEncryptionKey; exports.decryptPayload = decryptPayload; exports.encryptPayload = encryptPayload; exports.encryptForStorage = encryptForStorage; exports.decryptFromStorage = decryptFromStorage; exports.CRED_ID_MAX_LENGTH = CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = CRED_PUT_BATCH_MAX; exports.CRED_ROWS_PER_USER_MAX = CRED_ROWS_PER_USER_MAX; exports.CRED_REFS_MAX_ENTRIES = CRED_REFS_MAX_ENTRIES; exports.supportsLlmCredentialsStore = supportsLlmCredentialsStore; exports.isValidCredId = isValidCredId; exports.validateCredValue = validateCredValue; exports.validateCredRefs = validateCredRefs; exports.hasChatCredRef = hasChatCredRef; exports.resolveLlmCredential = resolveLlmCredential; exports.isValidUrl = _amsgshared.isValidUrl; exports.validateAvatarUrl = _amsgshared.validateAvatarUrl; exports.isValidISO8601 = isValidISO8601; exports.isValidUUID = isValidUUID; exports.isValidUUIDv4 = isValidUUIDv4; exports.validateSplitPattern = validateSplitPattern; exports.validateLlmMessagesArray = validateLlmMessagesArray; exports.MAX_TASK_PAYLOAD_BYTES = MAX_TASK_PAYLOAD_BYTES; exports.validateScheduleMessagePayload = validateScheduleMessagePayload; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = DEFAULT_MAX_REQUEST_BODY_BYTES; exports.readRequestBody = readRequestBody; exports.parseJsonBody = parseJsonBody; exports.getHeader = getHeader; exports.createGetUserKeyHandler = createGetUserKeyHandler; exports.NonRetryableError = NonRetryableError; exports.isNonRetryableError = isNonRetryableError; exports.DeploymentConfigError = DeploymentConfigError; exports.sanitizeErrorSummary = sanitizeErrorSummary; exports.summarizeErrorCause = summarizeErrorCause; exports.DEFAULT_MAX_DELIVERY_RETRIES = DEFAULT_MAX_DELIVERY_RETRIES; exports.WEB_PUSH_MAX_BODY_BYTES = WEB_PUSH_MAX_BODY_BYTES; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.MAX_PUSH_PAYLOAD_BYTES = MAX_PUSH_PAYLOAD_BYTES; exports.PUSH_ENVELOPE_RESERVED_BYTES = PUSH_ENVELOPE_RESERVED_BYTES; exports.measurePushPayload = measurePushPayload; exports.createWebCryptoWebPush = createWebCryptoWebPush; exports.DEFAULT_MAX_TOOL_ITERATIONS = DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = DEFAULT_TOTAL_TIMEOUT_MS; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.MIN_SCHEDULE_LEAD_MS = MIN_SCHEDULE_LEAD_MS; exports.MAX_DEFER_AFTER_MS = MAX_DEFER_AFTER_MS; exports.createScheduleMessageHandler = createScheduleMessageHandler; exports.DEFAULT_CLAIM_LEASE_MS = DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.runScheduledTick = runScheduledTick; exports.runTask = runTask; exports.createUpdateMessageHandler = createUpdateMessageHandler; exports.createCancelMessageHandler = createCancelMessageHandler; exports.createMessagesHandler = createMessagesHandler; exports.createGetMessageHandler = createGetMessageHandler; exports.createPushSubscriptionHandler = createPushSubscriptionHandler; exports.createLlmCredentialsHandler = createLlmCredentialsHandler; exports.createD1Adapter = createD1Adapter; exports.createSingleUserServer = createSingleUserServer; exports.SCHEMA_VERSION = SCHEMA_VERSION; exports.getSchemaVersion = getSchemaVersion; exports.ensureSchema = ensureSchema; exports.CORS_ALLOW_HEADERS = CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = CORS_ALLOW_METHODS; exports.createSingleUserCloudflareWorker = createSingleUserCloudflareWorker;