
























@@ -655,6 +655,178 @@ describe("buildGatewayCronService", () => {
655655}
656656});
657657658+it("routes relative cron wake session keys to the configured default agent", () => {
659+const cfg = {
660+session: { mainKey: "main" },
661+cron: {
662+store: path.join(os.tmpdir(), `server-cron-relative-default-${Date.now()}`, "cron.json"),
663+},
664+agents: {
665+list: [
666+{ id: "primary", default: true, model: "test/primary" },
667+{ id: "main", model: "test/main" },
668+],
669+},
670+} as unknown as OpenClawConfig;
671+loadConfigMock.mockReturnValue(cfg);
672+673+const state = buildGatewayCronService({
674+ cfg,
675+deps: {} as CliDeps,
676+broadcast: () => {},
677+});
678+try {
679+const cronDeps = (
680+state.cron as unknown as {
681+state?: {
682+deps?: {
683+enqueueSystemEvent?: (text: string, opts?: { sessionKey?: string }) => void;
684+requestHeartbeat?: (opts?: {
685+sessionKey?: string | null;
686+source?: string;
687+intent?: string;
688+reason?: string;
689+}) => void;
690+};
691+};
692+}
693+).state?.deps;
694+695+cronDeps?.enqueueSystemEvent?.("hello", {
696+sessionKey: "discord:channel:ops",
697+});
698+cronDeps?.requestHeartbeat?.({
699+source: "cron",
700+intent: "event",
701+reason: "cron:test",
702+sessionKey: "discord:channel:ops",
703+});
704+705+const enqueueCall = enqueueSystemEventMock.mock.calls.at(-1);
706+const wakeCall = requestHeartbeatMock.mock.calls.at(-1);
707+expect((enqueueCall?.[1] as { sessionKey?: string } | undefined)?.sessionKey).toBe(
708+"agent:primary:discord:channel:ops",
709+);
710+expect(wakeCall?.[0]).toMatchObject({
711+agentId: "primary",
712+sessionKey: "agent:primary:discord:channel:ops",
713+});
714+} finally {
715+state.cron.stop();
716+}
717+});
718+719+it("falls back to the configured default agent while preserving unknown agent-prefixed wake suffixes", () => {
720+const cfg = {
721+session: { mainKey: "main" },
722+cron: {
723+store: path.join(os.tmpdir(), `server-cron-unknown-agent-${Date.now()}`, "cron.json"),
724+},
725+agents: {
726+list: [
727+{ id: "primary", default: true, model: "test/primary" },
728+{ id: "ops", model: "test/ops" },
729+],
730+},
731+} as unknown as OpenClawConfig;
732+loadConfigMock.mockReturnValue(cfg);
733+734+const state = buildGatewayCronService({
735+ cfg,
736+deps: {} as CliDeps,
737+broadcast: () => {},
738+});
739+try {
740+const cronDeps = (
741+state.cron as unknown as {
742+state?: {
743+deps?: {
744+enqueueSystemEvent?: (text: string, opts?: { sessionKey?: string }) => void;
745+requestHeartbeat?: (opts?: {
746+sessionKey?: string | null;
747+source?: string;
748+intent?: string;
749+reason?: string;
750+}) => void;
751+};
752+};
753+}
754+).state?.deps;
755+756+cronDeps?.enqueueSystemEvent?.("hello", {
757+sessionKey: "agent:ghost:discord:channel:ops",
758+});
759+cronDeps?.requestHeartbeat?.({
760+source: "cron",
761+intent: "event",
762+reason: "cron:test",
763+sessionKey: "agent:ghost:discord:channel:ops",
764+});
765+766+const enqueueCall = enqueueSystemEventMock.mock.calls.at(-1);
767+const wakeCall = requestHeartbeatMock.mock.calls.at(-1);
768+expect((enqueueCall?.[1] as { sessionKey?: string } | undefined)?.sessionKey).toBe(
769+"agent:primary:discord:channel:ops",
770+);
771+expect(wakeCall?.[0]).toMatchObject({
772+agentId: "primary",
773+sessionKey: "agent:primary:discord:channel:ops",
774+});
775+} finally {
776+state.cron.stop();
777+}
778+});
779+780+it("threads cron wake sessionKey through the CronService adapter", () => {
781+const cfg = {
782+session: { mainKey: "main" },
783+cron: {
784+store: path.join(os.tmpdir(), `server-cron-wake-service-${Date.now()}`, "cron.json"),
785+},
786+agents: {
787+list: [
788+{ id: "primary", default: true, model: "test/primary" },
789+{ id: "ops", model: "test/ops" },
790+],
791+},
792+} as unknown as OpenClawConfig;
793+loadConfigMock.mockReturnValue(cfg);
794+795+const state = buildGatewayCronService({
796+ cfg,
797+deps: {} as CliDeps,
798+broadcast: () => {},
799+});
800+try {
801+const sessionKey = "agent:ops:cron:nightly:run:abc-123";
802+expect(
803+state.cron.wake({
804+mode: "now",
805+text: "hello",
806+ sessionKey,
807+}),
808+).toEqual({ ok: true });
809+810+const enqueueCall = enqueueSystemEventMock.mock.calls.at(-1);
811+const wakeCall = requestHeartbeatMock.mock.calls.at(-1);
812+expect(enqueueCall?.[0]).toBe("hello");
813+expect((enqueueCall?.[1] as { sessionKey?: string } | undefined)?.sessionKey).toMatch(
814+/^agent:ops:/,
815+);
816+expect(wakeCall?.[0]).toMatchObject({
817+source: "manual",
818+intent: "immediate",
819+reason: "wake",
820+agentId: "ops",
821+});
822+expect((wakeCall?.[0] as { sessionKey?: string } | undefined)?.sessionKey).toMatch(
823+/^agent:ops:/,
824+);
825+} finally {
826+state.cron.stop();
827+}
828+});
829+658830it("preserves trust downgrades when cron enqueues system events", () => {
659831const cfg = createCronConfig("server-cron-untrusted");
660832loadConfigMock.mockReturnValue(cfg);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。