@rei-standard/amsg-server 2.6.0-next.32 → 2.6.0-next.33
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 +49 -2
- package/dist/{chunk-KZT3YC62.cjs → chunk-AM2MMY46.cjs} +95 -12
- package/dist/{chunk-INNZZFGJ.mjs → chunk-Q4VQ3NVR.mjs} +94 -11
- package/dist/cloudflare.cjs +4 -2
- package/dist/cloudflare.d.ts +1 -0
- 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 +43 -1
- package/dist/lib/message-processor.d.ts +9 -1
- package/dist/lib/run-tick.d.ts +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -364,6 +364,16 @@ hook 在 `pushPayloads` 里自己写了这几个字段的话会被库覆盖:
|
|
|
364
364
|
|
|
365
365
|
配上 `hooks: { onBeforeFire, onLLMOutput, executeToolCalls }` 之后,AI 类任务的 prompt 不再是排程那一刻冻结的文本,而是 cron 触发时现场组装,工具也在服务端就地跑完,全程不需要客户端在线。完整用法见 [`examples/cloudflare-single-user/README.md`](https://github.com/Tosd0/ReiStandard/blob/main/packages/rei-standard-amsg/server/examples/cloudflare-single-user/README.md) 的「Fire 时刻 hooks」。
|
|
366
366
|
|
|
367
|
+
`onBeforeFire` 的返回值决定这次触发怎么走:
|
|
368
|
+
|
|
369
|
+
| 返回值 | 这次触发怎么走 |
|
|
370
|
+
|---|---|
|
|
371
|
+
| 消息数组,或 `{ messages, maxToolIterations?, totalTimeoutMs?, tools?, toolChoice? }` | 用这份 prompt 生成 |
|
|
372
|
+
| `{ skip: true }` | 这次不发,按零推送的成功收场:一次性任务删掉、循环任务推进到下一次 |
|
|
373
|
+
| `{ defer: { afterMs } }` | 现在不合适,`afterMs` 毫秒后再来问。见下面[推迟这次触发](#推迟这次触发defer) |
|
|
374
|
+
| `null` | 交还给排程时冻结的 prompt |
|
|
375
|
+
| 抛错 | 按投递失败处理:占一次重试、写 `last_error`、2 / 4 / 6 分钟退避 |
|
|
376
|
+
|
|
367
377
|
三个 hook 拿到的 ctx 上都有这几个口子:
|
|
368
378
|
|
|
369
379
|
| ctx 上的口子 | 干什么 |
|
|
@@ -383,6 +393,41 @@ hook 在 `pushPayloads` 里自己写了这几个字段的话会被库覆盖:
|
|
|
383
393
|
|
|
384
394
|
`sessionId` 是给日志和去重用的不透明字符串,格式随版本变,别拆它拿上面这些值。
|
|
385
395
|
|
|
396
|
+
### 推迟这次触发(defer)
|
|
397
|
+
|
|
398
|
+
任务到点了,但宿主这会儿不方便生成——比如同一段对话里正有另一轮回复在生成,想等它结束再说。`onBeforeFire` 返回 `{ defer: { afterMs } }`:
|
|
399
|
+
|
|
400
|
+
```js
|
|
401
|
+
async onBeforeFire(ctx) {
|
|
402
|
+
if (await isConversationBusy(ctx)) return { defer: { afterMs: 30_000 } };
|
|
403
|
+
return buildMessages(ctx);
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
这一次不调 LLM、不推送,也不调 `onLLMOutput` / `onAfterSend`。任务行上只有两处变化:`retry_after` 写成现在 + `afterMs`,租约放掉。到点之后 cron 把它重新捞起来,从 `onBeforeFire` 再走一遍,那时可以继续推迟。
|
|
408
|
+
|
|
409
|
+
跟抛错重试的区别:
|
|
410
|
+
|
|
411
|
+
| | 抛错 | `{ defer }` |
|
|
412
|
+
|---|---|---|
|
|
413
|
+
| `retry_count` | 加一,用完就终审 | 不变 |
|
|
414
|
+
| `last_error` | 写上这次的原因 | 不写,也不清已有的 |
|
|
415
|
+
| 多久之后再来 | 固定 2 / 4 / 6 分钟 | 宿主给的 `afterMs` |
|
|
416
|
+
| `onFireSettled` 的 `status` | `failed` | `deferred`,带 `retryAfter` |
|
|
417
|
+
|
|
418
|
+
两边相同的是 `next_send_at` 都不动:它一直是这次触发的名义时刻,`ctx.task.nextSendAt`、`occurrenceMs`、推送的 `messageId` 在推迟前后都是同一套。循环任务被推迟时不会推进到下一次。
|
|
419
|
+
|
|
420
|
+
几条规矩:
|
|
421
|
+
|
|
422
|
+
- `afterMs` 必须是有限正数,最大 24 小时(导出为 `MAX_DEFER_AFTER_MS`)。不合法的值按 hook 契约违约处理(`AGENTIC_BAD_BEFORE_FIRE`,一跳终审)。没有下限,但 cron 一分钟一跳,实际是「到点之后的第一跳」才会再问;用 `runTask` 自己触发的话,到点之前调它会得到 `retry_pending`。
|
|
423
|
+
- 推迟不会让任务永远挂着。名义时刻过去超过 `staleAfterMs`(默认 60 分钟)的任务照常按过期处理:一次性任务标 `failed`、循环任务快进到下一次,并调 `onStaleSkip`。某次推迟的唤醒时刻已经越过这条线时,库不再等那一轮,当场按过期收场——这时宿主会先收到 `status: 'deferred'` 的 `onFireSettled`,紧接着收到 `onStaleSkip`。
|
|
424
|
+
- 每次被重新问到都是一次新的 fire:`scratch` 是新的,`onFireSettled` 各调各的。
|
|
425
|
+
- 只有定时投递(`runScheduledTick`、单用户 Worker 的 `scheduled()`、`runTask`)支持推迟,并且适配器要实现 `claimTask`(任务表有 `retry_after` 列,内置适配器都有)。`messageType: 'instant'` 在请求里当场投递的任务、以及没实现 `claimTask` 的自定义适配器返回 `{ defer }` 会得到 `AGENTIC_DEFER_UNSUPPORTED`,按[部署配错了算可重试](#部署配错了算可重试)处理。
|
|
426
|
+
- 内容已经落进收件箱、只是推送没发完的重试不调 `onBeforeFire`(见[生成成功之后推送失败:只补推送](#生成成功之后推送失败只补推送)),那一跳也就不会被推迟。
|
|
427
|
+
- 配了 `serializeBy` 时,被推迟的任务不占着分组:同组里排在它后面的任务可以先跑。
|
|
428
|
+
|
|
429
|
+
tick 汇总的 `details.deferredTasks`(`{ taskId, retryAfter }`)列出这一跳被推迟的任务,它们不计入 `successCount` / `failedCount`。`GET /capabilities` 的 features 里有 `before-fire-defer`。
|
|
430
|
+
|
|
386
431
|
### config 级 hook
|
|
387
432
|
|
|
388
433
|
这两个挂在 worker 工厂 config 的顶层(不在 `hooks` 里):
|
|
@@ -390,7 +435,7 @@ hook 在 `pushPayloads` 里自己写了这几个字段的话会被库覆盖:
|
|
|
390
435
|
| hook | 什么时候调 | 载荷 |
|
|
391
436
|
|---|---|---|
|
|
392
437
|
| `onAfterSend` | fire 的 pushPayloads 逐段发完,或中途发挂 | `{ task, sentCount, pushedCount, total, error, usage, usageTotal, llmCalls, outboxed, scratch, readState, writeState, emitResult }` |
|
|
393
|
-
| `onFireSettled` | 一次 fire 收尾——只要 `onBeforeFire` 被调用过,什么结局都调一次 | `{ task, status, skipReason, sentCount, pushedCount, total, iterations, error, metadata, usage, usageTotal, llmCalls, outboxed, scratch, readState, writeState, emitResult }` |
|
|
438
|
+
| `onFireSettled` | 一次 fire 收尾——只要 `onBeforeFire` 被调用过,什么结局都调一次 | `{ task, status, skipReason, retryAfter, sentCount, pushedCount, total, iterations, error, metadata, usage, usageTotal, llmCalls, outboxed, scratch, readState, writeState, emitResult }` |
|
|
394
439
|
| `onStaleSkip` | 任务错过触发时刻超过 60 分钟、这一次(或这几次)不再补发 | `{ reason, action, metadata, recurrenceType, occurrenceMs, skippedCount, skippedOccurrences, skippedTruncated, nextSendAt, readState, writeState, emitResult }` |
|
|
395
440
|
|
|
396
441
|
三个 hook 都自带 `readState` / `writeState` / `emitResult`,作用于当前用户,语义与 fire 级那套一致。`onStaleSkip` 尤其需要:服务停摆恢复后的第一跳里可能一次 fire 都没跑过,而那正是它要留痕迹的时候。
|
|
@@ -405,7 +450,9 @@ hook 在 `pushPayloads` 里自己写了这几个字段的话会被库覆盖:
|
|
|
405
450
|
|---|---|
|
|
406
451
|
| `sent` | pushPayloads 全部发完(`sentCount === total`) |
|
|
407
452
|
| `skipped` | 这次不发。`skipReason` 区分是 `onBeforeFire` 直接 `{ skip: true }`(`'before-fire'`)还是模型跑完后判定不发(`'skip-push'`) |
|
|
453
|
+
| `deferred` | `onBeforeFire` 返回了 `{ defer: { afterMs } }`:这次没生成也没发,任务还在。`retryAfter`(ISO 时刻)之后会从 `onBeforeFire` 重新走一遍;这个字段在其余结局都是 `null` |
|
|
408
454
|
| `failed` | 链路抛错,`error` 带原始错误。发到第 k 段挂了也是这个:`sentCount = k`、`total` 是原本要发的段数 |
|
|
455
|
+
| `cancelled` | 任务在投递期间被取消或顶替,见[取消正在执行的任务](#取消正在执行的生成与工具) |
|
|
409
456
|
| `not-handled` | `onBeforeFire` 返回 `null`,这条任务交还给排程时冻结的 prompt 老链路。那条链路不归 fire hook 管,它后面发没发出去不体现在这里 |
|
|
410
457
|
|
|
411
458
|
**用量记账**看这三个字段,两个 hook 都带,`onFireSettled` 的每种结局(发完、跳过、失败)都带——失败也花了钱:
|
|
@@ -507,7 +554,7 @@ const { messageId, pushed } = await ctx.emitResult({
|
|
|
507
554
|
|
|
508
555
|
### hook 契约违约算确定性失败
|
|
509
556
|
|
|
510
|
-
宿主 hook 返回了库不认的东西(`onBeforeFire` 的返回形状、`onLLMOutput` 的决策标签),或者建后续任务时 `createTask` 没把行交回来——这些错误带 `permanent: true` 和一个稳定的 `code`(`AGENTIC_BAD_BEFORE_FIRE` / `AGENTIC_BAD_DECISION` / `AGENTIC_SCHEDULE_FAILED` / `TASK_PAYLOAD_TOO_LARGE`),投递侧据此跳过退避阶梯:一次性任务直接标 `failed`,循环任务作废本次 occurrence。重试也是同一个结果,而每重试一轮都要把 `onBeforeFire` 和一整轮 LLM 重跑一遍。
|
|
557
|
+
宿主 hook 返回了库不认的东西(`onBeforeFire` 的返回形状、`{ defer }` 里不合法的 `afterMs`、`onLLMOutput` 的决策标签),或者建后续任务时 `createTask` 没把行交回来——这些错误带 `permanent: true` 和一个稳定的 `code`(`AGENTIC_BAD_BEFORE_FIRE` / `AGENTIC_BAD_DECISION` / `AGENTIC_SCHEDULE_FAILED` / `TASK_PAYLOAD_TOO_LARGE`),投递侧据此跳过退避阶梯:一次性任务直接标 `failed`,循环任务作废本次 occurrence。重试也是同一个结果,而每重试一轮都要把 `onBeforeFire` 和一整轮 LLM 重跑一遍。
|
|
511
558
|
|
|
512
559
|
分界线是「谁写错了」:契约由宿主代码定死,重掷一次还是同一个形状;而模型这一轮掷出了什么则是每轮都可能不同的。所以「tool-request 决策里没有能解析的 `toolCalls`」(`AGENTIC_EMPTY_TOOL_REQUEST`)和「轮数用尽也没等到 `finish` / `skip-push`」(`AGENTIC_LOOP_EXCEEDED`)带 `code` 但不带 `permanent`,留在退避阶梯上——隔两分钟重掷一次多半就正常收尾了,判终态的话一次性任务第一次掷歪就永久 `failed`,行离开 `pending` 之后连 `PUT /update-message` 都救不回来(回 409)。
|
|
513
560
|
|
|
@@ -1636,6 +1636,7 @@ var DEFAULT_MAX_TOOL_ITERATIONS = 5;
|
|
|
1636
1636
|
var DEFAULT_TOTAL_TIMEOUT_MS = 24e4;
|
|
1637
1637
|
var DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = 2;
|
|
1638
1638
|
var MIN_SCHEDULE_LEAD_MS = 6e4;
|
|
1639
|
+
var MAX_DEFER_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
1639
1640
|
var SCHEDULABLE_MESSAGE_TYPES = /* @__PURE__ */ new Set(["auto", "prompted", "fixed"]);
|
|
1640
1641
|
var SCHEDULABLE_RECURRENCE_TYPES = /* @__PURE__ */ new Set(["none", "daily", "weekly"]);
|
|
1641
1642
|
var SLEEP_BETWEEN_MESSAGES_MS = 1500;
|
|
@@ -1670,6 +1671,22 @@ function buildHookTask(task, decryptedPayload) {
|
|
|
1670
1671
|
retryCount: _nullishCoalesce(task.retry_count, () => ( 0))
|
|
1671
1672
|
});
|
|
1672
1673
|
}
|
|
1674
|
+
var BAD_BEFORE_FIRE_MESSAGE = "AGENTIC_BAD_BEFORE_FIRE: onBeforeFire must return ChatMessage[] | { messages, maxToolIterations?, totalTimeoutMs?, tools?, toolChoice? } | { skip: true } | { defer: { afterMs } } | null";
|
|
1675
|
+
function readDeferAfterMs(defer) {
|
|
1676
|
+
const afterMs = defer && typeof defer === "object" ? (
|
|
1677
|
+
/** @type {any} */
|
|
1678
|
+
defer.afterMs
|
|
1679
|
+
) : void 0;
|
|
1680
|
+
if (typeof afterMs !== "number" || !Number.isFinite(afterMs) || afterMs <= 0 || afterMs > MAX_DEFER_AFTER_MS) {
|
|
1681
|
+
throw markPermanent(
|
|
1682
|
+
new TypeError(
|
|
1683
|
+
`${BAD_BEFORE_FIRE_MESSAGE}\uFF08defer.afterMs \u5FC5\u987B\u662F\u6709\u9650\u6B63\u6570\uFF0C\u4E14\u4E0D\u8D85\u8FC7 ${MAX_DEFER_AFTER_MS} \u6BEB\u79D2\uFF09`
|
|
1684
|
+
),
|
|
1685
|
+
"AGENTIC_BAD_BEFORE_FIRE"
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
return afterMs;
|
|
1689
|
+
}
|
|
1673
1690
|
function normalizeBeforeFireResult(result) {
|
|
1674
1691
|
if (Array.isArray(result)) {
|
|
1675
1692
|
return { messages: result };
|
|
@@ -1686,12 +1703,7 @@ function normalizeBeforeFireResult(result) {
|
|
|
1686
1703
|
toolChoice: result.toolChoice
|
|
1687
1704
|
};
|
|
1688
1705
|
}
|
|
1689
|
-
throw markPermanent(
|
|
1690
|
-
new TypeError(
|
|
1691
|
-
"AGENTIC_BAD_BEFORE_FIRE: onBeforeFire must return ChatMessage[] | { messages, maxToolIterations?, totalTimeoutMs?, tools?, toolChoice? } | { skip: true } | null"
|
|
1692
|
-
),
|
|
1693
|
-
"AGENTIC_BAD_BEFORE_FIRE"
|
|
1694
|
-
);
|
|
1706
|
+
throw markPermanent(new TypeError(BAD_BEFORE_FIRE_MESSAGE), "AGENTIC_BAD_BEFORE_FIRE");
|
|
1695
1707
|
}
|
|
1696
1708
|
function firstPositiveInt(values, fallback) {
|
|
1697
1709
|
for (const v of values) {
|
|
@@ -2026,6 +2038,7 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
|
|
|
2026
2038
|
};
|
|
2027
2039
|
let settledStatus = "failed";
|
|
2028
2040
|
let settledError = null;
|
|
2041
|
+
let settledRetryAfter = null;
|
|
2029
2042
|
try {
|
|
2030
2043
|
const outcome = await runFireChain({
|
|
2031
2044
|
task,
|
|
@@ -2050,7 +2063,11 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
|
|
|
2050
2063
|
occurrenceMs
|
|
2051
2064
|
});
|
|
2052
2065
|
throwIfCancelled();
|
|
2053
|
-
|
|
2066
|
+
if (!outcome.handled) settledStatus = "not-handled";
|
|
2067
|
+
else if (outcome.result.status === "deferred") {
|
|
2068
|
+
settledStatus = "deferred";
|
|
2069
|
+
settledRetryAfter = outcome.result.retryAfter;
|
|
2070
|
+
} else settledStatus = outcome.result.status === "skipped" ? "skipped" : "sent";
|
|
2054
2071
|
return outcome;
|
|
2055
2072
|
} catch (error) {
|
|
2056
2073
|
if (isCancelled() && !isTaskCancelledError(error)) {
|
|
@@ -2070,6 +2087,7 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
|
|
|
2070
2087
|
status: settledStatus,
|
|
2071
2088
|
...settledStatus === "cancelled" ? { willRetry: false, failureStage: null } : settledStatus === "failed" && ctx._deliveryState ? failureRetryDecision(ctx._deliveryState, settledError) : { willRetry: null, failureStage: null },
|
|
2072
2089
|
skipReason: settledStatus === "skipped" ? progress.skipReason : null,
|
|
2090
|
+
retryAfter: settledStatus === "deferred" ? settledRetryAfter : null,
|
|
2073
2091
|
sentCount: progress.sentCount,
|
|
2074
2092
|
pushedCount: progress.pushedCount,
|
|
2075
2093
|
total: progress.total,
|
|
@@ -2127,6 +2145,27 @@ async function runFireChain({
|
|
|
2127
2145
|
progress.skipReason = "before-fire";
|
|
2128
2146
|
return { handled: true, result: { success: true, messagesSent: 0, status: "skipped", iterations: 0 } };
|
|
2129
2147
|
}
|
|
2148
|
+
if (typeof before === "object" && !Array.isArray(before) && before.defer != null) {
|
|
2149
|
+
const afterMs = readDeferAfterMs(before.defer);
|
|
2150
|
+
if (ctx._deferSupported !== true) {
|
|
2151
|
+
throw new DeploymentConfigError(
|
|
2152
|
+
"AGENTIC_DEFER_UNSUPPORTED: onBeforeFire \u8FD4\u56DE\u4E86 { defer }\uFF0C\u4F46\u8FD9\u6761\u6295\u9012\u8DEF\u5F84\u4E0D\u652F\u6301\u63A8\u8FDF\u2014\u2014\u53EA\u6709 runScheduledTick / runTask \u4E14\u9002\u914D\u5668\u5B9E\u73B0\u4E86 claimTask\uFF08\u4EFB\u52A1\u8868\u6709 retry_after \u5217\uFF09\u65F6\u53EF\u7528\uFF0C\u8BF7\u6C42\u5185\u5F53\u573A\u6295\u9012\u7684 instant \u4EFB\u52A1\u4E0D\u53EF\u7528",
|
|
2153
|
+
{ code: "AGENTIC_DEFER_UNSUPPORTED" }
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
return {
|
|
2157
|
+
handled: true,
|
|
2158
|
+
result: {
|
|
2159
|
+
success: false,
|
|
2160
|
+
deferred: true,
|
|
2161
|
+
// 用真实时钟:这个时刻要落进 retry_after,跟数据库捞取条件里的「现在」比。
|
|
2162
|
+
retryAfter: new Date(Date.now() + afterMs).toISOString(),
|
|
2163
|
+
messagesSent: 0,
|
|
2164
|
+
status: "deferred",
|
|
2165
|
+
iterations: 0
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2130
2169
|
const normalized = normalizeBeforeFireResult(before);
|
|
2131
2170
|
const maxToolIterations = firstPositiveInt(
|
|
2132
2171
|
[normalized.maxToolIterations, ctx.maxToolIterations],
|
|
@@ -3240,6 +3279,7 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3240
3279
|
deletedOnceOffTasks: 0,
|
|
3241
3280
|
updatedRecurringTasks: 0,
|
|
3242
3281
|
staleTasks: [],
|
|
3282
|
+
deferredTasks: [],
|
|
3243
3283
|
cancelledTasks: [],
|
|
3244
3284
|
reasoningSkippedTasks: [],
|
|
3245
3285
|
redeliveredTasks: [],
|
|
@@ -3533,7 +3573,7 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3533
3573
|
const occurrenceMs = Date.parse(task.next_send_at);
|
|
3534
3574
|
const retryAfterMs = task.retry_after ? Date.parse(task.retry_after) : NaN;
|
|
3535
3575
|
const notOnFreshRetryChain = (task.retry_count || 0) === 0 || Number.isFinite(retryAfterMs) && Date.now() - retryAfterMs > staleAfterMs;
|
|
3536
|
-
|
|
3576
|
+
const settleAsStale = async () => {
|
|
3537
3577
|
try {
|
|
3538
3578
|
const stateAccessors = createStateAccessors({
|
|
3539
3579
|
db,
|
|
@@ -3593,8 +3633,32 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3593
3633
|
results.failedCount++;
|
|
3594
3634
|
results.failedTasks.push({ taskId: task.id, reason: error.message || "\u8FC7\u671F\u4EFB\u52A1\u5904\u7406\u5931\u8D25", status: "stale_update_failed" });
|
|
3595
3635
|
}
|
|
3636
|
+
};
|
|
3637
|
+
if (Number.isFinite(occurrenceMs) && Date.now() - occurrenceMs > staleAfterMs && notOnFreshRetryChain) {
|
|
3638
|
+
await settleAsStale();
|
|
3596
3639
|
return;
|
|
3597
3640
|
}
|
|
3641
|
+
const handleDeferred = async (retryAfter) => {
|
|
3642
|
+
const wakeMs = Date.parse(retryAfter);
|
|
3643
|
+
if (Number.isFinite(occurrenceMs) && wakeMs - occurrenceMs > staleAfterMs) {
|
|
3644
|
+
await settleAsStale();
|
|
3645
|
+
return;
|
|
3646
|
+
}
|
|
3647
|
+
try {
|
|
3648
|
+
const updated = await updateTaskWithLastError(task.id, {
|
|
3649
|
+
retry_after: retryAfter,
|
|
3650
|
+
lease_until: null
|
|
3651
|
+
});
|
|
3652
|
+
if (rowVanished(updated)) {
|
|
3653
|
+
await recordCancelled(task, "cancelled_mid_delivery");
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
results.deferredTasks.push({ taskId: task.id, retryAfter });
|
|
3657
|
+
} catch (error) {
|
|
3658
|
+
results.failedCount++;
|
|
3659
|
+
results.failedTasks.push({ taskId: task.id, reason: error.message || "\u63A8\u8FDF\u5199\u5E93\u5931\u8D25", status: "defer_update_failed" });
|
|
3660
|
+
}
|
|
3661
|
+
};
|
|
3598
3662
|
let sendResult;
|
|
3599
3663
|
try {
|
|
3600
3664
|
sendResult = await processSingleMessage(
|
|
@@ -3605,7 +3669,11 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3605
3669
|
masterKey,
|
|
3606
3670
|
webpush: guardWebpushWithLease(ctx.webpush, lease),
|
|
3607
3671
|
isTaskCancelled: () => lease.lost,
|
|
3608
|
-
signal: lease.signal
|
|
3672
|
+
signal: lease.signal,
|
|
3673
|
+
// onBeforeFire 的 { defer } 要靠 retry_after 这一列落地,没实现
|
|
3674
|
+
// claimTask 的适配器没有这一列(见 lib/agentic-fire.js 的
|
|
3675
|
+
// AGENTIC_DEFER_UNSUPPORTED)。
|
|
3676
|
+
_deferSupported: supportsClaim
|
|
3609
3677
|
},
|
|
3610
3678
|
masterKey,
|
|
3611
3679
|
{ userKey, payload: decryptedPayload }
|
|
@@ -3625,6 +3693,14 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3625
3693
|
);
|
|
3626
3694
|
return;
|
|
3627
3695
|
}
|
|
3696
|
+
if (sendResult.deferred) {
|
|
3697
|
+
if (lease.lost) {
|
|
3698
|
+
await recordCancelled(task, "cancelled_mid_delivery");
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
await handleDeferred(sendResult.retryAfter);
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3628
3704
|
if (!sendResult.success) {
|
|
3629
3705
|
if (lease.lost) {
|
|
3630
3706
|
await recordCancelled(task, "cancelled_mid_delivery");
|
|
@@ -3717,6 +3793,9 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3717
3793
|
deletedOnceOffTasks: results.deletedOnceOffTasks,
|
|
3718
3794
|
updatedRecurringTasks: results.updatedRecurringTasks,
|
|
3719
3795
|
staleTasks: results.staleTasks,
|
|
3796
|
+
// onBeforeFire 返回 { defer } 被推迟的任务({ taskId, retryAfter }):这一
|
|
3797
|
+
// 跳没生成也没发,到 retryAfter 之后再问。不计入 successCount / failedCount。
|
|
3798
|
+
deferredTasks: results.deferredTasks,
|
|
3720
3799
|
// 投递期间行被取消 / 顶替的任务。`cancelled_mid_delivery` = 推送在发出去
|
|
3721
3800
|
// 之前被拦下;`cancelled_after_delivery` = 推送已经发完,收尾写库才发现
|
|
3722
3801
|
// 行没了。两种都不计入 successCount / failedCount。
|
|
@@ -6012,7 +6091,7 @@ function createClientStateNamespacesHandler(ctx) {
|
|
|
6012
6091
|
}
|
|
6013
6092
|
|
|
6014
6093
|
// src/server/lib/version.js
|
|
6015
|
-
var SERVER_VERSION = true ? "2.6.0-next.
|
|
6094
|
+
var SERVER_VERSION = true ? "2.6.0-next.33" : "0.0.0-dev";
|
|
6016
6095
|
|
|
6017
6096
|
// src/server/handlers/capabilities.js
|
|
6018
6097
|
var SERVER_FEATURES = Object.freeze([
|
|
@@ -6125,7 +6204,10 @@ var SERVER_FEATURES = Object.freeze([
|
|
|
6125
6204
|
// 工厂配置认 maxDeliveryRetries(投递失败的重试次数上限,默认 3)。
|
|
6126
6205
|
"max-delivery-retries",
|
|
6127
6206
|
// Per-task pre-commit retry limit and fire receipt retry decision.
|
|
6128
|
-
"max-generation-retries"
|
|
6207
|
+
"max-generation-retries",
|
|
6208
|
+
// onBeforeFire 认 { defer: { afterMs } }:这次不生成,过一会儿再来问(不占重试
|
|
6209
|
+
// 次数);onFireSettled 的 status 多一种 'deferred',带 retryAfter。
|
|
6210
|
+
"before-fire-defer"
|
|
6129
6211
|
]);
|
|
6130
6212
|
function createCapabilitiesHandler(ctx) {
|
|
6131
6213
|
async function GET(url, headers) {
|
|
@@ -6753,4 +6835,5 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
|
|
|
6753
6835
|
|
|
6754
6836
|
|
|
6755
6837
|
|
|
6756
|
-
|
|
6838
|
+
|
|
6839
|
+
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;
|
|
@@ -1636,6 +1636,7 @@ var DEFAULT_MAX_TOOL_ITERATIONS = 5;
|
|
|
1636
1636
|
var DEFAULT_TOTAL_TIMEOUT_MS = 24e4;
|
|
1637
1637
|
var DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = 2;
|
|
1638
1638
|
var MIN_SCHEDULE_LEAD_MS = 6e4;
|
|
1639
|
+
var MAX_DEFER_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
1639
1640
|
var SCHEDULABLE_MESSAGE_TYPES = /* @__PURE__ */ new Set(["auto", "prompted", "fixed"]);
|
|
1640
1641
|
var SCHEDULABLE_RECURRENCE_TYPES = /* @__PURE__ */ new Set(["none", "daily", "weekly"]);
|
|
1641
1642
|
var SLEEP_BETWEEN_MESSAGES_MS = 1500;
|
|
@@ -1670,6 +1671,22 @@ function buildHookTask(task, decryptedPayload) {
|
|
|
1670
1671
|
retryCount: task.retry_count ?? 0
|
|
1671
1672
|
});
|
|
1672
1673
|
}
|
|
1674
|
+
var BAD_BEFORE_FIRE_MESSAGE = "AGENTIC_BAD_BEFORE_FIRE: onBeforeFire must return ChatMessage[] | { messages, maxToolIterations?, totalTimeoutMs?, tools?, toolChoice? } | { skip: true } | { defer: { afterMs } } | null";
|
|
1675
|
+
function readDeferAfterMs(defer) {
|
|
1676
|
+
const afterMs = defer && typeof defer === "object" ? (
|
|
1677
|
+
/** @type {any} */
|
|
1678
|
+
defer.afterMs
|
|
1679
|
+
) : void 0;
|
|
1680
|
+
if (typeof afterMs !== "number" || !Number.isFinite(afterMs) || afterMs <= 0 || afterMs > MAX_DEFER_AFTER_MS) {
|
|
1681
|
+
throw markPermanent(
|
|
1682
|
+
new TypeError(
|
|
1683
|
+
`${BAD_BEFORE_FIRE_MESSAGE}\uFF08defer.afterMs \u5FC5\u987B\u662F\u6709\u9650\u6B63\u6570\uFF0C\u4E14\u4E0D\u8D85\u8FC7 ${MAX_DEFER_AFTER_MS} \u6BEB\u79D2\uFF09`
|
|
1684
|
+
),
|
|
1685
|
+
"AGENTIC_BAD_BEFORE_FIRE"
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
return afterMs;
|
|
1689
|
+
}
|
|
1673
1690
|
function normalizeBeforeFireResult(result) {
|
|
1674
1691
|
if (Array.isArray(result)) {
|
|
1675
1692
|
return { messages: result };
|
|
@@ -1686,12 +1703,7 @@ function normalizeBeforeFireResult(result) {
|
|
|
1686
1703
|
toolChoice: result.toolChoice
|
|
1687
1704
|
};
|
|
1688
1705
|
}
|
|
1689
|
-
throw markPermanent(
|
|
1690
|
-
new TypeError(
|
|
1691
|
-
"AGENTIC_BAD_BEFORE_FIRE: onBeforeFire must return ChatMessage[] | { messages, maxToolIterations?, totalTimeoutMs?, tools?, toolChoice? } | { skip: true } | null"
|
|
1692
|
-
),
|
|
1693
|
-
"AGENTIC_BAD_BEFORE_FIRE"
|
|
1694
|
-
);
|
|
1706
|
+
throw markPermanent(new TypeError(BAD_BEFORE_FIRE_MESSAGE), "AGENTIC_BAD_BEFORE_FIRE");
|
|
1695
1707
|
}
|
|
1696
1708
|
function firstPositiveInt(values, fallback) {
|
|
1697
1709
|
for (const v of values) {
|
|
@@ -2026,6 +2038,7 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
|
|
|
2026
2038
|
};
|
|
2027
2039
|
let settledStatus = "failed";
|
|
2028
2040
|
let settledError = null;
|
|
2041
|
+
let settledRetryAfter = null;
|
|
2029
2042
|
try {
|
|
2030
2043
|
const outcome = await runFireChain({
|
|
2031
2044
|
task,
|
|
@@ -2050,7 +2063,11 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
|
|
|
2050
2063
|
occurrenceMs
|
|
2051
2064
|
});
|
|
2052
2065
|
throwIfCancelled();
|
|
2053
|
-
|
|
2066
|
+
if (!outcome.handled) settledStatus = "not-handled";
|
|
2067
|
+
else if (outcome.result.status === "deferred") {
|
|
2068
|
+
settledStatus = "deferred";
|
|
2069
|
+
settledRetryAfter = outcome.result.retryAfter;
|
|
2070
|
+
} else settledStatus = outcome.result.status === "skipped" ? "skipped" : "sent";
|
|
2054
2071
|
return outcome;
|
|
2055
2072
|
} catch (error) {
|
|
2056
2073
|
if (isCancelled() && !isTaskCancelledError(error)) {
|
|
@@ -2070,6 +2087,7 @@ async function runAgenticFire({ task, decryptedPayload, userKey, ctx }) {
|
|
|
2070
2087
|
status: settledStatus,
|
|
2071
2088
|
...settledStatus === "cancelled" ? { willRetry: false, failureStage: null } : settledStatus === "failed" && ctx._deliveryState ? failureRetryDecision(ctx._deliveryState, settledError) : { willRetry: null, failureStage: null },
|
|
2072
2089
|
skipReason: settledStatus === "skipped" ? progress.skipReason : null,
|
|
2090
|
+
retryAfter: settledStatus === "deferred" ? settledRetryAfter : null,
|
|
2073
2091
|
sentCount: progress.sentCount,
|
|
2074
2092
|
pushedCount: progress.pushedCount,
|
|
2075
2093
|
total: progress.total,
|
|
@@ -2127,6 +2145,27 @@ async function runFireChain({
|
|
|
2127
2145
|
progress.skipReason = "before-fire";
|
|
2128
2146
|
return { handled: true, result: { success: true, messagesSent: 0, status: "skipped", iterations: 0 } };
|
|
2129
2147
|
}
|
|
2148
|
+
if (typeof before === "object" && !Array.isArray(before) && before.defer != null) {
|
|
2149
|
+
const afterMs = readDeferAfterMs(before.defer);
|
|
2150
|
+
if (ctx._deferSupported !== true) {
|
|
2151
|
+
throw new DeploymentConfigError(
|
|
2152
|
+
"AGENTIC_DEFER_UNSUPPORTED: onBeforeFire \u8FD4\u56DE\u4E86 { defer }\uFF0C\u4F46\u8FD9\u6761\u6295\u9012\u8DEF\u5F84\u4E0D\u652F\u6301\u63A8\u8FDF\u2014\u2014\u53EA\u6709 runScheduledTick / runTask \u4E14\u9002\u914D\u5668\u5B9E\u73B0\u4E86 claimTask\uFF08\u4EFB\u52A1\u8868\u6709 retry_after \u5217\uFF09\u65F6\u53EF\u7528\uFF0C\u8BF7\u6C42\u5185\u5F53\u573A\u6295\u9012\u7684 instant \u4EFB\u52A1\u4E0D\u53EF\u7528",
|
|
2153
|
+
{ code: "AGENTIC_DEFER_UNSUPPORTED" }
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
return {
|
|
2157
|
+
handled: true,
|
|
2158
|
+
result: {
|
|
2159
|
+
success: false,
|
|
2160
|
+
deferred: true,
|
|
2161
|
+
// 用真实时钟:这个时刻要落进 retry_after,跟数据库捞取条件里的「现在」比。
|
|
2162
|
+
retryAfter: new Date(Date.now() + afterMs).toISOString(),
|
|
2163
|
+
messagesSent: 0,
|
|
2164
|
+
status: "deferred",
|
|
2165
|
+
iterations: 0
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2130
2169
|
const normalized = normalizeBeforeFireResult(before);
|
|
2131
2170
|
const maxToolIterations = firstPositiveInt(
|
|
2132
2171
|
[normalized.maxToolIterations, ctx.maxToolIterations],
|
|
@@ -3240,6 +3279,7 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3240
3279
|
deletedOnceOffTasks: 0,
|
|
3241
3280
|
updatedRecurringTasks: 0,
|
|
3242
3281
|
staleTasks: [],
|
|
3282
|
+
deferredTasks: [],
|
|
3243
3283
|
cancelledTasks: [],
|
|
3244
3284
|
reasoningSkippedTasks: [],
|
|
3245
3285
|
redeliveredTasks: [],
|
|
@@ -3533,7 +3573,7 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3533
3573
|
const occurrenceMs = Date.parse(task.next_send_at);
|
|
3534
3574
|
const retryAfterMs = task.retry_after ? Date.parse(task.retry_after) : NaN;
|
|
3535
3575
|
const notOnFreshRetryChain = (task.retry_count || 0) === 0 || Number.isFinite(retryAfterMs) && Date.now() - retryAfterMs > staleAfterMs;
|
|
3536
|
-
|
|
3576
|
+
const settleAsStale = async () => {
|
|
3537
3577
|
try {
|
|
3538
3578
|
const stateAccessors = createStateAccessors({
|
|
3539
3579
|
db,
|
|
@@ -3593,8 +3633,32 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3593
3633
|
results.failedCount++;
|
|
3594
3634
|
results.failedTasks.push({ taskId: task.id, reason: error.message || "\u8FC7\u671F\u4EFB\u52A1\u5904\u7406\u5931\u8D25", status: "stale_update_failed" });
|
|
3595
3635
|
}
|
|
3636
|
+
};
|
|
3637
|
+
if (Number.isFinite(occurrenceMs) && Date.now() - occurrenceMs > staleAfterMs && notOnFreshRetryChain) {
|
|
3638
|
+
await settleAsStale();
|
|
3596
3639
|
return;
|
|
3597
3640
|
}
|
|
3641
|
+
const handleDeferred = async (retryAfter) => {
|
|
3642
|
+
const wakeMs = Date.parse(retryAfter);
|
|
3643
|
+
if (Number.isFinite(occurrenceMs) && wakeMs - occurrenceMs > staleAfterMs) {
|
|
3644
|
+
await settleAsStale();
|
|
3645
|
+
return;
|
|
3646
|
+
}
|
|
3647
|
+
try {
|
|
3648
|
+
const updated = await updateTaskWithLastError(task.id, {
|
|
3649
|
+
retry_after: retryAfter,
|
|
3650
|
+
lease_until: null
|
|
3651
|
+
});
|
|
3652
|
+
if (rowVanished(updated)) {
|
|
3653
|
+
await recordCancelled(task, "cancelled_mid_delivery");
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
results.deferredTasks.push({ taskId: task.id, retryAfter });
|
|
3657
|
+
} catch (error) {
|
|
3658
|
+
results.failedCount++;
|
|
3659
|
+
results.failedTasks.push({ taskId: task.id, reason: error.message || "\u63A8\u8FDF\u5199\u5E93\u5931\u8D25", status: "defer_update_failed" });
|
|
3660
|
+
}
|
|
3661
|
+
};
|
|
3598
3662
|
let sendResult;
|
|
3599
3663
|
try {
|
|
3600
3664
|
sendResult = await processSingleMessage(
|
|
@@ -3605,7 +3669,11 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3605
3669
|
masterKey,
|
|
3606
3670
|
webpush: guardWebpushWithLease(ctx.webpush, lease),
|
|
3607
3671
|
isTaskCancelled: () => lease.lost,
|
|
3608
|
-
signal: lease.signal
|
|
3672
|
+
signal: lease.signal,
|
|
3673
|
+
// onBeforeFire 的 { defer } 要靠 retry_after 这一列落地,没实现
|
|
3674
|
+
// claimTask 的适配器没有这一列(见 lib/agentic-fire.js 的
|
|
3675
|
+
// AGENTIC_DEFER_UNSUPPORTED)。
|
|
3676
|
+
_deferSupported: supportsClaim
|
|
3609
3677
|
},
|
|
3610
3678
|
masterKey,
|
|
3611
3679
|
{ userKey, payload: decryptedPayload }
|
|
@@ -3625,6 +3693,14 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3625
3693
|
);
|
|
3626
3694
|
return;
|
|
3627
3695
|
}
|
|
3696
|
+
if (sendResult.deferred) {
|
|
3697
|
+
if (lease.lost) {
|
|
3698
|
+
await recordCancelled(task, "cancelled_mid_delivery");
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
await handleDeferred(sendResult.retryAfter);
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3628
3704
|
if (!sendResult.success) {
|
|
3629
3705
|
if (lease.lost) {
|
|
3630
3706
|
await recordCancelled(task, "cancelled_mid_delivery");
|
|
@@ -3717,6 +3793,9 @@ async function deliverTasks(ctx, tasks) {
|
|
|
3717
3793
|
deletedOnceOffTasks: results.deletedOnceOffTasks,
|
|
3718
3794
|
updatedRecurringTasks: results.updatedRecurringTasks,
|
|
3719
3795
|
staleTasks: results.staleTasks,
|
|
3796
|
+
// onBeforeFire 返回 { defer } 被推迟的任务({ taskId, retryAfter }):这一
|
|
3797
|
+
// 跳没生成也没发,到 retryAfter 之后再问。不计入 successCount / failedCount。
|
|
3798
|
+
deferredTasks: results.deferredTasks,
|
|
3720
3799
|
// 投递期间行被取消 / 顶替的任务。`cancelled_mid_delivery` = 推送在发出去
|
|
3721
3800
|
// 之前被拦下;`cancelled_after_delivery` = 推送已经发完,收尾写库才发现
|
|
3722
3801
|
// 行没了。两种都不计入 successCount / failedCount。
|
|
@@ -6012,7 +6091,7 @@ function createClientStateNamespacesHandler(ctx) {
|
|
|
6012
6091
|
}
|
|
6013
6092
|
|
|
6014
6093
|
// src/server/lib/version.js
|
|
6015
|
-
var SERVER_VERSION = true ? "2.6.0-next.
|
|
6094
|
+
var SERVER_VERSION = true ? "2.6.0-next.33" : "0.0.0-dev";
|
|
6016
6095
|
|
|
6017
6096
|
// src/server/handlers/capabilities.js
|
|
6018
6097
|
var SERVER_FEATURES = Object.freeze([
|
|
@@ -6125,7 +6204,10 @@ var SERVER_FEATURES = Object.freeze([
|
|
|
6125
6204
|
// 工厂配置认 maxDeliveryRetries(投递失败的重试次数上限,默认 3)。
|
|
6126
6205
|
"max-delivery-retries",
|
|
6127
6206
|
// Per-task pre-commit retry limit and fire receipt retry decision.
|
|
6128
|
-
"max-generation-retries"
|
|
6207
|
+
"max-generation-retries",
|
|
6208
|
+
// onBeforeFire 认 { defer: { afterMs } }:这次不生成,过一会儿再来问(不占重试
|
|
6209
|
+
// 次数);onFireSettled 的 status 多一种 'deferred',带 retryAfter。
|
|
6210
|
+
"before-fire-defer"
|
|
6129
6211
|
]);
|
|
6130
6212
|
function createCapabilitiesHandler(ctx) {
|
|
6131
6213
|
async function GET(url, headers) {
|
|
@@ -6733,6 +6815,7 @@ export {
|
|
|
6733
6815
|
DEFAULT_TOTAL_TIMEOUT_MS,
|
|
6734
6816
|
DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE,
|
|
6735
6817
|
MIN_SCHEDULE_LEAD_MS,
|
|
6818
|
+
MAX_DEFER_AFTER_MS,
|
|
6736
6819
|
createScheduleMessageHandler,
|
|
6737
6820
|
DEFAULT_CLAIM_LEASE_MS,
|
|
6738
6821
|
DEFAULT_LEASE_HEARTBEAT_MS,
|
package/dist/cloudflare.cjs
CHANGED
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
var _chunkAM2MMY46cjs = require('./chunk-AM2MMY46.cjs');
|
|
31
32
|
require('./chunk-7NMQFTDJ.cjs');
|
|
32
33
|
|
|
33
34
|
|
|
@@ -58,4 +59,5 @@ require('./chunk-7NMQFTDJ.cjs');
|
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
|
|
63
|
+
exports.DEFAULT_MAX_DELIVERY_RETRIES = _chunkAM2MMY46cjs.DEFAULT_MAX_DELIVERY_RETRIES; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkAM2MMY46cjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.MAX_DEFER_AFTER_MS = _chunkAM2MMY46cjs.MAX_DEFER_AFTER_MS; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkAM2MMY46cjs.MAX_PUSH_PAYLOAD_BYTES; exports.NonRetryableError = _chunkAM2MMY46cjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkAM2MMY46cjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkAM2MMY46cjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkAM2MMY46cjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkAM2MMY46cjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkAM2MMY46cjs.advanceOccurrence; exports.createD1Adapter = _chunkAM2MMY46cjs.createD1Adapter; exports.createSingleUserCloudflareWorker = _chunkAM2MMY46cjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkAM2MMY46cjs.createSingleUserServer; exports.createWebCryptoWebPush = _chunkAM2MMY46cjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkAM2MMY46cjs.decryptFromStorage; exports.decryptPayload = _chunkAM2MMY46cjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkAM2MMY46cjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkAM2MMY46cjs.encryptForStorage; exports.ensureSchema = _chunkAM2MMY46cjs.ensureSchema; exports.getSchemaVersion = _chunkAM2MMY46cjs.getSchemaVersion; exports.isNonRetryableError = _chunkAM2MMY46cjs.isNonRetryableError; exports.isValidTimeZoneId = _chunkAM2MMY46cjs.isValidTimeZoneId; exports.measurePushPayload = _chunkAM2MMY46cjs.measurePushPayload; exports.nextFutureOccurrence = _chunkAM2MMY46cjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkAM2MMY46cjs.planNextOccurrence; exports.readRequestBody = _chunkAM2MMY46cjs.readRequestBody; exports.runScheduledTick = _chunkAM2MMY46cjs.runScheduledTick; exports.runTask = _chunkAM2MMY46cjs.runTask; exports.summarizeErrorCause = _chunkAM2MMY46cjs.summarizeErrorCause;
|
package/dist/cloudflare.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createSingleUserCloudflareWorker } from "./cloudflare/single-user-worker.js";
|
|
2
2
|
export { createSingleUserServer } from "./single-user.js";
|
|
3
3
|
export { createD1Adapter } from "./adapters/d1.js";
|
|
4
|
+
export { MAX_DEFER_AFTER_MS } from "./lib/agentic-fire.js";
|
|
4
5
|
export { createWebCryptoWebPush, measurePushPayload, MAX_PUSH_PAYLOAD_BYTES, PUSH_ENVELOPE_RESERVED_BYTES, WEB_PUSH_MAX_BODY_BYTES, WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES } from "./lib/webpush-webcrypto.js";
|
|
5
6
|
export { runScheduledTick, runTask, DEFAULT_MAX_DELIVERY_RETRIES } from "./lib/run-tick.js";
|
|
6
7
|
export { getSchemaVersion, ensureSchema, SCHEMA_VERSION } from "./lib/schema-version.js";
|
package/dist/cloudflare.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_MAX_DELIVERY_RETRIES,
|
|
3
3
|
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
4
|
+
MAX_DEFER_AFTER_MS,
|
|
4
5
|
MAX_PUSH_PAYLOAD_BYTES,
|
|
5
6
|
NonRetryableError,
|
|
6
7
|
PUSH_ENVELOPE_RESERVED_BYTES,
|
|
@@ -27,11 +28,12 @@ import {
|
|
|
27
28
|
runScheduledTick,
|
|
28
29
|
runTask,
|
|
29
30
|
summarizeErrorCause
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-Q4VQ3NVR.mjs";
|
|
31
32
|
import "./chunk-GN44PST5.mjs";
|
|
32
33
|
export {
|
|
33
34
|
DEFAULT_MAX_DELIVERY_RETRIES,
|
|
34
35
|
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
36
|
+
MAX_DEFER_AFTER_MS,
|
|
35
37
|
MAX_PUSH_PAYLOAD_BYTES,
|
|
36
38
|
NonRetryableError,
|
|
37
39
|
PUSH_ENVELOPE_RESERVED_BYTES,
|
package/dist/index.cjs
CHANGED
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
|
|
76
|
+
var _chunkAM2MMY46cjs = require('./chunk-AM2MMY46.cjs');
|
|
76
77
|
require('./chunk-7NMQFTDJ.cjs');
|
|
77
78
|
|
|
78
79
|
// src/server/adapters/factory.js
|
|
@@ -106,7 +107,7 @@ async function createAdapter(config) {
|
|
|
106
107
|
// src/server/handlers/init-tenant.js
|
|
107
108
|
function createInitTenantHandler(ctx) {
|
|
108
109
|
async function POST(headers, body) {
|
|
109
|
-
const initSecret =
|
|
110
|
+
const initSecret = _chunkAM2MMY46cjs.getHeader.call(void 0, headers, "x-init-secret");
|
|
110
111
|
if (ctx.tenant.initSecret && initSecret !== ctx.tenant.initSecret) {
|
|
111
112
|
return {
|
|
112
113
|
status: 401,
|
|
@@ -119,7 +120,7 @@ function createInitTenantHandler(ctx) {
|
|
|
119
120
|
}
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
|
-
const parsed =
|
|
123
|
+
const parsed = _chunkAM2MMY46cjs.parseJsonBody.call(void 0, body);
|
|
123
124
|
if (!parsed.ok) {
|
|
124
125
|
return {
|
|
125
126
|
status: 400,
|
|
@@ -133,7 +134,7 @@ function createInitTenantHandler(ctx) {
|
|
|
133
134
|
const tenantId = payload.tenantId || void 0;
|
|
134
135
|
const driver = payload.driver;
|
|
135
136
|
const databaseUrl = payload.databaseUrl || payload.connectionString;
|
|
136
|
-
if (tenantId && !
|
|
137
|
+
if (tenantId && !_chunkAM2MMY46cjs.isValidUUIDv4.call(void 0, tenantId)) {
|
|
137
138
|
return {
|
|
138
139
|
status: 400,
|
|
139
140
|
body: {
|
|
@@ -253,7 +254,7 @@ function createSendNotificationsHandler(ctx) {
|
|
|
253
254
|
}
|
|
254
255
|
};
|
|
255
256
|
}
|
|
256
|
-
const data = await
|
|
257
|
+
const data = await _chunkAM2MMY46cjs.runScheduledTick.call(void 0, { ...ctx, db, masterKey });
|
|
257
258
|
return { status: 200, body: { success: true, data } };
|
|
258
259
|
}
|
|
259
260
|
return { POST };
|
|
@@ -275,16 +276,16 @@ function encryptConfig(config, kekBuffer) {
|
|
|
275
276
|
const plaintext = Buffer.from(JSON.stringify(config), "utf8");
|
|
276
277
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
277
278
|
const authTag = cipher.getAuthTag();
|
|
278
|
-
return `v1.${
|
|
279
|
+
return `v1.${_chunkAM2MMY46cjs.bytesToBase64Url.call(void 0, iv)}.${_chunkAM2MMY46cjs.bytesToBase64Url.call(void 0, authTag)}.${_chunkAM2MMY46cjs.bytesToBase64Url.call(void 0, ciphertext)}`;
|
|
279
280
|
}
|
|
280
281
|
function decryptConfig(encrypted, kekBuffer) {
|
|
281
282
|
const parts = String(encrypted || "").split(".");
|
|
282
283
|
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
283
284
|
throw new Error("INVALID_TENANT_CONFIG");
|
|
284
285
|
}
|
|
285
|
-
const iv =
|
|
286
|
-
const authTag =
|
|
287
|
-
const ciphertext =
|
|
286
|
+
const iv = _chunkAM2MMY46cjs.base64UrlToBytes.call(void 0, parts[1]);
|
|
287
|
+
const authTag = _chunkAM2MMY46cjs.base64UrlToBytes.call(void 0, parts[2]);
|
|
288
|
+
const ciphertext = _chunkAM2MMY46cjs.base64UrlToBytes.call(void 0, parts[3]);
|
|
288
289
|
const decipher = _crypto.createDecipheriv.call(void 0, "aes-256-gcm", kekBuffer, iv);
|
|
289
290
|
decipher.setAuthTag(authTag);
|
|
290
291
|
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
@@ -352,7 +353,7 @@ function createTenantBlobStore(options) {
|
|
|
352
353
|
// src/server/tenant/token.js
|
|
353
354
|
|
|
354
355
|
function sign(input, secret) {
|
|
355
|
-
return
|
|
356
|
+
return _chunkAM2MMY46cjs.bytesToBase64Url.call(void 0, _crypto.createHmac.call(void 0, "sha256", secret).update(input).digest());
|
|
356
357
|
}
|
|
357
358
|
function nowEpochSeconds() {
|
|
358
359
|
return Math.floor(Date.now() / 1e3);
|
|
@@ -368,8 +369,8 @@ function createTenantToken(params, secret) {
|
|
|
368
369
|
v: 1
|
|
369
370
|
};
|
|
370
371
|
const header = { alg: "HS256", typ: "JWT" };
|
|
371
|
-
const encodedHeader =
|
|
372
|
-
const encodedPayload =
|
|
372
|
+
const encodedHeader = _chunkAM2MMY46cjs.bytesToBase64Url.call(void 0, _chunkAM2MMY46cjs.utf8.call(void 0, JSON.stringify(header)));
|
|
373
|
+
const encodedPayload = _chunkAM2MMY46cjs.bytesToBase64Url.call(void 0, _chunkAM2MMY46cjs.utf8.call(void 0, JSON.stringify(payload)));
|
|
373
374
|
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
374
375
|
const signature = sign(signingInput, secret);
|
|
375
376
|
return `${signingInput}.${signature}`;
|
|
@@ -388,17 +389,17 @@ function verifyTenantToken(token, secret, options = {}) {
|
|
|
388
389
|
let receivedBytes;
|
|
389
390
|
let expectedBytes;
|
|
390
391
|
try {
|
|
391
|
-
receivedBytes =
|
|
392
|
-
expectedBytes =
|
|
392
|
+
receivedBytes = _chunkAM2MMY46cjs.base64UrlToBytes.call(void 0, receivedSignature);
|
|
393
|
+
expectedBytes = _chunkAM2MMY46cjs.base64UrlToBytes.call(void 0, expectedSignature);
|
|
393
394
|
} catch (e) {
|
|
394
395
|
throw new Error("INVALID_TENANT_AUTH");
|
|
395
396
|
}
|
|
396
|
-
if (!
|
|
397
|
+
if (!_chunkAM2MMY46cjs.timingSafeEqualBytes.call(void 0, receivedBytes, expectedBytes)) {
|
|
397
398
|
throw new Error("INVALID_TENANT_AUTH");
|
|
398
399
|
}
|
|
399
400
|
let payload;
|
|
400
401
|
try {
|
|
401
|
-
payload = JSON.parse(
|
|
402
|
+
payload = JSON.parse(_chunkAM2MMY46cjs.utf8Decode.call(void 0, _chunkAM2MMY46cjs.base64UrlToBytes.call(void 0, encodedPayload)));
|
|
402
403
|
} catch (e2) {
|
|
403
404
|
throw new Error("INVALID_TENANT_AUTH");
|
|
404
405
|
}
|
|
@@ -626,15 +627,15 @@ async function createReiServer(config) {
|
|
|
626
627
|
return {
|
|
627
628
|
handlers: {
|
|
628
629
|
initTenant: createInitTenantHandler(ctx),
|
|
629
|
-
getUserKey:
|
|
630
|
-
scheduleMessage:
|
|
630
|
+
getUserKey: _chunkAM2MMY46cjs.createGetUserKeyHandler.call(void 0, ctx),
|
|
631
|
+
scheduleMessage: _chunkAM2MMY46cjs.createScheduleMessageHandler.call(void 0, ctx),
|
|
631
632
|
sendNotifications: createSendNotificationsHandler(ctx),
|
|
632
|
-
updateMessage:
|
|
633
|
-
cancelMessage:
|
|
634
|
-
messages:
|
|
635
|
-
getMessage:
|
|
636
|
-
pushSubscription:
|
|
637
|
-
llmCredentials:
|
|
633
|
+
updateMessage: _chunkAM2MMY46cjs.createUpdateMessageHandler.call(void 0, ctx),
|
|
634
|
+
cancelMessage: _chunkAM2MMY46cjs.createCancelMessageHandler.call(void 0, ctx),
|
|
635
|
+
messages: _chunkAM2MMY46cjs.createMessagesHandler.call(void 0, ctx),
|
|
636
|
+
getMessage: _chunkAM2MMY46cjs.createGetMessageHandler.call(void 0, ctx),
|
|
637
|
+
pushSubscription: _chunkAM2MMY46cjs.createPushSubscriptionHandler.call(void 0, ctx),
|
|
638
|
+
llmCredentials: _chunkAM2MMY46cjs.createLlmCredentialsHandler.call(void 0, ctx)
|
|
638
639
|
}
|
|
639
640
|
};
|
|
640
641
|
}
|
|
@@ -701,4 +702,5 @@ async function createReiServer(config) {
|
|
|
701
702
|
|
|
702
703
|
|
|
703
704
|
|
|
704
|
-
|
|
705
|
+
|
|
706
|
+
exports.CORS_ALLOW_HEADERS = _chunkAM2MMY46cjs.CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = _chunkAM2MMY46cjs.CORS_ALLOW_METHODS; exports.CRED_ID_MAX_LENGTH = _chunkAM2MMY46cjs.CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = _chunkAM2MMY46cjs.CRED_PUT_BATCH_MAX; exports.CRED_REFS_MAX_ENTRIES = _chunkAM2MMY46cjs.CRED_REFS_MAX_ENTRIES; exports.CRED_ROWS_PER_USER_MAX = _chunkAM2MMY46cjs.CRED_ROWS_PER_USER_MAX; exports.DEFAULT_CLAIM_LEASE_MS = _chunkAM2MMY46cjs.DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = _chunkAM2MMY46cjs.DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = _chunkAM2MMY46cjs.DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_MAX_DELIVERY_RETRIES = _chunkAM2MMY46cjs.DEFAULT_MAX_DELIVERY_RETRIES; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkAM2MMY46cjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = _chunkAM2MMY46cjs.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.DEFAULT_MAX_TOOL_ITERATIONS = _chunkAM2MMY46cjs.DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = _chunkAM2MMY46cjs.DEFAULT_TOTAL_TIMEOUT_MS; exports.DeploymentConfigError = _chunkAM2MMY46cjs.DeploymentConfigError; exports.MAX_DEFER_AFTER_MS = _chunkAM2MMY46cjs.MAX_DEFER_AFTER_MS; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkAM2MMY46cjs.MAX_PUSH_PAYLOAD_BYTES; exports.MAX_TASK_PAYLOAD_BYTES = _chunkAM2MMY46cjs.MAX_TASK_PAYLOAD_BYTES; exports.MIN_SCHEDULE_LEAD_MS = _chunkAM2MMY46cjs.MIN_SCHEDULE_LEAD_MS; exports.NonRetryableError = _chunkAM2MMY46cjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkAM2MMY46cjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkAM2MMY46cjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkAM2MMY46cjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkAM2MMY46cjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkAM2MMY46cjs.advanceOccurrence; exports.createAdapter = createAdapter; exports.createD1Adapter = _chunkAM2MMY46cjs.createD1Adapter; exports.createReiServer = createReiServer; exports.createSingleUserCloudflareWorker = _chunkAM2MMY46cjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkAM2MMY46cjs.createSingleUserServer; exports.createTenantToken = createTenantToken; exports.createWebCryptoWebPush = _chunkAM2MMY46cjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkAM2MMY46cjs.decryptFromStorage; exports.decryptPayload = _chunkAM2MMY46cjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkAM2MMY46cjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkAM2MMY46cjs.encryptForStorage; exports.ensureSchema = _chunkAM2MMY46cjs.ensureSchema; exports.getSchemaVersion = _chunkAM2MMY46cjs.getSchemaVersion; exports.hasChatCredRef = _chunkAM2MMY46cjs.hasChatCredRef; exports.isNonRetryableError = _chunkAM2MMY46cjs.isNonRetryableError; exports.isValidCredId = _chunkAM2MMY46cjs.isValidCredId; exports.isValidISO8601 = _chunkAM2MMY46cjs.isValidISO8601; exports.isValidTimeZoneId = _chunkAM2MMY46cjs.isValidTimeZoneId; exports.isValidUUID = _chunkAM2MMY46cjs.isValidUUID; exports.isValidUUIDv4 = _chunkAM2MMY46cjs.isValidUUIDv4; exports.isValidUrl = _chunkAM2MMY46cjs.isValidUrl; exports.measurePushPayload = _chunkAM2MMY46cjs.measurePushPayload; exports.nextFutureOccurrence = _chunkAM2MMY46cjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkAM2MMY46cjs.planNextOccurrence; exports.readRequestBody = _chunkAM2MMY46cjs.readRequestBody; exports.resolveLlmCredential = _chunkAM2MMY46cjs.resolveLlmCredential; exports.runScheduledTick = _chunkAM2MMY46cjs.runScheduledTick; exports.runTask = _chunkAM2MMY46cjs.runTask; exports.sanitizeErrorSummary = _chunkAM2MMY46cjs.sanitizeErrorSummary; exports.summarizeErrorCause = _chunkAM2MMY46cjs.summarizeErrorCause; exports.supportsLlmCredentialsStore = _chunkAM2MMY46cjs.supportsLlmCredentialsStore; exports.validateAvatarUrl = _chunkAM2MMY46cjs.validateAvatarUrl; exports.validateCredRefs = _chunkAM2MMY46cjs.validateCredRefs; exports.validateCredValue = _chunkAM2MMY46cjs.validateCredValue; exports.validateLlmMessagesArray = _chunkAM2MMY46cjs.validateLlmMessagesArray; exports.validateScheduleMessagePayload = _chunkAM2MMY46cjs.validateScheduleMessagePayload; exports.validateSplitPattern = _chunkAM2MMY46cjs.validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;
|
package/dist/index.d.cts
CHANGED
|
@@ -175,7 +175,7 @@ export type ReiServer = {
|
|
|
175
175
|
export { runScheduledTick, runTask, DEFAULT_CLAIM_LEASE_MS, DEFAULT_LEASE_HEARTBEAT_MS, DEFAULT_HEARTBEAT_LEASE_TTL_MS, DEFAULT_MAX_DELIVERY_RETRIES, sanitizeErrorSummary } from "./lib/run-tick.js";
|
|
176
176
|
export { NonRetryableError, DeploymentConfigError, isNonRetryableError, summarizeErrorCause } from "./lib/errors.js";
|
|
177
177
|
export { getSchemaVersion, ensureSchema, SCHEMA_VERSION } from "./lib/schema-version.js";
|
|
178
|
-
export { DEFAULT_MAX_TOOL_ITERATIONS, DEFAULT_TOTAL_TIMEOUT_MS, DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE, MIN_SCHEDULE_LEAD_MS } from "./lib/agentic-fire.js";
|
|
178
|
+
export { DEFAULT_MAX_TOOL_ITERATIONS, DEFAULT_TOTAL_TIMEOUT_MS, DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE, MIN_SCHEDULE_LEAD_MS, MAX_DEFER_AFTER_MS } from "./lib/agentic-fire.js";
|
|
179
179
|
export { CORS_ALLOW_HEADERS, CORS_ALLOW_METHODS, createSingleUserCloudflareWorker } from "./cloudflare/single-user-worker.js";
|
|
180
180
|
export { createWebCryptoWebPush, measurePushPayload, MAX_PUSH_PAYLOAD_BYTES, PUSH_ENVELOPE_RESERVED_BYTES, WEB_PUSH_MAX_BODY_BYTES, WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES } from "./lib/webpush-webcrypto.js";
|
|
181
181
|
export { deriveUserEncryptionKey, decryptPayload, encryptForStorage, decryptFromStorage } from "./lib/encryption.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -175,7 +175,7 @@ export type ReiServer = {
|
|
|
175
175
|
export { runScheduledTick, runTask, DEFAULT_CLAIM_LEASE_MS, DEFAULT_LEASE_HEARTBEAT_MS, DEFAULT_HEARTBEAT_LEASE_TTL_MS, DEFAULT_MAX_DELIVERY_RETRIES, sanitizeErrorSummary } from "./lib/run-tick.js";
|
|
176
176
|
export { NonRetryableError, DeploymentConfigError, isNonRetryableError, summarizeErrorCause } from "./lib/errors.js";
|
|
177
177
|
export { getSchemaVersion, ensureSchema, SCHEMA_VERSION } from "./lib/schema-version.js";
|
|
178
|
-
export { DEFAULT_MAX_TOOL_ITERATIONS, DEFAULT_TOTAL_TIMEOUT_MS, DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE, MIN_SCHEDULE_LEAD_MS } from "./lib/agentic-fire.js";
|
|
178
|
+
export { DEFAULT_MAX_TOOL_ITERATIONS, DEFAULT_TOTAL_TIMEOUT_MS, DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE, MIN_SCHEDULE_LEAD_MS, MAX_DEFER_AFTER_MS } from "./lib/agentic-fire.js";
|
|
179
179
|
export { CORS_ALLOW_HEADERS, CORS_ALLOW_METHODS, createSingleUserCloudflareWorker } from "./cloudflare/single-user-worker.js";
|
|
180
180
|
export { createWebCryptoWebPush, measurePushPayload, MAX_PUSH_PAYLOAD_BYTES, PUSH_ENVELOPE_RESERVED_BYTES, WEB_PUSH_MAX_BODY_BYTES, WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES } from "./lib/webpush-webcrypto.js";
|
|
181
181
|
export { deriveUserEncryptionKey, decryptPayload, encryptForStorage, decryptFromStorage } from "./lib/encryption.js";
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
DEFAULT_MAX_TOOL_ITERATIONS,
|
|
15
15
|
DEFAULT_TOTAL_TIMEOUT_MS,
|
|
16
16
|
DeploymentConfigError,
|
|
17
|
+
MAX_DEFER_AFTER_MS,
|
|
17
18
|
MAX_PUSH_PAYLOAD_BYTES,
|
|
18
19
|
MAX_TASK_PAYLOAD_BYTES,
|
|
19
20
|
MIN_SCHEDULE_LEAD_MS,
|
|
@@ -72,7 +73,7 @@ import {
|
|
|
72
73
|
validateLlmMessagesArray,
|
|
73
74
|
validateScheduleMessagePayload,
|
|
74
75
|
validateSplitPattern
|
|
75
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-Q4VQ3NVR.mjs";
|
|
76
77
|
import "./chunk-GN44PST5.mjs";
|
|
77
78
|
|
|
78
79
|
// src/server/adapters/factory.js
|
|
@@ -654,6 +655,7 @@ export {
|
|
|
654
655
|
DEFAULT_MAX_TOOL_ITERATIONS,
|
|
655
656
|
DEFAULT_TOTAL_TIMEOUT_MS,
|
|
656
657
|
DeploymentConfigError,
|
|
658
|
+
MAX_DEFER_AFTER_MS,
|
|
657
659
|
MAX_PUSH_PAYLOAD_BYTES,
|
|
658
660
|
MAX_TASK_PAYLOAD_BYTES,
|
|
659
661
|
MIN_SCHEDULE_LEAD_MS,
|
|
@@ -57,12 +57,18 @@ export function buildHookTask(task: any, decryptedPayload: any): Readonly<{
|
|
|
57
57
|
* @param {Object} args.decryptedPayload - decrypted task payload (has credentials; they stop here)
|
|
58
58
|
* @param {string} args.userKey - per-user storage key (for readState decryption)
|
|
59
59
|
* @param {Object} args.ctx - processor ctx ({ db, webpush, vapid, hooks, maxToolIterations, totalTimeoutMs, maxScheduledTasksPerFire })
|
|
60
|
-
* @returns {Promise<{ handled: false }
|
|
60
|
+
* @returns {Promise<{ handled: false }
|
|
61
|
+
* | { handled: true, result: { success: true, messagesSent: number, status: 'finished'|'skipped', iterations: number } }
|
|
62
|
+
* | { handled: true, result: { success: false, deferred: true, retryAfter: string, messagesSent: 0, status: 'deferred', iterations: 0 } }>}
|
|
61
63
|
* `handled: false` → caller falls back to the legacy frozen-prompt path
|
|
62
64
|
* (onBeforeFire returned null).
|
|
63
65
|
* `onBeforeFire` may also return `{ skip: true }` to complete the fire
|
|
64
66
|
* before the first LLM call → `status: 'skipped', iterations: 0`, same
|
|
65
67
|
* success handling as the post-LLM skip-push path.
|
|
68
|
+
* `{ defer: { afterMs } }` → `status: 'deferred'`,`retryAfter` 是「什么时候
|
|
69
|
+
* 再来问」的 ISO 时刻。`success` 给 false 而不是 true:这次什么都没发,调用
|
|
70
|
+
* 方得看 `deferred` 走推迟的收尾;万一有调用方不认识这个字段,也只会把它当
|
|
71
|
+
* 成没发成,不会当成发完了把任务删掉。
|
|
66
72
|
* Failures (timeout / loop exceeded / config errors) throw — the caller's
|
|
67
73
|
* existing error handling turns them into task retry/failure.
|
|
68
74
|
*
|
|
@@ -84,6 +90,16 @@ export function runAgenticFire({ task, decryptedPayload, userKey, ctx }: {
|
|
|
84
90
|
status: "finished" | "skipped";
|
|
85
91
|
iterations: number;
|
|
86
92
|
};
|
|
93
|
+
} | {
|
|
94
|
+
handled: true;
|
|
95
|
+
result: {
|
|
96
|
+
success: false;
|
|
97
|
+
deferred: true;
|
|
98
|
+
retryAfter: string;
|
|
99
|
+
messagesSent: 0;
|
|
100
|
+
status: "deferred";
|
|
101
|
+
iterations: 0;
|
|
102
|
+
};
|
|
87
103
|
}>;
|
|
88
104
|
/**
|
|
89
105
|
* 把这一轮的 usage 累加进整次 fire 的合计,返回新的合计(不改传入的对象)。
|
|
@@ -116,6 +132,32 @@ export const DEFAULT_MAX_TOOL_ITERATIONS: 5;
|
|
|
116
132
|
export const DEFAULT_TOTAL_TIMEOUT_MS: 240000;
|
|
117
133
|
export const DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE: 2;
|
|
118
134
|
export const MIN_SCHEDULE_LEAD_MS: 60000;
|
|
135
|
+
export const MAX_DEFER_AFTER_MS: number;
|
|
136
|
+
/**
|
|
137
|
+
* - 消息数组 / `{ messages, ... }`:用这份 prompt 生成;
|
|
138
|
+
* - `{ skip: true }`:这次不发,按零推送的成功收场(一次性任务删行、循环任务
|
|
139
|
+
* 推进到下一次);
|
|
140
|
+
* - `{ defer: { afterMs } }`:现在不合适,afterMs 毫秒后再来问。afterMs 必须
|
|
141
|
+
* 是有限正数,且不超过 MAX_DEFER_AFTER_MS(24 小时);
|
|
142
|
+
* - `null` / `undefined`:交还给排程时冻结的 prompt 老链路。
|
|
143
|
+
*/
|
|
144
|
+
export type BeforeFireResult = Array<any> | {
|
|
145
|
+
messages: Array<any>;
|
|
146
|
+
maxToolIterations?: number;
|
|
147
|
+
totalTimeoutMs?: number;
|
|
148
|
+
tools?: Array<any>;
|
|
149
|
+
toolChoice?: any;
|
|
150
|
+
} | {
|
|
151
|
+
skip: true;
|
|
152
|
+
} | {
|
|
153
|
+
defer: {
|
|
154
|
+
afterMs: number;
|
|
155
|
+
};
|
|
156
|
+
} | null | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* 收尾回执(onFireSettled)的 status。
|
|
159
|
+
*/
|
|
160
|
+
export type FireSettledStatus = "sent" | "skipped" | "deferred" | "failed" | "cancelled" | "not-handled";
|
|
119
161
|
/**
|
|
120
162
|
* 把任务的调度身份投影进一条 push。
|
|
121
163
|
*
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
* @param {string} [providedMasterKey]
|
|
10
10
|
* @param {{ userKey: string, payload: Object } | null} [predecrypted] - 调用方
|
|
11
11
|
* (run-tick 的预扫描)已经解好的 payload;传了就不再解第二遍。
|
|
12
|
-
* @returns {Promise<{ success: boolean, messagesSent: number, redelivered?: boolean, pushedCount?: number, error?: string, errorCode?: string|null, pushStatusCode?: number|null, permanent?: boolean, willRetry?: boolean, retryLimit?: number, failureStage?: string }>}
|
|
12
|
+
* @returns {Promise<{ success: boolean, messagesSent: number, redelivered?: boolean, pushedCount?: number, deferred?: boolean, retryAfter?: string, error?: string, errorCode?: string|null, pushStatusCode?: number|null, permanent?: boolean, willRetry?: boolean, retryLimit?: number, failureStage?: string }>}
|
|
13
13
|
* 失败时 `pushStatusCode` 是推送服务回的 HTTP 状态码(不是推送阶段炸的 → null)。
|
|
14
|
+
* `deferred: true` 表示 onBeforeFire 把这次触发推迟了(`retryAfter` 是唤醒
|
|
15
|
+
* 时刻):`success` 为 false 但不是失败,调用方要先看这个字段。只有 ctx 带
|
|
16
|
+
* `_deferSupported: true`(run-tick 在适配器有 retry_after 列时给)才会出现。
|
|
14
17
|
*/
|
|
15
18
|
export function processSingleMessage(task: import("../adapters/interface.js").TaskRow, ctx: ProcessorContext, providedMasterKey?: string, predecrypted?: {
|
|
16
19
|
userKey: string;
|
|
@@ -20,6 +23,8 @@ export function processSingleMessage(task: import("../adapters/interface.js").Ta
|
|
|
20
23
|
messagesSent: number;
|
|
21
24
|
redelivered?: boolean;
|
|
22
25
|
pushedCount?: number;
|
|
26
|
+
deferred?: boolean;
|
|
27
|
+
retryAfter?: string;
|
|
23
28
|
error?: string;
|
|
24
29
|
errorCode?: string | null;
|
|
25
30
|
pushStatusCode?: number | null;
|
|
@@ -40,6 +45,9 @@ export function processSingleMessage(task: import("../adapters/interface.js").Ta
|
|
|
40
45
|
* `reasoningError` 只在正文都发出去了、思考过程那一条没发成时出现(思考过程是
|
|
41
46
|
* 附赠内容,它发不出去不算这条消息失败)。调用方拿它提示用户这次没有思考过程,
|
|
42
47
|
* 不带这个字段就是整轮都送到了。
|
|
48
|
+
*
|
|
49
|
+
* 这条路是请求里当场投递,不支持 onBeforeFire 的 `{ defer }`:返回它会得到
|
|
50
|
+
* AGENTIC_DEFER_UNSUPPORTED 的配置错误,按这里的失败流程处理。
|
|
43
51
|
*/
|
|
44
52
|
export function processMessagesByUuid(uuid: string, ctx: ProcessorContext, maxRetries?: number, userId?: string, providedMasterKey?: string): Promise<{
|
|
45
53
|
success: boolean;
|
package/dist/lib/run-tick.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export function runScheduledTick(ctx: any): Promise<{
|
|
|
24
24
|
deletedOnceOffTasks: number;
|
|
25
25
|
updatedRecurringTasks: number;
|
|
26
26
|
staleTasks: any[];
|
|
27
|
+
deferredTasks: any[];
|
|
27
28
|
cancelledTasks: any[];
|
|
28
29
|
reasoningSkippedTasks: any[];
|
|
29
30
|
redeliveredTasks: any[];
|
|
@@ -45,8 +46,11 @@ export function runScheduledTick(ctx: any): Promise<{
|
|
|
45
46
|
* - `already_settled`:行还在,但已经是 sent / failed(`status` 带上是哪
|
|
46
47
|
* 个)。适配器没实现 `getTaskStatusByUuidOnly` 时这种情况并进 `not_found`。
|
|
47
48
|
* - `not_due`:还没到 `next_send_at`(`nextSendAt` 带上是什么时候)。
|
|
48
|
-
* - `retry_pending
|
|
49
|
-
*
|
|
49
|
+
* - `retry_pending`:上次投递失败还在退避窗口里,或者上次 onBeforeFire 把
|
|
50
|
+
* 它推迟了(`{ defer }`)还没到点(`retryAfter` 带上什么时候到点)。
|
|
51
|
+
*
|
|
52
|
+
* 这一次跑的时候 onBeforeFire 返回 `{ defer }` 的话,结果是 `ran: true`,任务
|
|
53
|
+
* 列在 summary.details.deferredTasks 里;到点之前再调都回 `retry_pending`。
|
|
50
54
|
*
|
|
51
55
|
* @param {Object} ctx - 与 runScheduledTick 同一份 ctx
|
|
52
56
|
* @param {string} uuid - 任务 uuid(pending 行)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rei-standard/amsg-server",
|
|
3
|
-
"version": "2.6.0-next.
|
|
3
|
+
"version": "2.6.0-next.33",
|
|
4
4
|
"description": "ReiStandard Active Messaging server SDK with pluggable database adapters. Three-axis push schema (messageKind / messageType / messageSubtype) from @rei-standard/amsg-shared. Auto-emits ReasoningPush when the LLM response carries reasoning_content.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|