Skip to content

Commit 922959f

Browse files
stephentoubCopilot
andauthored
Expose IncludeSubAgentStreamingEvents in all four SDKs (#1108)
* Expose IncludeSubAgentStreamingEvents in all four SDKs Add the includeSubAgentStreamingEvents property to session config types and wire payloads across Node/TS, Python, Go, and .NET. The property controls whether streaming delta events from sub-agents (e.g., assistant.message_delta, assistant.reasoning_delta, assistant.streaming_delta with agentId set) are forwarded to the connection. When false, only non-streaming sub-agent events and subagent.* lifecycle events are forwarded. The SDK defaults the property to true when not specified, so existing consumers automatically receive sub-agent streaming events without any code changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix gofmt alignment in types.go Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add unit tests for IncludeSubAgentStreamingEvents across all SDKs - .NET: Clone test assertions for SessionConfig and ResumeSessionConfig - Node.js: Wire payload tests for session.create and session.resume - Python: Payload capture tests for create_session and resume_session - Go: JSON marshal tests for createSessionRequest and resumeSessionRequest Each language tests both the default (true) and explicit false paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fd0495c commit 922959f

File tree

12 files changed

+437
-57
lines changed

12 files changed

+437
-57
lines changed

dotnet/src/Client.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
497497
hasHooks ? true : null,
498498
config.WorkingDirectory,
499499
config.Streaming is true ? true : null,
500+
config.IncludeSubAgentStreamingEvents,
500501
config.McpServers,
501502
"direct",
502503
config.CustomAgents,
@@ -622,6 +623,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
622623
config.EnableConfigDiscovery,
623624
config.DisableResume is true ? true : null,
624625
config.Streaming is true ? true : null,
626+
config.IncludeSubAgentStreamingEvents,
625627
config.McpServers,
626628
"direct",
627629
config.CustomAgents,
@@ -1636,6 +1638,7 @@ internal record CreateSessionRequest(
16361638
bool? Hooks,
16371639
string? WorkingDirectory,
16381640
bool? Streaming,
1641+
bool? IncludeSubAgentStreamingEvents,
16391642
IDictionary<string, McpServerConfig>? McpServers,
16401643
string? EnvValueMode,
16411644
IList<CustomAgentConfig>? CustomAgents,
@@ -1691,6 +1694,7 @@ internal record ResumeSessionRequest(
16911694
bool? EnableConfigDiscovery,
16921695
bool? DisableResume,
16931696
bool? Streaming,
1697+
bool? IncludeSubAgentStreamingEvents,
16941698
IDictionary<string, McpServerConfig>? McpServers,
16951699
string? EnvValueMode,
16961700
IList<CustomAgentConfig>? CustomAgents,

dotnet/src/Types.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,7 @@ protected SessionConfig(SessionConfig? other)
17321732
SessionId = other.SessionId;
17331733
SkillDirectories = other.SkillDirectories is not null ? [.. other.SkillDirectories] : null;
17341734
Streaming = other.Streaming;
1735+
IncludeSubAgentStreamingEvents = other.IncludeSubAgentStreamingEvents;
17351736
SystemMessage = other.SystemMessage;
17361737
Tools = other.Tools is not null ? [.. other.Tools] : null;
17371738
WorkingDirectory = other.WorkingDirectory;
@@ -1848,6 +1849,17 @@ protected SessionConfig(SessionConfig? other)
18481849
/// </summary>
18491850
public bool Streaming { get; set; }
18501851

1852+
/// <summary>
1853+
/// Include sub-agent streaming events in the event stream. When true, streaming
1854+
/// delta events from sub-agents (e.g., <c>assistant.message_delta</c>,
1855+
/// <c>assistant.reasoning_delta</c>, <c>assistant.streaming_delta</c> with
1856+
/// <c>agentId</c> set) are forwarded to this connection. When false, only
1857+
/// non-streaming sub-agent events and <c>subagent.*</c> lifecycle events are
1858+
/// forwarded; streaming deltas from sub-agents are suppressed.
1859+
/// Default: true.
1860+
/// </summary>
1861+
public bool IncludeSubAgentStreamingEvents { get; set; } = true;
1862+
18511863
/// <summary>
18521864
/// MCP server configurations for the session.
18531865
/// Keys are server names, values are server configurations (<see cref="McpStdioServerConfig"/> or <see cref="McpHttpServerConfig"/>).
@@ -1961,6 +1973,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
19611973
CreateSessionFsHandler = other.CreateSessionFsHandler;
19621974
SkillDirectories = other.SkillDirectories is not null ? [.. other.SkillDirectories] : null;
19631975
Streaming = other.Streaming;
1976+
IncludeSubAgentStreamingEvents = other.IncludeSubAgentStreamingEvents;
19641977
SystemMessage = other.SystemMessage;
19651978
Tools = other.Tools is not null ? [.. other.Tools] : null;
19661979
WorkingDirectory = other.WorkingDirectory;
@@ -2082,6 +2095,17 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
20822095
/// </summary>
20832096
public bool Streaming { get; set; }
20842097

2098+
/// <summary>
2099+
/// Include sub-agent streaming events in the event stream. When true, streaming
2100+
/// delta events from sub-agents (e.g., <c>assistant.message_delta</c>,
2101+
/// <c>assistant.reasoning_delta</c>, <c>assistant.streaming_delta</c> with
2102+
/// <c>agentId</c> set) are forwarded to this connection. When false, only
2103+
/// non-streaming sub-agent events and <c>subagent.*</c> lifecycle events are
2104+
/// forwarded; streaming deltas from sub-agents are suppressed.
2105+
/// Default: true.
2106+
/// </summary>
2107+
public bool IncludeSubAgentStreamingEvents { get; set; } = true;
2108+
20852109
/// <summary>
20862110
/// MCP server configurations for the session.
20872111
/// Keys are server names, values are server configurations (<see cref="McpStdioServerConfig"/> or <see cref="McpHttpServerConfig"/>).

dotnet/test/CloneTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public void SessionConfig_Clone_CopiesAllProperties()
8686
ExcludedTools = ["tool3"],
8787
WorkingDirectory = "/workspace",
8888
Streaming = true,
89+
IncludeSubAgentStreamingEvents = false,
8990
McpServers = new Dictionary<string, McpServerConfig> { ["server1"] = new McpStdioServerConfig { Command = "echo" } },
9091
CustomAgents = [new CustomAgentConfig { Name = "agent1" }],
9192
Agent = "agent1",
@@ -104,6 +105,7 @@ public void SessionConfig_Clone_CopiesAllProperties()
104105
Assert.Equal(original.ExcludedTools, clone.ExcludedTools);
105106
Assert.Equal(original.WorkingDirectory, clone.WorkingDirectory);
106107
Assert.Equal(original.Streaming, clone.Streaming);
108+
Assert.Equal(original.IncludeSubAgentStreamingEvents, clone.IncludeSubAgentStreamingEvents);
107109
Assert.Equal(original.McpServers.Count, clone.McpServers!.Count);
108110
Assert.Equal(original.CustomAgents.Count, clone.CustomAgents!.Count);
109111
Assert.Equal(original.Agent, clone.Agent);
@@ -243,6 +245,7 @@ public void Clone_WithNullCollections_ReturnsNullCollections()
243245
Assert.Null(clone.SkillDirectories);
244246
Assert.Null(clone.DisabledSkills);
245247
Assert.Null(clone.Tools);
248+
Assert.True(clone.IncludeSubAgentStreamingEvents);
246249
}
247250

248251
[Fact]
@@ -272,4 +275,27 @@ public void ResumeSessionConfig_Clone_CopiesAgentProperty()
272275

273276
Assert.Equal("test-agent", clone.Agent);
274277
}
278+
279+
[Fact]
280+
public void ResumeSessionConfig_Clone_CopiesIncludeSubAgentStreamingEvents()
281+
{
282+
var original = new ResumeSessionConfig
283+
{
284+
IncludeSubAgentStreamingEvents = false,
285+
};
286+
287+
var clone = original.Clone();
288+
289+
Assert.False(clone.IncludeSubAgentStreamingEvents);
290+
}
291+
292+
[Fact]
293+
public void ResumeSessionConfig_Clone_PreservesIncludeSubAgentStreamingEventsDefault()
294+
{
295+
var original = new ResumeSessionConfig();
296+
297+
var clone = original.Clone();
298+
299+
Assert.True(clone.IncludeSubAgentStreamingEvents);
300+
}
275301
}

