package runtime import ( "context" "fmt" "strings" applicationruntime "code.tczkiot.com/wlw/ai-agent/internal/ai/application/runtime" "code.tczkiot.com/wlw/ai-agent/internal/ai/runtime/graphs" "code.tczkiot.com/wlw/ai-agent/internal/models" ) type runtimeReplyExecutor struct{} type runtimeReplyRunInput struct { Conversation models.Conversation Message models.Message AIAgent models.AIAgent } type runtimeReplyResumeInput struct { Conversation models.Conversation Message models.Message AIAgent models.AIAgent PendingInterrupt *models.ConversationInterrupt } func newRuntimeReplyExecutor() *runtimeReplyExecutor { return &runtimeReplyExecutor{} } func (e *runtimeReplyExecutor) Run(ctx context.Context, input runtimeReplyRunInput) (*applicationruntime.RunResult, error) { config, err := applicationruntime.ResolveRuntimeAIConfig(ctx, input.AIAgent.AIConfigID) if err != nil { return nil, err } // The trigger layer may enrich an anonymous channel conversation with a // business subject resolved from the current message or recent history. // Run the already validated objects so that card/device identity is not lost // by reloading the original guest ownership record from the database. summary, err := applicationruntime.DefaultAgentApplicationService.RunPrepared(ctx, applicationruntime.RunInput{ Conversation: input.Conversation, UserMessage: input.Message, AIAgent: input.AIAgent, AIConfig: *config, }) return summary, err } func (e *runtimeReplyExecutor) ResumePendingInterrupt(ctx context.Context, input runtimeReplyResumeInput) (*applicationruntime.RunResult, error) { if input.PendingInterrupt == nil { return nil, fmt.Errorf("pending interrupt is required") } config, err := applicationruntime.ResolveRuntimeAIConfig(ctx, input.AIAgent.AIConfigID) if err != nil { return nil, err } summary, err := applicationruntime.DefaultAgentApplicationService.ResumePrepared(ctx, applicationruntime.ResumeInput{ Conversation: input.Conversation, UserMessage: input.Message, AIAgent: input.AIAgent, AIConfig: *config, CheckPointID: strings.TrimSpace(input.PendingInterrupt.CheckPointID), ResumeData: map[string]string{ strings.TrimSpace(input.PendingInterrupt.InterruptID): strings.TrimSpace(input.Message.Content), }, }) return summary, err } func expiredInterruptSummary() *applicationruntime.RunResult { return &applicationruntime.RunResult{ Status: "expired", ReplyText: graphs.ConfirmationExpiredReply, } }