Skip to content

Commit 00a8d38

Browse files
committed
Expose large output config on custom agents
1 parent 1935fd3 commit 00a8d38

13 files changed

Lines changed: 235 additions & 3 deletions

File tree

dotnet/src/Types.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,13 @@ public sealed class CustomAgentConfig
27962796
/// </summary>
27972797
[JsonPropertyName("reasoningEffort")]
27982798
public string? ReasoningEffort { get; set; }
2799+
2800+
/// <summary>
2801+
/// Large tool output handling for this agent.
2802+
/// </summary>
2803+
/// <remarks>When omitted, no agent-specific large output override is sent.</remarks>
2804+
[JsonPropertyName("largeOutput")]
2805+
public LargeToolOutputConfig? LargeOutput { get; set; }
27992806
}
28002807

28012808
/// <summary>

dotnet/test/Unit/SerializationTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,31 @@ public void SessionRequests_CanSerializePluginDirectoriesAndLargeOutput_WithSdkO
444444
Assert.Equal("/tmp/large-output", resumeLargeOutput.GetProperty("outputDir").GetString());
445445
}
446446

447+
[Fact]
448+
public void CustomAgentConfig_CanSerializeLargeOutput_WithSdkOptions()
449+
{
450+
var options = GetSerializerOptions();
451+
var agent = new CustomAgentConfig
452+
{
453+
Name = "large-output-agent",
454+
Prompt = "Handle large outputs.",
455+
LargeOutput = new LargeToolOutputConfig
456+
{
457+
Enabled = false,
458+
MaxSizeBytes = 2048,
459+
OutputDirectory = "/tmp/agent-large-output",
460+
},
461+
};
462+
463+
var json = JsonSerializer.Serialize(agent, options);
464+
using var document = JsonDocument.Parse(json);
465+
var largeOutput = document.RootElement.GetProperty("largeOutput");
466+
467+
Assert.False(largeOutput.GetProperty("enabled").GetBoolean());
468+
Assert.Equal(2048, largeOutput.GetProperty("maxSizeBytes").GetInt64());
469+
Assert.Equal("/tmp/agent-large-output", largeOutput.GetProperty("outputDir").GetString());
470+
}
471+
447472
[Fact]
448473
public void SessionRequests_CanSerializeMemory_WithSdkOptions()
449474
{

go/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,9 @@ type CustomAgentConfig struct {
10431043
// When empty, the runtime resolves model configuration, then inherits the
10441044
// parent effort only for the same model.
10451045
ReasoningEffort string `json:"reasoningEffort,omitempty"`
1046+
// LargeOutput configures large tool output handling for this agent. When
1047+
// nil, no agent-specific large output override is sent.
1048+
LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"`
10461049
}
10471050

10481051
// DefaultAgentConfig configures the default agent (the built-in agent that handles turns when no custom agent is selected).

go/types_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,44 @@ func TestCustomAgentConfig_JSONIncludesReasoningEffort(t *testing.T) {
186186
}
187187
}
188188

