


























@@ -19,6 +19,40 @@ afterEach(() => {
1919vi.resetAllMocks();
2020});
212122+function requireRecord(value: unknown, label: string): Record<string, unknown> {
23+if (!value || typeof value !== "object" || Array.isArray(value)) {
24+throw new Error(`expected ${label}`);
25+}
26+return value as Record<string, unknown>;
27+}
28+29+function requireFirstMockCall(mock: unknown, label: string): unknown[] {
30+const call = (mock as { mock?: { calls?: unknown[][] } }).mock?.calls?.[0];
31+if (!call) {
32+throw new Error(`expected ${label}`);
33+}
34+return call;
35+}
36+37+function expectGatewayAgentFollowup(expected: Record<string, unknown>) {
38+const call = requireFirstMockCall(callGatewayTool, "callGatewayTool call");
39+expect(call[0]).toBe("agent");
40+requireRecord(call[1], "gateway tool context");
41+const params = requireRecord(call[2], "gateway tool params");
42+for (const [key, value] of Object.entries(expected)) {
43+expect(params[key]).toBe(value);
44+}
45+expect(call[3]).toEqual({ expectFinal: true });
46+}
47+48+function expectDirectSend(expected: Record<string, unknown>) {
49+const call = requireFirstMockCall(sendMessage, "sendMessage call");
50+const params = requireRecord(call[0], "sendMessage params");
51+for (const [key, value] of Object.entries(expected)) {
52+expect(params[key]).toBe(value);
53+}
54+}
55+2256describe("exec approval followup", () => {
2357it("uses an explicit denial prompt when the command did not run", () => {
2458const prompt = buildExecApprovalFollowupPrompt(
@@ -44,17 +78,12 @@ describe("exec approval followup", () => {
4478resultText: "Exec completed: echo ok",
4579});
468047-expect(callGatewayTool).toHaveBeenCalledWith(
48-"agent",
49-expect.any(Object),
50-expect.objectContaining({
51-sessionKey: "agent:main:main",
52-deliver: false,
53-channel: undefined,
54-to: undefined,
55-}),
56-{ expectFinal: true },
57-);
81+expectGatewayAgentFollowup({
82+sessionKey: "agent:main:main",
83+deliver: false,
84+channel: undefined,
85+to: undefined,
86+});
5887expect(sendMessage).not.toHaveBeenCalled();
5988});
6089@@ -91,21 +120,16 @@ describe("exec approval followup", () => {
91120resultText: "slack exec approval smoke",
92121});
9312294-expect(callGatewayTool).toHaveBeenCalledWith(
95-"agent",
96-expect.any(Object),
97-expect.objectContaining({
98-sessionKey: target.sessionKey,
99-deliver: true,
100-bestEffortDeliver: true,
101-channel: target.channel,
102-to: target.to,
103-accountId: target.accountId,
104-threadId: target.threadId,
105-idempotencyKey: `exec-approval-followup:req-${target.channel}`,
106-}),
107-{ expectFinal: true },
108-);
123+expectGatewayAgentFollowup({
124+sessionKey: target.sessionKey,
125+deliver: true,
126+bestEffortDeliver: true,
127+channel: target.channel,
128+to: target.to,
129+accountId: target.accountId,
130+threadId: target.threadId,
131+idempotencyKey: `exec-approval-followup:req-${target.channel}`,
132+});
109133expect(sendMessage).not.toHaveBeenCalled();
110134});
111135@@ -119,16 +143,14 @@ describe("exec approval followup", () => {
119143resultText: "Exec finished (gateway id=req-no-session, session=sess_1, code 0)\nall good",
120144});
121145122-expect(sendMessage).toHaveBeenCalledWith(
123-expect.objectContaining({
124-channel: "discord",
125-to: "123",
126-accountId: "default",
127-threadId: "456",
128-content: "all good",
129-idempotencyKey: "exec-approval-followup:req-no-session",
130-}),
131-);
146+expectDirectSend({
147+channel: "discord",
148+to: "123",
149+accountId: "default",
150+threadId: "456",
151+content: "all good",
152+idempotencyKey: "exec-approval-followup:req-no-session",
153+});
132154expect(callGatewayTool).not.toHaveBeenCalled();
133155});
134156@@ -144,15 +166,13 @@ describe("exec approval followup", () => {
144166direct: true,
145167});
146168147-expect(sendMessage).toHaveBeenCalledWith(
148-expect.objectContaining({
149-channel: "telegram",
150-to: "123",
151-accountId: "default",
152-content: "pasteable diagnostics report",
153-idempotencyKey: "exec-approval-followup:req-direct",
154-}),
155-);
169+expectDirectSend({
170+channel: "telegram",
171+to: "123",
172+accountId: "default",
173+content: "pasteable diagnostics report",
174+idempotencyKey: "exec-approval-followup:req-direct",
175+});
156176expect(callGatewayTool).not.toHaveBeenCalled();
157177});
158178@@ -170,12 +190,10 @@ describe("exec approval followup", () => {
170190"Exec finished (gateway id=req-session-resume-failed, session=sess_1, code 0)\nall good",
171191});
172192173-expect(sendMessage).toHaveBeenCalledWith(
174-expect.objectContaining({
175-content: "all good",
176-idempotencyKey: "exec-approval-followup:req-session-resume-failed",
177-}),
178-);
193+expectDirectSend({
194+content: "all good",
195+idempotencyKey: "exec-approval-followup:req-session-resume-failed",
196+});
179197});
180198181199it("uses a generic summary when a no-session completion has no user-visible output", async () => {
@@ -188,12 +206,10 @@ describe("exec approval followup", () => {
188206resultText: "Exec finished (gateway id=req-no-session-empty, session=sess_2, code 0)",
189207});
190208191-expect(sendMessage).toHaveBeenCalledWith(
192-expect.objectContaining({
193-content: "Background command finished.",
194-idempotencyKey: "exec-approval-followup:req-no-session-empty",
195-}),
196-);
209+expectDirectSend({
210+content: "Background command finished.",
211+idempotencyKey: "exec-approval-followup:req-no-session-empty",
212+});
197213});
198214199215it("uses safe denied copy when session resume fails", async () => {
@@ -209,13 +225,11 @@ describe("exec approval followup", () => {
209225resultText: "Exec denied (gateway id=req-denied-resume-failed, approval-timeout): uname -a",
210226});
211227212-expect(sendMessage).toHaveBeenCalledWith(
213-expect.objectContaining({
214-content:
215-"Automatic session resume failed, so sending the status directly.\n\nCommand did not run: approval timed out.",
216-idempotencyKey: "exec-approval-followup:req-denied-resume-failed",
217-}),
218-);
228+expectDirectSend({
229+content:
230+"Automatic session resume failed, so sending the status directly.\n\nCommand did not run: approval timed out.",
231+idempotencyKey: "exec-approval-followup:req-denied-resume-failed",
232+});
219233});
220234221235it("suppresses denied followups for subagent sessions", async () => {
@@ -261,16 +275,11 @@ describe("exec approval followup", () => {
261275resultText: "Exec completed: systemctl status gateway",
262276});
263277264-expect(callGatewayTool).toHaveBeenCalledWith(
265-"agent",
266-expect.any(Object),
267-expect.objectContaining({
268-sessionKey: "agent:main:telegram:-100123",
269-deliver: false,
270-channel: "telegram",
271-}),
272-{ expectFinal: true },
273-);
278+expectGatewayAgentFollowup({
279+sessionKey: "agent:main:telegram:-100123",
280+deliver: false,
281+channel: "telegram",
282+});
274283expect(sendMessage).not.toHaveBeenCalled();
275284});
276285此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。