@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.
- package/README.md +110 -11
- package/dist/adapters/d1.d.ts +3 -3
- package/dist/adapters/interface.d.ts +6 -3
- package/dist/{chunk-YBDQJVFQ.cjs → chunk-3NIYNYZY.cjs} +221 -83
- package/dist/{chunk-RL7V7VSS.mjs → chunk-6TCZEACV.mjs} +174 -36
- package/dist/cloudflare.cjs +4 -2
- package/dist/cloudflare.d.ts +1 -1
- package/dist/cloudflare.mjs +3 -1
- package/dist/index.cjs +27 -25
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +3 -1
- package/dist/lib/agentic-fire.d.ts +1 -0
- package/dist/lib/attachments.d.ts +39 -0
- package/dist/lib/client-state-store.d.ts +6 -4
- package/dist/lib/state-accessors.d.ts +12 -8
- package/dist/lib/task-projection.d.ts +2 -2
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
1234
|
-
() =>
|
|
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',
|
|
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',
|
|
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
|
|
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',
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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',
|
|
2425
|
+
content: `Tool execution failed: ${_nullishCoalesce(_optionalChain([error, 'optionalAccess', _17 => _17.message]), () => ( String(error)))}`
|
|
2316
2426
|
}));
|
|
2317
2427
|
}
|
|
2318
|
-
|
|
2319
|
-
const
|
|
2320
|
-
const
|
|
2321
|
-
const
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
2719
|
+
owner: _nullishCoalesce(_optionalChain([guard, 'optionalAccess', _28 => _28.owner]), () => ( resolved.owner)),
|
|
2609
2720
|
kind: resolved.kind,
|
|
2610
|
-
generation: _nullishCoalesce(_optionalChain([guard, 'optionalAccess',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
3183
|
-
if (guard) attemptDb = createCloudGuardedAdapter(ctx.db, { masterKey, resolveOwner: _optionalChain([ctx, 'access',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
3971
|
-
const guardedDb = createCloudGuardedAdapter(db, { masterKey, resolveOwner: _optionalChain([ctx, 'access',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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
|
-
*
|
|
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(
|
|
6316
|
+
).bind(...params).all();
|
|
6191
6317
|
return res.results || [];
|
|
6192
6318
|
}
|
|
6193
6319
|
/**
|
|
6194
6320
|
* 例行清理:把指定命名空间下太久没更新的条目删掉(run-tick 每跳顺手调,
|
|
6195
|
-
*
|
|
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
|
|
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',
|
|
7267
|
-
const kind = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([stored, 'optionalAccess',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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
|
-
|
|
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;
|