

























@@ -783,6 +783,121 @@ describe("deliverSubagentAnnouncement active requester steering", () => {
783783);
784784});
785785786+it("waits through compaction and re-steers the active requester (86566)", async () => {
787+const previousTestFast = process.env.OPENCLAW_TEST_FAST;
788+process.env.OPENCLAW_TEST_FAST = "1";
789+try {
790+// First steer attempt observes a compacting run; once compaction ends the
791+// same wake succeeds, so completion must stay on the steering path instead
792+// of falling back to the direct requester-agent handoff.
793+const queueEmbeddedAgentMessageWithOutcome = createQueueOutcomeSequenceMock([
794+"compacting",
795+true,
796+]);
797+const callGateway = await deliverSteeredAnnouncement({
798+ queueEmbeddedAgentMessageWithOutcome,
799+requesterOrigin: {
800+channel: "slack",
801+to: "channel:C123",
802+accountId: "acct-1",
803+},
804+});
805+806+expect(callGateway).not.toHaveBeenCalled();
807+expect(queueEmbeddedAgentMessageWithOutcome).toHaveBeenCalledTimes(2);
808+expect(queueEmbeddedAgentMessageWithOutcome).toHaveBeenNthCalledWith(
809+2,
810+"paperclip-session",
811+"child done",
812+{
813+steeringMode: "all",
814+debounceMs: 0,
815+waitForTranscriptCommit: true,
816+deliveryTimeoutMs: 120_000,
817+},
818+);
819+} finally {
820+if (previousTestFast === undefined) {
821+delete process.env.OPENCLAW_TEST_FAST;
822+} else {
823+process.env.OPENCLAW_TEST_FAST = previousTestFast;
824+}
825+}
826+});
827+828+it("keeps retrying compaction past the backoff schedule until the delivery timeout (86566)", async () => {
829+const previousTestFast = process.env.OPENCLAW_TEST_FAST;
830+process.env.OPENCLAW_TEST_FAST = "1";
831+try {
832+// The backoff schedule has four entries, but a compaction that only
833+// finishes after the schedule is exhausted should still be retried while
834+// the run stays within the delivery timeout (120s here). Five compacting
835+// outcomes (more than the schedule length) precede the queued success, so
836+// the wake must keep retrying past the schedule instead of falling back.
837+const queueEmbeddedAgentMessageWithOutcome = createQueueOutcomeSequenceMock([
838+"compacting",
839+"compacting",
840+"compacting",
841+"compacting",
842+"compacting",
843+true,
844+]);
845+const callGateway = await deliverSteeredAnnouncement({
846+ queueEmbeddedAgentMessageWithOutcome,
847+requesterOrigin: {
848+channel: "slack",
849+to: "channel:C123",
850+accountId: "acct-1",
851+},
852+});
853+854+expect(callGateway).not.toHaveBeenCalled();
855+expect(queueEmbeddedAgentMessageWithOutcome).toHaveBeenCalledTimes(6);
856+} finally {
857+if (previousTestFast === undefined) {
858+delete process.env.OPENCLAW_TEST_FAST;
859+} else {
860+process.env.OPENCLAW_TEST_FAST = previousTestFast;
861+}
862+}
863+});
864+865+it("does not retry non-compacting steer failures (86566)", async () => {
866+// Only compacting is treated as transient; other wake failures keep their
867+// existing single-attempt fallback behavior.
868+const queueEmbeddedAgentMessageWithOutcome = createQueueOutcomeSequenceMock([
869+"no_active_run",
870+true,
871+]);
872+const callGateway = createGatewayMock();
873+testing.setDepsForTest({
874+ callGateway,
875+getRequesterSessionActivity: () => ({
876+sessionId: "paperclip-session",
877+isActive: true,
878+}),
879+ queueEmbeddedAgentMessageWithOutcome,
880+getRuntimeConfig: () =>
881+({
882+messages: { queue: { mode: "steer", debounceMs: 0 } },
883+}) as never,
884+});
885+886+const result = await deliverSubagentAnnouncement({
887+requesterSessionKey: "agent:eng:paperclip:issue:123",
888+targetRequesterSessionKey: "agent:eng:paperclip:issue:123",
889+triggerMessage: "child done",
890+steerMessage: "child done",
891+requesterIsSubagent: false,
892+expectsCompletionMessage: false,
893+directIdempotencyKey: "announce-no-active-run-no-retry",
894+});
895+896+// Non-compacting failure is not retried: the steer is attempted once.
897+expect(queueEmbeddedAgentMessageWithOutcome).toHaveBeenCalledOnce();
898+expectRecordFields(result, { path: "none" });
899+});
900+786901it("does not report delivery when active requester steering is rejected", async () => {
787902const queueEmbeddedAgentMessageWithOutcome = vi.fn(async (sessionId: string) => ({
788903queued: false as const,
@@ -915,6 +1030,42 @@ describe("deliverSubagentAnnouncement completion delivery", () => {
9151030expect(callGateway).not.toHaveBeenCalled();
9161031});
91710321033+it("waits through compaction on the completion handoff wake (86566)", async () => {
1034+const previousTestFast = process.env.OPENCLAW_TEST_FAST;
1035+process.env.OPENCLAW_TEST_FAST = "1";
1036+try {
1037+// The generated-completion active wake (expectsCompletionMessage) must also
1038+// wait through a compacting run and re-steer the same wake instead of
1039+// falling back to direct delivery.
1040+const callGateway = createGatewayMock();
1041+const queueEmbeddedAgentMessageWithOutcome = createQueueOutcomeSequenceMock([
1042+"compacting",
1043+true,
1044+]);
1045+const result = await deliverSlackThreadAnnouncement({
1046+ callGateway,
1047+sessionId: "requester-session-1",
1048+isActive: true,
1049+expectsCompletionMessage: true,
1050+directIdempotencyKey: "announce-compaction-completion",
1051+ queueEmbeddedAgentMessageWithOutcome,
1052+});
1053+1054+expectRecordFields(result, {
1055+delivered: true,
1056+path: "steered",
1057+});
1058+expect(queueEmbeddedAgentMessageWithOutcome).toHaveBeenCalledTimes(2);
1059+expect(callGateway).not.toHaveBeenCalled();
1060+} finally {
1061+if (previousTestFast === undefined) {
1062+delete process.env.OPENCLAW_TEST_FAST;
1063+} else {
1064+process.env.OPENCLAW_TEST_FAST = previousTestFast;
1065+}
1066+}
1067+});
1068+9181069it("does not also direct-run a queued active completion", async () => {
9191070const callGateway = createGatewayMock();
9201071const queueEmbeddedAgentMessageWithOutcome = createQueueOutcomeMock(true);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。