diff --git a/docs/features/image-input.md b/docs/features/image-input.md
index aa3bf2f64..147a5258f 100644
--- a/docs/features/image-input.md
+++ b/docs/features/image-input.md
@@ -117,7 +117,7 @@ func main() {
Prompt: "Describe what you see in this image",
Attachments: []copilot.Attachment{
{
- Type: copilot.File,
+ Type: copilot.AttachmentTypeFile,
Path: &path,
},
},
@@ -143,7 +143,7 @@ session.Send(ctx, copilot.MessageOptions{
Prompt: "Describe what you see in this image",
Attachments: []copilot.Attachment{
{
- Type: copilot.File,
+ Type: copilot.AttachmentTypeFile,
Path: &path,
},
},
diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs
index 606c0b052..0014ec7f0 100644
--- a/dotnet/src/Session.cs
+++ b/dotnet/src/Session.cs
@@ -749,6 +749,7 @@ public Task SetModelAsync(string model, CancellationToken cancellationToken = de
/// The message to log.
/// Log level (default: info).
/// When true, the message is not persisted to disk.
+ /// Optional URL to associate with the log entry.
/// Optional cancellation token.
///
///
@@ -758,9 +759,9 @@ public Task SetModelAsync(string model, CancellationToken cancellationToken = de
/// await session.LogAsync("Temporary status", ephemeral: true);
///
///
- public async Task LogAsync(string message, SessionLogRequestLevel? level = null, bool? ephemeral = null, CancellationToken cancellationToken = default)
+ public async Task LogAsync(string message, SessionLogRequestLevel? level = null, bool? ephemeral = null, string? url = null, CancellationToken cancellationToken = default)
{
- await Rpc.LogAsync(message, level, ephemeral, cancellationToken);
+ await Rpc.LogAsync(message, level, ephemeral, url, cancellationToken);
}
///
diff --git a/go/generated_session_events.go b/go/generated_session_events.go
index 9c28c07f3..d68436e8d 100644
--- a/go/generated_session_events.go
+++ b/go/generated_session_events.go
@@ -1243,78 +1243,78 @@ type ToolRequest struct {
type AgentMode string
const (
- AgentModeShell AgentMode = "shell"
- Autopilot AgentMode = "autopilot"
- Interactive AgentMode = "interactive"
- Plan AgentMode = "plan"
+ AgentModeShell AgentMode = "shell"
+ AgentModeAutopilot AgentMode = "autopilot"
+ AgentModeInteractive AgentMode = "interactive"
+ AgentModePlan AgentMode = "plan"
)
// Type of GitHub reference
type ReferenceType string
const (
- Discussion ReferenceType = "discussion"
- Issue ReferenceType = "issue"
- PR ReferenceType = "pr"
+ ReferenceTypeDiscussion ReferenceType = "discussion"
+ ReferenceTypeIssue ReferenceType = "issue"
+ ReferenceTypePr ReferenceType = "pr"
)
type AttachmentType string
const (
- Blob AttachmentType = "blob"
- Directory AttachmentType = "directory"
- File AttachmentType = "file"
- GithubReference AttachmentType = "github_reference"
- Selection AttachmentType = "selection"
+ AttachmentTypeBlob AttachmentType = "blob"
+ AttachmentTypeDirectory AttachmentType = "directory"
+ AttachmentTypeFile AttachmentType = "file"
+ AttachmentTypeGithubReference AttachmentType = "github_reference"
+ AttachmentTypeSelection AttachmentType = "selection"
)
// Hosting platform type of the repository (github or ado)
type HostType string
const (
- ADO HostType = "ado"
- Github HostType = "github"
+ HostTypeAdo HostType = "ado"
+ HostTypeGithub HostType = "github"
)
// Discovery source
type Source string
const (
- Project Source = "project"
- User Source = "user"
+ SourceProject Source = "project"
+ SourceUser Source = "user"
)
// Current status: running, disabled, failed, or starting
type ExtensionStatus string
const (
- PurpleDisabled ExtensionStatus = "disabled"
- PurpleFailed ExtensionStatus = "failed"
- Running ExtensionStatus = "running"
- Starting ExtensionStatus = "starting"
+ ExtensionStatusDisabled ExtensionStatus = "disabled"
+ ExtensionStatusFailed ExtensionStatus = "failed"
+ ExtensionStatusRunning ExtensionStatus = "running"
+ ExtensionStatusStarting ExtensionStatus = "starting"
)
// Whether the agent completed successfully or failed
type KindStatus string
const (
- Completed KindStatus = "completed"
- FluffyFailed KindStatus = "failed"
+ KindStatusCompleted KindStatus = "completed"
+ KindStatusFailed KindStatus = "failed"
)
type KindType string
const (
- AgentCompleted KindType = "agent_completed"
- AgentIdle KindType = "agent_idle"
- ShellCompleted KindType = "shell_completed"
- ShellDetachedCompleted KindType = "shell_detached_completed"
+ KindTypeAgentCompleted KindType = "agent_completed"
+ KindTypeAgentIdle KindType = "agent_idle"
+ KindTypeShellCompleted KindType = "shell_completed"
+ KindTypeShellDetachedCompleted KindType = "shell_detached_completed"
)
type Mode string
const (
- Form Mode = "form"
+ ModeForm Mode = "form"
)
// The type of operation performed on the plan file
@@ -1323,66 +1323,66 @@ const (
type Operation string
const (
- Create Operation = "create"
- Delete Operation = "delete"
- Update Operation = "update"
+ OperationCreate Operation = "create"
+ OperationDelete Operation = "delete"
+ OperationUpdate Operation = "update"
)
type PermissionRequestKind string
const (
- CustomTool PermissionRequestKind = "custom-tool"
- Hook PermissionRequestKind = "hook"
- KindShell PermissionRequestKind = "shell"
- MCP PermissionRequestKind = "mcp"
- Memory PermissionRequestKind = "memory"
- Read PermissionRequestKind = "read"
- URL PermissionRequestKind = "url"
- Write PermissionRequestKind = "write"
+ PermissionRequestKindCustomTool PermissionRequestKind = "custom-tool"
+ PermissionRequestKindHook PermissionRequestKind = "hook"
+ PermissionRequestKindShell PermissionRequestKind = "shell"
+ PermissionRequestKindMcp PermissionRequestKind = "mcp"
+ PermissionRequestKindMemory PermissionRequestKind = "memory"
+ PermissionRequestKindRead PermissionRequestKind = "read"
+ PermissionRequestKindURL PermissionRequestKind = "url"
+ PermissionRequestKindWrite PermissionRequestKind = "write"
)
type RequestedSchemaType string
const (
- Object RequestedSchemaType = "object"
+ RequestedSchemaTypeObject RequestedSchemaType = "object"
)
// Theme variant this icon is intended for
type Theme string
const (
- Dark Theme = "dark"
- Light Theme = "light"
+ ThemeDark Theme = "dark"
+ ThemeLight Theme = "light"
)
type ContentType string
const (
- Audio ContentType = "audio"
- Image ContentType = "image"
- Resource ContentType = "resource"
- ResourceLink ContentType = "resource_link"
- Terminal ContentType = "terminal"
- Text ContentType = "text"
+ ContentTypeAudio ContentType = "audio"
+ ContentTypeImage ContentType = "image"
+ ContentTypeResource ContentType = "resource"
+ ContentTypeResourceLink ContentType = "resource_link"
+ ContentTypeTerminal ContentType = "terminal"
+ ContentTypeText ContentType = "text"
)
// The outcome of the permission request
type ResultKind string
const (
- Approved ResultKind = "approved"
- DeniedByContentExclusionPolicy ResultKind = "denied-by-content-exclusion-policy"
- DeniedByRules ResultKind = "denied-by-rules"
- DeniedInteractivelyByUser ResultKind = "denied-interactively-by-user"
- DeniedNoApprovalRuleAndCouldNotRequestFromUser ResultKind = "denied-no-approval-rule-and-could-not-request-from-user"
+ ResultKindApproved ResultKind = "approved"
+ ResultKindDeniedByContentExclusionPolicy ResultKind = "denied-by-content-exclusion-policy"
+ ResultKindDeniedByRules ResultKind = "denied-by-rules"
+ ResultKindDeniedInteractivelyByUser ResultKind = "denied-interactively-by-user"
+ ResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser ResultKind = "denied-no-approval-rule-and-could-not-request-from-user"
)
// Message role: "system" for system prompts, "developer" for developer-injected instructions
type Role string
const (
- Developer Role = "developer"
- System Role = "system"
+ RoleDeveloper Role = "developer"
+ RoleSystem Role = "system"
)
// Connection status: connected, failed, pending, disabled, or not_configured
@@ -1391,27 +1391,27 @@ const (
type ServerStatus string
const (
- Connected ServerStatus = "connected"
- FluffyDisabled ServerStatus = "disabled"
- NotConfigured ServerStatus = "not_configured"
- Pending ServerStatus = "pending"
- TentacledFailed ServerStatus = "failed"
+ ServerStatusConnected ServerStatus = "connected"
+ ServerStatusDisabled ServerStatus = "disabled"
+ ServerStatusNotConfigured ServerStatus = "not_configured"
+ ServerStatusPending ServerStatus = "pending"
+ ServerStatusFailed ServerStatus = "failed"
)
// Whether the session ended normally ("routine") or due to a crash/fatal error ("error")
type ShutdownType string
const (
- Error ShutdownType = "error"
- Routine ShutdownType = "routine"
+ ShutdownTypeError ShutdownType = "error"
+ ShutdownTypeRoutine ShutdownType = "routine"
)
// Origin type of the session being handed off
type SourceType string
const (
- Local SourceType = "local"
- Remote SourceType = "remote"
+ SourceTypeLocal SourceType = "local"
+ SourceTypeRemote SourceType = "remote"
)
// Tool call type: "function" for standard tool calls, "custom" for grammar-based tool
@@ -1419,78 +1419,78 @@ const (
type ToolRequestType string
const (
- Custom ToolRequestType = "custom"
- Function ToolRequestType = "function"
+ ToolRequestTypeCustom ToolRequestType = "custom"
+ ToolRequestTypeFunction ToolRequestType = "function"
)
type SessionEventType string
const (
- Abort SessionEventType = "abort"
- AssistantIntent SessionEventType = "assistant.intent"
- AssistantMessage SessionEventType = "assistant.message"
- AssistantMessageDelta SessionEventType = "assistant.message_delta"
- AssistantReasoning SessionEventType = "assistant.reasoning"
- AssistantReasoningDelta SessionEventType = "assistant.reasoning_delta"
- AssistantStreamingDelta SessionEventType = "assistant.streaming_delta"
- AssistantTurnEnd SessionEventType = "assistant.turn_end"
- AssistantTurnStart SessionEventType = "assistant.turn_start"
- AssistantUsage SessionEventType = "assistant.usage"
- CommandCompleted SessionEventType = "command.completed"
- CommandQueued SessionEventType = "command.queued"
- ElicitationCompleted SessionEventType = "elicitation.completed"
- ElicitationRequested SessionEventType = "elicitation.requested"
- ExitPlanModeCompleted SessionEventType = "exit_plan_mode.completed"
- ExitPlanModeRequested SessionEventType = "exit_plan_mode.requested"
- ExternalToolCompleted SessionEventType = "external_tool.completed"
- ExternalToolRequested SessionEventType = "external_tool.requested"
- HookEnd SessionEventType = "hook.end"
- HookStart SessionEventType = "hook.start"
- PendingMessagesModified SessionEventType = "pending_messages.modified"
- PermissionCompleted SessionEventType = "permission.completed"
- PermissionRequested SessionEventType = "permission.requested"
- SessionBackgroundTasksChanged SessionEventType = "session.background_tasks_changed"
- SessionCompactionComplete SessionEventType = "session.compaction_complete"
- SessionCompactionStart SessionEventType = "session.compaction_start"
- SessionContextChanged SessionEventType = "session.context_changed"
- SessionError SessionEventType = "session.error"
- SessionExtensionsLoaded SessionEventType = "session.extensions_loaded"
- SessionHandoff SessionEventType = "session.handoff"
- SessionIdle SessionEventType = "session.idle"
- SessionInfo SessionEventType = "session.info"
- SessionMCPServerStatusChanged SessionEventType = "session.mcp_server_status_changed"
- SessionMCPServersLoaded SessionEventType = "session.mcp_servers_loaded"
- SessionModeChanged SessionEventType = "session.mode_changed"
- SessionModelChange SessionEventType = "session.model_change"
- SessionPlanChanged SessionEventType = "session.plan_changed"
- SessionResume SessionEventType = "session.resume"
- SessionShutdown SessionEventType = "session.shutdown"
- SessionSkillsLoaded SessionEventType = "session.skills_loaded"
- SessionSnapshotRewind SessionEventType = "session.snapshot_rewind"
- SessionStart SessionEventType = "session.start"
- SessionTaskComplete SessionEventType = "session.task_complete"
- SessionTitleChanged SessionEventType = "session.title_changed"
- SessionToolsUpdated SessionEventType = "session.tools_updated"
- SessionTruncation SessionEventType = "session.truncation"
- SessionUsageInfo SessionEventType = "session.usage_info"
- SessionWarning SessionEventType = "session.warning"
- SessionWorkspaceFileChanged SessionEventType = "session.workspace_file_changed"
- SkillInvoked SessionEventType = "skill.invoked"
- SubagentCompleted SessionEventType = "subagent.completed"
- SubagentDeselected SessionEventType = "subagent.deselected"
- SubagentFailed SessionEventType = "subagent.failed"
- SubagentSelected SessionEventType = "subagent.selected"
- SubagentStarted SessionEventType = "subagent.started"
- SystemMessage SessionEventType = "system.message"
- SystemNotification SessionEventType = "system.notification"
- ToolExecutionComplete SessionEventType = "tool.execution_complete"
- ToolExecutionPartialResult SessionEventType = "tool.execution_partial_result"
- ToolExecutionProgress SessionEventType = "tool.execution_progress"
- ToolExecutionStart SessionEventType = "tool.execution_start"
- ToolUserRequested SessionEventType = "tool.user_requested"
- UserInputCompleted SessionEventType = "user_input.completed"
- UserInputRequested SessionEventType = "user_input.requested"
- UserMessage SessionEventType = "user.message"
+ SessionEventTypeAbort SessionEventType = "abort"
+ SessionEventTypeAssistantIntent SessionEventType = "assistant.intent"
+ SessionEventTypeAssistantMessage SessionEventType = "assistant.message"
+ SessionEventTypeAssistantMessageDelta SessionEventType = "assistant.message_delta"
+ SessionEventTypeAssistantReasoning SessionEventType = "assistant.reasoning"
+ SessionEventTypeAssistantReasoningDelta SessionEventType = "assistant.reasoning_delta"
+ SessionEventTypeAssistantStreamingDelta SessionEventType = "assistant.streaming_delta"
+ SessionEventTypeAssistantTurnEnd SessionEventType = "assistant.turn_end"
+ SessionEventTypeAssistantTurnStart SessionEventType = "assistant.turn_start"
+ SessionEventTypeAssistantUsage SessionEventType = "assistant.usage"
+ SessionEventTypeCommandCompleted SessionEventType = "command.completed"
+ SessionEventTypeCommandQueued SessionEventType = "command.queued"
+ SessionEventTypeElicitationCompleted SessionEventType = "elicitation.completed"
+ SessionEventTypeElicitationRequested SessionEventType = "elicitation.requested"
+ SessionEventTypeExitPlanModeCompleted SessionEventType = "exit_plan_mode.completed"
+ SessionEventTypeExitPlanModeRequested SessionEventType = "exit_plan_mode.requested"
+ SessionEventTypeExternalToolCompleted SessionEventType = "external_tool.completed"
+ SessionEventTypeExternalToolRequested SessionEventType = "external_tool.requested"
+ SessionEventTypeHookEnd SessionEventType = "hook.end"
+ SessionEventTypeHookStart SessionEventType = "hook.start"
+ SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified"
+ SessionEventTypePermissionCompleted SessionEventType = "permission.completed"
+ SessionEventTypePermissionRequested SessionEventType = "permission.requested"
+ SessionEventTypeSessionBackgroundTasksChanged SessionEventType = "session.background_tasks_changed"
+ SessionEventTypeSessionCompactionComplete SessionEventType = "session.compaction_complete"
+ SessionEventTypeSessionCompactionStart SessionEventType = "session.compaction_start"
+ SessionEventTypeSessionContextChanged SessionEventType = "session.context_changed"
+ SessionEventTypeSessionError SessionEventType = "session.error"
+ SessionEventTypeSessionExtensionsLoaded SessionEventType = "session.extensions_loaded"
+ SessionEventTypeSessionHandoff SessionEventType = "session.handoff"
+ SessionEventTypeSessionIdle SessionEventType = "session.idle"
+ SessionEventTypeSessionInfo SessionEventType = "session.info"
+ SessionEventTypeSessionMcpServerStatusChanged SessionEventType = "session.mcp_server_status_changed"
+ SessionEventTypeSessionMcpServersLoaded SessionEventType = "session.mcp_servers_loaded"
+ SessionEventTypeSessionModeChanged SessionEventType = "session.mode_changed"
+ SessionEventTypeSessionModelChange SessionEventType = "session.model_change"
+ SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed"
+ SessionEventTypeSessionResume SessionEventType = "session.resume"
+ SessionEventTypeSessionShutdown SessionEventType = "session.shutdown"
+ SessionEventTypeSessionSkillsLoaded SessionEventType = "session.skills_loaded"
+ SessionEventTypeSessionSnapshotRewind SessionEventType = "session.snapshot_rewind"
+ SessionEventTypeSessionStart SessionEventType = "session.start"
+ SessionEventTypeSessionTaskComplete SessionEventType = "session.task_complete"
+ SessionEventTypeSessionTitleChanged SessionEventType = "session.title_changed"
+ SessionEventTypeSessionToolsUpdated SessionEventType = "session.tools_updated"
+ SessionEventTypeSessionTruncation SessionEventType = "session.truncation"
+ SessionEventTypeSessionUsageInfo SessionEventType = "session.usage_info"
+ SessionEventTypeSessionWarning SessionEventType = "session.warning"
+ SessionEventTypeSessionWorkspaceFileChanged SessionEventType = "session.workspace_file_changed"
+ SessionEventTypeSkillInvoked SessionEventType = "skill.invoked"
+ SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed"
+ SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected"
+ SessionEventTypeSubagentFailed SessionEventType = "subagent.failed"
+ SessionEventTypeSubagentSelected SessionEventType = "subagent.selected"
+ SessionEventTypeSubagentStarted SessionEventType = "subagent.started"
+ SessionEventTypeSystemMessage SessionEventType = "system.message"
+ SessionEventTypeSystemNotification SessionEventType = "system.notification"
+ SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete"
+ SessionEventTypeToolExecutionPartialResult SessionEventType = "tool.execution_partial_result"
+ SessionEventTypeToolExecutionProgress SessionEventType = "tool.execution_progress"
+ SessionEventTypeToolExecutionStart SessionEventType = "tool.execution_start"
+ SessionEventTypeToolUserRequested SessionEventType = "tool.user_requested"
+ SessionEventTypeUserInputCompleted SessionEventType = "user_input.completed"
+ SessionEventTypeUserInputRequested SessionEventType = "user_input.requested"
+ SessionEventTypeUserMessage SessionEventType = "user.message"
)
type ContextUnion struct {
diff --git a/go/internal/e2e/agent_and_compact_rpc_test.go b/go/internal/e2e/agent_and_compact_rpc_test.go
index 338f4da67..cbd52a326 100644
--- a/go/internal/e2e/agent_and_compact_rpc_test.go
+++ b/go/internal/e2e/agent_and_compact_rpc_test.go
@@ -215,7 +215,7 @@ func TestAgentSelectionRpc(t *testing.T) {
}
})
- t.Run("should return empty list when no custom agents configured", func(t *testing.T) {
+ t.Run("should return no custom agents when none configured", func(t *testing.T) {
client := copilot.NewClient(&copilot.ClientOptions{
CLIPath: cliPath,
UseStdio: copilot.Bool(true),
@@ -238,8 +238,13 @@ func TestAgentSelectionRpc(t *testing.T) {
t.Fatalf("Failed to list agents: %v", err)
}
- if len(result.Agents) != 0 {
- t.Errorf("Expected empty agent list, got %d agents", len(result.Agents))
+ // The CLI may return built-in/default agents even when no custom agents
+ // are configured, so just verify none of the known custom agent names appear.
+ customNames := map[string]bool{"test-agent": true, "another-agent": true}
+ for _, agent := range result.Agents {
+ if customNames[agent.Name] {
+ t.Errorf("Expected no custom agents, but found %q", agent.Name)
+ }
}
if err := client.Stop(); err != nil {
diff --git a/go/internal/e2e/compaction_test.go b/go/internal/e2e/compaction_test.go
index aee80704d..888ab2aa9 100644
--- a/go/internal/e2e/compaction_test.go
+++ b/go/internal/e2e/compaction_test.go
@@ -36,10 +36,10 @@ func TestCompaction(t *testing.T) {
var compactionCompleteEvents []copilot.SessionEvent
session.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.SessionCompactionStart {
+ if event.Type == copilot.SessionEventTypeSessionCompactionStart {
compactionStartEvents = append(compactionStartEvents, event)
}
- if event.Type == copilot.SessionCompactionComplete {
+ if event.Type == copilot.SessionEventTypeSessionCompactionComplete {
compactionCompleteEvents = append(compactionCompleteEvents, event)
}
})
@@ -105,7 +105,7 @@ func TestCompaction(t *testing.T) {
var compactionEvents []copilot.SessionEvent
session.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.SessionCompactionStart || event.Type == copilot.SessionCompactionComplete {
+ if event.Type == copilot.SessionEventTypeSessionCompactionStart || event.Type == copilot.SessionEventTypeSessionCompactionComplete {
compactionEvents = append(compactionEvents, event)
}
})
diff --git a/go/internal/e2e/multi_client_test.go b/go/internal/e2e/multi_client_test.go
index 9571ab58e..3c7dc34c3 100644
--- a/go/internal/e2e/multi_client_test.go
+++ b/go/internal/e2e/multi_client_test.go
@@ -79,13 +79,13 @@ func TestMultiClient(t *testing.T) {
client2Completed := make(chan struct{}, 1)
session1.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.ExternalToolRequested {
+ if event.Type == copilot.SessionEventTypeExternalToolRequested {
select {
case client1Requested <- struct{}{}:
default:
}
}
- if event.Type == copilot.ExternalToolCompleted {
+ if event.Type == copilot.SessionEventTypeExternalToolCompleted {
select {
case client1Completed <- struct{}{}:
default:
@@ -93,13 +93,13 @@ func TestMultiClient(t *testing.T) {
}
})
session2.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.ExternalToolRequested {
+ if event.Type == copilot.SessionEventTypeExternalToolRequested {
select {
case client2Requested <- struct{}{}:
default:
}
}
- if event.Type == copilot.ExternalToolCompleted {
+ if event.Type == copilot.SessionEventTypeExternalToolCompleted {
select {
case client2Completed <- struct{}{}:
default:
@@ -120,7 +120,7 @@ func TestMultiClient(t *testing.T) {
}
// Wait for all broadcast events to arrive on both clients
- timeout := time.After(10 * time.Second)
+ timeout := time.After(30 * time.Second)
for _, ch := range []chan struct{}{client1Requested, client2Requested, client1Completed, client2Completed} {
select {
case <-ch:
@@ -197,10 +197,10 @@ func TestMultiClient(t *testing.T) {
// Both clients should have seen permission.requested events
mu1.Lock()
- c1PermRequested := filterEventsByType(client1Events, copilot.PermissionRequested)
+ c1PermRequested := filterEventsByType(client1Events, copilot.SessionEventTypePermissionRequested)
mu1.Unlock()
mu2.Lock()
- c2PermRequested := filterEventsByType(client2Events, copilot.PermissionRequested)
+ c2PermRequested := filterEventsByType(client2Events, copilot.SessionEventTypePermissionRequested)
mu2.Unlock()
if len(c1PermRequested) == 0 {
@@ -212,10 +212,10 @@ func TestMultiClient(t *testing.T) {
// Both clients should have seen permission.completed events with approved result
mu1.Lock()
- c1PermCompleted := filterEventsByType(client1Events, copilot.PermissionCompleted)
+ c1PermCompleted := filterEventsByType(client1Events, copilot.SessionEventTypePermissionCompleted)
mu1.Unlock()
mu2.Lock()
- c2PermCompleted := filterEventsByType(client2Events, copilot.PermissionCompleted)
+ c2PermCompleted := filterEventsByType(client2Events, copilot.SessionEventTypePermissionCompleted)
mu2.Unlock()
if len(c1PermCompleted) == 0 {
@@ -293,10 +293,10 @@ func TestMultiClient(t *testing.T) {
// Both clients should have seen permission.requested events
mu1.Lock()
- c1PermRequested := filterEventsByType(client1Events, copilot.PermissionRequested)
+ c1PermRequested := filterEventsByType(client1Events, copilot.SessionEventTypePermissionRequested)
mu1.Unlock()
mu2.Lock()
- c2PermRequested := filterEventsByType(client2Events, copilot.PermissionRequested)
+ c2PermRequested := filterEventsByType(client2Events, copilot.SessionEventTypePermissionRequested)
mu2.Unlock()
if len(c1PermRequested) == 0 {
@@ -308,10 +308,10 @@ func TestMultiClient(t *testing.T) {
// Both clients should see the denial in the completed event
mu1.Lock()
- c1PermCompleted := filterEventsByType(client1Events, copilot.PermissionCompleted)
+ c1PermCompleted := filterEventsByType(client1Events, copilot.SessionEventTypePermissionCompleted)
mu1.Unlock()
mu2.Lock()
- c2PermCompleted := filterEventsByType(client2Events, copilot.PermissionCompleted)
+ c2PermCompleted := filterEventsByType(client2Events, copilot.SessionEventTypePermissionCompleted)
mu2.Unlock()
if len(c1PermCompleted) == 0 {
diff --git a/go/internal/e2e/permissions_test.go b/go/internal/e2e/permissions_test.go
index 328e7e788..98f620043 100644
--- a/go/internal/e2e/permissions_test.go
+++ b/go/internal/e2e/permissions_test.go
@@ -173,7 +173,7 @@ func TestPermissions(t *testing.T) {
permissionDenied := false
session.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.ToolExecutionComplete &&
+ if event.Type == copilot.SessionEventTypeToolExecutionComplete &&
event.Data.Success != nil && !*event.Data.Success &&
event.Data.Error != nil && event.Data.Error.ErrorClass != nil &&
strings.Contains(event.Data.Error.ErrorClass.Message, "Permission denied") {
@@ -223,7 +223,7 @@ func TestPermissions(t *testing.T) {
permissionDenied := false
session2.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.ToolExecutionComplete &&
+ if event.Type == copilot.SessionEventTypeToolExecutionComplete &&
event.Data.Success != nil && !*event.Data.Success &&
event.Data.Error != nil && event.Data.Error.ErrorClass != nil &&
strings.Contains(event.Data.Error.ErrorClass.Message, "Permission denied") {
diff --git a/go/internal/e2e/rpc_test.go b/go/internal/e2e/rpc_test.go
index ebcbe1130..3d69b97ad 100644
--- a/go/internal/e2e/rpc_test.go
+++ b/go/internal/e2e/rpc_test.go
@@ -219,16 +219,16 @@ func TestSessionRpc(t *testing.T) {
if err != nil {
t.Fatalf("Failed to get mode: %v", err)
}
- if initial.Mode != rpc.Interactive {
+ if initial.Mode != rpc.ModeInteractive {
t.Errorf("Expected initial mode 'interactive', got %q", initial.Mode)
}
// Switch to plan mode
- planResult, err := session.RPC.Mode.Set(t.Context(), &rpc.SessionModeSetParams{Mode: rpc.Plan})
+ planResult, err := session.RPC.Mode.Set(t.Context(), &rpc.SessionModeSetParams{Mode: rpc.ModePlan})
if err != nil {
t.Fatalf("Failed to set mode to plan: %v", err)
}
- if planResult.Mode != rpc.Plan {
+ if planResult.Mode != rpc.ModePlan {
t.Errorf("Expected mode 'plan', got %q", planResult.Mode)
}
@@ -237,16 +237,16 @@ func TestSessionRpc(t *testing.T) {
if err != nil {
t.Fatalf("Failed to get mode after plan: %v", err)
}
- if afterPlan.Mode != rpc.Plan {
+ if afterPlan.Mode != rpc.ModePlan {
t.Errorf("Expected mode 'plan' after set, got %q", afterPlan.Mode)
}
// Switch back to interactive
- interactiveResult, err := session.RPC.Mode.Set(t.Context(), &rpc.SessionModeSetParams{Mode: rpc.Interactive})
+ interactiveResult, err := session.RPC.Mode.Set(t.Context(), &rpc.SessionModeSetParams{Mode: rpc.ModeInteractive})
if err != nil {
t.Fatalf("Failed to set mode to interactive: %v", err)
}
- if interactiveResult.Mode != rpc.Interactive {
+ if interactiveResult.Mode != rpc.ModeInteractive {
t.Errorf("Expected mode 'interactive', got %q", interactiveResult.Mode)
}
})
diff --git a/go/internal/e2e/session_test.go b/go/internal/e2e/session_test.go
index c3c9cc009..f6542e9f7 100644
--- a/go/internal/e2e/session_test.go
+++ b/go/internal/e2e/session_test.go
@@ -506,7 +506,7 @@ func TestSession(t *testing.T) {
toolStartCh := make(chan *copilot.SessionEvent, 1)
toolStartErrCh := make(chan error, 1)
go func() {
- evt, err := testharness.GetNextEventOfType(session, copilot.ToolExecutionStart, 60*time.Second)
+ evt, err := testharness.GetNextEventOfType(session, copilot.SessionEventTypeToolExecutionStart, 60*time.Second)
if err != nil {
toolStartErrCh <- err
} else {
@@ -517,7 +517,7 @@ func TestSession(t *testing.T) {
sessionIdleCh := make(chan *copilot.SessionEvent, 1)
sessionIdleErrCh := make(chan error, 1)
go func() {
- evt, err := testharness.GetNextEventOfType(session, copilot.SessionIdle, 60*time.Second)
+ evt, err := testharness.GetNextEventOfType(session, copilot.SessionEventTypeSessionIdle, 60*time.Second)
if err != nil {
sessionIdleErrCh <- err
} else {
@@ -565,7 +565,7 @@ func TestSession(t *testing.T) {
// Verify messages contain an abort event
hasAbortEvent := false
for _, msg := range messages {
- if msg.Type == copilot.Abort {
+ if msg.Type == copilot.SessionEventTypeAbort {
hasAbortEvent = true
break
}
@@ -913,7 +913,7 @@ func TestSetModelWithReasoningEffort(t *testing.T) {
modelChanged := make(chan copilot.SessionEvent, 1)
session.On(func(event copilot.SessionEvent) {
- if event.Type == copilot.SessionModelChange {
+ if event.Type == copilot.SessionEventTypeSessionModelChange {
select {
case modelChanged <- event:
default:
@@ -986,7 +986,7 @@ func TestSessionLog(t *testing.T) {
t.Fatalf("Log failed: %v", err)
}
- evt := waitForEvent(t, &mu, &events, copilot.SessionInfo, "Info message", 5*time.Second)
+ evt := waitForEvent(t, &mu, &events, copilot.SessionEventTypeSessionInfo, "Info message", 5*time.Second)
if evt.Data.InfoType == nil || *evt.Data.InfoType != "notification" {
t.Errorf("Expected infoType 'notification', got %v", evt.Data.InfoType)
}
@@ -996,11 +996,11 @@ func TestSessionLog(t *testing.T) {
})
t.Run("should log warning message", func(t *testing.T) {
- if err := session.Log(t.Context(), "Warning message", &copilot.LogOptions{Level: rpc.Warning}); err != nil {
+ if err := session.Log(t.Context(), "Warning message", &copilot.LogOptions{Level: rpc.LevelWarning}); err != nil {
t.Fatalf("Log failed: %v", err)
}
- evt := waitForEvent(t, &mu, &events, copilot.SessionWarning, "Warning message", 5*time.Second)
+ evt := waitForEvent(t, &mu, &events, copilot.SessionEventTypeSessionWarning, "Warning message", 5*time.Second)
if evt.Data.WarningType == nil || *evt.Data.WarningType != "notification" {
t.Errorf("Expected warningType 'notification', got %v", evt.Data.WarningType)
}
@@ -1010,11 +1010,11 @@ func TestSessionLog(t *testing.T) {
})
t.Run("should log error message", func(t *testing.T) {
- if err := session.Log(t.Context(), "Error message", &copilot.LogOptions{Level: rpc.Error}); err != nil {
+ if err := session.Log(t.Context(), "Error message", &copilot.LogOptions{Level: rpc.LevelError}); err != nil {
t.Fatalf("Log failed: %v", err)
}
- evt := waitForEvent(t, &mu, &events, copilot.SessionError, "Error message", 5*time.Second)
+ evt := waitForEvent(t, &mu, &events, copilot.SessionEventTypeSessionError, "Error message", 5*time.Second)
if evt.Data.ErrorType == nil || *evt.Data.ErrorType != "notification" {
t.Errorf("Expected errorType 'notification', got %v", evt.Data.ErrorType)
}
@@ -1028,7 +1028,7 @@ func TestSessionLog(t *testing.T) {
t.Fatalf("Log failed: %v", err)
}
- evt := waitForEvent(t, &mu, &events, copilot.SessionInfo, "Ephemeral message", 5*time.Second)
+ evt := waitForEvent(t, &mu, &events, copilot.SessionEventTypeSessionInfo, "Ephemeral message", 5*time.Second)
if evt.Data.InfoType == nil || *evt.Data.InfoType != "notification" {
t.Errorf("Expected infoType 'notification', got %v", evt.Data.InfoType)
}
diff --git a/go/internal/e2e/testharness/helper.go b/go/internal/e2e/testharness/helper.go
index 05947c806..3b521f330 100644
--- a/go/internal/e2e/testharness/helper.go
+++ b/go/internal/e2e/testharness/helper.go
@@ -67,7 +67,7 @@ func GetNextEventOfType(session *copilot.Session, eventType copilot.SessionEvent
case result <- &event:
default:
}
- case copilot.SessionError:
+ case copilot.SessionEventTypeSessionError:
msg := "session error"
if event.Data.Message != nil {
msg = *event.Data.Message
diff --git a/go/rpc/generated_rpc.go b/go/rpc/generated_rpc.go
index 7929994cf..b6d171bfa 100644
--- a/go/rpc/generated_rpc.go
+++ b/go/rpc/generated_rpc.go
@@ -506,48 +506,48 @@ type SessionShellKillParams struct {
type Mode string
const (
- Autopilot Mode = "autopilot"
- Interactive Mode = "interactive"
- Plan Mode = "plan"
+ ModeAutopilot Mode = "autopilot"
+ ModeInteractive Mode = "interactive"
+ ModePlan Mode = "plan"
)
// Connection status: connected, failed, pending, disabled, or not_configured
type ServerStatus string
const (
- Connected ServerStatus = "connected"
- NotConfigured ServerStatus = "not_configured"
- Pending ServerStatus = "pending"
- PurpleDisabled ServerStatus = "disabled"
- PurpleFailed ServerStatus = "failed"
+ ServerStatusConnected ServerStatus = "connected"
+ ServerStatusNotConfigured ServerStatus = "not_configured"
+ ServerStatusPending ServerStatus = "pending"
+ ServerStatusDisabled ServerStatus = "disabled"
+ ServerStatusFailed ServerStatus = "failed"
)
// Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)
type Source string
const (
- Project Source = "project"
- User Source = "user"
+ SourceProject Source = "project"
+ SourceUser Source = "user"
)
// Current status: running, disabled, failed, or starting
type ExtensionStatus string
const (
- FluffyDisabled ExtensionStatus = "disabled"
- FluffyFailed ExtensionStatus = "failed"
- Running ExtensionStatus = "running"
- Starting ExtensionStatus = "starting"
+ ExtensionStatusDisabled ExtensionStatus = "disabled"
+ ExtensionStatusFailed ExtensionStatus = "failed"
+ ExtensionStatusRunning ExtensionStatus = "running"
+ ExtensionStatusStarting ExtensionStatus = "starting"
)
type Kind string
const (
- Approved Kind = "approved"
- DeniedByContentExclusionPolicy Kind = "denied-by-content-exclusion-policy"
- DeniedByRules Kind = "denied-by-rules"
- DeniedInteractivelyByUser Kind = "denied-interactively-by-user"
- DeniedNoApprovalRuleAndCouldNotRequestFromUser Kind = "denied-no-approval-rule-and-could-not-request-from-user"
+ KindApproved Kind = "approved"
+ KindDeniedByContentExclusionPolicy Kind = "denied-by-content-exclusion-policy"
+ KindDeniedByRules Kind = "denied-by-rules"
+ KindDeniedInteractivelyByUser Kind = "denied-interactively-by-user"
+ KindDeniedNoApprovalRuleAndCouldNotRequestFromUser Kind = "denied-no-approval-rule-and-could-not-request-from-user"
)
// Log severity level. Determines how the message is displayed in the timeline. Defaults to
@@ -555,18 +555,18 @@ const (
type Level string
const (
- Error Level = "error"
- Info Level = "info"
- Warning Level = "warning"
+ LevelError Level = "error"
+ LevelInfo Level = "info"
+ LevelWarning Level = "warning"
)
// Signal to send (default: SIGTERM)
type Signal string
const (
- Sigint Signal = "SIGINT"
- Sigkill Signal = "SIGKILL"
- Sigterm Signal = "SIGTERM"
+ SignalSIGINT Signal = "SIGINT"
+ SignalSIGKILL Signal = "SIGKILL"
+ SignalSIGTERM Signal = "SIGTERM"
)
type ResultUnion struct {
@@ -977,20 +977,20 @@ type McpRpcApi struct {
sessionID string
}
-func (a *McpRpcApi) List(ctx context.Context) (*SessionMcpListResult, error) {
+func (a *McpRpcApi) List(ctx context.Context) (*SessionMCPListResult, error) {
req := map[string]interface{}{"sessionId": a.sessionID}
raw, err := a.client.Request("session.mcp.list", req)
if err != nil {
return nil, err
}
- var result SessionMcpListResult
+ var result SessionMCPListResult
if err := json.Unmarshal(raw, &result); err != nil {
return nil, err
}
return &result, nil
}
-func (a *McpRpcApi) Enable(ctx context.Context, params *SessionMcpEnableParams) (*SessionMcpEnableResult, error) {
+func (a *McpRpcApi) Enable(ctx context.Context, params *SessionMCPEnableParams) (*SessionMCPEnableResult, error) {
req := map[string]interface{}{"sessionId": a.sessionID}
if params != nil {
req["serverName"] = params.ServerName
@@ -999,14 +999,14 @@ func (a *McpRpcApi) Enable(ctx context.Context, params *SessionMcpEnableParams)
if err != nil {
return nil, err
}
- var result SessionMcpEnableResult
+ var result SessionMCPEnableResult
if err := json.Unmarshal(raw, &result); err != nil {
return nil, err
}
return &result, nil
}
-func (a *McpRpcApi) Disable(ctx context.Context, params *SessionMcpDisableParams) (*SessionMcpDisableResult, error) {
+func (a *McpRpcApi) Disable(ctx context.Context, params *SessionMCPDisableParams) (*SessionMCPDisableResult, error) {
req := map[string]interface{}{"sessionId": a.sessionID}
if params != nil {
req["serverName"] = params.ServerName
@@ -1015,20 +1015,20 @@ func (a *McpRpcApi) Disable(ctx context.Context, params *SessionMcpDisableParams
if err != nil {
return nil, err
}
- var result SessionMcpDisableResult
+ var result SessionMCPDisableResult
if err := json.Unmarshal(raw, &result); err != nil {
return nil, err
}
return &result, nil
}
-func (a *McpRpcApi) Reload(ctx context.Context) (*SessionMcpReloadResult, error) {
+func (a *McpRpcApi) Reload(ctx context.Context) (*SessionMCPReloadResult, error) {
req := map[string]interface{}{"sessionId": a.sessionID}
raw, err := a.client.Request("session.mcp.reload", req)
if err != nil {
return nil, err
}
- var result SessionMcpReloadResult
+ var result SessionMCPReloadResult
if err := json.Unmarshal(raw, &result); err != nil {
return nil, err
}
diff --git a/go/samples/chat.go b/go/samples/chat.go
index f984f758a..4d5e98d7d 100644
--- a/go/samples/chat.go
+++ b/go/samples/chat.go
@@ -35,11 +35,11 @@ func main() {
session.On(func(event copilot.SessionEvent) {
var output string
switch event.Type {
- case copilot.AssistantReasoning:
+ case copilot.SessionEventTypeAssistantReasoning:
if event.Data.Content != nil {
output = fmt.Sprintf("[reasoning: %s]", *event.Data.Content)
}
- case copilot.ToolExecutionStart:
+ case copilot.SessionEventTypeToolExecutionStart:
if event.Data.ToolName != nil {
output = fmt.Sprintf("[tool: %s]", *event.Data.ToolName)
}
diff --git a/go/session.go b/go/session.go
index d2a5785be..107ac9824 100644
--- a/go/session.go
+++ b/go/session.go
@@ -182,17 +182,17 @@ func (s *Session) SendAndWait(ctx context.Context, options MessageOptions) (*Ses
unsubscribe := s.On(func(event SessionEvent) {
switch event.Type {
- case AssistantMessage:
+ case SessionEventTypeAssistantMessage:
mu.Lock()
eventCopy := event
lastAssistantMessage = &eventCopy
mu.Unlock()
- case SessionIdle:
+ case SessionEventTypeSessionIdle:
select {
case idleCh <- struct{}{}:
default:
}
- case SessionError:
+ case SessionEventTypeSessionError:
errMsg := "session error"
if event.Data.Message != nil {
errMsg = *event.Data.Message
@@ -501,7 +501,7 @@ func (s *Session) processEvents() {
// cause RPC deadlocks.
func (s *Session) handleBroadcastEvent(event SessionEvent) {
switch event.Type {
- case ExternalToolRequested:
+ case SessionEventTypeExternalToolRequested:
requestID := event.Data.RequestID
toolName := event.Data.ToolName
if requestID == nil || toolName == nil {
@@ -524,7 +524,7 @@ func (s *Session) handleBroadcastEvent(event SessionEvent) {
}
s.executeToolAndRespond(*requestID, *toolName, toolCallID, event.Data.Arguments, handler, tp, ts)
- case PermissionRequested:
+ case SessionEventTypePermissionRequested:
requestID := event.Data.RequestID
if requestID == nil || event.Data.PermissionRequest == nil {
return
@@ -585,7 +585,7 @@ func (s *Session) executePermissionAndRespond(requestID string, permissionReques
s.RPC.Permissions.HandlePendingPermissionRequest(context.Background(), &rpc.SessionPermissionsHandlePendingPermissionRequestParams{
RequestID: requestID,
Result: rpc.SessionPermissionsHandlePendingPermissionRequestParamsResult{
- Kind: rpc.DeniedNoApprovalRuleAndCouldNotRequestFromUser,
+ Kind: rpc.KindDeniedNoApprovalRuleAndCouldNotRequestFromUser,
},
})
}
@@ -600,7 +600,7 @@ func (s *Session) executePermissionAndRespond(requestID string, permissionReques
s.RPC.Permissions.HandlePendingPermissionRequest(context.Background(), &rpc.SessionPermissionsHandlePendingPermissionRequestParams{
RequestID: requestID,
Result: rpc.SessionPermissionsHandlePendingPermissionRequestParamsResult{
- Kind: rpc.DeniedNoApprovalRuleAndCouldNotRequestFromUser,
+ Kind: rpc.KindDeniedNoApprovalRuleAndCouldNotRequestFromUser,
},
})
return
@@ -770,8 +770,8 @@ func (s *Session) SetModel(ctx context.Context, model string, opts ...SetModelOp
// LogOptions configures optional parameters for [Session.Log].
type LogOptions struct {
- // Level sets the log severity. Valid values are [rpc.Info] (default),
- // [rpc.Warning], and [rpc.Error].
+ // Level sets the log severity. Valid values are [rpc.LevelInfo] (default),
+ // [rpc.LevelWarning], and [rpc.LevelError].
Level rpc.Level
// Ephemeral marks the message as transient so it is not persisted
// to the session event log on disk. When nil the server decides the
@@ -791,7 +791,7 @@ type LogOptions struct {
// session.Log(ctx, "Processing started")
//
// // Warning with options
-// session.Log(ctx, "Rate limit approaching", &copilot.LogOptions{Level: rpc.Warning})
+// session.Log(ctx, "Rate limit approaching", &copilot.LogOptions{Level: rpc.LevelWarning})
//
// // Ephemeral message (not persisted)
// session.Log(ctx, "Working...", &copilot.LogOptions{Ephemeral: copilot.Bool(true)})
diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py
index 1bc431ee6..4cdd3ebc7 100644
--- a/python/copilot/generated/rpc.py
+++ b/python/copilot/generated/rpc.py
@@ -2393,21 +2393,21 @@ def __init__(self, client: "JsonRpcClient", session_id: str):
self._client = client
self._session_id = session_id
- async def list(self, *, timeout: float | None = None) -> SessionMcpListResult:
- return SessionMcpListResult.from_dict(await self._client.request("session.mcp.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)))
+ async def list(self, *, timeout: float | None = None) -> SessionMCPListResult:
+ return SessionMCPListResult.from_dict(await self._client.request("session.mcp.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)))
- async def enable(self, params: SessionMcpEnableParams, *, timeout: float | None = None) -> SessionMcpEnableResult:
+ async def enable(self, params: SessionMCPEnableParams, *, timeout: float | None = None) -> SessionMCPEnableResult:
params_dict = {k: v for k, v in params.to_dict().items() if v is not None}
params_dict["sessionId"] = self._session_id
- return SessionMcpEnableResult.from_dict(await self._client.request("session.mcp.enable", params_dict, **_timeout_kwargs(timeout)))
+ return SessionMCPEnableResult.from_dict(await self._client.request("session.mcp.enable", params_dict, **_timeout_kwargs(timeout)))
- async def disable(self, params: SessionMcpDisableParams, *, timeout: float | None = None) -> SessionMcpDisableResult:
+ async def disable(self, params: SessionMCPDisableParams, *, timeout: float | None = None) -> SessionMCPDisableResult:
params_dict = {k: v for k, v in params.to_dict().items() if v is not None}
params_dict["sessionId"] = self._session_id
- return SessionMcpDisableResult.from_dict(await self._client.request("session.mcp.disable", params_dict, **_timeout_kwargs(timeout)))
+ return SessionMCPDisableResult.from_dict(await self._client.request("session.mcp.disable", params_dict, **_timeout_kwargs(timeout)))
- async def reload(self, *, timeout: float | None = None) -> SessionMcpReloadResult:
- return SessionMcpReloadResult.from_dict(await self._client.request("session.mcp.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)))
+ async def reload(self, *, timeout: float | None = None) -> SessionMCPReloadResult:
+ return SessionMCPReloadResult.from_dict(await self._client.request("session.mcp.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)))
class PluginsApi:
diff --git a/python/e2e/test_agent_and_compact_rpc.py b/python/e2e/test_agent_and_compact_rpc.py
index ec5958676..0f585c81b 100644
--- a/python/e2e/test_agent_and_compact_rpc.py
+++ b/python/e2e/test_agent_and_compact_rpc.py
@@ -155,7 +155,7 @@ async def test_should_deselect_current_agent(self):
@pytest.mark.asyncio
async def test_should_return_empty_list_when_no_custom_agents_configured(self):
- """Test listing agents returns empty when none configured."""
+ """Test listing agents returns no custom agents when none configured."""
client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True))
try:
@@ -165,7 +165,13 @@ async def test_should_return_empty_list_when_no_custom_agents_configured(self):
)
result = await session.rpc.agent.list()
- assert result.agents == []
+ # The CLI may return built-in/default agents even when no custom agents
+ # are configured. Verify no custom test agents appear in the list.
+ custom_names = {"test-agent", "another-agent"}
+ for agent in result.agents:
+ assert agent.name not in custom_names, (
+ f"Expected no custom agents, but found {agent.name!r}"
+ )
await session.disconnect()
await client.stop()
diff --git a/python/e2e/test_multi_client.py b/python/e2e/test_multi_client.py
index cb5d90cd2..4a6cbc27b 100644
--- a/python/e2e/test_multi_client.py
+++ b/python/e2e/test_multi_client.py
@@ -190,6 +190,7 @@ async def test_both_clients_see_tool_request_and_completion_events(
):
"""Both clients see tool request and completion events."""
+
class SeedParams(BaseModel):
seed: str = Field(description="A seed value")
diff --git a/scripts/codegen/go.ts b/scripts/codegen/go.ts
index 1ebc50797..21c329eb1 100644
--- a/scripts/codegen/go.ts
+++ b/scripts/codegen/go.ts
@@ -44,6 +44,77 @@ function toGoFieldName(jsonName: string): string {
.join("");
}
+/**
+ * Post-process Go enum constants so every constant follows the canonical
+ * Go `TypeNameValue` convention. quicktype disambiguates collisions with
+ * whimsical prefixes (Purple, Fluffy, …) that we replace.
+ */
+function postProcessEnumConstants(code: string): string {
+ const renames = new Map();
+
+ // Match constant declarations inside const ( … ) blocks.
+ const constLineRe = /^\s+(\w+)\s+(\w+)\s*=\s*"([^"]+)"/gm;
+ let m;
+ while ((m = constLineRe.exec(code)) !== null) {
+ const [, constName, typeName, value] = m;
+ if (constName.startsWith(typeName)) continue;
+
+ // Use the same initialism logic as toPascalCase so "url" → "URL", "mcp" → "MCP", etc.
+ const valuePascal = value
+ .split(/[._-]/)
+ .map((w) => goInitialisms.has(w.toLowerCase()) ? w.toUpperCase() : w.charAt(0).toUpperCase() + w.slice(1))
+ .join("");
+ const desired = typeName + valuePascal;
+ if (constName !== desired) {
+ renames.set(constName, desired);
+ }
+ }
+
+ // Replace each const block in place, then fix switch-case references
+ // in marshal/unmarshal functions. This avoids renaming struct fields.
+
+ // Phase 1: Rename inside const ( … ) blocks
+ code = code.replace(/^(const \([\s\S]*?\n\))/gm, (block) => {
+ let b = block;
+ for (const [oldName, newName] of renames) {
+ b = b.replace(new RegExp(`\\b${oldName}\\b`, "g"), newName);
+ }
+ return b;
+ });
+
+ // Phase 2: Rename inside func bodies (marshal/unmarshal helpers use case statements)
+ code = code.replace(/^(func \([\s\S]*?\n\})/gm, (funcBlock) => {
+ let b = funcBlock;
+ for (const [oldName, newName] of renames) {
+ b = b.replace(new RegExp(`\\b${oldName}\\b`, "g"), newName);
+ }
+ return b;
+ });
+
+ return code;
+}
+
+/**
+ * Extract a mapping from (structName, jsonFieldName) → goFieldName
+ * so the wrapper code references the actual quicktype-generated field names.
+ */
+function extractFieldNames(qtCode: string): Map> {
+ const result = new Map>();
+ const structRe = /^type\s+(\w+)\s+struct\s*\{([^}]*)\}/gm;
+ let sm;
+ while ((sm = structRe.exec(qtCode)) !== null) {
+ const [, structName, body] = sm;
+ const fields = new Map();
+ const fieldRe = /^\s+(\w+)\s+[^`\n]+`json:"([^",]+)/gm;
+ let fm;
+ while ((fm = fieldRe.exec(body)) !== null) {
+ fields.set(fm[2], fm[1]);
+ }
+ result.set(structName, fields);
+ }
+ return result;
+}
+
async function formatGoFile(filePath: string): Promise {
try {
await execFileAsync("go", ["fmt", filePath]);
@@ -92,7 +163,7 @@ async function generateSessionEvents(schemaPath?: string): Promise {
`;
- const outPath = await writeGeneratedFile("go/generated_session_events.go", banner + result.lines.join("\n"));
+ const outPath = await writeGeneratedFile("go/generated_session_events.go", banner + postProcessEnumConstants(result.lines.join("\n")));
console.log(` ✓ ${outPath}`);
await formatGoFile(outPath);
@@ -153,6 +224,22 @@ async function generateRpc(schemaPath?: string): Promise {
rendererOptions: { package: "copilot", "just-types": "true" },
});
+ // Post-process quicktype output: fix enum constant names
+ let qtCode = qtResult.lines.filter((l) => !l.startsWith("package ")).join("\n");
+ qtCode = postProcessEnumConstants(qtCode);
+
+ // Extract actual type names generated by quicktype (may differ from toPascalCase)
+ const actualTypeNames = new Map();
+ const structRe = /^type\s+(\w+)\s+struct\b/gm;
+ let sm;
+ while ((sm = structRe.exec(qtCode)) !== null) {
+ actualTypeNames.set(sm[1].toLowerCase(), sm[1]);
+ }
+ const resolveType = (name: string): string => actualTypeNames.get(name.toLowerCase()) ?? name;
+
+ // Extract field name mappings (quicktype may rename fields to avoid Go keyword conflicts)
+ const fieldNames = extractFieldNames(qtCode);
+
// Build method wrappers
const lines: string[] = [];
lines.push(`// AUTO-GENERATED FILE - DO NOT EDIT`);
@@ -168,19 +255,18 @@ async function generateRpc(schemaPath?: string): Promise {
lines.push(`)`);
lines.push(``);
- // Add quicktype-generated types (skip package line)
- const qtLines = qtResult.lines.filter((l) => !l.startsWith("package "));
- lines.push(...qtLines);
+ // Add quicktype-generated types
+ lines.push(qtCode);
lines.push(``);
// Emit ServerRpc
if (schema.server) {
- emitRpcWrapper(lines, schema.server, false);
+ emitRpcWrapper(lines, schema.server, false, resolveType, fieldNames);
}
// Emit SessionRpc
if (schema.session) {
- emitRpcWrapper(lines, schema.session, true);
+ emitRpcWrapper(lines, schema.session, true, resolveType, fieldNames);
}
const outPath = await writeGeneratedFile("go/rpc/generated_rpc.go", lines.join("\n"));
@@ -189,7 +275,7 @@ async function generateRpc(schemaPath?: string): Promise {
await formatGoFile(outPath);
}
-function emitRpcWrapper(lines: string[], node: Record, isSession: boolean): void {
+function emitRpcWrapper(lines: string[], node: Record, isSession: boolean, resolveType: (name: string) => string, fieldNames: Map>): void {
const groups = Object.entries(node).filter(([, v]) => typeof v === "object" && v !== null && !isRpcMethod(v));
const topLevelMethods = Object.entries(node).filter(([, v]) => isRpcMethod(v));
@@ -205,7 +291,7 @@ function emitRpcWrapper(lines: string[], node: Record, isSessio
lines.push(``);
for (const [key, value] of Object.entries(groupNode as Record)) {
if (!isRpcMethod(value)) continue;
- emitMethod(lines, apiName, key, value, isSession);
+ emitMethod(lines, apiName, key, value, isSession, resolveType, fieldNames);
}
}
@@ -224,7 +310,7 @@ function emitRpcWrapper(lines: string[], node: Record, isSessio
// Top-level methods (server only)
for (const [key, value] of topLevelMethods) {
if (!isRpcMethod(value)) continue;
- emitMethod(lines, wrapperName, key, value, isSession);
+ emitMethod(lines, wrapperName, key, value, isSession, resolveType, fieldNames);
}
// Constructor
@@ -244,15 +330,15 @@ function emitRpcWrapper(lines: string[], node: Record, isSessio
lines.push(``);
}
-function emitMethod(lines: string[], receiver: string, name: string, method: RpcMethod, isSession: boolean): void {
+function emitMethod(lines: string[], receiver: string, name: string, method: RpcMethod, isSession: boolean, resolveType: (name: string) => string, fieldNames: Map>): void {
const methodName = toPascalCase(name);
- const resultType = toPascalCase(method.rpcMethod) + "Result";
+ const resultType = resolveType(toPascalCase(method.rpcMethod) + "Result");
const paramProps = method.params?.properties || {};
const requiredParams = new Set(method.params?.required || []);
const nonSessionParams = Object.keys(paramProps).filter((k) => k !== "sessionId");
const hasParams = isSession ? nonSessionParams.length > 0 : Object.keys(paramProps).length > 0;
- const paramsType = hasParams ? toPascalCase(method.rpcMethod) + "Params" : "";
+ const paramsType = hasParams ? resolveType(toPascalCase(method.rpcMethod) + "Params") : "";
const sig = hasParams
? `func (a *${receiver}) ${methodName}(ctx context.Context, params *${paramsType}) (*${resultType}, error)`
@@ -265,7 +351,7 @@ function emitMethod(lines: string[], receiver: string, name: string, method: Rpc
if (hasParams) {
lines.push(` if params != nil {`);
for (const pName of nonSessionParams) {
- const goField = toGoFieldName(pName);
+ const goField = fieldNames.get(paramsType)?.get(pName) ?? toGoFieldName(pName);
const isOptional = !requiredParams.has(pName);
if (isOptional) {
// Optional fields are pointers - only add when non-nil and dereference
diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts
index 65563d741..6e2dd5dba 100644
--- a/scripts/codegen/python.ts
+++ b/scripts/codegen/python.ts
@@ -215,6 +215,16 @@ async function generateRpc(schemaPath?: string): Promise {
// Modernize to Python 3.11+ syntax
typesCode = modernizePython(typesCode);
+ // Extract actual class names generated by quicktype (may differ from toPascalCase,
+ // e.g. quicktype produces "SessionMCPList" not "SessionMcpList")
+ const actualTypeNames = new Map();
+ const classRe = /^class\s+(\w+)\b/gm;
+ let cm;
+ while ((cm = classRe.exec(typesCode)) !== null) {
+ actualTypeNames.set(cm[1].toLowerCase(), cm[1]);
+ }
+ const resolveType = (name: string): string => actualTypeNames.get(name.toLowerCase()) ?? name;
+
const lines: string[] = [];
lines.push(`"""
AUTO-GENERATED FILE - DO NOT EDIT
@@ -239,17 +249,17 @@ def _timeout_kwargs(timeout: float | None) -> dict:
// Emit RPC wrapper classes
if (schema.server) {
- emitRpcWrapper(lines, schema.server, false);
+ emitRpcWrapper(lines, schema.server, false, resolveType);
}
if (schema.session) {
- emitRpcWrapper(lines, schema.session, true);
+ emitRpcWrapper(lines, schema.session, true, resolveType);
}
const outPath = await writeGeneratedFile("python/copilot/generated/rpc.py", lines.join("\n"));
console.log(` ✓ ${outPath}`);
}
-function emitRpcWrapper(lines: string[], node: Record, isSession: boolean): void {
+function emitRpcWrapper(lines: string[], node: Record, isSession: boolean, resolveType: (name: string) => string): void {
const groups = Object.entries(node).filter(([, v]) => typeof v === "object" && v !== null && !isRpcMethod(v));
const topLevelMethods = Object.entries(node).filter(([, v]) => isRpcMethod(v));
@@ -272,7 +282,7 @@ function emitRpcWrapper(lines: string[], node: Record, isSessio
lines.push(``);
for (const [key, value] of Object.entries(groupNode as Record)) {
if (!isRpcMethod(value)) continue;
- emitMethod(lines, key, value, isSession);
+ emitMethod(lines, key, value, isSession, resolveType);
}
lines.push(``);
}
@@ -301,19 +311,19 @@ function emitRpcWrapper(lines: string[], node: Record, isSessio
// Top-level methods
for (const [key, value] of topLevelMethods) {
if (!isRpcMethod(value)) continue;
- emitMethod(lines, key, value, isSession);
+ emitMethod(lines, key, value, isSession, resolveType);
}
lines.push(``);
}
-function emitMethod(lines: string[], name: string, method: RpcMethod, isSession: boolean): void {
+function emitMethod(lines: string[], name: string, method: RpcMethod, isSession: boolean, resolveType: (name: string) => string): void {
const methodName = toSnakeCase(name);
- const resultType = toPascalCase(method.rpcMethod) + "Result";
+ const resultType = resolveType(toPascalCase(method.rpcMethod) + "Result");
const paramProps = method.params?.properties || {};
const nonSessionParams = Object.keys(paramProps).filter((k) => k !== "sessionId");
const hasParams = isSession ? nonSessionParams.length > 0 : Object.keys(paramProps).length > 0;
- const paramsType = toPascalCase(method.rpcMethod) + "Params";
+ const paramsType = resolveType(toPascalCase(method.rpcMethod) + "Params");
// Build signature with typed params + optional timeout
const sig = hasParams