@@ -38,7 +38,7 @@ public class OpenClawGatewayClient : WebSocketClientBase, IOperatorGatewayClient
3838 private GatewayUsageStatusInfo ? _usageStatus ;
3939 private GatewayCostUsageInfo ? _usageCost ;
4040 private readonly Dictionary < string , string > _pendingRequestMethods = new ( ) ;
41- private readonly Dictionary < string , TaskCompletionSource < bool > > _pendingChatSendRequests = new ( ) ;
41+ private readonly Dictionary < string , TaskCompletionSource < ChatSendResult > > _pendingChatSendRequests = new ( ) ;
4242 private readonly object _pendingRequestLock = new ( ) ;
4343 private readonly object _pendingChatSendLock = new ( ) ;
4444 private readonly object _sessionsLock = new ( ) ;
@@ -175,6 +175,7 @@ protected override void OnDisposing()
175175 public event EventHandler < JsonElement > ? AgentsListUpdated ;
176176 public event EventHandler < JsonElement > ? AgentFilesListUpdated ;
177177 public event EventHandler < JsonElement > ? AgentFileContentUpdated ;
178+ public event EventHandler < AgentEventInfo > ? ChatEventReceived ;
178179
179180 /// <summary>Raised when a device token is received from the gateway during hello-ok handshake.</summary>
180181 public event EventHandler < DeviceTokenReceivedEventArgs > ? DeviceTokenReceived ;
@@ -251,6 +252,11 @@ public async Task CheckHealthAsync()
251252 }
252253
253254 public async Task SendChatMessageAsync ( string message , string ? sessionKey = null )
255+ {
256+ _ = await SendChatMessageForRunAsync ( message , sessionKey ) . ConfigureAwait ( false ) ;
257+ }
258+
259+ public async Task < ChatSendResult > SendChatMessageForRunAsync ( string message , string ? sessionKey = null )
254260 {
255261 if ( ! IsConnected )
256262 throw new InvalidOperationException ( "Gateway connection is not open" ) ;
@@ -262,7 +268,7 @@ public async Task SendChatMessageAsync(string message, string? sessionKey = null
262268 : sessionKey . Trim ( ) ;
263269
264270 var requestId = Guid . NewGuid ( ) . ToString ( ) ;
265- var completion = new TaskCompletionSource < bool > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
271+ var completion = new TaskCompletionSource < ChatSendResult > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
266272 TrackPendingChatSend ( requestId , completion ) ;
267273
268274 var req = new
@@ -287,8 +293,9 @@ public async Task SendChatMessageAsync(string message, string? sessionKey = null
287293 throw new TimeoutException ( "Timed out waiting for chat.send response from gateway" ) ;
288294 }
289295
290- await completion . Task ;
296+ var result = await completion . Task . ConfigureAwait ( false ) ;
291297 _logger . Info ( $ "Sent chat message ({ message . Length } chars)") ;
298+ return result ;
292299 }
293300
294301 /// <summary>
@@ -886,7 +893,7 @@ private void ClearPendingRequests()
886893 _pendingWizardResponses . Clear ( ) ;
887894 }
888895
889- private void TrackPendingChatSend ( string requestId , TaskCompletionSource < bool > completion )
896+ private void TrackPendingChatSend ( string requestId , TaskCompletionSource < ChatSendResult > completion )
890897 {
891898 lock ( _pendingChatSendLock )
892899 {
@@ -902,7 +909,7 @@ private void RemovePendingChatSend(string requestId)
902909 }
903910 }
904911
905- private TaskCompletionSource < bool > ? TakePendingChatSend ( string ? requestId )
912+ private TaskCompletionSource < ChatSendResult > ? TakePendingChatSend ( string ? requestId )
906913 {
907914 if ( string . IsNullOrWhiteSpace ( requestId ) )
908915 {
@@ -975,7 +982,7 @@ private void HandleResponse(JsonElement root)
975982 return ;
976983 }
977984
978- pendingChatSend . TrySetResult ( true ) ;
985+ pendingChatSend . TrySetResult ( ParseChatSendResult ( root ) ) ;
979986 return ;
980987 }
981988
@@ -1205,6 +1212,36 @@ private bool HandleKnownResponse(string method, JsonElement payload)
12051212 }
12061213 }
12071214
1215+ private static ChatSendResult ParseChatSendResult ( JsonElement root )
1216+ {
1217+ string ? runId = null ;
1218+ string ? sessionKey = null ;
1219+ var cached = false ;
1220+
1221+ if ( root . TryGetProperty ( "payload" , out var payload ) && payload . ValueKind == JsonValueKind . Object )
1222+ {
1223+ if ( payload . TryGetProperty ( "runId" , out var runIdProp ) )
1224+ runId = runIdProp . GetString ( ) ;
1225+ if ( payload . TryGetProperty ( "sessionKey" , out var sessionKeyProp ) )
1226+ sessionKey = sessionKeyProp . GetString ( ) ;
1227+ }
1228+
1229+ if ( root . TryGetProperty ( "meta" , out var meta ) &&
1230+ meta . ValueKind == JsonValueKind . Object &&
1231+ meta . TryGetProperty ( "cached" , out var cachedProp ) &&
1232+ cachedProp . ValueKind is JsonValueKind . True or JsonValueKind . False )
1233+ {
1234+ cached = cachedProp . GetBoolean ( ) ;
1235+ }
1236+
1237+ return new ChatSendResult
1238+ {
1239+ RunId = runId ,
1240+ SessionKey = sessionKey ,
1241+ Cached = cached
1242+ } ;
1243+ }
1244+
12081245 private void HandleRequestError ( string ? method , JsonElement root )
12091246 {
12101247 var message = TryGetErrorMessage ( root ) ?? "request failed" ;
@@ -1979,6 +2016,7 @@ private void HandleChatEvent(JsonElement root)
19792016 _logger . Debug ( $ "Chat event received: { rawText [ ..Math . Min ( 200 , rawText . Length ) ] } ") ;
19802017
19812018 if ( ! root . TryGetProperty ( "payload" , out var payload ) ) return ;
2019+ EmitRawChatEvent ( payload ) ;
19822020
19832021 // Try new format: payload.message.role + payload.message.content[].text
19842022 if ( payload . TryGetProperty ( "message" , out var message ) )
@@ -2021,6 +2059,38 @@ private void HandleChatEvent(JsonElement root)
20212059 }
20222060 }
20232061
2062+ private void EmitRawChatEvent ( JsonElement payload )
2063+ {
2064+ try
2065+ {
2066+ var stream = "chat" ;
2067+ if ( payload . TryGetProperty ( "message" , out var message ) &&
2068+ message . TryGetProperty ( "role" , out var roleProp ) )
2069+ {
2070+ stream = roleProp . GetString ( ) ?? stream ;
2071+ }
2072+ else if ( payload . TryGetProperty ( "role" , out var legacyRoleProp ) )
2073+ {
2074+ stream = legacyRoleProp . GetString ( ) ?? stream ;
2075+ }
2076+
2077+ var evt = new AgentEventInfo
2078+ {
2079+ RunId = payload . TryGetProperty ( "runId" , out var rid ) ? rid . GetString ( ) ?? "" : "" ,
2080+ Seq = payload . TryGetProperty ( "seq" , out var seqProp ) && seqProp . ValueKind == JsonValueKind . Number ? seqProp . GetInt32 ( ) : 0 ,
2081+ Stream = stream ,
2082+ Ts = DateTimeOffset . UtcNow . ToUnixTimeMilliseconds ( ) ,
2083+ Data = payload . Clone ( ) ,
2084+ SessionKey = payload . TryGetProperty ( "sessionKey" , out var sk ) ? sk . GetString ( ) : null
2085+ } ;
2086+ ChatEventReceived ? . Invoke ( this , evt ) ;
2087+ }
2088+ catch ( Exception ex )
2089+ {
2090+ _logger . Warn ( $ "Failed to emit chat event: { ex . Message } ") ;
2091+ }
2092+ }
2093+
20242094 private void EmitChatNotification ( string text )
20252095 {
20262096 var displayText = text . Length > 200 ? text [ ..200 ] + "…" : text ;
0 commit comments