Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions dotnet/test/Unit/CloneTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public void SessionConfig_Clone_CopiesAllProperties()
WorkingDirectory = "/workspace",
Streaming = true,
EnableSessionTelemetry = false,
EnableOnDemandInstructionDiscovery = true,
IncludeSubAgentStreamingEvents = false,
McpServers = new Dictionary<string, McpServerConfig> { ["server1"] = new McpStdioServerConfig { Command = "echo" } },
McpOAuthTokenStorage = McpOAuthTokenStorageMode.Persistent,
Expand Down Expand Up @@ -112,6 +113,7 @@ public void SessionConfig_Clone_CopiesAllProperties()
Assert.Equal(original.WorkingDirectory, clone.WorkingDirectory);
Assert.Equal(original.Streaming, clone.Streaming);
Assert.Equal(original.EnableSessionTelemetry, clone.EnableSessionTelemetry);
Assert.Equal(original.EnableOnDemandInstructionDiscovery, clone.EnableOnDemandInstructionDiscovery);
Assert.Equal(original.IncludeSubAgentStreamingEvents, clone.IncludeSubAgentStreamingEvents);
Assert.Equal(original.McpServers.Count, clone.McpServers!.Count);
Assert.Equal(original.McpOAuthTokenStorage, clone.McpOAuthTokenStorage);
Expand Down Expand Up @@ -423,6 +425,52 @@ public void ResumeSessionConfig_Clone_PreservesEnableSessionTelemetryDefault()
Assert.Null(clone.EnableSessionTelemetry);
}

[Fact]
public void SessionConfig_Clone_CopiesEnableOnDemandInstructionDiscovery()
{
var original = new SessionConfig
{
EnableOnDemandInstructionDiscovery = false,
};

var clone = original.Clone();

Assert.False(clone.EnableOnDemandInstructionDiscovery);
}

[Fact]
public void ResumeSessionConfig_Clone_CopiesEnableOnDemandInstructionDiscovery()
{
var original = new ResumeSessionConfig
{
EnableOnDemandInstructionDiscovery = true,
};

var clone = original.Clone();

Assert.True(clone.EnableOnDemandInstructionDiscovery);
}

[Fact]
public void SessionConfig_Clone_PreservesEnableOnDemandInstructionDiscoveryDefault()
{
var original = new SessionConfig();

var clone = original.Clone();

Assert.Null(clone.EnableOnDemandInstructionDiscovery);
}

[Fact]
public void ResumeSessionConfig_Clone_PreservesEnableOnDemandInstructionDiscoveryDefault()
{
var original = new ResumeSessionConfig();

var clone = original.Clone();

Assert.Null(clone.EnableOnDemandInstructionDiscovery);
}

[Fact]
public void SessionConfig_Clone_CopiesMcpOAuthTokenStorage()
{
Expand Down
54 changes: 54 additions & 0 deletions dotnet/test/Unit/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,60 @@ public void ResumeSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptio
Assert.False(root.GetProperty("enableSessionTelemetry").GetBoolean());
}

[Fact]
public void CreateSessionRequest_CanSerializeEnableOnDemandInstructionDiscovery_WithSdkOptions()
{
var options = GetSerializerOptions();
var requestType = GetNestedType(typeof(CopilotClient), "CreateSessionRequest");

var requestTrue = CreateInternalRequest(
requestType,
("SessionId", "session-id"),
("EnableOnDemandInstructionDiscovery", true));
var rootTrue = JsonDocument.Parse(JsonSerializer.Serialize(requestTrue, requestType, options)).RootElement;
Assert.True(rootTrue.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean());

var requestFalse = CreateInternalRequest(
requestType,
("SessionId", "session-id"),
("EnableOnDemandInstructionDiscovery", false));
var rootFalse = JsonDocument.Parse(JsonSerializer.Serialize(requestFalse, requestType, options)).RootElement;
Assert.False(rootFalse.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean());

var requestOmitted = CreateInternalRequest(
requestType,
("SessionId", "session-id"));
var rootOmitted = JsonDocument.Parse(JsonSerializer.Serialize(requestOmitted, requestType, options)).RootElement;
Assert.False(rootOmitted.TryGetProperty("enableOnDemandInstructionDiscovery", out _));
}

