@@ -189,6 +189,42 @@ export class TaskService {
189189 }
190190 }
191191
192+ private async finalizeAgentTaskWithoutReport (
193+ workspaceId : string ,
194+ reportMarkdown : string
195+ ) : Promise < void > {
196+ const workspaceConfig = this . config . getWorkspaceConfig ( workspaceId ) ;
197+ if ( ! workspaceConfig ) {
198+ log . error ( "Failed to finalize agent task without report: unknown workspace" , {
199+ workspaceId,
200+ } ) ;
201+ return ;
202+ }
203+
204+ // If this isn't a properly-parented task workspace, at least mark it complete so it doesn't
205+ // consume a scheduler slot indefinitely.
206+ if ( ! workspaceConfig . workspace . parentWorkspaceId ) {
207+ await this . updateTaskWorkspace ( workspaceId , { taskStatus : "reported" } ) ;
208+ await this . maybeCleanupReportedWorkspace ( workspaceId ) ;
209+ await this . queueScheduling ( ) ;
210+ return ;
211+ }
212+
213+ try {
214+ await this . handleAgentReport ( workspaceId , { reportMarkdown } ) ;
215+ } catch ( error : unknown ) {
216+ // Ensure a failed report doesn't leave the queue stuck.
217+ log . error ( "Failed to finalize agent task without agent_report" , {
218+ workspaceId,
219+ error,
220+ } ) ;
221+
222+ await this . updateTaskWorkspace ( workspaceId , { taskStatus : "reported" } ) ;
223+ await this . maybeCleanupReportedWorkspace ( workspaceId ) ;
224+ await this . queueScheduling ( ) ;
225+ }
226+ }
227+
192228 async createAgentTask ( params : CreateAgentTaskParams ) : Promise < { childWorkspaceId : string } > {
193229 const preset = getAgentPreset ( params . agentType ) ;
194230
@@ -437,53 +473,80 @@ export class TaskService {
437473 await this . updateTaskWorkspace ( workspaceId , { taskStatus : "awaiting_report" } ) ;
438474
439475 const preset = getAgentPreset ( agentType ) ;
440- if ( ! preset ) {
441- return ;
442- }
443-
444- // Force a report-only follow-up.
445- const requirePolicy : ToolPolicy = [ { action : "require" , regex_match : "^agent_report$" } ] ;
446-
447- const nudgeMessage = createMuxMessage (
448- this . config . generateStableId ( ) ,
449- "user" ,
450- "You must now call agent_report with your final reportMarkdown. Do not do anything else." ,
451- { synthetic : true }
452- ) ;
476+ if ( preset ) {
477+ // Force a report-only follow-up.
478+ const requirePolicy : ToolPolicy = [ { action : "require" , regex_match : "^agent_report$" } ] ;
479+
480+ const nudgeMessage = createMuxMessage (
481+ this . config . generateStableId ( ) ,
482+ "user" ,
483+ "You must now call agent_report with your final reportMarkdown. Do not do anything else." ,
484+ { synthetic : true }
485+ ) ;
453486
454- const appendResult = await this . historyService . appendToHistory ( workspaceId , nudgeMessage ) ;
455- if ( ! appendResult . success ) {
456- throw new Error ( appendResult . error ) ;
457- }
487+ const appendResult = await this . historyService . appendToHistory ( workspaceId , nudgeMessage ) ;
488+ if ( ! appendResult . success ) {
489+ log . error ( "Failed to append agent_report enforcement message" , {
490+ workspaceId,
491+ error : appendResult . error ,
492+ } ) ;
493+ } else {
494+ this . workspaceService . emitChatEvent ( workspaceId , {
495+ ...nudgeMessage ,
496+ type : "message" ,
497+ } satisfies WorkspaceChatMessage ) ;
498+ }
458499
459- this . workspaceService . emitChatEvent ( workspaceId , {
460- ...nudgeMessage ,
461- type : "message" ,
462- } satisfies WorkspaceChatMessage ) ;
500+ const model = config . workspace . taskModel ?? DEFAULT_MODEL ;
501+ const resumeResult = await this . workspaceService . resumeStream ( workspaceId , {
502+ model,
503+ mode : "agent" ,
504+ additionalSystemInstructions : preset . systemPrompt ,
505+ toolPolicy : requirePolicy ,
506+ } ) ;
507+ if ( resumeResult . success ) {
508+ return ;
509+ }
463510
464- const model = config . workspace . taskModel ?? DEFAULT_MODEL ;
465- const resumeResult = await this . workspaceService . resumeStream ( workspaceId , {
466- model,
467- mode : "agent" ,
468- additionalSystemInstructions : preset . systemPrompt ,
469- toolPolicy : requirePolicy ,
470- } ) ;
471- if ( ! resumeResult . success ) {
472511 log . error ( "Failed to resume agent task for report enforcement" , {
473512 workspaceId,
474513 error : resumeResult . error ,
475514 } ) ;
515+
516+ const fallbackReport = await this . buildFallbackReportFromHistory ( workspaceId ) ;
517+ const reportMarkdown = [
518+ "Mux was unable to resume this agent task to collect a final agent_report." ,
519+ "" ,
520+ "Resume error:" ,
521+ "```" ,
522+ this . formatErrorForReport ( resumeResult . error ) ,
523+ "```" ,
524+ ...( fallbackReport
525+ ? [ "" , "Best-effort output extracted from the task history:" , "" , fallbackReport ]
526+ : [
527+ "" ,
528+ "Mux could not extract any assistant text from the task history (best-effort fallback)." ,
529+ ] ) ,
530+ ] . join ( "\n" ) ;
531+
532+ await this . finalizeAgentTaskWithoutReport ( workspaceId , reportMarkdown ) ;
533+ return ;
476534 }
477- return ;
535+
536+ log . error ( "Agent task ended without agent_report, but no preset exists for enforcement" , {
537+ workspaceId,
538+ agentType,
539+ } ) ;
540+ // Fall through to best-effort extraction.
478541 }
479542
480543 // Second failure: fall back to best-effort report extraction.
481544 const fallbackReport = await this . buildFallbackReportFromHistory ( workspaceId ) ;
482- if ( ! fallbackReport ) {
483- return ;
484- }
545+ const reportMarkdown =
546+ fallbackReport ??
547+ "Mux did not receive an agent_report for this task and could not extract any assistant text from the task history." ;
485548
486- await this . handleAgentReport ( workspaceId , { reportMarkdown : fallbackReport } ) ;
549+ await this . finalizeAgentTaskWithoutReport ( workspaceId , reportMarkdown ) ;
487550 }
488551
489552 private async tryResolveParentTaskToolCall ( params : {
0 commit comments