@@ -16,6 +16,8 @@ use futures::future::LocalBoxFuture;
1616use std:: rc:: Rc ;
1717use std:: time:: Duration ;
1818use tracing:: { info, warn} ;
19+ #[ cfg( not( coverage) ) ]
20+ use trogon_nats:: jetstream:: JsMessage ;
1921use trogon_nats:: { FlushClient , PublishClient , RequestClient , SubscribeClient } ;
2022
2123pub enum ConnectionError {
@@ -313,6 +315,137 @@ where
313315 . map_err ( DispatchError :: NotificationHandler )
314316}
315317
318+ /// JetStream-aware dispatch: receives a `JsMessage`, dispatches to the agent,
319+ /// and signals ack/term based on the outcome.
320+ ///
321+ /// - Unknown subject → `term()` (no redelivery)
322+ /// - Deserialize failure → reply error + `term()` (bad payload won't fix itself)
323+ /// - Handler success → reply + `ack()`
324+ /// - Handler error → reply error + `ack()` (application-level error, not transient)
325+ /// - Notification handler → `ack()`
326+ #[ cfg( not( coverage) ) ]
327+ #[ allow( dead_code) ] // Will be used when JetStream serve path is wired up
328+ async fn dispatch_js_message < N : PublishClient + FlushClient , A : Agent > (
329+ js_msg : & JsMessage ,
330+ agent : & A ,
331+ nats : & N ,
332+ ) {
333+ let msg = js_msg. message ( ) ;
334+ let subject = msg. subject . as_str ( ) ;
335+
336+ let parsed = match parse_agent_subject ( subject) {
337+ Some ( p) => p,
338+ None => {
339+ if let Err ( e) = js_msg. term ( ) . await {
340+ warn ! ( error = %e, subject, "Failed to term unknown subject" ) ;
341+ }
342+ return ;
343+ }
344+ } ;
345+
346+ let result = match parsed. method {
347+ AgentMethod :: Initialize => {
348+ handle_request ( msg, nats, |req : InitializeRequest | agent. initialize ( req) ) . await
349+ }
350+ AgentMethod :: Authenticate => {
351+ handle_request ( msg, nats, |req : AuthenticateRequest | {
352+ agent. authenticate ( req)
353+ } )
354+ . await
355+ }
356+ AgentMethod :: SessionNew => {
357+ handle_request ( msg, nats, |req : NewSessionRequest | agent. new_session ( req) ) . await
358+ }
359+ AgentMethod :: SessionList => {
360+ handle_request ( msg, nats, |req : ListSessionsRequest | {
361+ agent. list_sessions ( req)
362+ } )
363+ . await
364+ }
365+ AgentMethod :: SessionLoad => {
366+ handle_request ( msg, nats, |req : LoadSessionRequest | agent. load_session ( req) ) . await
367+ }
368+ AgentMethod :: SessionPrompt => {
369+ handle_request ( msg, nats, |req : PromptRequest | agent. prompt ( req) ) . await
370+ }
371+ AgentMethod :: SessionCancel => {
372+ handle_notification ( msg, |req : CancelNotification | agent. cancel ( req) ) . await
373+ }
374+ AgentMethod :: SessionSetMode => {
375+ handle_request ( msg, nats, |req : SetSessionModeRequest | {
376+ agent. set_session_mode ( req)
377+ } )
378+ . await
379+ }
380+ AgentMethod :: SessionSetConfigOption => {
381+ handle_request ( msg, nats, |req : SetSessionConfigOptionRequest | {
382+ agent. set_session_config_option ( req)
383+ } )
384+ . await
385+ }
386+ AgentMethod :: SessionSetModel => {
387+ handle_request ( msg, nats, |req : SetSessionModelRequest | {
388+ agent. set_session_model ( req)
389+ } )
390+ . await
391+ }
392+ AgentMethod :: SessionFork => {
393+ handle_request ( msg, nats, |req : ForkSessionRequest | agent. fork_session ( req) ) . await
394+ }
395+ AgentMethod :: SessionResume => {
396+ handle_request ( msg, nats, |req : ResumeSessionRequest | {
397+ agent. resume_session ( req)
398+ } )
399+ . await
400+ }
401+ AgentMethod :: SessionClose => {
402+ handle_request ( msg, nats, |req : CloseSessionRequest | {
403+ agent. close_session ( req)
404+ } )
405+ . await
406+ }
407+ AgentMethod :: Ext ( _) => {
408+ if msg. reply . is_some ( ) {
409+ handle_request ( msg, nats, |req : ExtRequest | agent. ext_method ( req) ) . await
410+ } else {
411+ handle_notification ( msg, |req : ExtNotification | agent. ext_notification ( req) ) . await
412+ }
413+ }
414+ } ;
415+
416+ match & result {
417+ Ok ( ( ) ) => {
418+ if let Err ( e) = js_msg. ack ( ) . await {
419+ warn ! ( subject, error = %e, "Failed to ack JetStream message" ) ;
420+ }
421+ }
422+ Err ( DispatchError :: DeserializeRequest ( _) | DispatchError :: DeserializeNotification ( _) ) => {
423+ if let Err ( e) = js_msg. term ( ) . await {
424+ warn ! ( subject, error = %e, "Failed to term bad payload" ) ;
425+ }
426+ }
427+ Err ( DispatchError :: NoReplySubject ) => {
428+ if let Err ( e) = js_msg. term ( ) . await {
429+ warn ! ( subject, error = %e, "Failed to term missing reply subject" ) ;
430+ }
431+ }
432+ Err ( _) => {
433+ if let Err ( e) = js_msg. ack ( ) . await {
434+ warn ! ( subject, error = %e, "Failed to ack after handler error" ) ;
435+ }
436+ }
437+ }
438+
439+ if let Err ( e) = result {
440+ let sid = parsed
441+ . session_id
442+ . as_ref ( )
443+ . map ( |s| s. as_str ( ) )
444+ . unwrap_or ( "-" ) ;
445+ warn ! ( subject, session_id = sid, error = %e, "Error handling agent request" ) ;
446+ }
447+ }
448+
316449#[ cfg( test) ]
317450mod tests {
318451 use super :: * ;
0 commit comments