@rei-standard/amsg-server 3.0.0 → 3.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -428,6 +428,9 @@ function taskPayloadByteLength(serializedPayload) {
428
428
  return taskPayloadEncoder.encode(serializedPayload).length;
429
429
  }
430
430
  function validateScheduleMessagePayload(payload) {
431
+ if (payload.taskInput != null && (typeof payload.taskInput !== "object" || Array.isArray(payload.taskInput))) {
432
+ return { valid: false, errorCode: "INVALID_TASK_INPUT", errorMessage: "taskInput must be an object or null" };
433
+ }
431
434
  if (!payload.contactName || typeof payload.contactName !== "string") {
432
435
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["contactName"] } };
433
436
  }
@@ -982,6 +985,40 @@ function createWebCryptoWebPush(vapid = {}, { ttl = SCHEDULED_DEFAULT_TTL } = {}
982
985
  };
983
986
  }
984
987
 
988
+ // src/server/lib/attachments.js
989
+ var ATTACHMENT_NAMESPACE = "amsg:attachments";
990
+ var ATTACHMENT_RETENTION_DAYS = 30;
991
+ var MAX_ATTACHMENT_TTL_MS = ATTACHMENT_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
992
+ function createAttachmentStore({ task, occurrenceMs, writeState, now }) {
993
+ return async function storeAttachment(value, { ttlMs = MAX_ATTACHMENT_TTL_MS, owner, kind = "attachment", ownerGeneration } = {}) {
994
+ if (typeof value !== "string") throw new TypeError("storeAttachment value must be a serialized string");
995
+ if (!Number.isSafeInteger(ttlMs) || ttlMs <= 0 || ttlMs > MAX_ATTACHMENT_TTL_MS) {
996
+ throw new RangeError("storeAttachment ttlMs must be between 1 millisecond and 30 days");
997
+ }
998
+ if (typeof task.uuid !== "string" || !task.uuid || !Number.isFinite(occurrenceMs)) {
999
+ throw new TypeError("storeAttachment requires a task UUID and a valid occurrence");
1000
+ }
1001
+ const at = now();
1002
+ const ref = { version: (
1003
+ /** @type {1} */
1004
+ 1
1005
+ ), namespace: ATTACHMENT_NAMESPACE, key: _amsgshared.randomUUID.call(void 0, ), taskUuid: task.uuid, occurrenceMs, expiresAt: at + ttlMs };
1006
+ let result;
1007
+ try {
1008
+ result = await writeState(ATTACHMENT_NAMESPACE, [{ key: ref.key, value: JSON.stringify({ ...ref, owner, kind, ownerGeneration, value }), updatedAt: at, owner, kind, ownerGeneration }]);
1009
+ } catch (error) {
1010
+ if (_optionalChain([error, 'optionalAccess', _5 => _5.code]) === "AGENTIC_STATE_WRITE_UNSUPPORTED") {
1011
+ throw new DeploymentConfigError("ATTACHMENT_STORAGE_UNSUPPORTED: attachments require a client_state adapter (built-in support: D1)", { code: "ATTACHMENT_STORAGE_UNSUPPORTED" });
1012
+ }
1013
+ throw error;
1014
+ }
1015
+ if (result.upserted !== 1 || result.skipped !== 0) {
1016
+ throw new Error("ATTACHMENT_WRITE_INCOMPLETE: attachment was not stored");
1017
+ }
1018
+ return ref;
1019
+ };
1020
+ }
1021
+
985
1022
  // src/server/lib/agentic-fire.js
986
1023
 
987
1024
 
@@ -1192,6 +1229,7 @@ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1192
1229
  let upserted = 0;
1193
1230
  let skipped = 0;
1194
1231
  let deleted = 0;
1232
+ let unknown = 0;
1195
1233
  const skippedEntries = [];
1196
1234
  if (Array.isArray(result.outcomes) && result.outcomes.length === physicalRows.length) {
1197
1235
  for (let i = 0; i < rootRowIndexes.length; i++) {
@@ -1203,9 +1241,10 @@ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1203
1241
  skippedEntries.push({ namespace: entry.namespace, key: entry.key });
1204
1242
  }
1205
1243
  }
1244
+ } else if (result.upserted === physicalRows.length && result.skipped === 0) {
1245
+ upserted = rootRowIndexes.length;
1206
1246
  } else {
1207
- upserted = result.upserted;
1208
- skipped = result.skipped;
1247
+ unknown = rootRowIndexes.length;
1209
1248
  }
1210
1249
  const cleanupOutcomes = Array.isArray(result.cleanupOutcomes) && result.cleanupOutcomes.length === cleanups.length ? result.cleanupOutcomes : null;
1211
1250
  for (const { cleanupIndex, entry } of deletions) {
@@ -1216,22 +1255,26 @@ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1216
1255
  deleted++;
1217
1256
  }
1218
1257
  }
1219
- return { upserted, skipped, deleted, skippedEntries };
1258
+ return { upserted, skipped, deleted, skippedEntries, ...unknown > 0 ? { unknown } : {} };
1220
1259
  }
1221
1260
 
1222
1261
  // src/server/lib/state-accessors.js
