77
88import { semanticChalk } from '../../design-system/theme-helpers.js' ;
99import type { AgentEvent , AgentStepInfo , AgentEditInfo } from '../ServerAgentClient.js' ;
10+ import { BaseRenderer } from './BaseRenderer.js' ;
1011
1112/**
12- * ServerRenderer implements the unified JsCodingAgentRenderer interface
13+ * ServerRenderer extends BaseRenderer and implements the unified JsCodingAgentRenderer interface
1314 * defined in mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt
1415 */
15- export class ServerRenderer {
16- // Required by Kotlin JS export interface
17- readonly __doNotUseOrImplementIt : any = { } ;
16+ export class ServerRenderer extends BaseRenderer {
17+ // BaseRenderer already has __doNotUseOrImplementIt
1818
1919 private currentIteration : number = 0 ;
2020 private maxIterations : number = 20 ;
21- private llmBuffer : string = '' ;
21+ // llmBuffer removed - use reasoningBuffer from BaseRenderer
2222 private toolCallsInProgress : Map < string , { toolName : string ; params : string } > = new Map ( ) ;
2323 private isCloning : boolean = false ;
2424 private lastCloneProgress : number = 0 ;
2525 private hasStartedLLMOutput : boolean = false ;
26- private lastOutputLength : number = 0 ;
26+
27+ // ============================================================================
28+ // Platform-specific output methods (required by BaseRenderer)
29+ // ============================================================================
30+
31+ protected outputContent ( content : string ) : void {
32+ process . stdout . write ( content ) ;
33+ }
34+
35+ protected outputNewline ( ) : void {
36+ console . log ( ) ;
37+ }
2738
2839 // ============================================================================
2940 // JsCodingAgentRenderer Interface Implementation
@@ -34,8 +45,8 @@ export class ServerRenderer {
3445 }
3546
3647 renderLLMResponseStart ( ) : void {
48+ this . baseLLMResponseStart ( ) ; // Use BaseRenderer helper
3749 this . hasStartedLLMOutput = false ;
38- this . lastOutputLength = 0 ;
3950 }
4051
4152 renderLLMResponseChunk ( chunk : string ) : void {
@@ -44,14 +55,14 @@ export class ServerRenderer {
4455
4556 renderLLMResponseEnd ( ) : void {
4657 // Flush any buffered LLM output
47- if ( this . llmBuffer . trim ( ) ) {
48- const finalContent = this . filterDevinBlocks ( this . llmBuffer ) ;
58+ if ( this . reasoningBuffer . trim ( ) ) {
59+ const finalContent = this . filterDevinBlocks ( this . reasoningBuffer ) ;
4960 const remainingContent = finalContent . slice ( this . lastOutputLength || 0 ) ;
5061 if ( remainingContent . trim ( ) ) {
51- process . stdout . write ( remainingContent ) ;
62+ this . outputContent ( remainingContent ) ;
5263 }
53- console . log ( '' ) ; // Ensure newline
54- this . llmBuffer = '' ;
64+ this . outputNewline ( ) ; // Ensure newline
65+ this . reasoningBuffer = '' ;
5566 this . hasStartedLLMOutput = false ;
5667 this . lastOutputLength = 0 ;
5768 }
@@ -182,15 +193,15 @@ export class ServerRenderer {
182193 }
183194
184195 // Add chunk to buffer
185- this . llmBuffer += chunk ;
196+ this . reasoningBuffer += chunk ;
186197
187198 // Wait for more content if we detect an incomplete devin block
188- if ( this . hasIncompleteDevinBlock ( this . llmBuffer ) ) {
199+ if ( this . hasIncompleteDevinBlock ( this . reasoningBuffer ) ) {
189200 return ; // Don't output anything yet, wait for more chunks
190201 }
191202
192203 // Process the buffer to filter out devin blocks
193- const processedContent = this . filterDevinBlocks ( this . llmBuffer ) ;
204+ const processedContent = this . filterDevinBlocks ( this . reasoningBuffer ) ;
194205
195206 // Only output new content that hasn't been printed yet
196207 if ( processedContent . length > 0 ) {
@@ -204,49 +215,15 @@ export class ServerRenderer {
204215 }
205216 }
206217
207- private hasIncompleteDevinBlock ( content : string ) : boolean {
208- // Check if there's an incomplete devin block
209- const lastOpenDevin = content . lastIndexOf ( '<devin' ) ;
210- const lastCloseDevin = content . lastIndexOf ( '</devin>' ) ;
211-
212- // If we have an opening tag without a closing tag after it, it's incomplete
213- // Also check for partial opening tags like '<de' or '<dev' or just '<'
214- const partialDevinPattern = / < d e (?: v (?: i (?: n ) ? ) ? ) ? $ | < $ / ;
215- const hasPartialTag = partialDevinPattern . test ( content ) ;
216-
217- return lastOpenDevin > lastCloseDevin || hasPartialTag ;
218- }
219-
220- private filterDevinBlocks ( content : string ) : string {
221- // Remove all complete devin blocks
222- let filtered = content . replace ( / < d e v i n [ ^ > ] * > [ \s \S ] * ?< \/ d e v i n > / g, '' ) ;
223-
224- // Handle incomplete devin blocks at the end - remove them completely
225- const openDevinIndex = filtered . lastIndexOf ( '<devin' ) ;
226- if ( openDevinIndex !== - 1 ) {
227- const closeDevinIndex = filtered . indexOf ( '</devin>' , openDevinIndex ) ;
228- if ( closeDevinIndex === - 1 ) {
229- // Incomplete devin block, remove it
230- filtered = filtered . substring ( 0 , openDevinIndex ) ;
231- }
232- }
233-
234- // Also remove partial devin tags at the end and any standalone '<' that might be part of a devin tag
235- const partialDevinPattern = / < d e (?: v (?: i (?: n ) ? ) ? ) ? $ | < $ / ;
236- filtered = filtered . replace ( partialDevinPattern , '' ) ;
237-
238- return filtered ;
239- }
240-
241218 // ============================================================================
242219 // Tool Execution Methods (JsCodingAgentRenderer)
243220 // ============================================================================
244221
245222 renderToolCall ( toolName : string , params : string ) : void {
246223 // Flush any buffered LLM output first
247- if ( this . llmBuffer . trim ( ) ) {
224+ if ( this . reasoningBuffer . trim ( ) ) {
248225 console . log ( '' ) ; // New line before tool
249- this . llmBuffer = '' ;
226+ this . reasoningBuffer = '' ;
250227 this . hasStartedLLMOutput = false ;
251228 this . lastOutputLength = 0 ;
252229 }
@@ -480,9 +457,9 @@ export class ServerRenderer {
480457 this . maxIterations = max ;
481458
482459 // Flush any buffered LLM output
483- if ( this . llmBuffer . trim ( ) ) {
460+ if ( this . reasoningBuffer . trim ( ) ) {
484461 console . log ( '' ) ; // Just a newline
485- this . llmBuffer = '' ;
462+ this . reasoningBuffer = '' ;
486463 }
487464
488465 // Reset LLM output state for new iteration
@@ -501,14 +478,14 @@ export class ServerRenderer {
501478 edits : AgentEditInfo [ ]
502479 ) : void {
503480 // Flush any buffered LLM output
504- if ( this . llmBuffer . trim ( ) ) {
505- const finalContent = this . filterDevinBlocks ( this . llmBuffer ) ;
481+ if ( this . reasoningBuffer . trim ( ) ) {
482+ const finalContent = this . filterDevinBlocks ( this . reasoningBuffer ) ;
506483 const remainingContent = finalContent . slice ( this . lastOutputLength || 0 ) ;
507484 if ( remainingContent . trim ( ) ) {
508485 process . stdout . write ( remainingContent ) ;
509486 }
510487 console . log ( '' ) ; // Ensure newline
511- this . llmBuffer = '' ;
488+ this . reasoningBuffer = '' ;
512489 }
513490
514491 console . log ( '' ) ;
0 commit comments