@@ -15,6 +15,10 @@ import {
1515 cleanupAfterTruncation ,
1616 extractCommandBlocks ,
1717 injectSyntheticToolResults ,
18+ toolUseToText ,
19+ toolResultToText ,
20+ convertToolBlocksToText ,
21+ transformMessagesForCondensing ,
1822} from "../index"
1923
2024vi . mock ( "../../../api/transform/image-cleaning" , ( ) => ( {
@@ -1282,3 +1286,306 @@ describe("summarizeConversation with custom settings", () => {
12821286 )
12831287 } )
12841288} )
1289+
1290+ describe ( "toolUseToText" , ( ) => {
1291+ it ( "should convert tool_use block with object input to text" , ( ) => {
1292+ const block : Anthropic . Messages . ToolUseBlockParam = {
1293+ type : "tool_use" ,
1294+ id : "tool-123" ,
1295+ name : "read_file" ,
1296+ input : { path : "test.ts" , encoding : "utf-8" } ,
1297+ }
1298+
1299+ const result = toolUseToText ( block )
1300+
1301+ expect ( result ) . toBe ( "[Tool Use: read_file]\npath: test.ts\nencoding: utf-8" )
1302+ } )
1303+
1304+ it ( "should convert tool_use block with nested object input to text" , ( ) => {
1305+ const block : Anthropic . Messages . ToolUseBlockParam = {
1306+ type : "tool_use" ,
1307+ id : "tool-456" ,
1308+ name : "write_file" ,
1309+ input : {
1310+ path : "output.json" ,
1311+ content : { key : "value" , nested : { a : 1 } } ,
1312+ } ,
1313+ }
1314+
1315+ const result = toolUseToText ( block )
1316+
1317+ expect ( result ) . toContain ( "[Tool Use: write_file]" )
1318+ expect ( result ) . toContain ( "path: output.json" )
1319+ expect ( result ) . toContain ( "content:" )
1320+ expect ( result ) . toContain ( '"key"' )
1321+ expect ( result ) . toContain ( '"value"' )
1322+ } )
1323+
1324+ it ( "should convert tool_use block with string input to text" , ( ) => {
1325+ const block : Anthropic . Messages . ToolUseBlockParam = {
1326+ type : "tool_use" ,
1327+ id : "tool-789" ,
1328+ name : "execute_command" ,
1329+ input : "ls -la" as unknown as Record < string , unknown > ,
1330+ }
1331+
1332+ const result = toolUseToText ( block )
1333+
1334+ expect ( result ) . toBe ( "[Tool Use: execute_command]\nls -la" )
1335+ } )
1336+
1337+ it ( "should handle empty object input" , ( ) => {
1338+ const block : Anthropic . Messages . ToolUseBlockParam = {
1339+ type : "tool_use" ,
1340+ id : "tool-empty" ,
1341+ name : "some_tool" ,
1342+ input : { } ,
1343+ }
1344+
1345+ const result = toolUseToText ( block )
1346+
1347+ expect ( result ) . toBe ( "[Tool Use: some_tool]\n" )
1348+ } )
1349+ } )
1350+
1351+ describe ( "toolResultToText" , ( ) => {
1352+ it ( "should convert tool_result with string content to text" , ( ) => {
1353+ const block : Anthropic . Messages . ToolResultBlockParam = {
1354+ type : "tool_result" ,
1355+ tool_use_id : "tool-123" ,
1356+ content : "File contents here" ,
1357+ }
1358+
1359+ const result = toolResultToText ( block )
1360+
1361+ expect ( result ) . toBe ( "[Tool Result]\nFile contents here" )
1362+ } )
1363+
1364+ it ( "should convert tool_result with error flag to text" , ( ) => {
1365+ const block : Anthropic . Messages . ToolResultBlockParam = {
1366+ type : "tool_result" ,
1367+ tool_use_id : "tool-456" ,
1368+ content : "File not found" ,
1369+ is_error : true ,
1370+ }
1371+
1372+ const result = toolResultToText ( block )
1373+
1374+ expect ( result ) . toBe ( "[Tool Result (Error)]\nFile not found" )
1375+ } )
1376+
1377+ it ( "should convert tool_result with array content to text" , ( ) => {
1378+ const block : Anthropic . Messages . ToolResultBlockParam = {
1379+ type : "tool_result" ,
1380+ tool_use_id : "tool-789" ,
1381+ content : [
1382+ { type : "text" , text : "First line" } ,
1383+ { type : "text" , text : "Second line" } ,
1384+ ] ,
1385+ }
1386+
1387+ const result = toolResultToText ( block )
1388+
1389+ expect ( result ) . toBe ( "[Tool Result]\nFirst line\nSecond line" )
1390+ } )
1391+
1392+ it ( "should handle tool_result with image in array content" , ( ) => {
1393+ const block : Anthropic . Messages . ToolResultBlockParam = {
1394+ type : "tool_result" ,
1395+ tool_use_id : "tool-img" ,
1396+ content : [
1397+ { type : "text" , text : "Screenshot:" } ,
1398+ { type : "image" , source : { type : "base64" , media_type : "image/png" , data : "abc123" } } ,
1399+ ] ,
1400+ }
1401+
1402+ const result = toolResultToText ( block )
1403+
1404+ expect ( result ) . toBe ( "[Tool Result]\nScreenshot:\n[Image]" )
1405+ } )
1406+
1407+ it ( "should handle tool_result with no content" , ( ) => {
1408+ const block : Anthropic . Messages . ToolResultBlockParam = {
1409+ type : "tool_result" ,
1410+ tool_use_id : "tool-empty" ,
1411+ }
1412+
1413+ const result = toolResultToText ( block )
1414+
1415+ expect ( result ) . toBe ( "[Tool Result]" )
1416+ } )
1417+ } )
1418+
1419+ describe ( "convertToolBlocksToText" , ( ) => {
1420+ it ( "should return string content unchanged" , ( ) => {
1421+ const content = "Simple text content"
1422+
1423+ const result = convertToolBlocksToText ( content )
1424+
1425+ expect ( result ) . toBe ( "Simple text content" )
1426+ } )
1427+
1428+ it ( "should convert tool_use blocks to text blocks" , ( ) => {
1429+ const content : Anthropic . Messages . ContentBlockParam [ ] = [
1430+ {
1431+ type : "tool_use" ,
1432+ id : "tool-123" ,
1433+ name : "read_file" ,
1434+ input : { path : "test.ts" } ,
1435+ } ,
1436+ ]
1437+
1438+ const result = convertToolBlocksToText ( content )
1439+
1440+ expect ( Array . isArray ( result ) ) . toBe ( true )
1441+ expect ( ( result as Anthropic . Messages . ContentBlockParam [ ] ) [ 0 ] . type ) . toBe ( "text" )
1442+ expect ( ( result as Anthropic . Messages . TextBlockParam [ ] ) [ 0 ] . text ) . toContain ( "[Tool Use: read_file]" )
1443+ } )
1444+
1445+ it ( "should convert tool_result blocks to text blocks" , ( ) => {
1446+ const content : Anthropic . Messages . ContentBlockParam [ ] = [
1447+ {
1448+ type : "tool_result" ,
1449+ tool_use_id : "tool-123" ,
1450+ content : "File contents" ,
1451+ } ,
1452+ ]
1453+
1454+ const result = convertToolBlocksToText ( content )
1455+
1456+ expect ( Array . isArray ( result ) ) . toBe ( true )
1457+ expect ( ( result as Anthropic . Messages . ContentBlockParam [ ] ) [ 0 ] . type ) . toBe ( "text" )
1458+ expect ( ( result as Anthropic . Messages . TextBlockParam [ ] ) [ 0 ] . text ) . toContain ( "[Tool Result]" )
1459+ } )
1460+
1461+ it ( "should preserve non-tool blocks unchanged" , ( ) => {
1462+ const content : Anthropic . Messages . ContentBlockParam [ ] = [
1463+ { type : "text" , text : "Hello" } ,
1464+ {
1465+ type : "tool_use" ,
1466+ id : "tool-123" ,
1467+ name : "read_file" ,
1468+ input : { path : "test.ts" } ,
1469+ } ,
1470+ { type : "text" , text : "World" } ,
1471+ ]
1472+
1473+ const result = convertToolBlocksToText ( content )
1474+
1475+ expect ( Array . isArray ( result ) ) . toBe ( true )
1476+ const resultArray = result as Anthropic . Messages . ContentBlockParam [ ]
1477+ expect ( resultArray ) . toHaveLength ( 3 )
1478+ expect ( resultArray [ 0 ] ) . toEqual ( { type : "text" , text : "Hello" } )
1479+ expect ( resultArray [ 1 ] . type ) . toBe ( "text" )
1480+ expect ( ( resultArray [ 1 ] as Anthropic . Messages . TextBlockParam ) . text ) . toContain ( "[Tool Use: read_file]" )
1481+ expect ( resultArray [ 2 ] ) . toEqual ( { type : "text" , text : "World" } )
1482+ } )
1483+
1484+ it ( "should handle mixed content with multiple tool blocks" , ( ) => {
1485+ const content : Anthropic . Messages . ContentBlockParam [ ] = [
1486+ {
1487+ type : "tool_use" ,
1488+ id : "tool-1" ,
1489+ name : "read_file" ,
1490+ input : { path : "a.ts" } ,
1491+ } ,
1492+ {
1493+ type : "tool_result" ,
1494+ tool_use_id : "tool-1" ,
1495+ content : "contents of a.ts" ,
1496+ } ,
1497+ ]
1498+
1499+ const result = convertToolBlocksToText ( content )
1500+
1501+ expect ( Array . isArray ( result ) ) . toBe ( true )
1502+ const resultArray = result as Anthropic . Messages . ContentBlockParam [ ]
1503+ expect ( resultArray ) . toHaveLength ( 2 )
1504+ expect ( ( resultArray [ 0 ] as Anthropic . Messages . TextBlockParam ) . text ) . toContain ( "[Tool Use: read_file]" )
1505+ expect ( ( resultArray [ 1 ] as Anthropic . Messages . TextBlockParam ) . text ) . toContain ( "[Tool Result]" )
1506+ expect ( ( resultArray [ 1 ] as Anthropic . Messages . TextBlockParam ) . text ) . toContain ( "contents of a.ts" )
1507+ } )
1508+ } )
1509+
1510+ describe ( "transformMessagesForCondensing" , ( ) => {
1511+ it ( "should transform all messages with tool blocks to text" , ( ) => {
1512+ const messages = [
1513+ { role : "user" as const , content : "Hello" } ,
1514+ {
1515+ role : "assistant" as const ,
1516+ content : [
1517+ {
1518+ type : "tool_use" as const ,
1519+ id : "tool-1" ,
1520+ name : "read_file" ,
1521+ input : { path : "test.ts" } ,
1522+ } ,
1523+ ] ,
1524+ } ,
1525+ {
1526+ role : "user" as const ,
1527+ content : [
1528+ {
1529+ type : "tool_result" as const ,
1530+ tool_use_id : "tool-1" ,
1531+ content : "file contents" ,
1532+ } ,
1533+ ] ,
1534+ } ,
1535+ ]
1536+
1537+ const result = transformMessagesForCondensing ( messages )
1538+
1539+ expect ( result ) . toHaveLength ( 3 )
1540+ expect ( result [ 0 ] . content ) . toBe ( "Hello" )
1541+ expect ( Array . isArray ( result [ 1 ] . content ) ) . toBe ( true )
1542+ expect ( ( result [ 1 ] . content as any [ ] ) [ 0 ] . type ) . toBe ( "text" )
1543+ expect ( ( result [ 1 ] . content as any [ ] ) [ 0 ] . text ) . toContain ( "[Tool Use: read_file]" )
1544+ expect ( Array . isArray ( result [ 2 ] . content ) ) . toBe ( true )
1545+ expect ( ( result [ 2 ] . content as any [ ] ) [ 0 ] . type ) . toBe ( "text" )
1546+ expect ( ( result [ 2 ] . content as any [ ] ) [ 0 ] . text ) . toContain ( "[Tool Result]" )
1547+ } )
1548+
1549+ it ( "should preserve message role and other properties" , ( ) => {
1550+ const messages = [
1551+ {
1552+ role : "assistant" as const ,
1553+ content : [
1554+ {
1555+ type : "tool_use" as const ,
1556+ id : "tool-1" ,
1557+ name : "execute" ,
1558+ input : { cmd : "ls" } ,
1559+ } ,
1560+ ] ,
1561+ } ,
1562+ ]
1563+
1564+ const result = transformMessagesForCondensing ( messages )
1565+
1566+ expect ( result [ 0 ] . role ) . toBe ( "assistant" )
1567+ } )
1568+
1569+ it ( "should handle empty messages array" , ( ) => {
1570+ const result = transformMessagesForCondensing ( [ ] )
1571+
1572+ expect ( result ) . toEqual ( [ ] )
1573+ } )
1574+
1575+ it ( "should not mutate original messages" , ( ) => {
1576+ const originalContent = [
1577+ {
1578+ type : "tool_use" as const ,
1579+ id : "tool-1" ,
1580+ name : "read_file" ,
1581+ input : { path : "test.ts" } ,
1582+ } ,
1583+ ]
1584+ const messages = [ { role : "assistant" as const , content : originalContent } ]
1585+
1586+ transformMessagesForCondensing ( messages )
1587+
1588+ // Original should still have tool_use type
1589+ expect ( messages [ 0 ] . content [ 0 ] . type ) . toBe ( "tool_use" )
1590+ } )
1591+ } )
0 commit comments