1223
1262
  function createStateAccessors({ db, userId, userKey, maxStateValueBytes, now }) {
1224
1263
  const nowFn = typeof now === "function" ? now : Date.now;
1225
1264
  const valueCeiling = Number.isInteger(maxStateValueBytes) && maxStateValueBytes > 0 ? maxStateValueBytes : DEFAULT_MAX_STATE_VALUE_BYTES;
1226
- const readState = async (namespace) => {
1265
+ const readState = async (namespace, { key } = {}) => {
1227
1266
  if (typeof namespace !== "string" || !namespace.trim()) {
1228
1267
  throw new TypeError("readState(namespace) requires a non-empty string");
1229
1268
  }
1269
+ if (key !== void 0 && (typeof key !== "string" || !key.trim() || key.length > MAX_KEY_CHARS || INTERNAL_STATE_CHAR_RE.test(key))) {
1270
+ throw new TypeError(`readState key must be a non-empty string of at most ${MAX_KEY_CHARS} characters without control characters`);
1271
+ }
1230
1272
  if (!db || typeof db.getClientState !== "function") return [];
1231
- const rows = await db.getClientState(userId, namespace);
1273
+ const rows = await db.getClientState(userId, namespace, key === void 0 ? void 0 : { key });
1232
1274
  return resolveClientStateEntries(
1233
- rows,
1234
- () => db.getClientState(userId, chunkNamespaceFor(namespace)),
1275
+ // 老适配器可能忽略第三个参数,仍在解密前过滤根行,保持返回契约。
1276
+ key === void 0 ? rows : rows.filter((row) => row.key === key),
1277
+ () => db.getClientState(userId, chunkNamespaceFor(namespace), key === void 0 ? void 0 : { keyPrefix: chunkKeyPrefixFor(key) }),
1235
1278
  (value) => decryptFromStorage(value, userKey)
1236
1279
  );
1237
1280
  };
@@ -1287,6 +1330,9 @@ function createStateAccessors({ db, userId, userKey, maxStateValueBytes, now })
1287
1330
  namespace,
1288
1331
  key: entry.key,
1289
1332
  value: entry.value,
1333
+ owner: entry.owner,
1334
+ kind: entry.kind,
1335
+ ownerGeneration: entry.ownerGeneration,
1290
1336
  updatedAt: _nullishCoalesce(entry.updatedAt, () => ( at)),
1291
1337
  ...entry.version !== void 0 ? { version: entry.version } : {}
1292
1338
  };
@@ -1309,7 +1355,7 @@ async function appendPushesToOutbox({ db, userId, userKey, pushes }) {
1309
1355
  await db.appendOutboxMessages(userId, await toOutboxRows(pushes, userKey, Date.now()));
1310
1356
  return true;
1311
1357
  } catch (error) {
1312
- if (_optionalChain([error, 'optionalAccess', _5 => _5.code]) === "CLOUD_OWNER_RETIRED") throw error;
1358
+ if (_optionalChain([error, 'optionalAccess', _6 => _6.code]) === "CLOUD_OWNER_RETIRED") throw error;
1313
1359
  console.warn("[amsg-server] outbox \u843D\u884C\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u6295\u9012\uFF09:", error && error.message);
1314
1360
  return false;
1315
1361
  }
@@ -1583,7 +1629,7 @@ async function sendResultPush({ db, task, userKey, decryptedPayload, webpush, pu
1583
1629
  await sendTaggedPush(webpush, subscription, JSON.stringify(push));
1584
1630
  return true;
1585
1631
  } catch (error) {
1586
- if (isTaskCancelledError(error) || _optionalChain([error, 'optionalAccess', _6 => _6.code]) === "CLOUD_OWNER_RETIRED") throw error;
1632
+ if (isTaskCancelledError(error) || _optionalChain([error, 'optionalAccess', _7 => _7.code]) === "CLOUD_OWNER_RETIRED") throw error;
1587
1633
  console.warn(
1588
1634
  `[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
1635
  error && error.message
@@ -1618,7 +1664,7 @@ function projectTask(row, decryptedPayload, options = {}) {
1618
1664
  // 凭据引用只是名字(cred_id),本体在 llm_credentials 表里,凭据字段本身
1619
1665
  // 照旧被白名单挡在外面。
1620
1666
  credRefs: _nullishCoalesce(payload.credRefs, () => ( null)),
1621
- // 整份 metadata 只在单条查询里给(见上面的 includeMetadata)。
1667
+ // 整份 metadata 由 includeMetadata 控制,单条查询与 hook 回执会带。
1622
1668
  ...options.includeMetadata ? { metadata: _nullishCoalesce(payload.metadata, () => ( null)) } : {},
1623
1669
  // 上一次没发出去的原因。reason 'stale' 表示错过触发时刻太久被判定不再补
1624
1670
  // 发;其余是投递失败的错误信息。没有记录 → null。
@@ -1709,6 +1755,9 @@ function normalizeBeforeFireResult(result) {
1709
1755
  return { messages: result };
1710
1756
  }
1711
1757
  if (result && typeof result === "object" && Array.isArray(result.messages)) {
1758
+ if (result.allowedToolNames !== void 0 && (!Array.isArray(result.allowedToolNames) || result.allowedToolNames.some((name) => typeof name !== "string" || !name.trim()))) {
1759
+ throw markPermanent(new TypeError("allowedToolNames must be an array of non-empty strings"), "AGENTIC_BAD_BEFORE_FIRE");
1760
+ }
1712
1761
  return {
1713
1762
  messages: result.messages,
1714
1763
  maxToolIterations: result.maxToolIterations,
@@ -1717,7 +1766,8 @@ function normalizeBeforeFireResult(result) {
1717
1766
  // 用真值。真正决定请求体带不带 tools 的规则住在 llm.js 的
1718
1767
  // buildAiRequestBody(那边有测试钉着),这层只是不把空数组往下传。
1719
1768
  tools: Array.isArray(result.tools) && result.tools.length > 0 ? result.tools : void 0,
1720
- toolChoice: result.toolChoice
1769
+ toolChoice: result.toolChoice,
1770
+ allowedToolNames: result.allowedToolNames === void 0 ? void 0 : new Set(result.allowedToolNames)
1721
1771
  };
1722
1772
  }
1723
1773
  throw markPermanent(new TypeError(BAD_BEFORE_FIRE_MESSAGE), "AGENTIC_BAD_BEFORE_FIRE");
@@ -1737,7 +1787,7 @@ function firstPositiveNumber(values, fallback) {
1737
1787
  async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1738
1788
  const hooks = ctx.hooks;
1739
1789
  const signal = ctx.signal || new AbortController().signal;
1740
- const isCancelled = () => signal.aborted || _optionalChain([ctx, 'access', _7 => _7.isTaskCancelled, 'optionalCall', _8 => _8()]) === true;
1790
+ const isCancelled = () => signal.aborted || _optionalChain([ctx, 'access', _8 => _8.isTaskCancelled, 'optionalCall', _9 => _9()]) === true;
1741
1791
  const throwIfCancelled = () => {
1742
1792
  if (!isCancelled()) return;
1743
1793
  if (isTaskCancelledError(signal.reason)) throw signal.reason;
@@ -1769,6 +1819,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1769
1819
  const sessionId = task.id != null ? `sess_task_${task.id}${occurrenceSuffix(task)}` : `sess_${_amsgshared.randomUUID.call(void 0, )}`;
1770
1820
  const messageIdBase = task.id != null ? `msg_task_${task.id}${occurrenceSuffix(task)}` : `msg_${_amsgshared.randomUUID.call(void 0, )}`;
1771
1821
  const occurrenceMs = occurrenceMsOf(task);
1822
+ const storeAttachment = createAttachmentStore({ task, occurrenceMs, writeState, now: nowFn });
1823
+ const storeSettledAttachment = createAttachmentStore({ task, occurrenceMs, writeState: writeSettledState, now: nowFn });
1772
1824
  const { emitResult } = createResultEmitter({
1773
1825
  db: ctx.db,
1774
1826
  task,
@@ -1791,17 +1843,37 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1791
1843
  const row = await ctx.db.getTaskByUuid(uuid, task.user_id);
1792
1844
  if (!row) return null;
1793
1845
  const payload = JSON.parse(await decryptFromStorage(row.encrypted_payload, userKey));
1794
- return projectTask(row, payload);
1846
+ return projectTask(row, payload, { includeMetadata: true });
1795
1847
  } catch (error) {
1796
1848
  console.warn("[amsg-server] scheduleTask: \u8BFB\u53D6\u649E\u8F66\u4EFB\u52A1\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09:", error && error.message);
1797
1849
  return null;
1798
1850
  }
1799
1851
  }
1852
+ const listTasks = async (options = {}) => {
1853
+ throwIfCancelled();
1854
+ const { status = "pending", limit = 100, offset = 0, includeMetadata = true } = options;
1855
+ if (!["pending", "sent", "failed", "all"].includes(status) || !Number.isInteger(limit) || limit < 1 || limit > 100 || !Number.isInteger(offset) || offset < 0) {
1856
+ throw new TypeError("listTasks requires a valid status, limit 1-100, and non-negative offset");
1857
+ }
1858
+ if (typeof _optionalChain([ctx, 'access', _10 => _10.db, 'optionalAccess', _11 => _11.listTasks]) !== "function") {
1859
+ throw new DeploymentConfigError("AGENTIC_TASK_VIEW_UNSUPPORTED: adapter has no listTasks", { code: "AGENTIC_TASK_VIEW_UNSUPPORTED" });
1860
+ }
1861
+ const { tasks, total } = await ctx.db.listTasks(task.user_id, { status, limit, offset });
1862
+ const projected = await Promise.all(tasks.map(async (row) => projectTask(
1863
+ row,
1864
+ JSON.parse(await decryptFromStorage(row.encrypted_payload, userKey)),
1865
+ { includeMetadata }
1866
+ )));
1867
+ return { tasks: projected, pagination: { total, limit, offset, hasMore: offset + tasks.length < total } };
1868
+ };
1800
1869
  const scheduleTask = async (options) => {
1801
1870
  throwIfCancelled();
1802
1871
  if (!options || typeof options !== "object" || Array.isArray(options)) {
1803
1872
  throw new TypeError("scheduleTask(options) \u9700\u8981\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u81F3\u5C11\u5305\u542B { firstSendTime }");
1804
1873
  }
1874
+ if (options.taskInput != null && (typeof options.taskInput !== "object" || Array.isArray(options.taskInput))) {
1875
+ throw new TypeError("scheduleTask: taskInput must be an object or null");
1876
+ }
1805
1877
  if (typeof options.firstSendTime !== "string" || !options.firstSendTime.trim()) {
1806
1878
  throw new RangeError("scheduleTask: firstSendTime \u5FC5\u586B\uFF0C\u4E14\u5FC5\u987B\u662F ISO 8601 \u5B57\u7B26\u4E32");
1807
1879
  }
@@ -1896,7 +1968,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1896
1968
  // 与凭据同列的投递配置:中转的非标准参数(thinking 之类)跟着任务走,
1897
1969
  // 自排的后续任务不该突然掉档。
1898
1970
  llmExtraBody: _nullishCoalesce(decryptedPayload.llmExtraBody, () => ( null)),
1899
- metadata
1971
+ metadata,
1972
+ taskInput: _nullishCoalesce(options.taskInput, () => ( null))
1900
1973
  };
1901
1974
  const serializedTaskData = JSON.stringify(fullTaskData);
1902
1975
  const sizeError = validateTaskPayloadSize(serializedTaskData);
@@ -1950,7 +2023,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
1950
2023
  created: true,
1951
2024
  id: _nullishCoalesce(created.id, () => ( null)),
1952
2025
  uuid: _nullishCoalesce(created.uuid, () => ( uuid)),
1953
- nextSendAt: _nullishCoalesce(created.next_send_at, () => ( nextSendAt))
2026
+ nextSendAt: _nullishCoalesce(created.next_send_at, () => ( nextSendAt)),
2027
+ task: projectTask({ status: "pending", next_send_at: nextSendAt, ...created }, fullTaskData, { includeMetadata: true })
1954
2028
  };
1955
2029
  };
1956
2030
  const cancelTask = async (uuid) => {
@@ -2018,7 +2092,11 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2018
2092
  const stillPending = await ctx.db.getTaskByUuid(uuid, task.user_id);
2019
2093
  return { renewed: false, reason: stillPending ? "in_flight" : "not_found" };
2020
2094
  }
2021
- return { renewed: true, uuid, nextSendAt: nextSendAtIso };
2095
+ return { renewed: true, uuid, nextSendAt: nextSendAtIso, task: projectTask(
2096
+ { ...row, next_send_at: nextSendAtIso, retry_count: 0 },
2097
+ payload,
2098
+ { includeMetadata: true }
2099
+ ) };
2022
2100
  };
2023
2101
  const resolveLlmCredential2 = async (credId) => {
2024
2102
  if (typeof credId !== "string" || !credId.trim()) {
@@ -2035,7 +2113,9 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2035
2113
  readState,
2036
2114
  writeState,
2037
2115
  emitResult,
2116
+ storeAttachment,
2038
2117
  scheduleTask,
2118
+ listTasks,
2039
2119
  cancelTask,
2040
2120
  renewTask,
2041
2121
  resolveLlmCredential: resolveLlmCredential2,
@@ -2068,8 +2148,10 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2068
2148
  readState,
2069
2149
  writeState,
2070
2150
  emitResult,
2151
+ storeAttachment,
2071
2152
  scratch,
2072
2153
  scheduleTask,
2154
+ listTasks,
2073
2155
  cancelTask,
2074
2156
  renewTask,
2075
2157
  resolveLlmCredential: resolveLlmCredential2,
@@ -2128,7 +2210,8 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
2128
2210
  scratch,
2129
2211
  readState,
2130
2212
  writeState: writeSettledState,
2131
- emitResult
2213
+ emitResult,
2214
+ storeAttachment: storeSettledAttachment
2132
2215
  });
2133
2216
  }
2134
2217
  }
@@ -2143,8 +2226,10 @@ async function runFireChain({
2143
2226
  readState,
2144
2227
  writeState,
2145
2228
  emitResult,
2229
+ storeAttachment,
2146
2230
  scratch,
2147
2231
  scheduleTask,
2232
+ listTasks,
2148
2233
  cancelTask,
2149
2234
  renewTask,
2150
2235
  resolveLlmCredential: resolveLlmCredential2,
@@ -2243,15 +2328,35 @@ async function runFireChain({
2243
2328
  readState,
2244
2329
  writeState,
2245
2330
  emitResult,
2331
+ storeAttachment,
2246
2332
  scheduleTask,
2333
+ listTasks,
2247
2334
  cancelTask,
2248
2335
  renewTask,
2249
2336
  resolveLlmCredential: resolveLlmCredential2
2250
2337
  });
2251
2338
  const decision = await hooks.onLLMOutput(sessionCtx);
2252
2339
  ctx.throwIfCancelled();
2340
+ const rawCalls = _optionalChain([decision, 'optionalAccess', _12 => _12.decision]) === "tool-request" ? _amsgshared.extractToolCallsFromDecision.call(void 0, decision) : [];
2341
+ const validId = (call) => typeof _optionalChain([call, 'optionalAccess', _13 => _13.id]) === "string" && call.id.trim().length > 0;
2342
+ const nativeCalls = Array.isArray(assistantMessage.tool_calls) ? assistantMessage.tool_calls : [];
2343
+ const usedIds = new Set([...rawCalls, ...nativeCalls].filter(validId).map((call) => call.id));
2344
+ const malformedIds = /* @__PURE__ */ new Set();
2345
+ const toolCalls = rawCalls.map((call, index) => {
2346
+ let id = validId(call) ? call.id : `call_${iteration + 1}_${index + 1}`;
2347
+ if (!validId(call)) {
2348
+ while (usedIds.has(id)) id += "_";
2349
+ usedIds.add(id);
2350
+ }
2351
+ if (typeof _optionalChain([call, 'optionalAccess', _14 => _14.function, 'optionalAccess', _15 => _15.name]) !== "string" || !call.function.name.trim()) {
2352
+ malformedIds.add(id);
2353
+ return { id, type: "function", function: { name: "invalid_tool_call", arguments: "{}" } };
2354
+ }
2355
+ return { ...call, id };
2356
+ });
2357
+ const validationDecision = _optionalChain([decision, 'optionalAccess', _16 => _16.decision]) === "tool-request" && Array.isArray(decision.toolCalls) && decision.toolCalls.length > 0 ? { ...decision, toolCalls } : decision;
2253
2358
  try {
2254
- _amsgshared.assertValidDecision.call(void 0, decision, { inlineToolCalls: true });
2359
+ _amsgshared.assertValidDecision.call(void 0, validationDecision, { inlineToolCalls: true });
2255
2360
  } catch (error) {
2256
2361
  throw markPermanent(error, "AGENTIC_BAD_DECISION");
2257
2362
  }
@@ -2282,14 +2387,19 @@ async function runFireChain({
2282
2387
  });
2283
2388
  return { handled: true, result: { success: true, messagesSent, status: "finished", iterations: iteration + 1 } };
2284
2389
  }
2285
- const toolCalls = _amsgshared.extractToolCallsFromDecision.call(void 0, decision);
2286
2390
  if (toolCalls.length === 0) {
2287
2391
  throw taggedRetryableError(
2288
2392
  "AGENTIC_EMPTY_TOOL_REQUEST: tool-request decision carried no toolCalls (neither decision.toolCalls nor pushPayloads[].toolCalls)",
2289
2393
  "AGENTIC_EMPTY_TOOL_REQUEST"
2290
2394
  );
2291
2395
  }
2292
- if (typeof hooks.executeToolCalls !== "function") {
2396
+ const permittedCalls = toolCalls.filter((call) => !malformedIds.has(call.id) && (normalized.allowedToolNames === void 0 || normalized.allowedToolNames.has(call.function.name)));
2397
+ const deniedResults = toolCalls.filter((call) => !permittedCalls.includes(call)).map((call) => ({
2398
+ role: "tool",
2399
+ tool_call_id: call.id,
2400
+ 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" } })
2401
+ }));
2402
+ if (permittedCalls.length > 0 && typeof hooks.executeToolCalls !== "function") {
2293
2403
  throw new DeploymentConfigError(
2294
2404
  "AGENTIC_CONFIG_ERROR: onLLMOutput returned tool-request but hooks.executeToolCalls is not configured",
2295
2405
  { code: "AGENTIC_CONFIG_ERROR" }
@@ -2301,7 +2411,7 @@ async function runFireChain({
2301
2411
  let toolResults;
2302
2412
  try {
2303
2413
  ctx.throwIfCancelled();
2304
- toolResults = await hooks.executeToolCalls(toolCalls, sessionCtx);
2414
+ toolResults = permittedCalls.length > 0 ? await hooks.executeToolCalls(permittedCalls, sessionCtx) : [];
2305
2415
  ctx.throwIfCancelled();
2306
2416
  if (!Array.isArray(toolResults)) {
2307
2417
  throw new TypeError('executeToolCalls must resolve to an array of { tool_call_id, role: "tool", content }');
@@ -2309,16 +2419,17 @@ async function runFireChain({
2309
2419
  } catch (error) {
2310
2420
  ctx.throwIfCancelled();
2311
2421
  if (isTaskCancelledError(error)) throw error;
2312
- toolResults = toolCalls.map((toolCall) => ({
2422
+ toolResults = permittedCalls.map((toolCall) => ({
2313
2423
  tool_call_id: toolCall && typeof toolCall === "object" && typeof toolCall.id === "string" ? toolCall.id : "",
2314
2424
  role: "tool",
2315
- content: `Tool execution failed: ${_nullishCoalesce(_optionalChain([error, 'optionalAccess', _9 => _9.message]), () => ( String(error)))}`
2425
+ content: `Tool execution failed: ${_nullishCoalesce(_optionalChain([error, 'optionalAccess', _17 => _17.message]), () => ( String(error)))}`
2316
2426
  }));
2317
2427
  }
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] };
2428
+ toolResults = [...toolResults, ...deniedResults];
2429
+ const declared = nativeCalls.filter(validId).map((call) => toolCalls.find((item) => item.id === call.id) || call);
2430
+ const nativeIds = new Set(declared.map((call) => call.id));
2431
+ const synthesized = toolCalls.filter((call) => !nativeIds.has(call.id));
2432
+ const assistantWithTools = { ...assistantMessage, tool_calls: [...declared, ...synthesized] };
2322
2433
  messages = [...messages.slice(0, -1), assistantWithTools, ...toolResults];
2323
2434
  }
2324
2435
  throw taggedRetryableError(
@@ -2497,7 +2608,7 @@ function validOwner(owner) {
2497
2608
  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
2609
  }
2499
2610
  function validateGuard(guard) {
2500
- if (!validOwner(_optionalChain([guard, 'optionalAccess', _10 => _10.owner])))
2611
+ if (!validOwner(_optionalChain([guard, 'optionalAccess', _18 => _18.owner])))
2501
2612
  throw guardError("INVALID_CLOUD_OWNER", "Cloud owner is invalid");
2502
2613
  if (!Number.isSafeInteger(guard.generation) || guard.generation < 0) {
2503
2614
  throw guardError(
@@ -2525,14 +2636,14 @@ async function assertCloudGuard(db, userId, guard) {
2525
2636
  }
2526
2637
  async function resolveMetadata(resolveOwner, input, explicit) {
2527
2638
  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)));
2639
+ 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
2640
  if (owner !== null && !validOwner(owner))
2530
2641
  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)) };
2642
+ return { owner, kind: _nullishCoalesce(_nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _23 => _23.kind]), () => ( _optionalChain([resolved, 'optionalAccess', _24 => _24.kind]))), () => ( null)) };
2532
2643
  }
2533
2644
  async function captureCloudTaskGuard(db, userId, payload, resolveOwner, task) {
2534
2645
  if (!db.cloudDataManagement) return null;
2535
- if (_optionalChain([payload, 'optionalAccess', _17 => _17.__cloudOwnerGuard])) {
2646
+ if (_optionalChain([payload, 'optionalAccess', _25 => _25.__cloudOwnerGuard])) {
2536
2647
  const guard2 = validateGuard(payload.__cloudOwnerGuard);
2537
2648
  await assertCloudGuard(db, userId, guard2);
2538
2649
  return guard2;
@@ -2545,7 +2656,7 @@ async function captureCloudTaskGuard(db, userId, payload, resolveOwner, task) {
2545
2656
  if (!owner) return null;
2546
2657
  const guard = validateGuard({
2547
2658
  owner,
2548
- generation: _nullishCoalesce(_optionalChain([payload, 'optionalAccess', _18 => _18.ownerGeneration]), () => ( 0))
2659
+ generation: _nullishCoalesce(_optionalChain([payload, 'optionalAccess', _26 => _26.ownerGeneration]), () => ( 0))
2549
2660
  });
2550
2661
  await assertCloudGuard(db, userId, guard);
2551
2662
  return guard;
@@ -2597,7 +2708,7 @@ function createCloudGuardedAdapter(db, {
2597
2708
  }
2598
2709
  guard = inheritedGuard || validateGuard({
2599
2710
  owner: resolved.owner,
2600
- generation: _nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _19 => _19.ownerGeneration]), () => ( 0))
2711
+ generation: _nullishCoalesce(_optionalChain([explicit, 'optionalAccess', _27 => _27.ownerGeneration]), () => ( 0))
2601
2712
  });
2602
2713
  } else {
2603
2714
  guard = null;
@@ -2605,9 +2716,9 @@ function createCloudGuardedAdapter(db, {
2605
2716
  await assertCloudGuard(db, userId, inheritedGuard);
2606
2717
  await assertCloudGuard(db, userId, guard);
2607
2718
  const metadata = {
2608
- owner: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _20 => _20.owner]), () => ( resolved.owner)),
2719
+ owner: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _28 => _28.owner]), () => ( resolved.owner)),
2609
2720
  kind: resolved.kind,
2610
- generation: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _21 => _21.generation]), () => ( 0))
2721
+ generation: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _29 => _29.generation]), () => ( 0))
2611
2722
  };
2612
2723
  return {
2613
2724
  cloudGuard: guard,
@@ -2787,7 +2898,7 @@ function createCloudGuardedAdapter(db, {
2787
2898
  else delete payload[field];
2788
2899
  }
2789
2900
  if (payload.metadata && typeof payload.metadata === "object") {
2790
- if (_optionalChain([originalPayload, 'access', _22 => _22.metadata, 'optionalAccess', _23 => _23.cloudOwner]) !== void 0)
2901
+ if (_optionalChain([originalPayload, 'access', _30 => _30.metadata, 'optionalAccess', _31 => _31.cloudOwner]) !== void 0)
2791
2902
  payload.metadata.cloudOwner = originalPayload.metadata.cloudOwner;
2792
2903
  else delete payload.metadata.cloudOwner;
2793
2904
  }
@@ -2979,11 +3090,11 @@ async function processSingleMessage(task, ctx, providedMasterKey, predecrypted =
2979
3090
  }
2980
3091
  const userKey = predecrypted && predecrypted.userKey || await deriveUserEncryptionKey(task.user_id, masterKey);
2981
3092
  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);
3093
+ const cloudGuard = await captureCloudTaskGuard(ctx.db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _32 => _32.cloudData, 'optionalAccess', _33 => _33.resolveOwner]), task);
2983
3094
  if (cloudGuard) {
2984
3095
  await assertCloudGuard(ctx.db, task.user_id, cloudGuard);
2985
3096
  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 });
3097
+ const guardedDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _34 => _34.cloudData, 'optionalAccess', _35 => _35.resolveOwner]), guard: cloudGuard, userId: task.user_id });
2987
3098
  ctx = { ...ctx, db: guardedDb, webpush: originalWebpush && {
2988
3099
  ...originalWebpush,
2989
3100
  async sendNotification(...args) {
@@ -3179,8 +3290,8 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
3179
3290
  try {
3180
3291
  const userKey = await deriveUserEncryptionKey(task.user_id, masterKey);
3181
3292
  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 });
3293
+ const guard = await captureCloudTaskGuard(ctx.db, task.user_id, payload, _optionalChain([ctx, 'access', _36 => _36.cloudData, 'optionalAccess', _37 => _37.resolveOwner]), task);
3294
+ if (guard) attemptDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _38 => _38.cloudData, 'optionalAccess', _39 => _39.resolveOwner]), guard, userId: task.user_id });
3184
3295
  } catch (error) {
3185
3296
  return { success: false, error: { code: error.code || "PROCESSING_ERROR", message: error.message, retriesAttempted: retryCount } };
3186
3297
  }
@@ -3216,7 +3327,7 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
3216
3327
  })
3217
3328
  });
3218
3329
  } catch (_updateError) {
3219
- if (_optionalChain([_updateError, 'optionalAccess', _32 => _32.code]) === "CLOUD_OWNER_RETIRED") {
3330
+ if (_optionalChain([_updateError, 'optionalAccess', _40 => _40.code]) === "CLOUD_OWNER_RETIRED") {
3220
3331
  return { success: false, error: { code: _updateError.code, message: _updateError.message, retriesAttempted: retryCount } };
3221
3332
  }
3222
3333
  try {
@@ -3392,6 +3503,9 @@ function createScheduleMessageHandler(ctx) {
3392
3503
  owner: payload.owner,
3393
3504
  kind: payload.kind,
3394
3505
  ownerGeneration: payload.ownerGeneration,
3506
+ // Input is persisted with the task in the same create/supersede transaction.
3507
+ // Shared credentials remain references and continue to resolve at fire time.
3508
+ taskInput: _nullishCoalesce(payload.taskInput, () => ( null)),
3395
3509
  metadata: payload.metadata || {}
3396
3510
  };
3397
3511
  const serializedTaskData = JSON.stringify(fullTaskData);
@@ -3599,7 +3713,7 @@ async function runScheduledTick(ctx) {
3599
3713
  }
3600
3714
  async function cleanupExpiredClientState(ctx) {
3601
3715
  if (typeof ctx.db.cleanupClientState !== "function") return;
3602
- const targets = planClientStateCleanup(ctx.clientStateTtl, Date.now());
3716
+ const targets = planClientStateCleanup({ [ATTACHMENT_NAMESPACE]: ATTACHMENT_RETENTION_DAYS, ...ctx.clientStateTtl }, Date.now());
3603
3717
  if (targets.length === 0) return;
3604
3718
  try {
3605
3719
  await ctx.db.cleanupClientState(targets);
@@ -3689,7 +3803,7 @@ async function deliverTasks(ctx, tasks) {
3689
3803
  const until = new Date(Date.now() + heartbeatLeaseTtlMs).toISOString();
3690
3804
  renewed = guard ? await db.renewTaskLease(task.id, until, guard) : await db.renewTaskLease(task.id, until);
3691
3805
  } catch (error) {
3692
- if (_optionalChain([error, 'optionalAccess', _33 => _33.code]) === "CLOUD_OWNER_RETIRED") renewed = false;
3806
+ if (_optionalChain([error, 'optionalAccess', _41 => _41.code]) === "CLOUD_OWNER_RETIRED") renewed = false;
3693
3807
  else console.warn("[amsg-server] \u79DF\u7EA6\u7EED\u79DF\u5931\u8D25\uFF08\u4E0B\u4E2A\u5FC3\u8DF3\u518D\u8BD5\uFF09:", error && error.message);
3694
3808
  }
3695
3809
  if (renewed === false) {
@@ -3732,7 +3846,7 @@ async function deliverTasks(ctx, tasks) {
3732
3846
  lastErrorColumnSuspicions.delete(db);
3733
3847
  return result2;
3734
3848
  } catch (error) {
3735
- if (_optionalChain([error, 'optionalAccess', _34 => _34.code]) === "CLOUD_OWNER_RETIRED") throw error;
3849
+ if (_optionalChain([error, 'optionalAccess', _42 => _42.code]) === "CLOUD_OWNER_RETIRED") throw error;
3736
3850
  combinedError = error;
3737
3851
  }
3738
3852
  const result = await updateLifecycleTask(taskId, stateFields);
@@ -3927,12 +4041,12 @@ async function deliverTasks(ctx, tasks) {
3927
4041
  let claimed;
3928
4042
  try {
3929
4043
  if (decrypted.ok) {
3930
- const guard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _35 => _35.cloudData, 'optionalAccess', _36 => _36.resolveOwner]), task);
4044
+ const guard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _43 => _43.cloudData, 'optionalAccess', _44 => _44.resolveOwner]), task);
3931
4045
  if (guard) taskCloudGuards.set(task.id, guard);
3932
4046
  }
3933
4047
  claimed = await claimForThisTick(task, serializeGroup);
3934
4048
  } catch (error) {
3935
- if (_optionalChain([error, 'optionalAccess', _37 => _37.code]) === "CLOUD_OWNER_RETIRED") {
4049
+ if (_optionalChain([error, 'optionalAccess', _45 => _45.code]) === "CLOUD_OWNER_RETIRED") {
3936
4050
  await recordCancelled(task, "owner_retired");
3937
4051
  return;
3938
4052
  }
@@ -3967,8 +4081,8 @@ async function deliverTasks(ctx, tasks) {
3967
4081
  const notOnFreshRetryChain = (task.retry_count || 0) === 0 || Number.isFinite(retryAfterMs) && Date.now() - retryAfterMs > staleAfterMs;
3968
4082
  const settleAsStale = async () => {
3969
4083
  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 });
4084
+ const cloudGuard = await captureCloudTaskGuard(db, task.user_id, decryptedPayload, _optionalChain([ctx, 'access', _46 => _46.cloudData, 'optionalAccess', _47 => _47.resolveOwner]), task);
4085
+ const guardedDb = createCloudGuardedAdapter(db, { masterKey, resolveOwner: _optionalChain([ctx, 'access', _48 => _48.cloudData, 'optionalAccess', _49 => _49.resolveOwner]), guard: cloudGuard, userId: task.user_id });
3972
4086
  const guardedWebpush = cloudGuard && ctx.webpush ? {
3973
4087
  ...ctx.webpush,
3974
4088
  async sendNotification(...args) {
@@ -4259,6 +4373,9 @@ function createUpdateMessageHandler(ctx) {
4259
4373
  if (!isPlainObject(updates)) {
4260
4374
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF" } } };
4261
4375
  }
4376
+ if (Object.prototype.hasOwnProperty.call(updates, "taskInput")) {
4377
+ 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"] } } } };
4378
+ }
4262
4379
  if (updates.nextSendAt && !isValidISO8601(updates.nextSendAt)) {
4263
4380
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["nextSendAt"] } } } };
4264
4381
  }
@@ -5270,7 +5387,7 @@ var D1Adapter = class {
5270
5387
  return typeof this._db.batch === "function";
5271
5388
  }
5272
5389
  _cloudGuardPredicate(userId, guard) {
5273
- if (!_optionalChain([guard, 'optionalAccess', _42 => _42.owner]) || !Number.isSafeInteger(guard.generation) || guard.generation < 0) {
5390
+ if (!_optionalChain([guard, 'optionalAccess', _50 => _50.owner]) || !Number.isSafeInteger(guard.generation) || guard.generation < 0) {
5274
5391
  throw new Error("Invalid cloud owner guard");
5275
5392
  }
5276
5393
  return {
@@ -5297,7 +5414,7 @@ var D1Adapter = class {
5297
5414
  indexes.push(expanded.length);
5298
5415
  expanded.push(statements[i]);
5299
5416
  const item = metadata.get(i);
5300
- if (_optionalChain([item, 'optionalAccess', _43 => _43.value])) {
5417
+ if (_optionalChain([item, 'optionalAccess', _51 => _51.value])) {
5301
5418
  expanded.push(this._db.prepare(
5302
5419
  `INSERT INTO cloud_resource_metadata
5303
5420
  (user_id, resource_key, encrypted_value) SELECT ?, ?, ? WHERE changes() > 0
@@ -5332,7 +5449,7 @@ var D1Adapter = class {
5332
5449
  }
5333
5450
  async getCloudResourceMetadata(userId, resourceKey) {
5334
5451
  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));
5452
+ return _nullishCoalesce(_optionalChain([row, 'optionalAccess', _52 => _52.encrypted_value]), () => ( null));
5336
5453
  }
5337
5454
  async listCloudResourceRows(userId) {
5338
5455
  const tables = { task: "scheduled_messages", state: "client_state", credential: "llm_credentials", outbox: "message_outbox", subscription: "push_subscriptions", metadata: "cloud_resource_metadata" };
@@ -5537,7 +5654,7 @@ var D1Adapter = class {
5537
5654
  const name = "metadata-sidecars-v1";
5538
5655
  const batch = await this.getCloudMaintenanceBatch(name, "cloud_resource_metadata", limit);
5539
5656
  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));
5657
+ const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _53 => _53.rows, 'access', _54 => _54.at, 'call', _55 => _55(-1), 'optionalAccess', _56 => _56.maintenance_rowid]), () => ( batch.upperBound));
5541
5658
  if (batch.rows.length) await this.cleanupCloudResourceMetadata(null, { after: batch.cursor, through: cursor });
