@@ -1188,6 +1188,283 @@ const extSemanticPrompts: Probe = {
11881188 } ,
11891189}
11901190
1191+ const extOsc633Vscode : Probe = {
1192+ id : "extensions.osc-633-vscode" ,
1193+ name : "VS Code shell integration (OSC 633)" ,
1194+ async run ( ) {
1195+ // OSC 633 is VS Code's shell integration markers (prompt, command, output)
1196+ process . stdout . write ( "\x1b]633;A\x07" ) // prompt start
1197+ process . stdout . write ( "\x1b]633;B\x07" ) // prompt end
1198+ process . stdout . write ( "\x1b]633;C\x07" ) // pre-execution
1199+ process . stdout . write ( "\x1b]633;D;0\x07" ) // execution finished (exit code 0)
1200+ const pos = await queryCursorPosition ( )
1201+ return {
1202+ pass : pos !== null ,
1203+ note : pos ? undefined : "No cursor response after OSC 633" ,
1204+ }
1205+ } ,
1206+ }
1207+
1208+ const extNotifications : Probe = {
1209+ id : "extensions.notifications" ,
1210+ name : "Desktop notifications (OSC 9)" ,
1211+ async run ( ) {
1212+ // OSC 9 sends a desktop notification (used by ConEmu, iTerm2, etc.)
1213+ process . stdout . write ( "\x1b]9;Test\x07" )
1214+ const pos = await queryCursorPosition ( )
1215+ return {
1216+ pass : pos !== null ,
1217+ note : pos ? undefined : "No cursor response after OSC 9" ,
1218+ }
1219+ } ,
1220+ }
1221+
1222+ const extIterm2Images : Probe = {
1223+ id : "extensions.iterm2-images" ,
1224+ name : "iTerm2 inline images (OSC 1337)" ,
1225+ async run ( ) {
1226+ // Send a minimal iTerm2 inline image sequence
1227+ process . stdout . write ( "\x1b]1337;File=inline=1:AAAA\x07" )
1228+ const pos = await queryCursorPosition ( )
1229+ return {
1230+ pass : pos !== null ,
1231+ note : pos ? undefined : "No cursor response after OSC 1337" ,
1232+ }
1233+ } ,
1234+ }
1235+
1236+ // ═══════════════════════════════════════════════════════════════════════════
1237+ // ── Device probes (additional) ──
1238+ // ═══════════════════════════════════════════════════════════════════════════
1239+
1240+ const secondaryDA : Probe = {
1241+ id : "device.secondary-da" ,
1242+ name : "Secondary device attributes (DA2)" ,
1243+ async run ( ) {
1244+ const match = await query ( "\x1b[>c" , / \x1b \[ > ( [ 0 - 9 ; ] + ) c / , 1000 )
1245+ if ( ! match ) return { pass : false , note : "No DA2 response" }
1246+ return { pass : true , response : match [ 0 ] }
1247+ } ,
1248+ }
1249+
1250+ const tertiaryDA : Probe = {
1251+ id : "device.tertiary-da" ,
1252+ name : "Tertiary device attributes (DA3)" ,
1253+ async run ( ) {
1254+ // DA3 responds with DCS ! | <unit-id> ST
1255+ const match = await queryWithSentinel ( "\x1b[=c" , / \x1b P ! ? \| ( [ ^ \x1b ] * ) \x1b \\ / )
1256+ if ( match ) return { pass : true , response : match [ 1 ] }
1257+ return { pass : false , note : "No DA3 response" }
1258+ } ,
1259+ }
1260+
1261+ // ═══════════════════════════════════════════════════════════════════════════
1262+ // ── Input protocol probes ──
1263+ // ═══════════════════════════════════════════════════════════════════════════
1264+
1265+ const inputModifyOtherKeys : Probe = {
1266+ id : "input.modify-other-keys" ,
1267+ name : "Modify other keys (mode 2)" ,
1268+ async run ( ) {
1269+ // Enable modifyOtherKeys mode 2 (xterm CSI u-style reporting)
1270+ process . stdout . write ( "\x1b[>4;2m" )
1271+ const pos = await queryCursorPosition ( )
1272+ // Disable modifyOtherKeys
1273+ process . stdout . write ( "\x1b[>4;0m" )
1274+ return {
1275+ pass : pos !== null ,
1276+ note : pos ? undefined : "No cursor response after enabling modifyOtherKeys" ,
1277+ }
1278+ } ,
1279+ }
1280+
1281+ const inputPixelMouse : Probe = {
1282+ id : "input.pixel-mouse" ,
1283+ name : "Pixel mouse mode (1016)" ,
1284+ async run ( ) {
1285+ process . stdout . write ( "\x1b[?1016h" ) // enable pixel mouse
1286+ const pos = await queryCursorPosition ( )
1287+ process . stdout . write ( "\x1b[?1016l" ) // disable
1288+ return {
1289+ pass : pos !== null ,
1290+ note : pos ? undefined : "No cursor response after enabling pixel mouse" ,
1291+ }
1292+ } ,
1293+ }
1294+
1295+ const inputUrxvtMouse : Probe = {
1296+ id : "input.urxvt-mouse" ,
1297+ name : "urxvt mouse mode (1015)" ,
1298+ async run ( ) {
1299+ process . stdout . write ( "\x1b[?1015h" ) // enable urxvt mouse
1300+ const pos = await queryCursorPosition ( )
1301+ process . stdout . write ( "\x1b[?1015l" ) // disable
1302+ return {
1303+ pass : pos !== null ,
1304+ note : pos ? undefined : "No cursor response after enabling urxvt mouse" ,
1305+ }
1306+ } ,
1307+ }
1308+
1309+ const inputX10Mouse : Probe = {
1310+ id : "input.x10-mouse" ,
1311+ name : "X10 mouse mode (9)" ,
1312+ async run ( ) {
1313+ process . stdout . write ( "\x1b[?9h" ) // enable X10 mouse
1314+ const pos = await queryCursorPosition ( )
1315+ process . stdout . write ( "\x1b[?9l" ) // disable
1316+ return {
1317+ pass : pos !== null ,
1318+ note : pos ? undefined : "No cursor response after enabling X10 mouse" ,
1319+ }
1320+ } ,
1321+ }
1322+
1323+ const inputButtonEventMouse : Probe = {
1324+ id : "input.button-event-mouse" ,
1325+ name : "Button-event mouse mode (1002)" ,
1326+ async run ( ) {
1327+ process . stdout . write ( "\x1b[?1002h" ) // enable button-event mouse
1328+ const pos = await queryCursorPosition ( )
1329+ process . stdout . write ( "\x1b[?1002l" ) // disable
1330+ return {
1331+ pass : pos !== null ,
1332+ note : pos ? undefined : "No cursor response after enabling button-event mouse" ,
1333+ }
1334+ } ,
1335+ }
1336+
1337+ // ═══════════════════════════════════════════════════════════════════════════
1338+ // ── Mode probes (additional) ──
1339+ // ═══════════════════════════════════════════════════════════════════════════
1340+
1341+ const modesLeftRightMargin : Probe = {
1342+ id : "modes.left-right-margin" ,
1343+ name : "Left/right margin mode (DECLRMM)" ,
1344+ async run ( ) {
1345+ process . stdout . write ( "\x1b[?69h" ) // enable DECLRMM
1346+ const pos = await queryCursorPosition ( )
1347+ process . stdout . write ( "\x1b[?69l" ) // disable
1348+ return {
1349+ pass : pos !== null ,
1350+ note : pos ? undefined : "No cursor response after DECLRMM" ,
1351+ }
1352+ } ,
1353+ }
1354+
1355+ // ═══════════════════════════════════════════════════════════════════════════
1356+ // ── Cursor probes (additional) ──
1357+ // ═══════════════════════════════════════════════════════════════════════════
1358+
1359+ const cursorReverseWrap : Probe = {
1360+ id : "cursor.reverse-wrap" ,
1361+ name : "Reverse wrap mode (DECSET 45)" ,
1362+ async run ( ) {
1363+ process . stdout . write ( "\x1b[?45h" ) // enable reverse wrap
1364+ const pos = await queryCursorPosition ( )
1365+ process . stdout . write ( "\x1b[?45l" ) // disable
1366+ return {
1367+ pass : pos !== null ,
1368+ note : pos ? undefined : "No cursor response after enabling reverse wrap" ,
1369+ }
1370+ } ,
1371+ }
1372+
1373+ // ═══════════════════════════════════════════════════════════════════════════
1374+ // ── Erase probes (additional) ──
1375+ // ═══════════════════════════════════════════════════════════════════════════
1376+
1377+ const eraseSelective : Probe = {
1378+ id : "erase.selective" ,
1379+ name : "Selective erase (DECSED)" ,
1380+ async run ( ) {
1381+ process . stdout . write ( "\x1b[1;1H\x1b[2K" )
1382+ process . stdout . write ( "ABCDE" )
1383+ process . stdout . write ( "\x1b[?2J" ) // DECSED — selective erase entire screen
1384+ const pos = await queryCursorPosition ( )
1385+ if ( ! pos ) return { pass : false , note : "No cursor response after DECSED" }
1386+ return { pass : true }
1387+ } ,
1388+ }
1389+
1390+ // ═══════════════════════════════════════════════════════════════════════════
1391+ // ── Text probes (additional) ──
1392+ // ═══════════════════════════════════════════════════════════════════════════
1393+
1394+ const textReverseIndexScroll : Probe = {
1395+ id : "text.reverse-index-scroll" ,
1396+ name : "Reverse index scrolls at top of region" ,
1397+ async run ( ) {
1398+ // Set scroll region, move to top of region, RI should scroll content down
1399+ process . stdout . write ( "\x1b[3;10r" ) // scroll region rows 3-10
1400+ process . stdout . write ( "\x1b[3;1H" ) // move to row 3 (top of region)
1401+ process . stdout . write ( "\x1bM" ) // RI — reverse index at top of region
1402+ const pos = await queryCursorPosition ( )
1403+ process . stdout . write ( "\x1b[r" ) // reset scroll region
1404+ if ( ! pos ) return { pass : false , note : "No cursor response after RI in region" }
1405+ // Cursor should stay at row 3 (content scrolled down within region)
1406+ return {
1407+ pass : pos [ 0 ] === 3 ,
1408+ note : pos [ 0 ] === 3 ? undefined : `cursor at row ${ pos [ 0 ] } , expected 3` ,
1409+ }
1410+ } ,
1411+ }
1412+
1413+ // ═══════════════════════════════════════════════════════════════════════════
1414+ // ── Unicode probes ──
1415+ // ═══════════════════════════════════════════════════════════════════════════
1416+
1417+ const unicodeEastAsianAmbiguous : Probe = {
1418+ id : "unicode.east-asian-ambiguous" ,
1419+ name : "East Asian ambiguous width" ,
1420+ async run ( ) {
1421+ // ● (U+25CF BLACK CIRCLE) is East Asian Ambiguous — most terminals render as 1 col
1422+ // Write ●X and check cursor position
1423+ const width = await measureRenderedWidth ( "●" )
1424+ if ( width === null ) return { pass : false , note : "Cannot measure width" }
1425+ return {
1426+ pass : width === 1 || width === 2 ,
1427+ note : `width=${ width } (ambiguous chars vary by terminal/locale)` ,
1428+ response : String ( width ) ,
1429+ }
1430+ } ,
1431+ }
1432+
1433+ const unicodeWrapBoundary : Probe = {
1434+ id : "unicode.wrap-boundary" ,
1435+ name : "Wide char wrap at line boundary" ,
1436+ async run ( ) {
1437+ const cols = process . stdout . columns || 80
1438+ // Write (cols-1) narrow chars, then a wide char — should wrap to next line
1439+ process . stdout . write ( "\x1b[1;1H\x1b[2J" )
1440+ process . stdout . write ( "A" . repeat ( cols - 1 ) )
1441+ process . stdout . write ( "\u4e2d" ) // CJK char (2 cols wide)
1442+ const pos = await queryCursorPosition ( )
1443+ if ( ! pos ) return { pass : false , note : "No cursor response" }
1444+ // Wide char should wrap: cursor at row 2, col 3 (wide char on col 1-2 of row 2)
1445+ return {
1446+ pass : pos [ 0 ] === 2 ,
1447+ note : pos [ 0 ] === 2 ? undefined : `cursor at row ${ pos [ 0 ] } , expected 2 (wide char should wrap)` ,
1448+ }
1449+ } ,
1450+ }
1451+
1452+ const unicodeTabStops : Probe = {
1453+ id : "unicode.tab-stops" ,
1454+ name : "Tab stops with text" ,
1455+ async run ( ) {
1456+ process . stdout . write ( "\x1b[1;1H\x1b[2K" )
1457+ process . stdout . write ( "A\tB" )
1458+ const pos = await queryCursorPosition ( )
1459+ if ( ! pos ) return { pass : false , note : "No cursor response" }
1460+ // A at col 1, tab to col 9, B at col 9 → cursor at col 10
1461+ return {
1462+ pass : pos [ 1 ] === 10 ,
1463+ note : pos [ 1 ] === 10 ? undefined : `cursor at col ${ pos [ 1 ] } , expected 10 (A + tab to 9 + B)` ,
1464+ }
1465+ } ,
1466+ }
1467+
11911468// ═══════════════════════════════════════════════════════════════════════════
11921469// ── All probes ──
11931470// ═══════════════════════════════════════════════════════════════════════════
@@ -1206,9 +1483,12 @@ export const ALL_PROBES: Probe[] = [
12061483 cursorMoveHome ,
12071484 cursorHorizontalAbsolute ,
12081485 cursorNextLine ,
1486+ cursorReverseWrap ,
12091487
12101488 // ── Device ──
12111489 primaryDA ,
1490+ secondaryDA ,
1491+ tertiaryDA ,
12121492 deviceStatusReport ,
12131493 deviceDecrpm ,
12141494
@@ -1228,6 +1508,7 @@ export const ALL_PROBES: Probe[] = [
12281508 combiningChars ,
12291509 tabStop ,
12301510 backspace ,
1511+ textReverseIndexScroll ,
12311512
12321513 // ── Erase ──
12331514 eraseLineRight ,
@@ -1238,6 +1519,7 @@ export const ALL_PROBES: Probe[] = [
12381519 eraseScreenAbove ,
12391520 eraseScreenScrollback ,
12401521 eraseCharacter ,
1522+ eraseSelective ,
12411523
12421524 // ── Editing ──
12431525 insertChars ,
@@ -1252,6 +1534,7 @@ export const ALL_PROBES: Probe[] = [
12521534 modesBracketedPaste ,
12531535 modesInsertReplace ,
12541536 modesApplicationKeypad ,
1537+ modesLeftRightMargin ,
12551538
12561539 // ── Modes (DECRPM with behavioral fallback) ──
12571540 behavioralModeProbe (
@@ -1378,6 +1661,21 @@ export const ALL_PROBES: Probe[] = [
13781661 extOsc8Hyperlink ,
13791662 extOsc0IconTitle ,
13801663 extSemanticPrompts ,
1664+ extOsc633Vscode ,
1665+ extNotifications ,
1666+ extIterm2Images ,
1667+
1668+ // ── Input protocols ──
1669+ inputModifyOtherKeys ,
1670+ inputPixelMouse ,
1671+ inputUrxvtMouse ,
1672+ inputX10Mouse ,
1673+ inputButtonEventMouse ,
1674+
1675+ // ── Unicode ──
1676+ unicodeEastAsianAmbiguous ,
1677+ unicodeWrapBoundary ,
1678+ unicodeTabStops ,
13811679
13821680 // ── Previously "untestable" features ──
13831681
0 commit comments