Skip to content

Commit 5748ae2

Browse files
authored
Merge pull request #124 from copilot-community-sdk/copilot/convert-pretoolusehookoutput-to-record
Convert PreToolUseHookOutput to record with factory methods
2 parents d3692fe + 432ab8a commit 5748ae2

File tree

6 files changed

+58
-135
lines changed

6 files changed

+58
-135
lines changed

src/main/java/com/github/copilot/sdk/json/PreToolUseHookOutput.java

Lines changed: 41 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -11,128 +11,74 @@
1111
/**
1212
* Output for a pre-tool-use hook.
1313
*
14+
* @param permissionDecision
15+
* "allow", "deny", or "ask"
16+
* @param permissionDecisionReason
17+
* the reason for the permission decision
18+
* @param modifiedArgs
19+
* the modified tool arguments, or {@code null} to use original
20+
* @param additionalContext
21+
* additional context to provide to the model
22+
* @param suppressOutput
23+
* {@code true} to suppress output
1424
* @since 1.0.6
1525
*/
1626
@JsonInclude(JsonInclude.Include.NON_NULL)
17-
public class PreToolUseHookOutput {
18-
19-
@JsonProperty("permissionDecision")
20-
private String permissionDecision;
21-
22-
@JsonProperty("permissionDecisionReason")
23-
private String permissionDecisionReason;
24-
25-
@JsonProperty("modifiedArgs")
26-
private JsonNode modifiedArgs;
27-
28-
@JsonProperty("additionalContext")
29-
private String additionalContext;
30-
31-
@JsonProperty("suppressOutput")
32-
private Boolean suppressOutput;
27+
public record PreToolUseHookOutput(@JsonProperty("permissionDecision") String permissionDecision,
28+
@JsonProperty("permissionDecisionReason") String permissionDecisionReason,
29+
@JsonProperty("modifiedArgs") JsonNode modifiedArgs,
30+
@JsonProperty("additionalContext") String additionalContext,
31+
@JsonProperty("suppressOutput") Boolean suppressOutput) {
3332

3433
/**
35-
* Gets the permission decision.
34+
* Creates an output that allows the tool to execute.
3635
*
37-
* @return "allow", "deny", or "ask"
36+
* @return a new PreToolUseHookOutput with permission decision "allow"
3837
*/
39-
public String getPermissionDecision() {
40-
return permissionDecision;
38+
public static PreToolUseHookOutput allow() {
39+
return new PreToolUseHookOutput("allow", null, null, null, null);
4140
}
4241

4342
/**
44-
* Sets the permission decision.
43+
* Creates an output that denies the tool execution.
4544
*
46-
* @param permissionDecision
47-
* "allow", "deny", or "ask"
48-
* @return this instance for method chaining
45+
* @return a new PreToolUseHookOutput with permission decision "deny"
4946
*/
50-
public PreToolUseHookOutput setPermissionDecision(String permissionDecision) {
51-
this.permissionDecision = permissionDecision;
52-
return this;
47+
public static PreToolUseHookOutput deny() {
48+
return new PreToolUseHookOutput("deny", null, null, null, null);
5349
}
5450

5551
/**
56-
* Gets the reason for the permission decision.
52+
* Creates an output that denies the tool execution with a reason.
5753
*
58-
* @return the reason text
54+
* @param reason
55+
* the reason for denying the tool execution
56+
* @return a new PreToolUseHookOutput with permission decision "deny" and reason
5957
*/
60-
public String getPermissionDecisionReason() {
61-
return permissionDecisionReason;
58+
public static PreToolUseHookOutput deny(String reason) {
59+
return new PreToolUseHookOutput("deny", reason, null, null, null);
6260
}
6361

6462
/**
65-
* Sets the reason for the permission decision.
63+
* Creates an output that asks for user confirmation before executing the tool.
6664
*
67-
* @param permissionDecisionReason
68-
* the reason text
69-
* @return this instance for method chaining
65+
* @return a new PreToolUseHookOutput with permission decision "ask"
7066
*/
71-
public PreToolUseHookOutput setPermissionDecisionReason(String permissionDecisionReason) {
72-
this.permissionDecisionReason = permissionDecisionReason;
73-
return this;
67+
public static PreToolUseHookOutput ask() {
68+
return new PreToolUseHookOutput("ask", null, null, null, null);
7469
}
7570

7671
/**
77-
* Gets the modified tool arguments.
78-
*
79-
* @return the modified arguments, or {@code null} to use original
80-
*/
81-
public JsonNode getModifiedArgs() {
82-
return modifiedArgs;
83-
}
84-
85-
/**
86-
* Sets the modified tool arguments.
72+
* Creates an output with modified tool arguments.
8773
*
74+
* @param permissionDecision
75+
* "allow", "deny", or "ask"
8876
* @param modifiedArgs
89-
* the modified arguments
90-
* @return this instance for method chaining
91-
*/
92-
public PreToolUseHookOutput setModifiedArgs(JsonNode modifiedArgs) {
93-
this.modifiedArgs = modifiedArgs;
94-
return this;
95-
}
96-
97-
/**
98-
* Gets additional context to provide to the model.
99-
*
100-
* @return the additional context
101-
*/
102-
public String getAdditionalContext() {
103-
return additionalContext;
104-
}
105-
106-
/**
107-
* Sets additional context to provide to the model.
108-
*
109-
* @param additionalContext
110-
* the additional context
111-
* @return this instance for method chaining
112-
*/
113-
public PreToolUseHookOutput setAdditionalContext(String additionalContext) {
114-
this.additionalContext = additionalContext;
115-
return this;
116-
}
117-
118-
/**
119-
* Returns whether to suppress output.
120-
*
121-
* @return {@code true} to suppress output
122-
*/
123-
public Boolean getSuppressOutput() {
124-
return suppressOutput;
125-
}
126-
127-
/**
128-
* Sets whether to suppress output.
129-
*
130-
* @param suppressOutput
131-
* {@code true} to suppress output
132-
* @return this instance for method chaining
77+
* the modified tool arguments
78+
* @return a new PreToolUseHookOutput with the specified permission and modified
79+
* arguments
13380
*/
134-
public PreToolUseHookOutput setSuppressOutput(Boolean suppressOutput) {
135-
this.suppressOutput = suppressOutput;
136-
return this;
81+
public static PreToolUseHookOutput withModifiedArgs(String permissionDecision, JsonNode modifiedArgs) {
82+
return new PreToolUseHookOutput(permissionDecision, null, modifiedArgs, null, null);
13783
}
13884
}

src/main/java/com/github/copilot/sdk/json/SessionHooks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* <pre>{@code
1616
* var hooks = new SessionHooks().setOnPreToolUse((input, invocation) -> {
1717
* System.out.println("Tool being called: " + input.getToolName());
18-
* return CompletableFuture.completedFuture(new PreToolUseHookOutput().setPermissionDecision("allow"));
18+
* return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
1919
* }).setOnPostToolUse((input, invocation) -> {
2020
* System.out.println("Tool result: " + input.getToolResult());
2121
* return CompletableFuture.completedFuture(null);

src/site/markdown/advanced.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,7 @@ Intercept tool execution and session lifecycle events using hooks.
364364
var hooks = new SessionHooks()
365365
.setOnPreToolUse((input, invocation) -> {
366366
System.out.println("Tool: " + input.getToolName());
367-
return CompletableFuture.completedFuture(
368-
new PreToolUseHookOutput().setPermissionDecision("allow")
369-
);
367+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
370368
})
371369
.setOnPostToolUse((input, invocation) -> {
372370
System.out.println("Result: " + input.getToolResult());

src/site/markdown/hooks.md

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ Register hooks when creating a session:
2626
var hooks = new SessionHooks()
2727
.setOnPreToolUse((input, invocation) -> {
2828
System.out.println("Tool: " + input.getToolName());
29-
return CompletableFuture.completedFuture(
30-
new PreToolUseHookOutput().setPermissionDecision("allow")
31-
);
29+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
3230
})
3331
.setOnPostToolUse((input, invocation) -> {
3432
System.out.println("Result: " + input.getToolResult());
@@ -83,24 +81,17 @@ var hooks = new SessionHooks()
8381
// Block file deletion
8482
if (tool.equals("delete_file")) {
8583
return CompletableFuture.completedFuture(
86-
new PreToolUseHookOutput()
87-
.setPermissionDecision("deny")
88-
.setPermissionDecisionReason("File deletion is not allowed")
84+
PreToolUseHookOutput.deny("File deletion is not allowed")
8985
);
9086
}
9187

9288
// Require confirmation for shell commands
9389
if (tool.equals("run_terminal_cmd")) {
94-
return CompletableFuture.completedFuture(
95-
new PreToolUseHookOutput()
96-
.setPermissionDecision("ask")
97-
);
90+
return CompletableFuture.completedFuture(PreToolUseHookOutput.ask());
9891
}
9992

10093
// Allow everything else
101-
return CompletableFuture.completedFuture(
102-
new PreToolUseHookOutput().setPermissionDecision("allow")
103-
);
94+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
10495
});
10596
```
10697

@@ -119,14 +110,10 @@ var hooks = new SessionHooks()
119110
modifiedArgs.set("query", input.getToolArgs().get("query"));
120111

121112
return CompletableFuture.completedFuture(
122-
new PreToolUseHookOutput()
123-
.setPermissionDecision("allow")
124-
.setModifiedArgs(modifiedArgs)
113+
PreToolUseHookOutput.withModifiedArgs("allow", modifiedArgs)
125114
);
126115
}
127-
return CompletableFuture.completedFuture(
128-
new PreToolUseHookOutput().setPermissionDecision("allow")
129-
);
116+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
130117
});
131118
```
132119

@@ -315,15 +302,11 @@ public class HooksExample {
315302
// Deny dangerous operations
316303
if (input.getToolName().contains("delete")) {
317304
return CompletableFuture.completedFuture(
318-
new PreToolUseHookOutput()
319-
.setPermissionDecision("deny")
320-
.setPermissionDecisionReason("Deletion not allowed")
305+
PreToolUseHookOutput.deny("Deletion not allowed")
321306
);
322307
}
323308

324-
return CompletableFuture.completedFuture(
325-
new PreToolUseHookOutput().setPermissionDecision("allow")
326-
);
309+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
327310
})
328311

329312
// Logging: track tool results
@@ -390,16 +373,12 @@ To handle errors gracefully in your hooks:
390373
.setOnPreToolUse((input, invocation) -> {
391374
try {
392375
// Your logic here
393-
return CompletableFuture.completedFuture(
394-
new PreToolUseHookOutput().setPermissionDecision("allow")
395-
);
376+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
396377
} catch (Exception e) {
397378
logger.error("Hook error", e);
398379
// Fail-safe: deny if something goes wrong
399380
return CompletableFuture.completedFuture(
400-
new PreToolUseHookOutput()
401-
.setPermissionDecision("deny")
402-
.setPermissionDecisionReason("Internal error")
381+
PreToolUseHookOutput.deny("Internal error")
403382
);
404383
}
405384
})

src/test/java/com/github/copilot/sdk/HooksTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void testInvokePreToolUseHookWhenModelRunsATool() throws Exception {
7070
var config = new SessionConfig().setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> {
7171
preToolUseInputs.add(input);
7272
assertEquals(sessionIdHolder[0], invocation.getSessionId());
73-
return CompletableFuture.completedFuture(new PreToolUseHookOutput().setPermissionDecision("allow"));
73+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
7474
}));
7575

7676
try (CopilotClient client = ctx.createClient()) {
@@ -149,7 +149,7 @@ void testInvokeBothHooksForSingleToolCall() throws Exception {
149149

150150
var config = new SessionConfig().setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> {
151151
preToolUseInputs.add(input);
152-
return CompletableFuture.completedFuture(new PreToolUseHookOutput().setPermissionDecision("allow"));
152+
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
153153
}).setOnPostToolUse((input, invocation) -> {
154154
postToolUseInputs.add(input);
155155
return CompletableFuture.completedFuture(null);
@@ -195,7 +195,7 @@ void testDenyToolExecutionWhenPreToolUseReturnsDeny() throws Exception {
195195
var config = new SessionConfig().setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> {
196196
preToolUseInputs.add(input);
197197
// Deny all tool calls
198-
return CompletableFuture.completedFuture(new PreToolUseHookOutput().setPermissionDecision("deny"));
198+
return CompletableFuture.completedFuture(PreToolUseHookOutput.deny());
199199
}));
200200

201201
try (CopilotClient client = ctx.createClient()) {

src/test/java/com/github/copilot/sdk/RpcHandlerDispatcherTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,8 @@ void hooksInvokeWithNullOutput() throws Exception {
467467
@Test
468468
void hooksInvokeWithNonNullOutput() throws Exception {
469469
CopilotSession session = createSession("s1");
470-
session.registerHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> CompletableFuture
471-
.completedFuture(new PreToolUseHookOutput().setPermissionDecision("allow"))));
470+
session.registerHooks(new SessionHooks().setOnPreToolUse(
471+
(input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.allow())));
472472

473473
ObjectNode params = MAPPER.createObjectNode();
474474
params.put("sessionId", "s1");

0 commit comments

Comments
 (0)