189+
func TestCustomAgentConfig_JSONIncludesLargeOutput(t *testing.T) {
190+
enabled := false
191+
maxSizeBytes := int64(2048)
192+
cfg := CustomAgentConfig{
193+
Name: "large-output-agent",
194+
Prompt: "Handle large outputs.",
195+
LargeOutput: &LargeToolOutputConfig{
196+
Enabled: &enabled,
197+
MaxSizeBytes: &maxSizeBytes,
198+
OutputDirectory: "/tmp/agent-large-output",
199+
},
200+
}
201+
202+
data, err := json.Marshal(cfg)
203+
if err != nil {
204+
t.Fatalf("failed to marshal CustomAgentConfig: %v", err)
205+
}
206+
207+
var decoded map[string]any
208+
if err := json.Unmarshal(data, &decoded); err != nil {
209+
t.Fatalf("failed to unmarshal CustomAgentConfig: %v", err)
210+
}
211+
212+
largeOutput, ok := decoded["largeOutput"].(map[string]any)
213+
if !ok {
214+
t.Fatalf("expected largeOutput object, got %v", decoded["largeOutput"])
215+
}
216+
if largeOutput["enabled"] != false {
217+
t.Errorf("expected enabled false, got %v", largeOutput["enabled"])
218+
}
219+
if largeOutput["maxSizeBytes"] != float64(2048) {
220+
t.Errorf("expected maxSizeBytes 2048, got %v", largeOutput["maxSizeBytes"])
221+
}
222+
if largeOutput["outputDir"] != "/tmp/agent-large-output" {
223+
t.Errorf("expected outputDir '/tmp/agent-large-output', got %v", largeOutput["outputDir"])
224+
}
225+
}
226+
189227
func TestCustomAgentConfig_JSONIncludesEmptyTools(t *testing.T) {
190228
cfg := CustomAgentConfig{
191229
Name: "no-tools-agent",

java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ public class CustomAgentConfig {
6666
@JsonProperty("reasoningEffort")
6767
private String reasoningEffort;
6868

69+
@JsonProperty("largeOutput")
70+
private LargeToolOutputConfig largeOutput;
71+
6972
/**
7073
* Gets the unique identifier name for this agent.
7174
*
@@ -309,4 +312,27 @@ public CustomAgentConfig setReasoningEffort(String reasoningEffort) {
309312
this.reasoningEffort = reasoningEffort;
310313
return this;
311314
}
315+
316+
/**
317+
* Gets the large tool output handling configuration for this agent.
318+
*
319+
* @return the large output configuration, or {@code null} if not set
320+
*/
321+
public LargeToolOutputConfig getLargeOutput() {
322+
return largeOutput;
323+
}
324+
325+
/**
326+
* Sets the large tool output handling configuration for this agent.
327+
* <p>
328+
* When omitted, no agent-specific large output override is sent.
329+
*
330+
* @param largeOutput
331+
* the large output configuration
332+
* @return this config for method chaining
333+
*/
334+
public CustomAgentConfig setLargeOutput(LargeToolOutputConfig largeOutput) {
335+
this.largeOutput = largeOutput;
336+
return this;
337+
}
312338
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,24 @@ void customAgentConfigReasoningEffortOmittedWhenNull() throws Exception {
278278
assertFalse(json.contains("\"reasoningEffort\""));
279279
}
280280

281+
@Test
282+
void customAgentConfigLargeOutputSerializationRoundTrip() throws Exception {
283+
var mapper = JsonRpcClient.getObjectMapper();
284+
var cfg = new CustomAgentConfig().setName("large-output-agent")
285+
.setLargeOutput(new LargeToolOutputConfig().setEnabled(false).setMaxSizeBytes(2048L)
286+
.setOutputDirectory("/tmp/agent-large-output"));
287+
288+
var json = mapper.writeValueAsString(cfg);
289+
assertTrue(json.contains("\"largeOutput\""));
290+
assertTrue(json.contains("\"outputDir\":\"/tmp/agent-large-output\""));
291+
292+
var deserialized = mapper.readValue(json, CustomAgentConfig.class);
293+
assertNotNull(deserialized.getLargeOutput());
294+
assertEquals(false, deserialized.getLargeOutput().getEnabled());
295+
assertEquals(2048L, deserialized.getLargeOutput().getMaxSizeBytes());
296+
assertEquals("/tmp/agent-large-output", deserialized.getLargeOutput().getOutputDirectory());
297+
}
298+
281299
// ===== PermissionRequestResult setRules =====
282300

283301
@Test

nodejs/src/client.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,12 @@ function toWireMcpServers(
248248
function toWireCustomAgents(agents: CustomAgentConfig[] | undefined): unknown[] | undefined {
249249
if (!agents) return undefined;
250250
return agents.map((agent) => {
251-
if (!agent.mcpServers) return agent;
252-
const { mcpServers, ...rest } = agent;
253-
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
251+
const { mcpServers, largeOutput, ...rest } = agent;
252+
return {
253+
...rest,
254+
...(mcpServers ? { mcpServers: toWireMcpServers(mcpServers) } : {}),
255+
...(largeOutput ? { largeOutput: toWireLargeOutput(largeOutput) } : {}),
256+
};
254257
});
255258
}
256259

nodejs/src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,11 @@ export interface CustomAgentConfig {
17911791
* then inherits the parent effort only if this agent uses the same model.
17921792
*/
17931793
reasoningEffort?: ReasoningEffort;
1794+
/**
1795+
* Large tool output handling for this agent.
1796+
* When unset, no agent-specific large output override is sent.
1797+
*/
1798+
largeOutput?: LargeToolOutputConfig;
17941799
}
17951800

17961801
/**

nodejs/test/client.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2646,6 +2646,40 @@ describe("CopilotClient", () => {
26462646
]);
26472647
});
26482648

2649+
it("forwards custom agent large output in session.create request", async () => {
2650+
const client = new CopilotClient();
2651+
await client.start();
2652+
onTestFinished(() => stopClient(client));
2653+
2654+
const spy = vi.spyOn((client as any).connection!, "sendRequest");
2655+
await client.createSession({
2656+
onPermissionRequest: approveAll,
2657+
customAgents: [
2658+
{
2659+
name: "large-output-agent",
2660+
prompt: "You are a large output agent.",
2661+
largeOutput: {
2662+
enabled: false,
2663+
maxSizeBytes: 2048,
2664+
outputDirectory: "/tmp/agent-large-output",
2665+
},
2666+
},
2667+
],
2668+
});
2669+
2670+
const payload = spy.mock.calls.find((c) => c[0] === "session.create")![1] as any;
2671+
expect(payload.customAgents).toEqual([
2672+
expect.objectContaining({
2673+
name: "large-output-agent",
2674+
largeOutput: {
2675+
enabled: false,
2676+
maxSizeBytes: 2048,
2677+
outputDir: "/tmp/agent-large-output",
2678+
},
2679+
}),
2680+
]);
2681+
});
2682+
26492683
it("forwards agent in session.resume request", async () => {
26502684
const client = new CopilotClient();
26512685
await client.start();

python/copilot/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4035,6 +4035,8 @@ def _convert_custom_agent_to_wire_format(
40354035
wire_agent["model"] = agent["model"]
40364036
if "reasoning_effort" in agent:
40374037
wire_agent["reasoningEffort"] = agent["reasoning_effort"]
4038+
if "large_output" in agent:
4039+
wire_agent["largeOutput"] = _large_output_to_wire(agent["large_output"])
40384040
return wire_agent
40394041

40404042
def _convert_default_agent_to_wire_format(

0 commit comments

Comments
 (0)