[Fact]
public void ResumeSessionRequest_CanSerializeEnableOnDemandInstructionDiscovery_WithSdkOptions()
{
var options = GetSerializerOptions();
var requestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest");

var requestTrue = CreateInternalRequest(
requestType,
("SessionId", "session-id"),
("EnableOnDemandInstructionDiscovery", true));
var rootTrue = JsonDocument.Parse(JsonSerializer.Serialize(requestTrue, requestType, options)).RootElement;
Assert.True(rootTrue.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean());

var requestFalse = CreateInternalRequest(
requestType,
("SessionId", "session-id"),
("EnableOnDemandInstructionDiscovery", false));
var rootFalse = JsonDocument.Parse(JsonSerializer.Serialize(requestFalse, requestType, options)).RootElement;
Assert.False(rootFalse.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean());

var requestOmitted = CreateInternalRequest(
requestType,
("SessionId", "session-id"));
var rootOmitted = JsonDocument.Parse(JsonSerializer.Serialize(requestOmitted, requestType, options)).RootElement;
Assert.False(rootOmitted.TryGetProperty("enableOnDemandInstructionDiscovery", out _));
}

[Fact]
public void ResumeSessionRequest_CanSerializeOpenCanvases_WithSdkOptions()
{
Expand Down
94 changes: 94 additions & 0 deletions go/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,100 @@ func TestResumeSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) {
})
}

func TestCreateSessionRequest_EnableOnDemandInstructionDiscovery(t *testing.T) {
t.Run("forwards explicit true", func(t *testing.T) {
req := createSessionRequest{
EnableOnDemandInstructionDiscovery: Bool(true),
}
data, err := json.Marshal(req)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if m["enableOnDemandInstructionDiscovery"] != true {
t.Errorf("Expected enableOnDemandInstructionDiscovery to be true, got %v", m["enableOnDemandInstructionDiscovery"])
}
})

t.Run("preserves explicit false", func(t *testing.T) {
req := createSessionRequest{
EnableOnDemandInstructionDiscovery: Bool(false),
}
data, err := json.Marshal(req)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if m["enableOnDemandInstructionDiscovery"] != false {
t.Errorf("Expected enableOnDemandInstructionDiscovery to be false, got %v", m["enableOnDemandInstructionDiscovery"])
}
})

t.Run("omits enableOnDemandInstructionDiscovery when not set", func(t *testing.T) {
req := createSessionRequest{}
data, _ := json.Marshal(req)
var m map[string]any
json.Unmarshal(data, &m)
if _, ok := m["enableOnDemandInstructionDiscovery"]; ok {
t.Error("Expected enableOnDemandInstructionDiscovery to be omitted when not set")
}
})
}

func TestResumeSessionRequest_EnableOnDemandInstructionDiscovery(t *testing.T) {
t.Run("forwards explicit true", func(t *testing.T) {
req := resumeSessionRequest{
SessionID: "s1",
EnableOnDemandInstructionDiscovery: Bool(true),
}
data, err := json.Marshal(req)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if m["enableOnDemandInstructionDiscovery"] != true {
t.Errorf("Expected enableOnDemandInstructionDiscovery to be true, got %v", m["enableOnDemandInstructionDiscovery"])
}
})

t.Run("preserves explicit false", func(t *testing.T) {
req := resumeSessionRequest{
SessionID: "s1",
EnableOnDemandInstructionDiscovery: Bool(false),
}
data, err := json.Marshal(req)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if m["enableOnDemandInstructionDiscovery"] != false {
t.Errorf("Expected enableOnDemandInstructionDiscovery to be false, got %v", m["enableOnDemandInstructionDiscovery"])
}
})

t.Run("omits enableOnDemandInstructionDiscovery when not set", func(t *testing.T) {
req := resumeSessionRequest{SessionID: "s1"}
data, _ := json.Marshal(req)
var m map[string]any
json.Unmarshal(data, &m)
if _, ok := m["enableOnDemandInstructionDiscovery"]; ok {
t.Error("Expected enableOnDemandInstructionDiscovery to be omitted when not set")
}
})
}

