mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-28 05:43:57 +02:00
feat: add thinking protocol events
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -214,6 +214,8 @@ The protocol also carries **turn-scoped lifecycle events** alongside output delt
|
|||||||
|
|
||||||
- **Sub-agent events**: started, completed, failed, selected, deselected — surfaced when custom agents are defined
|
- **Sub-agent events**: started, completed, failed, selected, deselected — surfaced when custom agents are defined
|
||||||
- **Skill invocation events**: emitted when an agent-side skill is triggered
|
- **Skill invocation events**: emitted when an agent-side skill is triggered
|
||||||
|
- **Message reclassification events**: let the sidecar retroactively mark a streamed assistant message as `thinking` once the SDK confirms that message requested tool work, so the UI can separate intermediate planning chatter from the final response without sacrificing live streaming
|
||||||
|
- **Assistant intent and reasoning-delta events**: optional Copilot SDK metadata that exposes short "what I'm doing" labels plus incremental reasoning text for richer thinking-process surfaces
|
||||||
- **Hook lifecycle events**: start and end of configured project hook commands discovered from `.github/hooks/*.json`; Aryx suppresses the SDK's built-in no-op hook chatter so the UI only sees meaningful hook activity
|
- **Hook lifecycle events**: start and end of configured project hook commands discovered from `.github/hooks/*.json`; Aryx suppresses the SDK's built-in no-op hook chatter so the UI only sees meaningful hook activity
|
||||||
- **Assistant usage events**: per-LLM-call tokens, cost, AIU, and quota snapshots from the Copilot SDK's `assistant.usage` stream
|
- **Assistant usage events**: per-LLM-call tokens, cost, AIU, and quota snapshots from the Copilot SDK's `assistant.usage` stream
|
||||||
- **Session compaction events**: start and complete, with token-reduction metrics when infinite sessions trigger context trimming
|
- **Session compaction events**: start and complete, with token-reduction metrics when infinite sessions trigger context trimming
|
||||||
|
|||||||
@@ -331,6 +331,13 @@ public sealed class TurnCompleteEventDto : SidecarEventDto
|
|||||||
public bool Cancelled { get; init; }
|
public bool Cancelled { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class MessageReclassifiedEventDto : SidecarEventDto
|
||||||
|
{
|
||||||
|
public string SessionId { get; init; } = string.Empty;
|
||||||
|
public string MessageId { get; init; } = string.Empty;
|
||||||
|
public string NewKind { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class AgentActivityEventDto : SidecarEventDto
|
public sealed class AgentActivityEventDto : SidecarEventDto
|
||||||
{
|
{
|
||||||
public string SessionId { get; init; } = string.Empty;
|
public string SessionId { get; init; } = string.Empty;
|
||||||
@@ -376,6 +383,23 @@ public sealed class SkillInvokedEventDto : SidecarEventDto
|
|||||||
public string? Description { get; init; }
|
public string? Description { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class AssistantIntentEventDto : SidecarEventDto
|
||||||
|
{
|
||||||
|
public string SessionId { get; init; } = string.Empty;
|
||||||
|
public string? AgentId { get; init; }
|
||||||
|
public string? AgentName { get; init; }
|
||||||
|
public string Intent { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ReasoningDeltaEventDto : SidecarEventDto
|
||||||
|
{
|
||||||
|
public string SessionId { get; init; } = string.Empty;
|
||||||
|
public string? AgentId { get; init; }
|
||||||
|
public string? AgentName { get; init; }
|
||||||
|
public string ReasoningId { get; init; } = string.Empty;
|
||||||
|
public string ContentDelta { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class HookLifecycleEventDto : SidecarEventDto
|
public sealed class HookLifecycleEventDto : SidecarEventDto
|
||||||
{
|
{
|
||||||
public string SessionId { get; init; } = string.Empty;
|
public string SessionId { get; init; } = string.Empty;
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
{
|
{
|
||||||
private readonly RunTurnCommandDto _command;
|
private readonly RunTurnCommandDto _command;
|
||||||
private readonly HashSet<string> _startedAgents = new(StringComparer.OrdinalIgnoreCase);
|
private readonly HashSet<string> _startedAgents = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly HashSet<string> _reclassifiedMessageIds = new(StringComparer.Ordinal);
|
||||||
private readonly ConcurrentQueue<SidecarEventDto> _pendingEvents = new();
|
private readonly ConcurrentQueue<SidecarEventDto> _pendingEvents = new();
|
||||||
private readonly ConcurrentQueue<McpOauthRequiredEventDto> _pendingMcpOauthRequests = new();
|
private readonly ConcurrentQueue<McpOauthRequiredEventDto> _pendingMcpOauthRequests = new();
|
||||||
private readonly ConcurrentDictionary<string, AgentIdentity> _observedAgentsByMessageId = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, AgentIdentity> _observedAgentsByMessageId = new(StringComparer.Ordinal);
|
||||||
private readonly StreamingTranscriptBuffer _transcriptBuffer = new();
|
private readonly StreamingTranscriptBuffer _transcriptBuffer = new();
|
||||||
private int _fallbackMessageIndex;
|
private int _fallbackMessageIndex;
|
||||||
|
private string? _lastObservedMessageId;
|
||||||
|
|
||||||
public CopilotTurnExecutionState(RunTurnCommandDto command)
|
public CopilotTurnExecutionState(RunTurnCommandDto command)
|
||||||
{
|
{
|
||||||
@@ -79,15 +81,34 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
case AssistantMessageEvent assistantMessage when !string.IsNullOrWhiteSpace(assistantMessage.Data?.MessageId):
|
case AssistantMessageEvent assistantMessage when !string.IsNullOrWhiteSpace(assistantMessage.Data?.MessageId):
|
||||||
RecordObservedAgentForMessage(agent, assistantMessage.Data!.MessageId);
|
RecordObservedAgentForMessage(agent, assistantMessage.Data!.MessageId);
|
||||||
QueueThinkingIfNeeded(agent);
|
QueueThinkingIfNeeded(agent);
|
||||||
|
if (assistantMessage.Data?.ToolRequests is { Length: > 0 })
|
||||||
|
{
|
||||||
|
QueueMessageReclassifiedIfNeeded(assistantMessage.Data.MessageId);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ToolExecutionStartEvent toolExecutionStart
|
case ToolExecutionStartEvent toolExecutionStart
|
||||||
when !string.IsNullOrWhiteSpace(toolExecutionStart.Data?.ToolCallId)
|
when !string.IsNullOrWhiteSpace(toolExecutionStart.Data?.ToolCallId)
|
||||||
&& !string.IsNullOrWhiteSpace(toolExecutionStart.Data?.ToolName):
|
&& !string.IsNullOrWhiteSpace(toolExecutionStart.Data?.ToolName):
|
||||||
ToolNamesByCallId[toolExecutionStart.Data.ToolCallId.Trim()] = toolExecutionStart.Data.ToolName.Trim();
|
ToolNamesByCallId[toolExecutionStart.Data.ToolCallId.Trim()] = toolExecutionStart.Data.ToolName.Trim();
|
||||||
|
QueueMessageReclassifiedIfNeeded(_lastObservedMessageId);
|
||||||
break;
|
break;
|
||||||
case AssistantReasoningDeltaEvent:
|
case AssistantIntentEvent intentEvent:
|
||||||
ActiveAgent = agent;
|
ActiveAgent = agent;
|
||||||
QueueThinkingIfNeeded(agent);
|
QueueThinkingIfNeeded(agent);
|
||||||
|
AssistantIntentEventDto? assistantIntent = CreateAssistantIntentEvent(agent, intentEvent.Data);
|
||||||
|
if (assistantIntent is not null)
|
||||||
|
{
|
||||||
|
_pendingEvents.Enqueue(assistantIntent);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AssistantReasoningDeltaEvent reasoningDelta:
|
||||||
|
ActiveAgent = agent;
|
||||||
|
QueueThinkingIfNeeded(agent);
|
||||||
|
ReasoningDeltaEventDto? reasoningDeltaEvent = CreateReasoningDeltaEvent(agent, reasoningDelta.Data);
|
||||||
|
if (reasoningDeltaEvent is not null)
|
||||||
|
{
|
||||||
|
_pendingEvents.Enqueue(reasoningDeltaEvent);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SubagentStartedEvent started:
|
case SubagentStartedEvent started:
|
||||||
ActiveAgent = agent;
|
ActiveAgent = agent;
|
||||||
@@ -218,6 +239,23 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
{
|
{
|
||||||
ActiveAgent = agent;
|
ActiveAgent = agent;
|
||||||
_observedAgentsByMessageId[messageId] = agent;
|
_observedAgentsByMessageId[messageId] = agent;
|
||||||
|
_lastObservedMessageId = messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueueMessageReclassifiedIfNeeded(string? messageId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(messageId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string normalizedMessageId = messageId.Trim();
|
||||||
|
if (!_reclassifiedMessageIds.Add(normalizedMessageId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingEvents.Enqueue(CreateMessageReclassifiedEvent(normalizedMessageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AgentActivityEventDto? CreateThinkingActivityIfNeeded(AgentIdentity agent)
|
private AgentActivityEventDto? CreateThinkingActivityIfNeeded(AgentIdentity agent)
|
||||||
@@ -240,6 +278,18 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MessageReclassifiedEventDto CreateMessageReclassifiedEvent(string messageId)
|
||||||
|
{
|
||||||
|
return new MessageReclassifiedEventDto
|
||||||
|
{
|
||||||
|
Type = "message-reclassified",
|
||||||
|
RequestId = _command.RequestId,
|
||||||
|
SessionId = _command.SessionId,
|
||||||
|
MessageId = messageId,
|
||||||
|
NewKind = "thinking",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateCompletedMessages(
|
public void UpdateCompletedMessages(
|
||||||
IReadOnlyList<ChatMessage> allMessages,
|
IReadOnlyList<ChatMessage> allMessages,
|
||||||
IReadOnlyList<ChatMessage> inputMessages)
|
IReadOnlyList<ChatMessage> inputMessages)
|
||||||
@@ -354,6 +404,50 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AssistantIntentEventDto? CreateAssistantIntentEvent(
|
||||||
|
AgentIdentity agent,
|
||||||
|
AssistantIntentData? data)
|
||||||
|
{
|
||||||
|
string? intent = data?.Intent?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(intent))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AssistantIntentEventDto
|
||||||
|
{
|
||||||
|
Type = "assistant-intent",
|
||||||
|
RequestId = _command.RequestId,
|
||||||
|
SessionId = _command.SessionId,
|
||||||
|
AgentId = agent.AgentId,
|
||||||
|
AgentName = agent.AgentName,
|
||||||
|
Intent = intent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReasoningDeltaEventDto? CreateReasoningDeltaEvent(
|
||||||
|
AgentIdentity agent,
|
||||||
|
AssistantReasoningDeltaData? data)
|
||||||
|
{
|
||||||
|
if (data is null
|
||||||
|
|| string.IsNullOrWhiteSpace(data.ReasoningId)
|
||||||
|
|| string.IsNullOrEmpty(data.DeltaContent))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReasoningDeltaEventDto
|
||||||
|
{
|
||||||
|
Type = "reasoning-delta",
|
||||||
|
RequestId = _command.RequestId,
|
||||||
|
SessionId = _command.SessionId,
|
||||||
|
AgentId = agent.AgentId,
|
||||||
|
AgentName = agent.AgentName,
|
||||||
|
ReasoningId = data.ReasoningId,
|
||||||
|
ContentDelta = data.DeltaContent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private SkillInvokedEventDto CreateSkillInvokedEvent(
|
private SkillInvokedEventDto CreateSkillInvokedEvent(
|
||||||
AgentIdentity agent,
|
AgentIdentity agent,
|
||||||
SkillInvokedData? data)
|
SkillInvokedData? data)
|
||||||
|
|||||||
@@ -83,6 +83,132 @@ public sealed class CopilotTurnExecutionStateTests
|
|||||||
Assert.Equal("view", toolName);
|
Assert.Equal("view", toolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveSessionEvent_AssistantMessageWithToolRequests_QueuesMessageReclassifiedEvent()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "assistant.message",
|
||||||
|
"data": {
|
||||||
|
"messageId": "msg-2",
|
||||||
|
"content": "Let me search for that.",
|
||||||
|
"toolRequests": [
|
||||||
|
{
|
||||||
|
"toolCallId": "tool-call-1",
|
||||||
|
"name": "rg",
|
||||||
|
"arguments": {
|
||||||
|
"pattern": "identifierUri"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"id": "3f75988b-8e69-4c90-a203-6b01d1c1f90b",
|
||||||
|
"timestamp": "2026-03-27T00:00:00Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
IReadOnlyList<SidecarEventDto> pending = state.DrainPendingEvents();
|
||||||
|
|
||||||
|
AgentActivityEventDto thinking = Assert.Single(pending.OfType<AgentActivityEventDto>());
|
||||||
|
Assert.Equal("thinking", thinking.ActivityType);
|
||||||
|
|
||||||
|
MessageReclassifiedEventDto reclassified = Assert.Single(pending.OfType<MessageReclassifiedEventDto>());
|
||||||
|
Assert.Equal("session-1", reclassified.SessionId);
|
||||||
|
Assert.Equal("msg-2", reclassified.MessageId);
|
||||||
|
Assert.Equal("thinking", reclassified.NewKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveSessionEvent_ToolExecutionStart_ReclassifiesLastObservedMessageOnce()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "assistant.message_delta",
|
||||||
|
"data": {
|
||||||
|
"messageId": "msg-3",
|
||||||
|
"deltaContent": "Searching"
|
||||||
|
},
|
||||||
|
"id": "0b65f0e9-d0fb-417e-ab5c-7a3343d8581b",
|
||||||
|
"timestamp": "2026-03-27T00:00:00Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
_ = state.DrainPendingEvents();
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "tool.execution_start",
|
||||||
|
"data": {
|
||||||
|
"toolCallId": "tool-call-1",
|
||||||
|
"toolName": "rg"
|
||||||
|
},
|
||||||
|
"id": "8f33240e-bd3f-475c-aeb6-a4b7908e47b0",
|
||||||
|
"timestamp": "2026-03-27T00:00:01Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "tool.execution_start",
|
||||||
|
"data": {
|
||||||
|
"toolCallId": "tool-call-2",
|
||||||
|
"toolName": "view"
|
||||||
|
},
|
||||||
|
"id": "a23f9c9a-f947-4282-866d-f599451c3899",
|
||||||
|
"timestamp": "2026-03-27T00:00:02Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
IReadOnlyList<SidecarEventDto> pending = state.DrainPendingEvents();
|
||||||
|
|
||||||
|
MessageReclassifiedEventDto reclassified = Assert.Single(pending.OfType<MessageReclassifiedEventDto>());
|
||||||
|
Assert.Equal("msg-3", reclassified.MessageId);
|
||||||
|
Assert.True(state.ToolNamesByCallId.TryGetValue("tool-call-1", out string? firstToolName));
|
||||||
|
Assert.Equal("rg", firstToolName);
|
||||||
|
Assert.True(state.ToolNamesByCallId.TryGetValue("tool-call-2", out string? secondToolName));
|
||||||
|
Assert.Equal("view", secondToolName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveSessionEvent_AssistantMessageWithoutToolRequests_DoesNotQueueMessageReclassifiedEvent()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "assistant.message",
|
||||||
|
"data": {
|
||||||
|
"messageId": "msg-4",
|
||||||
|
"content": "Final answer."
|
||||||
|
},
|
||||||
|
"id": "d07fe954-1258-4f6a-bf79-1550d6143ed0",
|
||||||
|
"timestamp": "2026-03-27T00:00:00Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
Assert.Empty(state.DrainPendingEvents().OfType<MessageReclassifiedEventDto>());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task EmitThinkingIfNeeded_DoesNotDuplicateQueuedThinkingActivity()
|
public async Task EmitThinkingIfNeeded_DoesNotDuplicateQueuedThinkingActivity()
|
||||||
{
|
{
|
||||||
@@ -119,6 +245,70 @@ public sealed class CopilotTurnExecutionStateTests
|
|||||||
Assert.Equal("agent-1", thinking.AgentId);
|
Assert.Equal("agent-1", thinking.AgentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveSessionEvent_AssistantIntent_QueuesIntentEvent()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "assistant.intent",
|
||||||
|
"data": {
|
||||||
|
"intent": "Searching incident playbooks"
|
||||||
|
},
|
||||||
|
"id": "64cf59fe-63f0-4217-adf4-9bd6b3a80452",
|
||||||
|
"timestamp": "2026-03-27T00:00:00Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
IReadOnlyList<SidecarEventDto> pending = state.DrainPendingEvents();
|
||||||
|
|
||||||
|
AgentActivityEventDto thinking = Assert.Single(pending.OfType<AgentActivityEventDto>());
|
||||||
|
Assert.Equal("thinking", thinking.ActivityType);
|
||||||
|
|
||||||
|
AssistantIntentEventDto intent = Assert.Single(pending.OfType<AssistantIntentEventDto>());
|
||||||
|
Assert.Equal("session-1", intent.SessionId);
|
||||||
|
Assert.Equal("agent-1", intent.AgentId);
|
||||||
|
Assert.Equal("Searching incident playbooks", intent.Intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveSessionEvent_AssistantReasoningDelta_QueuesReasoningDeltaEvent()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
command.Pattern.Agents[0],
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "assistant.reasoning_delta",
|
||||||
|
"data": {
|
||||||
|
"reasoningId": "reasoning-2",
|
||||||
|
"deltaContent": "Searching logs."
|
||||||
|
},
|
||||||
|
"id": "bd269258-5e5d-46b6-bf3f-bd8cba793b1a",
|
||||||
|
"timestamp": "2026-03-27T00:00:00Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
IReadOnlyList<SidecarEventDto> pending = state.DrainPendingEvents();
|
||||||
|
|
||||||
|
AgentActivityEventDto thinking = Assert.Single(pending.OfType<AgentActivityEventDto>());
|
||||||
|
Assert.Equal("thinking", thinking.ActivityType);
|
||||||
|
|
||||||
|
ReasoningDeltaEventDto reasoning = Assert.Single(pending.OfType<ReasoningDeltaEventDto>());
|
||||||
|
Assert.Equal("session-1", reasoning.SessionId);
|
||||||
|
Assert.Equal("agent-1", reasoning.AgentId);
|
||||||
|
Assert.Equal("reasoning-2", reasoning.ReasoningId);
|
||||||
|
Assert.Equal("Searching logs.", reasoning.ContentDelta);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DrainPendingMcpOauthRequests_ReturnsQueuedRequestsAndClearsQueue()
|
public void DrainPendingMcpOauthRequests_ReturnsQueuedRequestsAndClearsQueue()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -239,6 +239,14 @@ export interface TurnCompleteEvent {
|
|||||||
cancelled?: boolean;
|
cancelled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MessageReclassifiedEvent {
|
||||||
|
type: 'message-reclassified';
|
||||||
|
requestId: string;
|
||||||
|
sessionId: string;
|
||||||
|
messageId: string;
|
||||||
|
newKind: 'thinking';
|
||||||
|
}
|
||||||
|
|
||||||
export type AgentActivityType = 'thinking' | 'tool-calling' | 'handoff' | 'completed';
|
export type AgentActivityType = 'thinking' | 'tool-calling' | 'handoff' | 'completed';
|
||||||
|
|
||||||
export interface ToolCallFileChangePreview {
|
export interface ToolCallFileChangePreview {
|
||||||
@@ -297,6 +305,25 @@ export interface SkillInvokedEvent {
|
|||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AssistantIntentEvent {
|
||||||
|
type: 'assistant-intent';
|
||||||
|
requestId: string;
|
||||||
|
sessionId: string;
|
||||||
|
agentId?: string;
|
||||||
|
agentName?: string;
|
||||||
|
intent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReasoningDeltaEvent {
|
||||||
|
type: 'reasoning-delta';
|
||||||
|
requestId: string;
|
||||||
|
sessionId: string;
|
||||||
|
agentId?: string;
|
||||||
|
agentName?: string;
|
||||||
|
reasoningId: string;
|
||||||
|
contentDelta: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HookLifecycleEvent {
|
export interface HookLifecycleEvent {
|
||||||
type: 'hook-lifecycle';
|
type: 'hook-lifecycle';
|
||||||
requestId: string;
|
requestId: string;
|
||||||
@@ -526,9 +553,12 @@ export type SidecarEvent =
|
|||||||
| PatternValidationEvent
|
| PatternValidationEvent
|
||||||
| TurnDeltaEvent
|
| TurnDeltaEvent
|
||||||
| TurnCompleteEvent
|
| TurnCompleteEvent
|
||||||
|
| MessageReclassifiedEvent
|
||||||
| AgentActivityEvent
|
| AgentActivityEvent
|
||||||
| SubagentEvent
|
| SubagentEvent
|
||||||
| SkillInvokedEvent
|
| SkillInvokedEvent
|
||||||
|
| AssistantIntentEvent
|
||||||
|
| ReasoningDeltaEvent
|
||||||
| HookLifecycleEvent
|
| HookLifecycleEvent
|
||||||
| SessionUsageEvent
|
| SessionUsageEvent
|
||||||
| SessionCompactionEvent
|
| SessionCompactionEvent
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { SessionRunRecord } from '@shared/domain/runTimeline';
|
import type { SessionRunRecord } from '@shared/domain/runTimeline';
|
||||||
|
import type { ChatMessageKind } from '@shared/domain/session';
|
||||||
|
|
||||||
import type { QuotaSnapshot, ToolCallFileChangePreview } from '@shared/contracts/sidecar';
|
import type { QuotaSnapshot, ToolCallFileChangePreview } from '@shared/contracts/sidecar';
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@ export type SessionEventKind =
|
|||||||
| 'status'
|
| 'status'
|
||||||
| 'message-delta'
|
| 'message-delta'
|
||||||
| 'message-complete'
|
| 'message-complete'
|
||||||
|
| 'message-reclassified'
|
||||||
| 'agent-activity'
|
| 'agent-activity'
|
||||||
| 'run-updated'
|
| 'run-updated'
|
||||||
| 'error'
|
| 'error'
|
||||||
@@ -27,6 +29,7 @@ export interface SessionEventRecord {
|
|||||||
occurredAt: string;
|
occurredAt: string;
|
||||||
status?: 'idle' | 'running' | 'error';
|
status?: 'idle' | 'running' | 'error';
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
|
messageKind?: ChatMessageKind;
|
||||||
authorName?: string;
|
authorName?: string;
|
||||||
contentDelta?: string;
|
contentDelta?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import type { ChatMessageAttachment } from '@shared/domain/attachment';
|
|||||||
import type { InteractionMode } from '@shared/contracts/sidecar';
|
import type { InteractionMode } from '@shared/contracts/sidecar';
|
||||||
|
|
||||||
export type ChatRole = 'system' | 'user' | 'assistant';
|
export type ChatRole = 'system' | 'user' | 'assistant';
|
||||||
|
export type ChatMessageKind = 'response' | 'thinking';
|
||||||
export type SessionStatus = 'idle' | 'running' | 'error';
|
export type SessionStatus = 'idle' | 'running' | 'error';
|
||||||
export type SessionTitleSource = 'auto' | 'manual';
|
export type SessionTitleSource = 'auto' | 'manual';
|
||||||
export type SessionBranchOriginAction = 'branch' | 'regenerate' | 'edit-and-resend';
|
export type SessionBranchOriginAction = 'branch' | 'regenerate' | 'edit-and-resend';
|
||||||
@@ -33,6 +34,7 @@ export interface ChatMessageRecord {
|
|||||||
authorName: string;
|
authorName: string;
|
||||||
content: string;
|
content: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
messageKind?: ChatMessageKind;
|
||||||
isPinned?: boolean;
|
isPinned?: boolean;
|
||||||
pending?: boolean;
|
pending?: boolean;
|
||||||
attachments?: ChatMessageAttachment[];
|
attachments?: ChatMessageAttachment[];
|
||||||
|
|||||||
Reference in New Issue
Block a user