5542
5659
  await this.finishCloudMaintenanceBatch(name, cursor, batch.rows.length < limit || cursor >= batch.upperBound);
5543
5660
  }
@@ -5611,7 +5728,7 @@ var D1Adapter = class {
5611
5728
  if (!guard) return true;
5612
5729
  const condition = this._cloudGuardPredicate(userId, guard);
5613
5730
  const row = await this._db.prepare(`SELECT ${condition.sql} AS valid`).bind(...condition.args).first();
5614
- return !!_optionalChain([row, 'optionalAccess', _49 => _49.valid]);
5731
+ return !!_optionalChain([row, 'optionalAccess', _57 => _57.valid]);
5615
5732
  }
5616
5733
  /** Delete only the exact physical snapshot; no untrusted SQL identifiers. */
5617
5734
  async deleteCloudResourceRows(userId, locators, { operationId, leaseToken } = {}) {
@@ -5864,7 +5981,7 @@ var D1Adapter = class {
5864
5981
  ).bind(...values);
5865
5982
  const ownerUser = cloudGuard ? await this._db.prepare("SELECT user_id FROM scheduled_messages WHERE id=?").bind(taskId).first() : null;
5866
5983
  if (cloudGuard && !ownerUser) return null;
5867
- await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _50 => _50.user_id]), [cloudGuard]);
5984
+ await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _58 => _58.user_id]), [cloudGuard]);
5868
5985
  return this._db.prepare("SELECT * FROM scheduled_messages WHERE id = ?").bind(taskId).first();