func TestCreateSessionResponse_Capabilities(t *testing.T) {
t.Run("reads capabilities from session.create response", func(t *testing.T) {
responseJSON := `{"sessionId":"s1","workspacePath":"/tmp","capabilities":{"ui":{"elicitation":true}}}`
Expand Down
10 changes: 7 additions & 3 deletions go/internal/e2e/client_options_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,10 @@ func TestClientOptionsE2E(t *testing.T) {
}

session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{
EnableConfigDiscovery: true,
IncludeSubAgentStreamingEvents: copilot.Bool(false),
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
EnableConfigDiscovery: true,
EnableOnDemandInstructionDiscovery: copilot.Bool(true),
IncludeSubAgentStreamingEvents: copilot.Bool(false),
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
t.Fatalf("CreateSession failed: %v", err)
Expand All @@ -187,6 +188,9 @@ func TestClientOptionsE2E(t *testing.T) {
if v, ok := params["enableConfigDiscovery"].(bool); !ok || v != true {
t.Errorf("Expected session.create.params.enableConfigDiscovery=true, got %v", params["enableConfigDiscovery"])
}
if v, ok := params["enableOnDemandInstructionDiscovery"].(bool); !ok || v != true {
t.Errorf("Expected session.create.params.enableOnDemandInstructionDiscovery=true, got %v", params["enableOnDemandInstructionDiscovery"])
}
if v, ok := params["includeSubAgentStreamingEvents"].(bool); !ok || v != false {
t.Errorf("Expected session.create.params.includeSubAgentStreamingEvents=false, got %v", params["includeSubAgentStreamingEvents"])
}
Expand Down
44 changes: 44 additions & 0 deletions nodejs/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,50 @@ describe("CopilotClient", () => {
spy.mockRestore();
});

it("forwards enableOnDemandInstructionDiscovery in session.create request", async () => {
const client = new CopilotClient();
await client.start();
onTestFinished(() => client.forceStop());

const spy = vi.spyOn((client as any).connection!, "sendRequest");
await client.createSession({
enableOnDemandInstructionDiscovery: false,
onPermissionRequest: approveAll,
});

expect(spy).toHaveBeenCalledWith(
"session.create",
expect.objectContaining({ enableOnDemandInstructionDiscovery: false })
);
});

it("forwards enableOnDemandInstructionDiscovery in session.resume request", async () => {
const client = new CopilotClient();
await client.start();
onTestFinished(() => client.forceStop());

const session = await client.createSession({ onPermissionRequest: approveAll });
const spy = vi
.spyOn((client as any).connection!, "sendRequest")
.mockImplementation(async (method: string, params: any) => {
if (method === "session.resume") return { sessionId: params.sessionId };
throw new Error(`Unexpected method: ${method}`);
});
await client.resumeSession(session.sessionId, {
enableOnDemandInstructionDiscovery: false,
onPermissionRequest: approveAll,
});

expect(spy).toHaveBeenCalledWith(
"session.resume",
expect.objectContaining({
enableOnDemandInstructionDiscovery: false,
sessionId: session.sessionId,
})
);
spy.mockRestore();
});

it("defaults includeSubAgentStreamingEvents to true in session.create when not specified", async () => {
const client = new CopilotClient();
await client.start();
Expand Down
3 changes: 3 additions & 0 deletions nodejs/test/e2e/client_options.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ describe("Client options", async () => {
const session = await client.createSession({
onPermissionRequest: approveAll,
enableConfigDiscovery: true,
enableOnDemandInstructionDiscovery: true,
includeSubAgentStreamingEvents: false,
});

Expand All @@ -300,13 +301,15 @@ describe("Client options", async () => {
method: string;
params: {
enableConfigDiscovery?: boolean;
enableOnDemandInstructionDiscovery?: boolean;
includeSubAgentStreamingEvents?: boolean;
};
}[];
};
const createRequests = updated.requests.filter((r) => r.method === "session.create");
expect(createRequests).toHaveLength(1);
expect(createRequests[0].params.enableConfigDiscovery).toBe(true);
expect(createRequests[0].params.enableOnDemandInstructionDiscovery).toBe(true);
expect(createRequests[0].params.includeSubAgentStreamingEvents).toBe(false);

await session.disconnect();
Expand Down
2 changes: 2 additions & 0 deletions python/e2e/test_client_options_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
enable_config_discovery=True,
enable_on_demand_instruction_discovery=True,
include_sub_agent_streaming_events=False,
)
try:
Expand All @@ -264,6 +265,7 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes
)
params = create_request["params"]
assert params["enableConfigDiscovery"] is True
assert params["enableOnDemandInstructionDiscovery"] is True
assert params["includeSubAgentStreamingEvents"] is False
finally:
await session.disconnect()
Expand Down
Loading
Loading