























@@ -480,7 +480,7 @@ describe("maybeRepairLegacyCronStore", () => {
480480expectNoteContaining("missing a canonical string `id`", "Cron");
481481});
482482483-it("warns instead of replacing announce delivery for notify fallback jobs", async () => {
483+it("migrates notify fallback alongside announce delivery without replacing it", async () => {
484484const storePath = await makeTempStorePath();
485485await fs.mkdir(path.dirname(storePath), { recursive: true });
486486await fs.writeFile(
@@ -499,7 +499,7 @@ describe("maybeRepairLegacyCronStore", () => {
499499sessionTarget: "isolated",
500500wakeMode: "now",
501501payload: { kind: "agentTurn", message: "Status" },
502-delivery: { mode: "announce", channel: "telegram", to: "123" },
502+delivery: { to: "telegram:123" },
503503state: {},
504504},
505505],
@@ -523,9 +523,17 @@ describe("maybeRepairLegacyCronStore", () => {
523523524524const jobs = await readPersistedJobs(storePath);
525525const job = requirePersistedJob(jobs, 0);
526-expect(job.notify).toBe(true);
527-expectNoteContaining(
528-'uses legacy notify fallback alongside delivery mode "announce"',
526+expect(job.notify).toBeUndefined();
527+const delivery = requireRecord(job.delivery, "cron delivery");
528+expect(delivery.mode).toBe("announce");
529+expect(delivery.channel).toBeUndefined();
530+expect(delivery.to).toBe("telegram:123");
531+expect(delivery.completionDestination).toEqual({
532+mode: "webhook",
533+to: "https://example.invalid/cron-finished",
534+});
535+expectNoNoteContaining(
536+"uses legacy notify fallback alongside delivery mode",
529537"Doctor warnings",
530538);
531539});
@@ -607,6 +615,87 @@ describe("maybeRepairLegacyCronStore", () => {
607615expect(delivery.to).toBe("https://example.invalid/cron-finished");
608616});
609617618+it("migrates invalid legacy notify webhook delivery jobs to cron.webhook", async () => {
619+const storePath = await makeTempStorePath();
620+await fs.mkdir(path.dirname(storePath), { recursive: true });
621+await fs.writeFile(
622+storePath,
623+JSON.stringify(
624+{
625+version: 1,
626+jobs: [
627+{
628+id: "notify-invalid-webhook",
629+name: "Notify invalid webhook",
630+notify: true,
631+createdAtMs: Date.parse("2026-02-01T00:00:00.000Z"),
632+updatedAtMs: Date.parse("2026-02-02T00:00:00.000Z"),
633+schedule: { kind: "every", everyMs: 60_000 },
634+payload: {
635+kind: "systemEvent",
636+text: "Status",
637+},
638+delivery: { mode: "webhook", to: "ftp://example.invalid/cron" },
639+state: {},
640+},
641+],
642+},
643+null,
644+2,
645+),
646+"utf-8",
647+);
648+649+await maybeRepairLegacyCronStore({
650+cfg: {
651+cron: {
652+store: storePath,
653+webhook: "https://example.invalid/cron-finished",
654+},
655+},
656+options: {},
657+prompter: makePrompter(true),
658+});
659+660+const jobs = await readPersistedJobs(storePath);
661+const job = requirePersistedJob(jobs, 0);
662+expect(job.notify).toBeUndefined();
663+const delivery = requireRecord(job.delivery, "cron delivery");
664+expect(delivery.mode).toBe("webhook");
665+expect(delivery.to).toBe("https://example.invalid/cron-finished");
666+});
667+668+it("keeps notify fallback when cron.webhook is invalid", async () => {
669+const storePath = await makeTempStorePath();
670+await writeCronStore(storePath, [
671+createLegacyCronJob({
672+id: "notify-invalid-config",
673+jobId: undefined,
674+delivery: undefined,
675+}),
676+]);
677+678+await maybeRepairLegacyCronStore({
679+cfg: {
680+cron: {
681+store: storePath,
682+webhook: "ftp://example.invalid/cron-finished",
683+},
684+},
685+options: {},
686+prompter: makePrompter(true),
687+});
688+689+const jobs = await readPersistedJobs(storePath);
690+const job = requirePersistedJob(jobs, 0);
691+expect(job.notify).toBe(true);
692+expect(job.delivery).toBeUndefined();
693+expectNoteContaining(
694+"cron.webhook is not a valid HTTP(S) URL so doctor cannot migrate it automatically",
695+"Doctor warnings",
696+);
697+});
698+610699it("repairs legacy root delivery threadId hints into delivery", async () => {
611700const storePath = await makeTempStorePath();
612701await writeCronStore(storePath, [
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。