5869
5986
  }
5870
5987
  /**
@@ -5989,7 +6106,7 @@ var D1Adapter = class {
5989
6106
  ).bind(...values);
5990
6107
  const ownerUser = cloudGuard ? await this._db.prepare("SELECT user_id FROM scheduled_messages WHERE id=?").bind(taskId).first() : null;
5991
6108
  if (cloudGuard && !ownerUser) return false;
5992
- const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _51 => _51.user_id]), [cloudGuard]);
6109
+ const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _59 => _59.user_id]), [cloudGuard]);
5993
6110
  return (res.meta.changes || 0) > 0;
5994
6111
  }
5995
6112
  /**
@@ -6008,7 +6125,7 @@ var D1Adapter = class {
6008
6125
  ).bind(this._iso(leaseUntil), taskId);
6009
6126
  const ownerUser = cloudGuard ? await this._db.prepare("SELECT user_id FROM scheduled_messages WHERE id=?").bind(taskId).first() : null;
6010
6127
  if (cloudGuard && !ownerUser) return false;
6011
- const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _52 => _52.user_id]), [cloudGuard]);
6128
+ const [res] = await this._cloudBatch([statement], _optionalChain([ownerUser, 'optionalAccess', _60 => _60.user_id]), [cloudGuard]);
6012
6129
  return (res.meta.changes || 0) > 0;
6013
6130
  }
6014
6131
  async listTasks(userId, opts = {}) {
@@ -6169,30 +6286,39 @@ var D1Adapter = class {
6169
6286
  result.cleanupOutcomes = probeIndexes.map((probeAt) => {
6170
6287
  if (probeAt === null) return null;
6171
6288
  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;
6289
+ return Number(_nullishCoalesce(_optionalChain([row, 'optionalAccess', _61 => _61.n]), () => ( 0))) === 0;
6173
6290
  });
6174
6291
  }
6175
6292
  return result;
6176
6293
  }
6177
6294
  /**
6178
- * All entries of one namespace (values still encrypted).
6295
+ * Entries of one namespace (values still encrypted); optional exact/prefix selection.
6179
6296
  *
6180
6297
  * @param {string} userId
6181
6298
  * @param {string} namespace
6182
6299
  * @returns {Promise<Array<{ namespace: string, key: string, value: string, updated_at: number }>>}
6183
6300
  */
