mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-04 02:48:44 +02:00
refactor: decouple turn execution state from Copilot SDK events
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
namespace Aryx.AgentHost.Contracts;
|
||||
|
||||
internal abstract record ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderAssistantMessageDeltaEvent(string MessageId) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderAssistantMessageEvent(
|
||||
string MessageId,
|
||||
bool HasToolRequests) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderToolExecutionStartEvent(
|
||||
string ToolCallId,
|
||||
string ToolName,
|
||||
IReadOnlyDictionary<string, object?>? ToolArguments) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderAssistantIntentEvent(string? Intent) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderAssistantReasoningDeltaEvent(
|
||||
string? ReasoningId,
|
||||
string? DeltaContent) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSubagentStartedEvent(
|
||||
string? ToolCallId,
|
||||
string? AgentName,
|
||||
string? AgentDisplayName,
|
||||
string? AgentDescription) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSubagentCompletedEvent(
|
||||
string? ToolCallId,
|
||||
string? AgentName,
|
||||
string? AgentDisplayName) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSubagentFailedEvent(
|
||||
string? ToolCallId,
|
||||
string? AgentName,
|
||||
string? AgentDisplayName,
|
||||
string? Error) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSubagentSelectedEvent(
|
||||
string? AgentName,
|
||||
string? AgentDisplayName,
|
||||
IReadOnlyList<string>? Tools) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSubagentDeselectedEvent() : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSkillInvokedEvent(
|
||||
string SkillName,
|
||||
string Path,
|
||||
string Content,
|
||||
IReadOnlyList<string>? AllowedTools,
|
||||
string? PluginName,
|
||||
string? PluginVersion) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderHookStartEvent(
|
||||
string HookInvocationId,
|
||||
string HookType,
|
||||
object? Input) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderHookEndEvent(
|
||||
string HookInvocationId,
|
||||
string HookType,
|
||||
bool? Success,
|
||||
object? Output,
|
||||
string? Error) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderAssistantUsageEvent(
|
||||
string Model,
|
||||
double? InputTokens,
|
||||
double? OutputTokens,
|
||||
double? CacheReadTokens,
|
||||
double? CacheWriteTokens,
|
||||
double? Cost,
|
||||
double? Duration,
|
||||
double? TotalNanoAiu,
|
||||
Dictionary<string, QuotaSnapshotDto>? QuotaSnapshots) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSessionUsageEvent(
|
||||
double TokenLimit,
|
||||
double CurrentTokens,
|
||||
double MessagesLength,
|
||||
double? SystemTokens,
|
||||
double? ConversationTokens,
|
||||
double? ToolDefinitionsTokens,
|
||||
bool? IsInitial) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSessionCompactionStartEvent(
|
||||
double? SystemTokens,
|
||||
double? ConversationTokens,
|
||||
double? ToolDefinitionsTokens) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderSessionCompactionCompleteEvent(
|
||||
bool? Success,
|
||||
string? Error,
|
||||
double? SystemTokens,
|
||||
double? ConversationTokens,
|
||||
double? ToolDefinitionsTokens,
|
||||
double? PreCompactionTokens,
|
||||
double? PostCompactionTokens,
|
||||
double? PreCompactionMessagesLength,
|
||||
double? MessagesRemoved,
|
||||
double? TokensRemoved,
|
||||
string? SummaryContent,
|
||||
double? CheckpointNumber,
|
||||
string? CheckpointPath) : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderPendingMessagesModifiedEvent() : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderMcpOauthRequiredEvent() : ProviderSessionEvent;
|
||||
|
||||
internal sealed record ProviderExitPlanModeRequestedEvent() : ProviderSessionEvent;
|
||||
@@ -0,0 +1,157 @@
|
||||
using Aryx.AgentHost.Contracts;
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
namespace Aryx.AgentHost.Services;
|
||||
|
||||
internal sealed class CopilotEventAdapter : IProviderEventAdapter
|
||||
{
|
||||
public ProviderSessionEvent? TryAdapt(object rawEvent)
|
||||
{
|
||||
return rawEvent switch
|
||||
{
|
||||
AssistantMessageDeltaEvent messageDelta
|
||||
when NormalizeRequiredString(messageDelta.Data?.MessageId) is { } messageId =>
|
||||
new ProviderAssistantMessageDeltaEvent(messageId),
|
||||
|
||||
AssistantMessageEvent assistantMessage
|
||||
when NormalizeRequiredString(assistantMessage.Data?.MessageId) is { } messageId =>
|
||||
new ProviderAssistantMessageEvent(
|
||||
messageId,
|
||||
assistantMessage.Data?.ToolRequests is { Length: > 0 }),
|
||||
|
||||
ToolExecutionStartEvent toolExecutionStart
|
||||
when NormalizeRequiredString(toolExecutionStart.Data?.ToolCallId) is { } toolCallId
|
||||
&& NormalizeRequiredString(toolExecutionStart.Data?.ToolName) is { } toolName =>
|
||||
new ProviderToolExecutionStartEvent(
|
||||
toolCallId,
|
||||
toolName,
|
||||
WorkflowRequestInfoInterpreter.NormalizeRawToolArguments(toolExecutionStart.Data?.Arguments)),
|
||||
|
||||
AssistantIntentEvent intentEvent =>
|
||||
new ProviderAssistantIntentEvent(NormalizeOptionalString(intentEvent.Data?.Intent)),
|
||||
|
||||
AssistantReasoningDeltaEvent reasoningDelta =>
|
||||
new ProviderAssistantReasoningDeltaEvent(
|
||||
NormalizeOptionalString(reasoningDelta.Data?.ReasoningId),
|
||||
reasoningDelta.Data?.DeltaContent),
|
||||
|
||||
SubagentStartedEvent started =>
|
||||
new ProviderSubagentStartedEvent(
|
||||
started.Data?.ToolCallId,
|
||||
started.Data?.AgentName,
|
||||
started.Data?.AgentDisplayName,
|
||||
started.Data?.AgentDescription),
|
||||
|
||||
SubagentCompletedEvent completed =>
|
||||
new ProviderSubagentCompletedEvent(
|
||||
completed.Data?.ToolCallId,
|
||||
completed.Data?.AgentName,
|
||||
completed.Data?.AgentDisplayName),
|
||||
|
||||
SubagentFailedEvent failed =>
|
||||
new ProviderSubagentFailedEvent(
|
||||
failed.Data?.ToolCallId,
|
||||
failed.Data?.AgentName,
|
||||
failed.Data?.AgentDisplayName,
|
||||
failed.Data?.Error),
|
||||
|
||||
SubagentSelectedEvent selected =>
|
||||
new ProviderSubagentSelectedEvent(
|
||||
selected.Data?.AgentName,
|
||||
selected.Data?.AgentDisplayName,
|
||||
selected.Data?.Tools),
|
||||
|
||||
SubagentDeselectedEvent =>
|
||||
new ProviderSubagentDeselectedEvent(),
|
||||
|
||||
SkillInvokedEvent skillInvoked =>
|
||||
new ProviderSkillInvokedEvent(
|
||||
skillInvoked.Data?.Name ?? string.Empty,
|
||||
skillInvoked.Data?.Path ?? string.Empty,
|
||||
skillInvoked.Data?.Content ?? string.Empty,
|
||||
skillInvoked.Data?.AllowedTools,
|
||||
skillInvoked.Data?.PluginName,
|
||||
skillInvoked.Data?.PluginVersion),
|
||||
|
||||
HookStartEvent hookStart =>
|
||||
new ProviderHookStartEvent(
|
||||
hookStart.Data?.HookInvocationId ?? string.Empty,
|
||||
hookStart.Data?.HookType ?? string.Empty,
|
||||
hookStart.Data?.Input),
|
||||
|
||||
HookEndEvent hookEnd =>
|
||||
new ProviderHookEndEvent(
|
||||
hookEnd.Data?.HookInvocationId ?? string.Empty,
|
||||
hookEnd.Data?.HookType ?? string.Empty,
|
||||
hookEnd.Data?.Success,
|
||||
hookEnd.Data?.Output,
|
||||
hookEnd.Data?.Error?.Message),
|
||||
|
||||
AssistantUsageEvent assistantUsage =>
|
||||
new ProviderAssistantUsageEvent(
|
||||
assistantUsage.Data?.Model ?? string.Empty,
|
||||
assistantUsage.Data?.InputTokens,
|
||||
assistantUsage.Data?.OutputTokens,
|
||||
assistantUsage.Data?.CacheReadTokens,
|
||||
assistantUsage.Data?.CacheWriteTokens,
|
||||
assistantUsage.Data?.Cost,
|
||||
assistantUsage.Data?.Duration,
|
||||
assistantUsage.Data?.CopilotUsage?.TotalNanoAiu,
|
||||
QuotaSnapshotMapper.MapOrNull(assistantUsage.Data?.QuotaSnapshots)),
|
||||
|
||||
SessionUsageInfoEvent usageInfo =>
|
||||
new ProviderSessionUsageEvent(
|
||||
usageInfo.Data?.TokenLimit ?? 0,
|
||||
usageInfo.Data?.CurrentTokens ?? 0,
|
||||
usageInfo.Data?.MessagesLength ?? 0,
|
||||
usageInfo.Data?.SystemTokens,
|
||||
usageInfo.Data?.ConversationTokens,
|
||||
usageInfo.Data?.ToolDefinitionsTokens,
|
||||
usageInfo.Data?.IsInitial),
|
||||
|
||||
SessionCompactionStartEvent compactionStart =>
|
||||
new ProviderSessionCompactionStartEvent(
|
||||
compactionStart.Data?.SystemTokens,
|
||||
compactionStart.Data?.ConversationTokens,
|
||||
compactionStart.Data?.ToolDefinitionsTokens),
|
||||
|
||||
SessionCompactionCompleteEvent compactionComplete =>
|
||||
new ProviderSessionCompactionCompleteEvent(
|
||||
compactionComplete.Data?.Success,
|
||||
compactionComplete.Data?.Error,
|
||||
compactionComplete.Data?.SystemTokens,
|
||||
compactionComplete.Data?.ConversationTokens,
|
||||
compactionComplete.Data?.ToolDefinitionsTokens,
|
||||
compactionComplete.Data?.PreCompactionTokens,
|
||||
compactionComplete.Data?.PostCompactionTokens,
|
||||
compactionComplete.Data?.PreCompactionMessagesLength,
|
||||
compactionComplete.Data?.MessagesRemoved,
|
||||
compactionComplete.Data?.TokensRemoved,
|
||||
compactionComplete.Data?.SummaryContent,
|
||||
compactionComplete.Data?.CheckpointNumber,
|
||||
compactionComplete.Data?.CheckpointPath),
|
||||
|
||||
PendingMessagesModifiedEvent =>
|
||||
new ProviderPendingMessagesModifiedEvent(),
|
||||
|
||||
McpOauthRequiredEvent =>
|
||||
new ProviderMcpOauthRequiredEvent(),
|
||||
|
||||
ExitPlanModeRequestedEvent =>
|
||||
new ProviderExitPlanModeRequestedEvent(),
|
||||
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static string? NormalizeRequiredString(string? value)
|
||||
{
|
||||
string? normalized = NormalizeOptionalString(value);
|
||||
return string.IsNullOrWhiteSpace(normalized) ? null : normalized;
|
||||
}
|
||||
|
||||
private static string? NormalizeOptionalString(string? value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Aryx.AgentHost.Contracts;
|
||||
using GitHub.Copilot.SDK;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Aryx.AgentHost.Services;
|
||||
@@ -70,7 +69,7 @@ internal sealed class CopilotTurnExecutionState
|
||||
}
|
||||
}
|
||||
|
||||
public void ObserveSessionEvent(WorkflowNodeDto agentDefinition, SessionEvent sessionEvent)
|
||||
public void ObserveSessionEvent(WorkflowNodeDto agentDefinition, ProviderSessionEvent sessionEvent)
|
||||
{
|
||||
AgentIdentity agent = AgentIdentityResolver.ResolveAgentIdentity(
|
||||
_command.Workflow,
|
||||
@@ -79,27 +78,25 @@ internal sealed class CopilotTurnExecutionState
|
||||
|
||||
switch (sessionEvent)
|
||||
{
|
||||
case AssistantMessageDeltaEvent messageDelta when !string.IsNullOrWhiteSpace(messageDelta.Data?.MessageId):
|
||||
RecordObservedAgentForMessage(agent, messageDelta.Data!.MessageId);
|
||||
case ProviderAssistantMessageDeltaEvent messageDelta:
|
||||
RecordObservedAgentForMessage(agent, messageDelta.MessageId);
|
||||
QueueThinkingIfNeeded(agent);
|
||||
break;
|
||||
case AssistantMessageEvent assistantMessage when !string.IsNullOrWhiteSpace(assistantMessage.Data?.MessageId):
|
||||
RecordObservedAgentForMessage(agent, assistantMessage.Data!.MessageId);
|
||||
case ProviderAssistantMessageEvent assistantMessage:
|
||||
RecordObservedAgentForMessage(agent, assistantMessage.MessageId);
|
||||
QueueThinkingIfNeeded(agent);
|
||||
if (assistantMessage.Data?.ToolRequests is { Length: > 0 })
|
||||
if (assistantMessage.HasToolRequests)
|
||||
{
|
||||
QueueMessageReclassifiedIfNeeded(assistantMessage.Data.MessageId);
|
||||
QueueMessageReclassifiedIfNeeded(assistantMessage.MessageId);
|
||||
}
|
||||
break;
|
||||
case ToolExecutionStartEvent toolExecutionStart
|
||||
when !string.IsNullOrWhiteSpace(toolExecutionStart.Data?.ToolCallId)
|
||||
&& !string.IsNullOrWhiteSpace(toolExecutionStart.Data?.ToolName):
|
||||
string toolCallId = toolExecutionStart.Data.ToolCallId.Trim();
|
||||
string toolName = toolExecutionStart.Data.ToolName.Trim();
|
||||
case ProviderToolExecutionStartEvent toolExecutionStart:
|
||||
string toolCallId = toolExecutionStart.ToolCallId;
|
||||
string toolName = toolExecutionStart.ToolName;
|
||||
ToolNamesByCallId[toolCallId] = toolName;
|
||||
ActiveAgent = agent;
|
||||
AgentActivityEventDto? toolActivity = CreateToolCallingActivity(
|
||||
agent, toolName, toolCallId, toolExecutionStart.Data.Arguments);
|
||||
agent, toolName, toolCallId, toolExecutionStart.ToolArguments);
|
||||
if (toolActivity is not null)
|
||||
{
|
||||
_pendingEvents.Enqueue(toolActivity);
|
||||
@@ -107,86 +104,101 @@ internal sealed class CopilotTurnExecutionState
|
||||
|
||||
QueueMessageReclassifiedIfNeeded(_lastObservedMessageId);
|
||||
break;
|
||||
case AssistantIntentEvent intentEvent:
|
||||
case ProviderAssistantIntentEvent intentEvent:
|
||||
ActiveAgent = agent;
|
||||
QueueThinkingIfNeeded(agent);
|
||||
AssistantIntentEventDto? assistantIntent = CreateAssistantIntentEvent(agent, intentEvent.Data);
|
||||
AssistantIntentEventDto? assistantIntent = CreateAssistantIntentEvent(agent, intentEvent.Intent);
|
||||
if (assistantIntent is not null)
|
||||
{
|
||||
_pendingEvents.Enqueue(assistantIntent);
|
||||
}
|
||||
break;
|
||||
case AssistantReasoningDeltaEvent reasoningDelta:
|
||||
case ProviderAssistantReasoningDeltaEvent reasoningDelta:
|
||||
ActiveAgent = agent;
|
||||
QueueThinkingIfNeeded(agent);
|
||||
ReasoningDeltaEventDto? reasoningDeltaEvent = CreateReasoningDeltaEvent(agent, reasoningDelta.Data);
|
||||
ReasoningDeltaEventDto? reasoningDeltaEvent = CreateReasoningDeltaEvent(
|
||||
agent,
|
||||
reasoningDelta.ReasoningId,
|
||||
reasoningDelta.DeltaContent);
|
||||
if (reasoningDeltaEvent is not null)
|
||||
{
|
||||
_pendingEvents.Enqueue(reasoningDeltaEvent);
|
||||
}
|
||||
break;
|
||||
case SubagentStartedEvent started:
|
||||
case ProviderSubagentStartedEvent started:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateSubagentEvent(agent, "started", started.Data));
|
||||
_pendingEvents.Enqueue(CreateSubagentStartedEvent(agent, started));
|
||||
break;
|
||||
case SubagentCompletedEvent completed:
|
||||
case ProviderSubagentCompletedEvent completed:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateSubagentCompletedEvent(agent, completed.Data));
|
||||
_pendingEvents.Enqueue(CreateSubagentCompletedEvent(agent, completed));
|
||||
break;
|
||||
case SubagentFailedEvent failed:
|
||||
case ProviderSubagentFailedEvent failed:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateSubagentFailedEvent(agent, failed.Data));
|
||||
_pendingEvents.Enqueue(CreateSubagentFailedEvent(agent, failed));
|
||||
break;
|
||||
case SubagentSelectedEvent selected:
|
||||
case ProviderSubagentSelectedEvent selected:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateSubagentSelectedEvent(agent, selected.Data));
|
||||
_pendingEvents.Enqueue(CreateSubagentSelectedEvent(agent, selected));
|
||||
break;
|
||||
case SubagentDeselectedEvent:
|
||||
case ProviderSubagentDeselectedEvent:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateSubagentDeselectedEvent(agent));
|
||||
break;
|
||||
case SkillInvokedEvent skillInvoked:
|
||||
case ProviderSkillInvokedEvent skillInvoked:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateSkillInvokedEvent(agent, skillInvoked.Data));
|
||||
_pendingEvents.Enqueue(CreateSkillInvokedEvent(agent, skillInvoked));
|
||||
break;
|
||||
case HookStartEvent hookStart:
|
||||
case ProviderHookStartEvent hookStart:
|
||||
ActiveAgent = agent;
|
||||
if (!SuppressHookLifecycleEvents)
|
||||
{
|
||||
_pendingEvents.Enqueue(CreateHookLifecycleEvent(agent, "start", hookStart.Data));
|
||||
_pendingEvents.Enqueue(CreateHookLifecycleEvent(
|
||||
agent,
|
||||
"start",
|
||||
hookStart.HookInvocationId,
|
||||
hookStart.HookType,
|
||||
input: hookStart.Input));
|
||||
}
|
||||
break;
|
||||
case HookEndEvent hookEnd:
|
||||
case ProviderHookEndEvent hookEnd:
|
||||
ActiveAgent = agent;
|
||||
if (!SuppressHookLifecycleEvents)
|
||||
{
|
||||
_pendingEvents.Enqueue(CreateHookLifecycleEvent(agent, "end", hookEnd.Data));
|
||||
_pendingEvents.Enqueue(CreateHookLifecycleEvent(
|
||||
agent,
|
||||
"end",
|
||||
hookEnd.HookInvocationId,
|
||||
hookEnd.HookType,
|
||||
success: hookEnd.Success,
|
||||
output: hookEnd.Output,
|
||||
error: hookEnd.Error));
|
||||
}
|
||||
break;
|
||||
case AssistantUsageEvent assistantUsage:
|
||||
case ProviderAssistantUsageEvent assistantUsage:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateAssistantUsageEvent(agent, assistantUsage.Data));
|
||||
_pendingEvents.Enqueue(CreateAssistantUsageEvent(agent, assistantUsage));
|
||||
break;
|
||||
case SessionUsageInfoEvent usageInfo:
|
||||
case ProviderSessionUsageEvent usageInfo:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateUsageEvent(agent, usageInfo.Data));
|
||||
_pendingEvents.Enqueue(CreateUsageEvent(agent, usageInfo));
|
||||
break;
|
||||
case SessionCompactionStartEvent compactionStart:
|
||||
case ProviderSessionCompactionStartEvent compactionStart:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateCompactionStartEvent(agent, compactionStart.Data));
|
||||
_pendingEvents.Enqueue(CreateCompactionStartEvent(agent, compactionStart));
|
||||
break;
|
||||
case SessionCompactionCompleteEvent compactionComplete:
|
||||
case ProviderSessionCompactionCompleteEvent compactionComplete:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreateCompactionCompleteEvent(agent, compactionComplete.Data));
|
||||
_pendingEvents.Enqueue(CreateCompactionCompleteEvent(agent, compactionComplete));
|
||||
break;
|
||||
case PendingMessagesModifiedEvent:
|
||||
case ProviderPendingMessagesModifiedEvent:
|
||||
ActiveAgent = agent;
|
||||
_pendingEvents.Enqueue(CreatePendingMessagesModifiedEvent(agent));
|
||||
break;
|
||||
case McpOauthRequiredEvent:
|
||||
case ProviderMcpOauthRequiredEvent:
|
||||
ActiveAgent = agent;
|
||||
break;
|
||||
case ExitPlanModeRequestedEvent:
|
||||
case ProviderExitPlanModeRequestedEvent:
|
||||
HasPendingExitPlanModeRequest = true;
|
||||
ActiveAgent = agent;
|
||||
break;
|
||||
@@ -297,7 +309,7 @@ internal sealed class CopilotTurnExecutionState
|
||||
AgentIdentity agent,
|
||||
string toolName,
|
||||
string toolCallId,
|
||||
object? rawArguments = null)
|
||||
IReadOnlyDictionary<string, object?>? toolArguments = null)
|
||||
{
|
||||
if (toolName.StartsWith("handoff_to_", StringComparison.Ordinal))
|
||||
{
|
||||
@@ -314,7 +326,7 @@ internal sealed class CopilotTurnExecutionState
|
||||
AgentName = agent.AgentName,
|
||||
ToolName = toolName,
|
||||
ToolCallId = toolCallId,
|
||||
ToolArguments = WorkflowRequestInfoInterpreter.NormalizeRawToolArguments(rawArguments),
|
||||
ToolArguments = toolArguments,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -377,29 +389,28 @@ internal sealed class CopilotTurnExecutionState
|
||||
return CompletedMessages;
|
||||
}
|
||||
|
||||
private SubagentEventDto CreateSubagentEvent(
|
||||
private SubagentEventDto CreateSubagentStartedEvent(
|
||||
AgentIdentity agent,
|
||||
string eventKind,
|
||||
SubagentStartedData? data)
|
||||
ProviderSubagentStartedEvent data)
|
||||
{
|
||||
return new SubagentEventDto
|
||||
{
|
||||
Type = "subagent-event",
|
||||
RequestId = _command.RequestId,
|
||||
SessionId = _command.SessionId,
|
||||
EventKind = eventKind,
|
||||
EventKind = "started",
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
ToolCallId = data?.ToolCallId,
|
||||
CustomAgentName = data?.AgentName,
|
||||
CustomAgentDisplayName = data?.AgentDisplayName,
|
||||
CustomAgentDescription = data?.AgentDescription,
|
||||
ToolCallId = data.ToolCallId,
|
||||
CustomAgentName = data.AgentName,
|
||||
CustomAgentDisplayName = data.AgentDisplayName,
|
||||
CustomAgentDescription = data.AgentDescription,
|
||||
};
|
||||
}
|
||||
|
||||
private SubagentEventDto CreateSubagentCompletedEvent(
|
||||
AgentIdentity agent,
|
||||
SubagentCompletedData? data)
|
||||
ProviderSubagentCompletedEvent data)
|
||||
{
|
||||
return new SubagentEventDto
|
||||
{
|
||||
@@ -409,15 +420,15 @@ internal sealed class CopilotTurnExecutionState
|
||||
EventKind = "completed",
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
ToolCallId = data?.ToolCallId,
|
||||
CustomAgentName = data?.AgentName,
|
||||
CustomAgentDisplayName = data?.AgentDisplayName,
|
||||
ToolCallId = data.ToolCallId,
|
||||
CustomAgentName = data.AgentName,
|
||||
CustomAgentDisplayName = data.AgentDisplayName,
|
||||
};
|
||||
}
|
||||
|
||||
private SubagentEventDto CreateSubagentFailedEvent(
|
||||
AgentIdentity agent,
|
||||
SubagentFailedData? data)
|
||||
ProviderSubagentFailedEvent data)
|
||||
{
|
||||
return new SubagentEventDto
|
||||
{
|
||||
@@ -427,16 +438,16 @@ internal sealed class CopilotTurnExecutionState
|
||||
EventKind = "failed",
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
ToolCallId = data?.ToolCallId,
|
||||
CustomAgentName = data?.AgentName,
|
||||
CustomAgentDisplayName = data?.AgentDisplayName,
|
||||
Error = data?.Error,
|
||||
ToolCallId = data.ToolCallId,
|
||||
CustomAgentName = data.AgentName,
|
||||
CustomAgentDisplayName = data.AgentDisplayName,
|
||||
Error = data.Error,
|
||||
};
|
||||
}
|
||||
|
||||
private SubagentEventDto CreateSubagentSelectedEvent(
|
||||
AgentIdentity agent,
|
||||
SubagentSelectedData? data)
|
||||
ProviderSubagentSelectedEvent data)
|
||||
{
|
||||
return new SubagentEventDto
|
||||
{
|
||||
@@ -446,9 +457,9 @@ internal sealed class CopilotTurnExecutionState
|
||||
EventKind = "selected",
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
CustomAgentName = data?.AgentName,
|
||||
CustomAgentDisplayName = data?.AgentDisplayName,
|
||||
Tools = data?.Tools,
|
||||
CustomAgentName = data.AgentName,
|
||||
CustomAgentDisplayName = data.AgentDisplayName,
|
||||
Tools = data.Tools,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -467,10 +478,10 @@ internal sealed class CopilotTurnExecutionState
|
||||
|
||||
private AssistantIntentEventDto? CreateAssistantIntentEvent(
|
||||
AgentIdentity agent,
|
||||
AssistantIntentData? data)
|
||||
string? intent)
|
||||
{
|
||||
string? intent = data?.Intent?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(intent))
|
||||
string? normalizedIntent = intent?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(normalizedIntent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -482,17 +493,17 @@ internal sealed class CopilotTurnExecutionState
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
Intent = intent,
|
||||
Intent = normalizedIntent,
|
||||
};
|
||||
}
|
||||
|
||||
private ReasoningDeltaEventDto? CreateReasoningDeltaEvent(
|
||||
AgentIdentity agent,
|
||||
AssistantReasoningDeltaData? data)
|
||||
string? reasoningId,
|
||||
string? deltaContent)
|
||||
{
|
||||
if (data is null
|
||||
|| string.IsNullOrWhiteSpace(data.ReasoningId)
|
||||
|| string.IsNullOrEmpty(data.DeltaContent))
|
||||
if (string.IsNullOrWhiteSpace(reasoningId)
|
||||
|| string.IsNullOrEmpty(deltaContent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -504,14 +515,14 @@ internal sealed class CopilotTurnExecutionState
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
ReasoningId = data.ReasoningId,
|
||||
ContentDelta = data.DeltaContent,
|
||||
ReasoningId = reasoningId,
|
||||
ContentDelta = deltaContent,
|
||||
};
|
||||
}
|
||||
|
||||
private SkillInvokedEventDto CreateSkillInvokedEvent(
|
||||
AgentIdentity agent,
|
||||
SkillInvokedData? data)
|
||||
ProviderSkillInvokedEvent data)
|
||||
{
|
||||
return new SkillInvokedEventDto
|
||||
{
|
||||
@@ -520,19 +531,24 @@ internal sealed class CopilotTurnExecutionState
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
SkillName = data?.Name ?? string.Empty,
|
||||
Path = data?.Path ?? string.Empty,
|
||||
Content = data?.Content ?? string.Empty,
|
||||
AllowedTools = data?.AllowedTools,
|
||||
PluginName = data?.PluginName,
|
||||
PluginVersion = data?.PluginVersion,
|
||||
SkillName = data.SkillName,
|
||||
Path = data.Path,
|
||||
Content = data.Content,
|
||||
AllowedTools = data.AllowedTools,
|
||||
PluginName = data.PluginName,
|
||||
PluginVersion = data.PluginVersion,
|
||||
};
|
||||
}
|
||||
|
||||
private HookLifecycleEventDto CreateHookLifecycleEvent(
|
||||
AgentIdentity agent,
|
||||
string phase,
|
||||
HookStartData? data)
|
||||
string hookInvocationId,
|
||||
string hookType,
|
||||
object? input = null,
|
||||
bool? success = null,
|
||||
object? output = null,
|
||||
string? error = null)
|
||||
{
|
||||
return new HookLifecycleEventDto
|
||||
{
|
||||
@@ -541,37 +557,19 @@ internal sealed class CopilotTurnExecutionState
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
HookInvocationId = data?.HookInvocationId ?? string.Empty,
|
||||
HookType = data?.HookType ?? string.Empty,
|
||||
HookInvocationId = hookInvocationId,
|
||||
HookType = hookType,
|
||||
Phase = phase,
|
||||
Input = data?.Input,
|
||||
};
|
||||
}
|
||||
|
||||
private HookLifecycleEventDto CreateHookLifecycleEvent(
|
||||
AgentIdentity agent,
|
||||
string phase,
|
||||
HookEndData? data)
|
||||
{
|
||||
return new HookLifecycleEventDto
|
||||
{
|
||||
Type = "hook-lifecycle",
|
||||
RequestId = _command.RequestId,
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
HookInvocationId = data?.HookInvocationId ?? string.Empty,
|
||||
HookType = data?.HookType ?? string.Empty,
|
||||
Phase = phase,
|
||||
Success = data?.Success,
|
||||
Output = data?.Output,
|
||||
Error = data?.Error?.Message,
|
||||
Input = input,
|
||||
Success = success,
|
||||
Output = output,
|
||||
Error = error,
|
||||
};
|
||||
}
|
||||
|
||||
private AssistantUsageEventDto CreateAssistantUsageEvent(
|
||||
AgentIdentity agent,
|
||||
AssistantUsageData? data)
|
||||
ProviderAssistantUsageEvent data)
|
||||
{
|
||||
return new AssistantUsageEventDto
|
||||
{
|
||||
@@ -580,19 +578,19 @@ internal sealed class CopilotTurnExecutionState
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
Model = data?.Model ?? string.Empty,
|
||||
InputTokens = data?.InputTokens,
|
||||
OutputTokens = data?.OutputTokens,
|
||||
CacheReadTokens = data?.CacheReadTokens,
|
||||
CacheWriteTokens = data?.CacheWriteTokens,
|
||||
Cost = data?.Cost,
|
||||
Duration = data?.Duration,
|
||||
TotalNanoAiu = data?.CopilotUsage?.TotalNanoAiu,
|
||||
QuotaSnapshots = QuotaSnapshotMapper.MapOrNull(data?.QuotaSnapshots),
|
||||
Model = data.Model,
|
||||
InputTokens = data.InputTokens,
|
||||
OutputTokens = data.OutputTokens,
|
||||
CacheReadTokens = data.CacheReadTokens,
|
||||
CacheWriteTokens = data.CacheWriteTokens,
|
||||
Cost = data.Cost,
|
||||
Duration = data.Duration,
|
||||
TotalNanoAiu = data.TotalNanoAiu,
|
||||
QuotaSnapshots = data.QuotaSnapshots,
|
||||
};
|
||||
}
|
||||
|
||||
private SessionUsageEventDto CreateUsageEvent(AgentIdentity agent, SessionUsageInfoData? data)
|
||||
private SessionUsageEventDto CreateUsageEvent(AgentIdentity agent, ProviderSessionUsageEvent data)
|
||||
{
|
||||
return new SessionUsageEventDto
|
||||
{
|
||||
@@ -601,19 +599,19 @@ internal sealed class CopilotTurnExecutionState
|
||||
SessionId = _command.SessionId,
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
TokenLimit = data?.TokenLimit ?? 0,
|
||||
CurrentTokens = data?.CurrentTokens ?? 0,
|
||||
MessagesLength = data?.MessagesLength ?? 0,
|
||||
SystemTokens = data?.SystemTokens,
|
||||
ConversationTokens = data?.ConversationTokens,
|
||||
ToolDefinitionsTokens = data?.ToolDefinitionsTokens,
|
||||
IsInitial = data?.IsInitial,
|
||||
TokenLimit = data.TokenLimit,
|
||||
CurrentTokens = data.CurrentTokens,
|
||||
MessagesLength = data.MessagesLength,
|
||||
SystemTokens = data.SystemTokens,
|
||||
ConversationTokens = data.ConversationTokens,
|
||||
ToolDefinitionsTokens = data.ToolDefinitionsTokens,
|
||||
IsInitial = data.IsInitial,
|
||||
};
|
||||
}
|
||||
|
||||
private SessionCompactionEventDto CreateCompactionStartEvent(
|
||||
AgentIdentity agent,
|
||||
SessionCompactionStartData? data)
|
||||
ProviderSessionCompactionStartEvent data)
|
||||
{
|
||||
return new SessionCompactionEventDto
|
||||
{
|
||||
@@ -623,15 +621,15 @@ internal sealed class CopilotTurnExecutionState
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
Phase = "start",
|
||||
SystemTokens = data?.SystemTokens,
|
||||
ConversationTokens = data?.ConversationTokens,
|
||||
ToolDefinitionsTokens = data?.ToolDefinitionsTokens,
|
||||
SystemTokens = data.SystemTokens,
|
||||
ConversationTokens = data.ConversationTokens,
|
||||
ToolDefinitionsTokens = data.ToolDefinitionsTokens,
|
||||
};
|
||||
}
|
||||
|
||||
private SessionCompactionEventDto CreateCompactionCompleteEvent(
|
||||
AgentIdentity agent,
|
||||
SessionCompactionCompleteData? data)
|
||||
ProviderSessionCompactionCompleteEvent data)
|
||||
{
|
||||
return new SessionCompactionEventDto
|
||||
{
|
||||
@@ -641,19 +639,19 @@ internal sealed class CopilotTurnExecutionState
|
||||
AgentId = agent.AgentId,
|
||||
AgentName = agent.AgentName,
|
||||
Phase = "complete",
|
||||
Success = data?.Success,
|
||||
Error = data?.Error,
|
||||
SystemTokens = data?.SystemTokens,
|
||||
ConversationTokens = data?.ConversationTokens,
|
||||
ToolDefinitionsTokens = data?.ToolDefinitionsTokens,
|
||||
PreCompactionTokens = data?.PreCompactionTokens,
|
||||
PostCompactionTokens = data?.PostCompactionTokens,
|
||||
PreCompactionMessagesLength = data?.PreCompactionMessagesLength,
|
||||
MessagesRemoved = data?.MessagesRemoved,
|
||||
TokensRemoved = data?.TokensRemoved,
|
||||
SummaryContent = data?.SummaryContent,
|
||||
CheckpointNumber = data?.CheckpointNumber,
|
||||
CheckpointPath = data?.CheckpointPath,
|
||||
Success = data.Success,
|
||||
Error = data.Error,
|
||||
SystemTokens = data.SystemTokens,
|
||||
ConversationTokens = data.ConversationTokens,
|
||||
ToolDefinitionsTokens = data.ToolDefinitionsTokens,
|
||||
PreCompactionTokens = data.PreCompactionTokens,
|
||||
PostCompactionTokens = data.PostCompactionTokens,
|
||||
PreCompactionMessagesLength = data.PreCompactionMessagesLength,
|
||||
MessagesRemoved = data.MessagesRemoved,
|
||||
TokensRemoved = data.TokensRemoved,
|
||||
SummaryContent = data.SummaryContent,
|
||||
CheckpointNumber = data.CheckpointNumber,
|
||||
CheckpointPath = data.CheckpointPath,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
|
||||
throw new InvalidOperationException(validationError);
|
||||
}
|
||||
|
||||
IProviderEventAdapter providerEventAdapter = new CopilotEventAdapter();
|
||||
CopilotTurnExecutionState state = new(command);
|
||||
using CancellationTokenSource runCancellation =
|
||||
CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
@@ -70,7 +71,11 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
|
||||
runCancellation.Token),
|
||||
(agent, sessionEvent) =>
|
||||
{
|
||||
state.ObserveSessionEvent(agent, sessionEvent);
|
||||
if (providerEventAdapter.TryAdapt(sessionEvent) is { } providerEvent)
|
||||
{
|
||||
state.ObserveSessionEvent(agent, providerEvent);
|
||||
}
|
||||
|
||||
if (sessionEvent is McpOauthRequiredEvent mcpOauthRequired)
|
||||
{
|
||||
state.EnqueuePendingMcpOauthRequest(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Aryx.AgentHost.Contracts;
|
||||
|
||||
namespace Aryx.AgentHost.Services;
|
||||
|
||||
internal interface IProviderEventAdapter
|
||||
{
|
||||
ProviderSessionEvent? TryAdapt(object rawEvent);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Aryx.AgentHost.Contracts;
|
||||
using Aryx.AgentHost.Services;
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
namespace Aryx.AgentHost.Tests;
|
||||
|
||||
internal static class SessionEventTestExtensions
|
||||
{
|
||||
private static readonly IProviderEventAdapter ProviderEventAdapter = new CopilotEventAdapter();
|
||||
|
||||
public static void ObserveSessionEvent(
|
||||
this CopilotTurnExecutionState state,
|
||||
WorkflowNodeDto agentDefinition,
|
||||
SessionEvent sessionEvent)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(state);
|
||||
ArgumentNullException.ThrowIfNull(agentDefinition);
|
||||
ArgumentNullException.ThrowIfNull(sessionEvent);
|
||||
|
||||
ProviderSessionEvent providerEvent = Assert.IsAssignableFrom<ProviderSessionEvent>(
|
||||
ProviderEventAdapter.TryAdapt(sessionEvent));
|
||||
|
||||
state.ObserveSessionEvent(agentDefinition, providerEvent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user