@@ -22,6 +22,7 @@ import (
2222 "github.com/humanlayer/agentcontrolplane/acp/internal/adapters"
2323 "github.com/humanlayer/agentcontrolplane/acp/internal/llmclient"
2424 "github.com/humanlayer/agentcontrolplane/acp/internal/mcpmanager"
25+ "github.com/humanlayer/agentcontrolplane/acp/internal/validation"
2526 "go.opentelemetry.io/otel/attribute"
2627 "go.opentelemetry.io/otel/codes"
2728 "go.opentelemetry.io/otel/trace"
@@ -152,54 +153,63 @@ func (r *TaskReconciler) validateTaskAndAgent(ctx context.Context, task *acp.Tas
152153 return & agent , ctrl.Result {}, nil
153154}
154155
156+ // Helper function for setting validation errors
157+ func (r * TaskReconciler ) setValidationError (ctx context.Context , task * acp.Task , statusUpdate * acp.Task , err error ) (ctrl.Result , error ) {
158+ logger := log .FromContext (ctx )
159+ logger .Error (err , "Validation failed" )
160+ statusUpdate .Status .Ready = false
161+ statusUpdate .Status .Status = acp .TaskStatusTypeError
162+ statusUpdate .Status .Phase = acp .TaskPhaseFailed
163+ statusUpdate .Status .StatusDetail = err .Error ()
164+ statusUpdate .Status .Error = err .Error ()
165+ r .recorder .Event (task , corev1 .EventTypeWarning , "ValidationFailed" , err .Error ())
166+ if updateErr := r .Status ().Update (ctx , statusUpdate ); updateErr != nil {
167+ logger .Error (updateErr , "Failed to update Task status" )
168+ return ctrl.Result {}, updateErr
169+ }
170+ return ctrl.Result {}, err
171+ }
172+
155173// prepareForLLM sets up the initial state of a Task with the correct context and phase
156174func (r * TaskReconciler ) prepareForLLM (ctx context.Context , task * acp.Task , statusUpdate * acp.Task , agent * acp.Agent ) (ctrl.Result , error ) {
157175 logger := log .FromContext (ctx )
158176
159- // If we're in Initializing or Pending phase, transition to ReadyForLLM
160177 if statusUpdate .Status .Phase == acp .TaskPhaseInitializing || statusUpdate .Status .Phase == acp .TaskPhasePending {
161- statusUpdate .Status .Phase = acp .TaskPhaseReadyForLLM
162- statusUpdate .Status .Ready = true
163-
164- if task .Spec .UserMessage == "" {
165- err := fmt .Errorf ("userMessage is required" )
166- logger .Error (err , "Missing message" )
167- statusUpdate .Status .Ready = false
168- statusUpdate .Status .Status = acp .TaskStatusTypeError
169- statusUpdate .Status .Phase = acp .TaskPhaseFailed
170- statusUpdate .Status .StatusDetail = err .Error ()
171- statusUpdate .Status .Error = err .Error ()
172- r .recorder .Event (task , corev1 .EventTypeWarning , "ValidationFailed" , err .Error ())
173- if updateErr := r .Status ().Update (ctx , statusUpdate ); updateErr != nil {
174- logger .Error (updateErr , "Failed to update Task status" )
175- return ctrl.Result {}, updateErr
176- }
177- return ctrl.Result {}, err
178+ if err := validation .ValidateTaskMessageInput (task .Spec .UserMessage , task .Spec .ContextWindow ); err != nil {
179+ return r .setValidationError (ctx , task , statusUpdate , err )
178180 }
179181
180- // Set the UserMsgPreview - truncate to 50 chars if needed
181- preview := task .Spec .UserMessage
182- if len (preview ) > 50 {
183- preview = preview [:47 ] + "..."
182+ var initialContextWindow []acp.Message
183+ if len (task .Spec .ContextWindow ) > 0 {
184+ initialContextWindow = append ([]acp.Message {}, task .Spec .ContextWindow ... )
185+ hasSystemMessage := false
186+ for _ , msg := range initialContextWindow {
187+ if msg .Role == acp .MessageRoleSystem {
188+ hasSystemMessage = true
189+ break
190+ }
191+ }
192+ if ! hasSystemMessage {
193+ initialContextWindow = append ([]acp.Message {
194+ {Role : acp .MessageRoleSystem , Content : agent .Spec .System },
195+ }, initialContextWindow ... )
196+ }
197+ } else {
198+ initialContextWindow = []acp.Message {
199+ {Role : acp .MessageRoleSystem , Content : agent .Spec .System },
200+ {Role : acp .MessageRoleUser , Content : task .Spec .UserMessage },
201+ }
184202 }
185- statusUpdate .Status .UserMsgPreview = preview
186203
187- // Set up the context window
188- statusUpdate .Status .ContextWindow = []acp.Message {
189- {
190- Role : "system" ,
191- Content : agent .Spec .System ,
192- },
193- {
194- Role : "user" ,
195- Content : task .Spec .UserMessage ,
196- },
197- }
204+ statusUpdate .Status .UserMsgPreview = validation .GetUserMessagePreview (task .Spec .UserMessage , task .Spec .ContextWindow )
205+ statusUpdate .Status .ContextWindow = initialContextWindow
206+ statusUpdate .Status .Phase = acp .TaskPhaseReadyForLLM
207+ statusUpdate .Status .Ready = true
198208 statusUpdate .Status .Status = acp .TaskStatusTypeReady
199209 statusUpdate .Status .StatusDetail = "Ready to send to LLM"
200- statusUpdate .Status .Error = "" // Clear previous error
201- r .recorder .Event (task , corev1 .EventTypeNormal , "ValidationSucceeded" , "Task validation succeeded" )
210+ statusUpdate .Status .Error = ""
202211
212+ r .recorder .Event (task , corev1 .EventTypeNormal , "ValidationSucceeded" , "Task validation succeeded" )
203213 if err := r .Status ().Update (ctx , statusUpdate ); err != nil {
204214 logger .Error (err , "Failed to update Task status" )
205215 return ctrl.Result {}, err
0 commit comments