6184
- async getClientState(userId, namespace) {
6301
+ async getClientState(userId, namespace, options = {}) {
6302
+ const params = [userId, namespace];
6303
+ let selector = "";
6304
+ if (options.key !== void 0) {
6305
+ selector = " AND key = ?";
6306
+ params.push(options.key);
6307
+ } else if (options.keyPrefix !== void 0) {
6308
+ selector = " AND key >= ? AND key < ?";
6309
+ params.push(options.keyPrefix, prefixRangeEnd(options.keyPrefix));
6310
+ }
6185
6311
  const res = await this._db.prepare(
6186
6312
  `SELECT namespace, key, value, updated_at
6187
6313
  FROM client_state
6188
- WHERE user_id = ? AND namespace = ?
6314
+ WHERE user_id = ? AND namespace = ?${selector}
6189
6315
  ORDER BY key ASC`
6190
- ).bind(userId, namespace).all();
6316
+ ).bind(...params).all();
6191
6317
  return res.results || [];
6192
6318
  }
6193
6319
  /**
6194
6320
  * 例行清理:把指定命名空间下太久没更新的条目删掉(run-tick 每跳顺手调,
6195
- * 宿主配了 `clientStateTtl` 才会调)。
6321
+ * 附件默认保留 30 天,宿主 `clientStateTtl` 可覆盖或配置其他命名空间)。
6196
6322
  *
6197
6323
  * 不限用户——「这个命名空间只留最近 N 天」是命名空间级的约定,单用户部署
6198
6324
  * 下也就是这一个用户的行。指令由 lib/client-state-store.js 的
@@ -6924,8 +7050,9 @@ function createClientStateHandler(ctx) {
6924
7050
  if (typeof db.upsertClientState !== "function") {
6925
7051
  return err3(501, "CLIENT_STATE_NOT_SUPPORTED", "\u5F53\u524D\u6570\u636E\u5E93\u9002\u914D\u5668\u4E0D\u652F\u6301 client_state");
6926
7052
  }
6927
- const { upserted, skipped, deleted, skippedEntries } = await writeClientStateEntries({ db, userId, userKey, entries: accepted });
7053
+ const { upserted, skipped, deleted, skippedEntries, unknown } = await writeClientStateEntries({ db, userId, userKey, entries: accepted });
6928
7054
  const data = { upserted, skipped };
7055
+ if (unknown > 0) data.unknown = unknown;
6929
7056
  if (deleted > 0) data.deleted = deleted;
6930
7057
  if (skippedEntries.length > 0) data.skippedEntries = skippedEntries;
6931
7058
  if (rejected.length > 0) data.rejected = rejected;
@@ -6938,7 +7065,12 @@ function createClientStateHandler(ctx) {
6938
7065
  const gate = requireUserId(headers);
6939
7066
  if (gate.error) return gate.error;
6940
7067
  const { userId } = gate;
6941
- const namespace = new URL(url, "https://dummy").searchParams.get("namespace") || "";
7068
+ const query = new URL(url, "https://dummy").searchParams;
7069
+ const namespace = query.get("namespace") || "";
7070
+ const key = query.get("key");
7071
+ if (key !== null && (!key.trim() || key.length > MAX_KEY_CHARS || INTERNAL_STATE_CHAR_RE.test(key))) {
7072
+ return err3(400, "INVALID_STATE_KEY", "key must be non-empty, at most 256 characters, without control characters");
7073
+ }
6942
7074
  if (!namespace.trim()) return err3(400, "NAMESPACE_REQUIRED", "\u5FC5\u987B\u63D0\u4F9B namespace \u67E5\u8BE2\u53C2\u6570");
6943
7075
  if (INTERNAL_STATE_CHAR_RE.test(namespace)) {
6944
7076
  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 +7079,10 @@ function createClientStateHandler(ctx) {
6947
7079
  return err3(501, "CLIENT_STATE_NOT_SUPPORTED", "\u5F53\u524D\u6570\u636E\u5E93\u9002\u914D\u5668\u4E0D\u652F\u6301 client_state");
6948
7080
  }
6949
7081
  const userKey = await deriveUserEncryptionKey(userId, masterKey);
6950
- const rows = await db.getClientState(userId, namespace);
7082
+ const rows = await db.getClientState(userId, namespace, key === null ? void 0 : { key });
6951
7083
  const decrypted = await resolveClientStateEntries(
6952
- rows,
6953
- () => db.getClientState(userId, chunkNamespaceFor(namespace)),
7084
+ key === null ? rows : rows.filter((row) => row.key === key),
7085
+ () => db.getClientState(userId, chunkNamespaceFor(namespace), key === null ? void 0 : { keyPrefix: chunkKeyPrefixFor(key) }),
6954
7086
  (value) => decryptFromStorage(value, userKey)
6955
7087
  );
6956
7088
  const encryptedResponse = await encryptPayload({ namespace, entries: decrypted }, userKey);
@@ -7028,7 +7160,7 @@ function createClientStateNamespacesHandler(ctx) {
7028
7160
  }
7029
7161
 
7030
7162
  // src/server/lib/version.js
7031
- var SERVER_VERSION = true ? "3.0.0" : "0.0.0-dev";
7163
+ var SERVER_VERSION = true ? "3.1.0-next.0" : "0.0.0-dev";
7032
7164
 
7033
7165
  // src/server/handlers/capabilities.js
7034
7166
  var SERVER_FEATURES = Object.freeze([
@@ -7144,7 +7276,12 @@ var SERVER_FEATURES = Object.freeze([
7144
7276
  "max-generation-retries",
7145
7277
  // onBeforeFire 认 { defer: { afterMs } }:这次不生成,过一会儿再来问(不占重试
7146
7278
  // 次数);onFireSettled 的 status 多一种 'deferred',带 retryAfter。
7147
- "before-fire-defer"
7279
+ "before-fire-defer",
7280
+ "task-input",
7281
+ "client-state-key",
7282
+ "agentic-list-tasks",
7283
+ "agentic-tool-allowlist",
7284
+ "attachments"
7148
7285
  ]);
7149
7286
  function createCapabilitiesHandler(ctx) {
7150
7287
  async function GET(url, headers) {
@@ -7263,8 +7400,8 @@ async function buildCloudDataInventory({
7263
7400
  });
7264
7401
  }
7265
7402
  }
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));
7403
+ const owner = safeOwner(_optionalChain([stored, 'optionalAccess', _62 => _62.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _63 => _63.__cloudOwnerGuard, 'optionalAccess', _64 => _64.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _65 => _65.metadata, 'optionalAccess', _66 => _66.cloudOwner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _67 => _67.metadata, 'optionalAccess', _68 => _68.owner])) || safeOwner(_optionalChain([payload, 'optionalAccess', _69 => _69.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _70 => _70.__cloudOwnerGuard, 'optionalAccess', _71 => _71.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _72 => _72.metadata, 'optionalAccess', _73 => _73.cloudOwner])) || safeOwner(_optionalChain([task, 'optionalAccess', _74 => _74.metadata, 'optionalAccess', _75 => _75.owner])) || safeOwner(_optionalChain([task, 'optionalAccess', _76 => _76.owner])) || safeOwner(_optionalChain([resolved, 'optionalAccess', _77 => _77.owner]));
7404
+ const kind = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([stored, 'optionalAccess', _78 => _78.kind]), () => ( _optionalChain([payload, 'optionalAccess', _79 => _79.metadata, 'optionalAccess', _80 => _80.kind]))), () => ( _optionalChain([payload, 'optionalAccess', _81 => _81.kind]))), () => ( _optionalChain([resolved, 'optionalAccess', _82 => _82.kind]))), () => ( null));
7268
7405
  const times = rows.map((row) => timestamp(_nullishCoalesce(row.updated_at, () => ( row.created_at)))).filter((time) => time !== null);
7269
7406
  const byteSize = rows.reduce(
7270
7407
  (sum, row) => sum + encoder.encode(
@@ -7407,7 +7544,7 @@ async function buildCloudDataInventory({
7407
7544
  const ownerLabels = /* @__PURE__ */ new Map();