go/client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,11 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses
611611
if config.Streaming {
612612
req.Streaming = Bool(true)
613613
}
614+
if config.IncludeSubAgentStreamingEvents != nil {
615+
req.IncludeSubAgentStreamingEvents = config.IncludeSubAgentStreamingEvents
616+
} else {
617+
req.IncludeSubAgentStreamingEvents = Bool(true)
618+
}
614619
if config.OnUserInputRequest != nil {
615620
req.RequestUserInput = Bool(true)
616621
}
@@ -744,6 +749,11 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string,
744749
if config.Streaming {
745750
req.Streaming = Bool(true)
746751
}
752+
if config.IncludeSubAgentStreamingEvents != nil {
753+
req.IncludeSubAgentStreamingEvents = config.IncludeSubAgentStreamingEvents
754+
} else {
755+
req.IncludeSubAgentStreamingEvents = Bool(true)
756+
}
747757
if config.OnUserInputRequest != nil {
748758
req.RequestUserInput = Bool(true)
749759
}

go/client_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,80 @@ func TestResumeSessionRequest_RequestElicitation(t *testing.T) {
861861
})
862862
}
863863

864+
func TestCreateSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) {
865+
t.Run("defaults to true when nil", func(t *testing.T) {
866+
req := createSessionRequest{
867+
IncludeSubAgentStreamingEvents: Bool(true),
868+
}
869+
data, err := json.Marshal(req)
870+
if err != nil {
871+
t.Fatalf("Failed to marshal: %v", err)
872+
}
873+
var m map[string]any
874+
if err := json.Unmarshal(data, &m); err != nil {
875+
t.Fatalf("Failed to unmarshal: %v", err)
876+
}
877+
if m["includeSubAgentStreamingEvents"] != true {
878+
t.Errorf("Expected includeSubAgentStreamingEvents to be true, got %v", m["includeSubAgentStreamingEvents"])
879+
}
880+
})
881+
882+
t.Run("preserves explicit false", func(t *testing.T) {
883+
req := createSessionRequest{
884+
IncludeSubAgentStreamingEvents: Bool(false),
885+
}
886+
data, err := json.Marshal(req)
887+
if err != nil {
888+
t.Fatalf("Failed to marshal: %v", err)
889+
}
890+
var m map[string]any
891+
if err := json.Unmarshal(data, &m); err != nil {
892+
t.Fatalf("Failed to unmarshal: %v", err)
893+
}
894+
if m["includeSubAgentStreamingEvents"] != false {
895+
t.Errorf("Expected includeSubAgentStreamingEvents to be false, got %v", m["includeSubAgentStreamingEvents"])
896+
}
897+
})
898+
}
899+
900+
func TestResumeSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) {
901+
t.Run("defaults to true when nil", func(t *testing.T) {
902+
req := resumeSessionRequest{
903+
SessionID: "s1",
904+
IncludeSubAgentStreamingEvents: Bool(true),
905+
}
906+
data, err := json.Marshal(req)
907+
if err != nil {
908+
t.Fatalf("Failed to marshal: %v", err)
909+
}
910+
var m map[string]any
911+
if err := json.Unmarshal(data, &m); err != nil {
912+
t.Fatalf("Failed to unmarshal: %v", err)
913+
}
914+
if m["includeSubAgentStreamingEvents"] != true {
915+
t.Errorf("Expected includeSubAgentStreamingEvents to be true, got %v", m["includeSubAgentStreamingEvents"])
916+
}
917+
})
918+
919+
t.Run("preserves explicit false", func(t *testing.T) {
920+
req := resumeSessionRequest{
921+
SessionID: "s1",
922+
IncludeSubAgentStreamingEvents: Bool(false),
923+
}
924+
data, err := json.Marshal(req)
925+
if err != nil {
926+
t.Fatalf("Failed to marshal: %v", err)
927+
}
928+
var m map[string]any
929+
if err := json.Unmarshal(data, &m); err != nil {
930+
t.Fatalf("Failed to unmarshal: %v", err)
931+
}
932+
if m["includeSubAgentStreamingEvents"] != false {
933+
t.Errorf("Expected includeSubAgentStreamingEvents to be false, got %v", m["includeSubAgentStreamingEvents"])
934+
}
935+
})
936+
}
937+
864938
func TestCreateSessionResponse_Capabilities(t *testing.T) {
865939
t.Run("reads capabilities from session.create response", func(t *testing.T) {
866940
responseJSON := `{"sessionId":"s1","workspacePath":"/tmp","capabilities":{"ui":{"elicitation":true}}}`

0 commit comments

Comments
 (0)