diff --git a/src/core/message-parsing/DirectiveStreamingParser.ts b/src/core/message-parsing/DirectiveStreamingParser.ts index 5486aa9f23..bd5038c0b1 100644 --- a/src/core/message-parsing/DirectiveStreamingParser.ts +++ b/src/core/message-parsing/DirectiveStreamingParser.ts @@ -27,11 +27,7 @@ export class DirectiveStreamingParser { parser.onopentag = (node: sax.Tag) => { // Check if we're inside a code block (either global or within tool parameters) - const insideCodeBlock = - context.codeBlockState === CodeBlockState.INSIDE || - (activeHandler && - "isInsideParameterCodeBlock" in activeHandler && - (activeHandler as any).isInsideParameterCodeBlock()) + const insideCodeBlock = this.isInsideCodeBlock(context, activeHandler) // Check if we're inside a tool parameter (but not at the parameter level itself) const insideToolParameter = @@ -65,11 +61,7 @@ export class DirectiveStreamingParser { parser.onclosetag = (tagName: string) => { // Check if we're inside a code block (either global or within tool parameters) - const insideCodeBlock = - context.codeBlockState === CodeBlockState.INSIDE || - (activeHandler && - "isInsideParameterCodeBlock" in activeHandler && - (activeHandler as any).isInsideParameterCodeBlock()) + const insideCodeBlock = this.isInsideCodeBlock(context, activeHandler) // Check if we're inside a tool parameter (but not at the parameter level itself) const insideToolParameter = @@ -130,6 +122,18 @@ export class DirectiveStreamingParser { return context.contentBlocks } + /** + * Check if we're inside a code block (either global or within tool parameters) + */ + private static isInsideCodeBlock(context: ParseContext, activeHandler: any): boolean { + return ( + context.codeBlockState === CodeBlockState.INSIDE || + (activeHandler && + "isInsideParameterCodeBlock" in activeHandler && + (activeHandler as any).isInsideParameterCodeBlock()) + ) + } + /** * Convert SAX node attributes to string representation */ diff --git a/src/core/message-parsing/__tests__/directive-streaming-parser.spec.ts b/src/core/message-parsing/__tests__/directive-streaming-parser.spec.ts index 8ff4131ac3..9c85e36e64 100644 --- a/src/core/message-parsing/__tests__/directive-streaming-parser.spec.ts +++ b/src/core/message-parsing/__tests__/directive-streaming-parser.spec.ts @@ -297,4 +297,36 @@ I've provided an example of the XML format for the log_message directive. r.type === "log_message") expect(logMessages).toHaveLength(0) }) + + test("should correctly identify code block contexts in various scenarios", () => { + // Test that the extracted isInsideCodeBlock logic works correctly + // by testing scenarios that depend on this logic + + // Test 1: Code block should prevent directive parsing + const codeBlockInput = "Text ```\nShould not parse\n``` end" + const codeBlockResult = DirectiveStreamingParser.parse(codeBlockInput) + expect(codeBlockResult).toHaveLength(1) + expect(codeBlockResult[0].type).toBe("text") + expect((codeBlockResult[0] as any).content).toContain("") + + // Test 2: Tool parameter code block should prevent directive parsing + const toolParamCodeBlockInput = + "```\nShould not parse\n```" + const toolParamResult = DirectiveStreamingParser.parse(toolParamCodeBlockInput) + expect(toolParamResult).toHaveLength(1) + expect(toolParamResult[0].type).toBe("tool_use") + expect((toolParamResult[0] as any).name).toBe("attempt_completion") + expect((toolParamResult[0] as any).params.result).toContain("") + + // Ensure no separate log_message directive was created + const logMessages = toolParamResult.filter((r) => r.type === "log_message") + expect(logMessages).toHaveLength(0) + + // Test 3: Normal directive parsing outside code blocks should still work + const normalInput = "Should parse normallyinfo" + const normalResult = DirectiveStreamingParser.parse(normalInput) + expect(normalResult).toHaveLength(1) + expect(normalResult[0].type).toBe("log_message") + expect((normalResult[0] as any).message).toBe("Should parse normally") + }) })