7408
7545
  for (const { resource } of entries) {
7409
7546
  const owner = resource.owner;
7410
- if (!_optionalChain([owner, 'optionalAccess', _75 => _75.label, 'optionalAccess', _76 => _76.trim, 'call', _77 => _77()])) continue;
7547
+ if (!_optionalChain([owner, 'optionalAccess', _83 => _83.label, 'optionalAccess', _84 => _84.trim, 'call', _85 => _85()])) continue;
7411
7548
  const identity = JSON.stringify([owner.type, owner.id]);
7412
7549
  const current = ownerLabels.get(identity);
7413
7550
  if (!current || (_nullishCoalesce(resource.updatedAt, () => ( 0))) > current.updatedAt) {
@@ -7419,7 +7556,7 @@ async function buildCloudDataInventory({
7419
7556
  }
7420
7557
  for (const { resource } of entries) {
7421
7558
  const owner = resource.owner;
7422
- if (!owner || _optionalChain([owner, 'access', _78 => _78.label, 'optionalAccess', _79 => _79.trim, 'call', _80 => _80()])) continue;
7559
+ if (!owner || _optionalChain([owner, 'access', _86 => _86.label, 'optionalAccess', _87 => _87.trim, 'call', _88 => _88()])) continue;
7423
7560
  const known = ownerLabels.get(JSON.stringify([owner.type, owner.id]));
7424
7561
  if (known) resource.owner = { ...owner, label: known.label };
7425
7562
  }
@@ -7447,7 +7584,7 @@ async function cloudInventory(ctx, db, userId, userKey) {
7447
7584
  db,
7448
7585
  userId,
7449
7586
  userKey,
7450
- resolveOwner: _optionalChain([ctx, 'access', _81 => _81.cloudData, 'optionalAccess', _82 => _82.resolveOwner])
7587
+ resolveOwner: _optionalChain([ctx, 'access', _89 => _89.cloudData, 'optionalAccess', _90 => _90.resolveOwner])
7451
7588
  });
7452
7589
  }
7453
7590
  function operationWork(operation) {
@@ -7504,7 +7641,7 @@ async function makeCleanupPlan(ctx, db, userId, key, input) {
7504
7641
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u8BF7\u9009\u62E9\u81F3\u5C11\u4E00\u9879\u8D44\u6E90");
7505
7642
  if (types !== void 0 && (!Array.isArray(types) || !types.length || types.some((type) => !CLOUD_RESOURCE_TYPES.includes(type))))
7506
7643
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u8D44\u6E90\u7C7B\u522B\u65E0\u6548");
7507
- if (mode === "purge" && !owner && !_optionalChain([resourceIds, 'optionalAccess', _83 => _83.length]))
7644
+ if (mode === "purge" && !owner && !_optionalChain([resourceIds, 'optionalAccess', _91 => _91.length]))
7508
7645
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u6E05\u7406\u9700\u8981\u660E\u786E\u8D44\u6E90\u6216\u5F52\u5C5E");
7509
7646
  if (mode === "retire-owner" && (resourceIds || types))
7510
7647
  throw cloudError("INVALID_CLEANUP_SELECTION", "\u5F7B\u5E95\u79FB\u9664\u4E0D\u80FD\u9650\u5236\u8D44\u6E90\u7C7B\u522B");
@@ -7869,12 +8006,12 @@ async function repairLegacyWorkIndex(ctx) {
7869
8006
  entries.push({ userId: row.user_id, kind: row.kind, id: row.id, work, writeToken: row.write_token });
7870
8007
  }
7871
8008
  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));
8009
+ const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _92 => _92.rows, 'access', _93 => _93.at, 'call', _94 => _94(-1), 'optionalAccess', _95 => _95.maintenance_rowid]), () => ( batch.upperBound));
7873
8010
  await db.finishCloudMaintenanceBatch(name, cursor, batch.rows.length < 100 || cursor >= batch.upperBound);
