-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathsubagent_hooks.e2e.test.ts
More file actions
81 lines (71 loc) · 3.47 KB
/
Copy pathsubagent_hooks.e2e.test.ts
File metadata and controls
81 lines (71 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
import { writeFile } from "fs/promises";
import { join } from "path";
import { describe, expect, it } from "vitest";
import type {
PreToolUseHookInput,
PreToolUseHookOutput,
PostToolUseHookInput,
PostToolUseHookOutput,
} from "../../src/index.js";
import { approveAll } from "../../src/index.js";
import { createSdkTestContext, isCI } from "./harness/sdkTestContext.js";
describe("Subagent hooks", async () => {
// For snapshot recording (non-CI), use RECORD_GH_TOKEN if available
const recordToken = !isCI ? process.env.RECORD_GH_TOKEN : undefined;
const { copilotClient: client, workDir } = await createSdkTestContext({
copilotClientOptions: {
...(recordToken ? { gitHubToken: recordToken } : {}),
env: { COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS: "true" },
},
});
it("should invoke preToolUse and postToolUse hooks for sub-agent tool calls", async () => {
const hookLog: { kind: "pre" | "post"; toolName: string; sessionId: string }[] = [];
const session = await client.createSession({
onPermissionRequest: approveAll,
hooks: {
onPreToolUse: async (input: PreToolUseHookInput) => {
hookLog.push({
kind: "pre",
toolName: input.toolName,
sessionId: input.sessionId,
});
return { permissionDecision: "allow" } as PreToolUseHookOutput;
},
onPostToolUse: async (input: PostToolUseHookInput) => {
hookLog.push({
kind: "post",
toolName: input.toolName,
sessionId: input.sessionId,
});
return null as PostToolUseHookOutput;
},
},
});
// Create a file for the sub-agent to read
await writeFile(join(workDir, "subagent-test.txt"), "Hello from subagent test!");
await session.sendAndWait({
prompt: "Use the task tool to spawn an explore agent that reads the file subagent-test.txt in the current directory and reports its contents. You must use the task tool.",
});
// Parent tool hooks fire for "task"
const taskPre = hookLog.find((h) => h.kind === "pre" && h.toolName === "task");
expect(taskPre, "preToolUse should fire for the parent's 'task' tool call").toBeDefined();
// Sub-agent tool hooks fire for "view"
const viewPre = hookLog.filter((h) => h.kind === "pre" && h.toolName === "view");
const viewPost = hookLog.filter((h) => h.kind === "post" && h.toolName === "view");
expect(
viewPre.length,
"preToolUse should fire for the sub-agent's 'view' tool call"
).toBeGreaterThan(0);
expect(
viewPost.length,
"postToolUse should fire for the sub-agent's 'view' tool call"
).toBeGreaterThan(0);
// input.sessionId distinguishes parent from sub-agent: parent tools and
// sub-agent tools carry different sessionIds
expect(viewPre[0].sessionId).not.toBe(taskPre!.sessionId);
await session.disconnect();
}, 120_000);
});