7874
8011
  }
7875
8012
  async function resumeCloudDataCleanups(ctx) {
7876
8013
  const db = ctx.db;
7877
- if (!_optionalChain([db, 'optionalAccess', _88 => _88.cloudDataManagement])) return;
8014
+ if (!_optionalChain([db, 'optionalAccess', _96 => _96.cloudDataManagement])) return;
7878
8015
  const indexed = WORK_INDEX_METHODS.every((method) => typeof db[method] === "function");
7879
8016
  let rows;
7880
8017
  if (indexed) {
@@ -7906,7 +8043,7 @@ async function resumeCloudDataCleanups(ctx) {
7906
8043
  }
7907
8044
  } catch (error) {
7908
8045
  console.warn("[amsg] A cloud cleanup operation could not be resumed");
7909
- if (!indexed || _optionalChain([error, 'optionalAccess', _89 => _89.code]) === "CLOUD_LEASE_LOST") continue;
8046
+ if (!indexed || _optionalChain([error, 'optionalAccess', _97 => _97.code]) === "CLOUD_LEASE_LOST") continue;
7910
8047
  await db.rescheduleCloudDataRecordWork(
7911
8048
  row.userId,
7912
8049
  "operation",
@@ -8014,7 +8151,7 @@ function createCloudDataHandler(ctx) {
8014
8151
  summary: inventorySummary(inventory),
8015
8152
  expiresAt: Date.now() + 15 * 60 * 1e3,
8016
8153
  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)
8154
+ (r) => (!type || r.type === type) && (!ownerId || _optionalChain([r, 'access', _98 => _98.owner, 'optionalAccess', _99 => _99.type]) === ownerType && r.owner.id === ownerId)
8018
8155
  ),
8019
8156
  complete: inventory.complete,
8020
8157
  gaps: inventory.gaps
@@ -8097,7 +8234,7 @@ function createCloudDataHandler(ctx) {
8097
8234
  const rows = await db.listCloudDataRecords(userId, "operation");
8098
8235
  for (const row of rows) {
8099
8236
  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))
8237
+ if (op.mode === "retire-owner" && _optionalChain([op, 'access', _100 => _100.owner, 'optionalAccess', _101 => _101.type]) === owner.type && _optionalChain([op, 'access', _102 => _102.owner, 'optionalAccess', _103 => _103.id]) === owner.id && ["pending", "running"].includes(op.status))
8101
8238
  throw cloudError(
8102
8239
  "CLOUD_CLEANUP_IN_PROGRESS",
8103
8240
  "\u6E05\u7406\u5B8C\u6210\u540E\u624D\u80FD\u91CD\u65B0\u542F\u7528",
@@ -8315,7 +8452,7 @@ function createSingleUserServer(config) {
8315
8452
  if (!config.masterKey) throw new Error("[amsg-server single-user] config.masterKey is required");
8316
8453
  const vapid = config.vapid || {};
8317
8454
  const tenantManager = createSingleUserContextManager({
8318
- db: createCloudGuardedAdapter(config.db, { masterKey: config.masterKey, resolveOwner: _optionalChain([config, 'access', _96 => _96.cloudData, 'optionalAccess', _97 => _97.resolveOwner]) }),
8455
+ db: createCloudGuardedAdapter(config.db, { masterKey: config.masterKey, resolveOwner: _optionalChain([config, 'access', _104 => _104.cloudData, 'optionalAccess', _105 => _105.resolveOwner]) }),
8319
8456
  masterKey: config.masterKey,
8320
8457
  serverToken: config.serverToken
8321
8458
  });
@@ -8401,7 +8538,7 @@ async function getSchemaVersion(db) {
8401
8538
  for (const index of SQLITE_REQUIRED_SCHEMA.indexes) {
8402
8539
  if (!liveIndexes.has(index)) missing.push(`index:${index}`);
8403
8540
  }
8404
- if (Array.isArray(_optionalChain([live, 'optionalAccess', _98 => _98.triggers]))) {
8541
+ if (Array.isArray(_optionalChain([live, 'optionalAccess', _106 => _106.triggers]))) {
8405
8542
  const liveTriggers = new Set(live.triggers);
8406
8543
  for (const name of SQLITE_REQUIRED_SCHEMA.triggers) {
8407
8544
  if (!liveTriggers.has(name)) missing.push(`trigger:${name}`);
@@ -8781,4 +8918,5 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
8781
8918
 
8782
8919
 
8783
8920
 
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;
8921
+
8922
+ 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;