claude-codes 2.1.310

Typed Rust SDK for the Claude Code agent CLI: serde models of its JSON-Lines (stream-json) protocol with streaming parsing, plus sync and async (Tokio) clients for driving Claude Code sessions, tools, and subagents.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.1.310] - 2026-10-23

Re-baseline against Claude CLI **2.1.310**. Two optional nested fields; no new
message types and no removals. `--help` and the embedded model-id set are
unchanged.

### Added

- `UnifiedWindowUsage::status` — the status the server sent for each
  `rate_limit_info.unifiedWindows` entry (`allowed_warning` past a warning
  threshold, `rejected` at or past the window's limit). A rejected window alone
  does not mean requests are refused; the top-level status still decides that.
- `UnifiedWindowUsage::surpassed_threshold` — the highest warning threshold
  passed for the window, as a fraction of its limit (1.0 once rejected).

### Breaking

- The new public fields on `UnifiedWindowUsage` break struct-literal
  construction.

## [2.1.309] - 2026-10-22

Re-baseline against Claude CLI **2.1.309**. One optional field; no new message
types and no removals. `--help` and the embedded model-id set are unchanged.

### Added

- `ResultMessage::model_usage_since_last_result` — per-model usage and cost
  of the calls counted since the previous result, keyed and shaped like
  `modelUsage` (`ModelUsageEntry`). Sum it across a conversation's results
  instead of subtracting `modelUsage` totals. Carried on both success and
  error results (`@internal`).

### Breaking

- The new public field on `ResultMessage` breaks struct-literal construction.

## [2.1.308] - 2026-10-22

Re-baseline against Claude CLI **2.1.308**. The changes below arrived in CLI
2.1.307; 2.1.308's extracted wire schemas are byte-identical to 2.1.307's.
Optional fields only; no new message types and no removals. `--help` and the
embedded model-id set are unchanged.

### Added

- `CommandLifecycleMessage::wakeup_source` (`CommandWakeupSource`:
  `loop_wakeup` / `schedule_wakeup`, open set) and
  `CommandLifecycleMessage::scheduled_for` — on `started` frames for scheduler
  fires.
- `UserMessage::requeue_if_interrupted_within_ms` — input-only request to
  requeue a prompt when an interrupt crosses with the turn taking it in.
- `RateLimitInfo::admin_request_offer` (`AdminRequestOffer`,
  `AdminRequestOfferState`) — the IDE limit banner's offer to ask an admin for
  a higher spend limit (`@internal`).
- `RemedyFeatureCause::UnsupportedRuntime` (`unsupported_runtime`).
- `empty_completion` documented as an `api_error` kind.

### Breaking

- New public fields on `UserMessage`, `CommandLifecycleMessage`, and
  `RateLimitInfo` break struct-literal construction, and the new
  `RemedyFeatureCause` variant breaks exhaustive matches.

## [2.1.306] - 2026-10-20

Re-baseline against Claude CLI **2.1.306**. No wire changes: the key, enum,
and literal set-diffs between the 2.1.304 and 2.1.306 schema extracts are
empty, the per-block diff shows only minifier renames, and `--help` and the
embedded model-id set are unchanged.

## [2.1.305] - 2026-10-20

Crate-side release; still tested against Claude CLI **2.1.304**.

### Added

- `ClaudeOutput::ControlCancelRequest(ControlCancelRequest)` for the
  `control_cancel_request` frame (#489). The CLI sends it to withdraw one of
  its own in-flight `control_request`s, such as a `can_use_tool` prompt after
  the turn was interrupted or after another client answered it. The CLI
  ignores any later `control_response` for that `request_id`, and the cancel
  itself gets no reply. Fields: `request_id`, plus an optional `session_id`
  (stamped only by the remote bridge). Also adds
  `is_control_cancel_request()` and `as_control_cancel_request()`.
  Previously this frame failed to deserialize.

### Breaking

- `ClaudeOutput` gains a variant, so exhaustive `match`es on it need a new arm.

## [2.1.304] - 2026-10-19

Re-baseline against Claude CLI **2.1.304**.

### Added

- `RateLimitInfo.usage_credit_offer: Option<UsageCreditOffer>` (wire
  `usageCreditOffer`, `@internal`): a limited-time usage-credit offer for the
  IDE extension's limit banner, with `kind` (`UsageCreditOfferKind::PctOff` /
  `ExtraCredit`, open set), `headline_pct`, `days_left` and `label`. Present
  only on rejected frames from VS Code extension sessions.

### Notes

- The `get_context_usage` control reply's category rows gained an optional
  `id` (`system_prompt`, `messages`, `free_space`, …). The crate does not model
  that reply. The assistant `context_usage` rows (`ContextCategory`) are
  unchanged.
- The `system/init.cloud_session` docs list a new `device-mcp-policy` notice
  key (an open set). `cloud_session` is carried as an opaque `Value`, so no
  type change is needed.
- `--help` changes are wording only (no new or removed flags), and the
  model-id set is identical to 2.1.303.

## [2.1.303] - 2026-10-19

Re-baseline against Claude CLI **2.1.303**. No stream-json drift: the
key, enum and literal sets of every extracted schema are identical to
2.1.302, the identifier-normalized schema extract is byte-identical, and
the `.describe()` strings, model-id set and `--help` output are unchanged.

## [2.1.302] - 2026-10-16

Re-baseline against Claude CLI **2.1.302**. No stream-json drift: the
key, enum and literal sets of every extracted schema are identical to
2.1.301, and a label-keyed block diff shows only minifier renames. The
model-id set, `--help` output and registered CLI flags are unchanged.

### Changed

- `SubagentStats::requested` rustdoc now lists when the `run_in_background`
  parameter is not offered (background tasks disabled, or subagents forced
  into the background in a terminal, the desktop app, the VS Code extension,
  or with `CLAUDE_CODE_FORK_SUBAGENT=1`), matching the CLI's updated
  schema description.

## [2.1.301] - 2026-10-15

Re-baseline against Claude CLI **2.1.301**. Models the 2.1.300 → 2.1.301
stream-json drift: three optional fields, no new message types and no
removals. The model-id set, `--help` output and registered CLI flags are
unchanged.

### Added

- `AssistantMessage::refused_message_id`: on the assistant message that
  reports a refusal, the API `msg_*` id of the refused response (the
  message's own `message.id` is a local UUID). Mirrors the existing
  `UserMessage::refused_message_id`.
- `InitMessage::original_cwd`: the folder the session's file permission
  checks are anchored at. Unlike `cwd` it does not follow the shell.
- `InitMessage::additional_directories`: absolute paths of folders added to
  the session (`--add-dir`, SDK option, settings, grants). `None` means not
  told; `Some(vec![])` means none.
- `AssistantUsage::speed` (`"standard"` / `"fast"`). Already in the CLI's
  usage schema and on `UsageInfo`; the live wrapping audit caught the API
  now sending it on per-message usage.

### Changed

- Stream-json schema snapshot refreshed to CLI 2.1.301.
- Live tool-approval tests now pass `--permission-mode default`. Headless
  sessions on accounts with the auto-mode gate enabled default to `auto`,
  which lets the classifier approve the edits/reads these tests expect to be
  prompted for (seen on both 2.1.300 and 2.1.301, so not a CLI-version change).

## [2.1.300] - 2026-10-14

Re-baseline against Claude CLI **2.1.300**. Models the 2.1.296 → 2.1.300
stream-json drift: optional fields only, no new message types and no
removals. The model-id set, alias table and `--model` argument list are
unchanged; `--help` differs only in the `--restricted` prose (not modeled by
this crate).

### Added

- `AssistantMessage::api_error_origin` and `ResultMessage::api_error_origin`.
  `first_party` when the API error came straight from Anthropic's own API
  endpoint; absent means unknown origin. Open set.
- `ToolUseMeta::server_id` (claude.ai `mcpsrv_` connector id),
  `ToolUseMeta::server_id_stamp` (an http entry's `X-MCP-Server-ID` claim)
  and `ToolUseMeta::tool_name` (the tool's name without the `mcp__` prefix).
- `LocalCommandRun::plugin`: the plugin that registered the command, or the
  plugins whose `command.run` hooks answered it.
- `CommandLifecycleMessage::ran_ahead`: an `immediate` command's plugin hooks
  answered ahead of its turn.
- `ResultMessage::interrupted_by_shutdown`: tells a multi-tenant worker
  shutdown apart from a client's Stop.
- `ResultMessage::handoff_intake_phases_ms`: how a `turn_handoff` request's
  wait before it was read splits up.
- `ResultMessage` process counters, sent only by Anthropic-hosted workers:
  `process_rss_mib_turn_start`, `process_anon_mib_turn_start`,
  `process_rss_mib_result`, `process_anon_mib_result`,
  `process_peak_rss_mib`, `process_cpu_ms_between_turns`,
  `process_wall_ms_between_turns`, `process_cpu_ms_turn` and
  `process_wall_ms_turn`.
- `InputAttachmentsDetail::touch_compare` and the new `TouchCompareCounts`
  type: how the `changed_files` producer's compares ended.
- `InformationalMessage::prompt_submit_outcome` with the new
  `PromptSubmitOutcome` type and open-set `PromptSubmitOutcomeKind` enum:
  how a submitted prompt ended before a model call (blocked, stopped or
  dropped by a hook, not passed on, or no verdict).
- `TurnHandoffAvailableMessage::shared_message_id`: capability marker for a
  worker that accepts consecutive assistant messages sharing a message id.
- `UiInvalidateMessage::prompts_screened`: a mod may change what is recorded
  of a typed line.
- `UserMessage::text_not_typed`: no person typed this message's text.

### Changed

- Tested against Claude CLI **2.1.300** (was 2.1.296); stream-json snapshot
  refreshed. The crate version now matches the tested CLI again.
- `MessageOrigin` absorbs the new `peer` origin keys (`flagged`, `members`)
  and the `task-notification` `taskKind: "mcp-task"` through its open `extra`
  map; no type change needed.

## [2.1.297] - 2026-10-10

Re-baseline against Claude CLI **2.1.296**. Models the 2.1.295 → 2.1.296
stream-json drift: two optional fields, no removals. `--help` and the
model-id set are unchanged.

### Added

- `AssistantMessage::thinking_display`. The `thinking.display` value Claude
  Code put in the API request body for the response that streamed this
  frame's thinking block (same meaning as
  `StreamEventMessage::thinking_display`). Sent only on a frame whose one
  content block is a streamed thinking block.
- `TurnHandoffAvailableMessage::hydrates_carried_lines`. Capability marker
  for a worker that rebuilt the conversation from the stored transcript
  before reading any request, so a client may re-send it a tool-call-free
  `turn_handoff` an earlier worker accepted.

### Changed

- Tested against Claude CLI **2.1.296** (was 2.1.295); stream-json snapshot
  refreshed.
- `TurnHandoffAvailableMessage::home_files` docs list the
  `unchecked_announcement` and `resent_announcement` noops.

## [2.1.296] - 2026-10-08

Re-baseline against Claude CLI **2.1.295**. Models the 2.1.293 → 2.1.295
stream-json drift (2.1.294 included): three
optional fields, no removals. `--help` and the model-id set are unchanged.

### Added

- `StreamEventMessage::api_message_id`. The API's `msg_…` id of the
  response an event belongs to, set on every event from `message_start` to
  `message_stop` so a consumer that missed the `message_start` can still
  tell responses apart.
- `StreamEventMessage::text_runs_joined`. Set on a response's
  `message_start` when `CLAUDE_CODE_WEBSEARCH_CITATIONS` is on in a
  non-interactive session; adjacent text blocks then arrive as one joined
  assistant text block with `span_start`/`span_end` citation offsets.
- `StatusMessage::compact_output_chars`. Characters of the compaction
  summary streamed so far on an automatic compaction's `compacting`
  statuses.

### Changed

- Tested against Claude CLI **2.1.295** (was 2.1.293); stream-json snapshot
  refreshed.

## [2.1.295] - 2026-10-07

Re-baseline against Claude CLI **2.1.293**. Models the 2.1.292 → 2.1.293
stream-json drift (optional fields and enum values added, one internal
`result` field removed) and adds the new Haiku 5.5 model. `--help` is
byte-identical.

### Added

- `ClaudeModel::Haiku55` (`claude-haiku-5-5`, "Haiku 5.5"). The `haiku`
  alias now resolves to it first-party; Bedrock, Vertex, Foundry and the
  gateway still resolve `haiku` to `claude-haiku-4-5`.
- `TaskStartedMessage::awaited` and `BackgroundTaskInfo::awaited`. These
  flag a run of a resumed subagent that the resuming tool call waits for.
  Its report goes into that call's result, so no follow-up turn comes.
- `BackgroundTaskInfo::subagent_type`. This is the agent type of a
  `local_agent` task, or `main-session` for a backgrounded main session.
- `ToolResultMeta::permission_decision` (`ToolPermissionDecision`). It
  reports how a tool call's permission check ended: an open-set
  `PermissionDecisionOutcome` (`accept` / `reject` / `cancelled`), the
  `source` that decided, and an optional open-set
  `PermissionDecisionReasonType` (`rule`, `mode`, `subcommandResults`,
  `permissionPromptTool`, `hook`, `asyncAgent`, `sandboxOverride`,
  `workingDir`, `safetyCheck`, `classifier`, `other`).
- `StartupFailureReason::OrgConfigRequiredUnavailable` and
  `StartupFailureReason::OrgConfigRefused`. Both mean the organization's
  required policy limits and managed settings were unavailable or refused
  at startup.

### Changed

- **Breaking:** `ClaudeModel` gains the `Haiku55` variant, which breaks
  exhaustive downstream matches.
- **Breaking:** `StartupFailureReason` gains two variants, which breaks
  exhaustive downstream matches.
- `MissingFileAttachment::reason` documents the new `not_found` code.
- `tests/schemas/claude_stream_json_snapshot.txt` re-snapshotted against
  CLI 2.1.293.

### Removed

- **Breaking:** `ResultMessage::system_prompt_detail` and
  `SystemPromptDetail`. CLI 2.1.293 no longer defines or emits the field.
  If an older CLI still sends it, it is now ignored.

## [2.1.294] - 2026-10-06

Re-baseline against Claude CLI **2.1.292**. Models the 2.1.291 → 2.1.292
stream-json drift: one new `system` subtype and a set of optional fields,
all additive on the wire. `--help` and the model-id set are byte-identical.

### Added

- `system/permission_check_status` (`SystemSubtype::PermissionCheckStatus`,
  `PermissionCheckStatusMessage`, open-set `PermissionCheckStatus`
  `checking`/`done`, `SystemMessage::as_permission_check_status`,
  `KnownSystemEvent::PermissionCheckStatus`) — a tool call has been waiting
  on its auto-mode permission check for longer than usual (`tool_use_id`,
  optional `agent_id`).
- `AssistantMessage::agent_id` — the subagent `task_id` that produced the
  message.
- `UserMessage::agent_id` — the same id on the prompt echoed into a
  subagent. The CLI's user-message schema does not list it; the live
  wrapping audit (`live_subagent_session_is_fully_wrapped`) found it.
- `ApiErrorParams::rate_limit_info` (`RateLimitInfo`) for the new
  `usage_limit_reached` `api_error` kind.
- `RateLimitInfo::limit_scope` and open-set `RateLimitScope` (`service`,
  `channel`, `group_pool`). The CLI had sent `limitScope` on
  `rate_limit_event` before 2.1.292; the crate had not modeled it.
- `ResultMessage::safety_stops` — running count of safety-system stops in
  the session.
- `StreamEventMessage::abandoned_blocks` and `AbandonedBlocks`
  (`api_message_id`, `from_block_index`) — on a CLI-produced `message_stop`,
  the streamed blocks that will never get an assistant message.
- `run_id` on `TaskStartedMessage`, `TaskUpdatedMessage`,
  `TaskProgressMessage`, `TaskNotificationMessage` and `BackgroundTaskInfo`.
- `parent_task_id` on `TaskStartedMessage` and `BackgroundTaskInfo`.
- `TaskNotificationMessage::handback` (open-set `TaskHandback`: `send`,
  `flagged`, `withheld`) and `TaskNotificationMessage::handback_report`
  (`HandbackReport`: `text`, optional `warning`).

### Changed

- **Breaking:** `ApiErrorParams` no longer implements `Eq`, because it now
  holds a `RateLimitInfo` (which has `f64` fields). It still implements
  `PartialEq`. `RateLimitInfo` now derives `PartialEq`.
- `tests/schemas/claude_stream_json_snapshot.txt` re-snapshotted against
  CLI 2.1.292 (59 → 60 SDK output union members). The new `ephemeral: text,
  warning` row is an extractor labelling artifact: it is the
  `handback_report` object, labelled by the `cache_control` literal that
  follows it in the bundle. It is not a new wire frame.

## [2.1.293] - 2026-10-06

Re-baseline against Claude CLI **2.1.291**. Pin-only: the 2.1.290 → 2.1.291
stream-json schemas are identical (object keys, enums, literals and field
descriptions), and `--help` and the model-id set are byte-identical.

## [2.1.292] - 2026-10-05

Re-baseline against Claude CLI **2.1.290**. Models the 2.1.289 → 2.1.290
stream-json drift: one new `system` subtype and three optional fields, all
additive. The model-id set is unchanged. `--help` only changed the
`attach`/`logs` background-session subcommands to accept a session name
(neither is modeled).

### Added

- `system/file_attachments_missing` (`SystemSubtype::FileAttachmentsMissing`,
  `FileAttachmentsMissingMessage`, `MissingFileAttachment`,
  `SystemMessage::as_file_attachments_missing`,
  `KnownSystemEvent::FileAttachmentsMissing`) — files a user message sent by
  reference did not arrive (`message_uuid`, `sent_count`, `missing[]` with
  `file_uuid` and an open-set `reason`). Claude Code Remote workers only.
- `ResultMessage::resume_store_confirm_detail` and `ResumeStoreConfirmDetail`
  (`write_ms`, `ahead_ms`, `post_ms`, `other_ms`, `posts`, `failed_posts`,
  `rows_ahead`, `rows`) — how the `resume_store_confirm` phase was spent.
- `ResultMessage::system_prompt_detail` and `SystemPromptDetail`
  (`user_context`, `refresh_reason`, `slowest_producer`,
  `slowest_producer_ms`, `cpu_ms`) — what the `system_prompt` phase waited
  for.
- `UserMessage::refused_message_id` — the API id of the refused response, on
  the synthetic message that re-asks the model after a refusal.

### Fixed

- The `StartupFailureReason` rustdoc had been attached to
  `InputAttachmentsDetail` since 2.1.290; it is back on the enum.

### Changed

- `tests/schemas/claude_stream_json_snapshot.txt` re-snapshotted against
  CLI 2.1.290 (58 → 59 SDK output union members).

## [2.1.291] - 2026-10-04

Re-baseline against Claude CLI **2.1.289**. No wire drift: the 128 extracted
schema blocks have identical labels, object keys, enum bodies and literals
to 2.1.288, `--help` is byte-identical and the model-id set is unchanged.

### Changed

- Re-pin the tested CLI version to 2.1.289.
- Rustdoc refresh for the upstream description changes: `system/ui_log`,
  `system/ui_toast` and `system/ui_status` text can now run to 10000
  characters plus an ellipsis (was 4096), `ui_toast` frames are no longer
  spaced 2 s apart by the hooks host, and `UiInvalidateMessage::instances`
  is also sent when a `Client` drawn in those instances failed
  (`ui.fault`).

## [2.1.290] - 2026-10-03

Re-baseline against Claude CLI **2.1.288**. Models the 2.1.287 → 2.1.288
stream-json drift: one new `system` subtype and seven optional fields, all
additive. The model-id set is unchanged. `--help` only swapped the
`project` management subcommand for `purge [path]` (neither is modeled).

### Added

- `system/instruction_size_warning` (`SystemSubtype::InstructionSizeWarning`,
  `InstructionSizeWarningMessage`, `SystemMessage::as_instruction_size_warning`,
  `KnownSystemEvent::InstructionSizeWarning`) — the session's instruction
  files add up to more than the recommended limit (`total_chars`,
  `total_limit_chars`, `file_count`, optional `largest_chars`). Sent to
  Claude Desktop's Code tab only; safe to ignore.
- `ResultMessage::input_attachments_detail` and `InputAttachmentsDetail`
  (`slowest_producer`, `slowest_producer_ms`, `cpu_ms`, `major_faults`,
  `changed_files_reread`) — what the `input_attachments` phase did on the
  turn.
- `ResultMessage::flag_fetch_kick` — whether start-up kicked off the
  feature-flag fetch early, or why not (open string set).
- `InformationalMessage::tag` — opaque feature tag on a line a host may treat
  specially.
- `TurnHandoffAvailableMessage::carried_writes` — the worker writes the files
  of already-answered Write calls a `turn_handoff` request carries.
- `ApiErrorParams::media` / `ApiErrorParams::media_reason` with the open-set
  `ApiErrorMedia` (`image` / `document`) and `ApiErrorMediaReason`
  (`unprocessable` / `unsupported_by_model` / `media_budget`), the parameters
  of the new `media_removed` value of `AssistantMessage::api_error`.

### Changed

- `tests/schemas/claude_stream_json_snapshot.txt` re-snapshotted against
  CLI 2.1.288 (57 → 58 SDK output union members).

## [2.1.289] - 2026-10-02

### Fixed

- `LoginFlow::submit_code` no longer wraps the authorization code in
  bracketed-paste markers unless the CLI's login screen has actually switched
  bracketed-paste mode on (`ESC[?2004h`). Claude CLI 2.1.280 never enables it
  there and does not strip the markers, so the opening `ESC[200~` was accepted
  as literal input and sent to the token endpoint as the first seven
  characters of the code (`"code":"\u001b[200~<real code>"`, intercepted
  live). The server answered `invalid_grant` and the CLI printed
  `Login failed: Request failed with status code 400` for every well-formed
  code, surfacing as `Error::CodeRejected`. The framing is still applied when
  the TUI advertises bracketed paste (2.1.220 behaviour), and the delayed lone
  CR that defeats the 64-byte paste-classification swallow is sent in both
  cases. `SUBMIT_PATH` is now `.../v7` so deployed builds are identifiable
  from the timeout channel line.

## [2.1.288] - 2026-10-02

Re-baseline against Claude CLI **2.1.287**. Models the 2.1.286 → 2.1.287
stream-json drift: six optional fields on three frames, all additive. No new
subtypes. The bundle dropped the internal `set_expanded_view` side-panel hint
(never modeled) and hid `--client-data-url` from `--help` while still
accepting it. The model-id set gained only three internal strings
(`claude-code-tools`, `claude-code-gh-standin`, `claude-mods`).

### Added

- `AssistantMessage::thinking_duration_ms` — how long a thinking-only frame's
  block took to stream, from `content_block_start` to `content_block_stop`,
  for display.
- `ResultMessage::first_text_post_queue_wait_ms` and
  `ResultMessage::first_text_post_queued_behind` (reusing
  `StreamPostQueuedBehind`) — the text POST twins of the stream POST queue
  timing added in 2.1.278.
- `ResultMessage::turn_start_resume_kind` and `TurnStartResumeKind`
  (`orphaned_permission` / `turn_handoff` / `deferred_tool_use` / `none`,
  open set) — what the turn had to settle before its own prompt.
- `TurnHandoffAvailableMessage::home_files` and `::file_names` — two more
  capability markers a cloud worker announces on
  `system/turn_handoff_available`.

### Changed

- `ResultMessage::time_to_request_phases_ms` docs follow the 2.1.287 phase
  split: `turn_start_resume` became `resume_other`, `resume_store_confirm`,
  `resume_staged_files_wait`, `resume_connector_wait` and
  `turn_start_tool_run`; `process_user_input` now excludes `input_images`,
  `input_attachments`, `input_command` and `input_hooks`; and
  `system_prompt_rebuild` is split out of `system_prompt`. The map was
  already an open set, so no type change.
- `TurnHandoffAvailableMessage::staged_files` docs note the `filestore_path`
  exception (a synced file is not waited for).
- `CliFlag::ClientDataUrl` docs note the flag is hidden from `--help` on
  2.1.287+ but still accepted.
- `tests/schemas/claude_stream_json_snapshot.txt` re-snapshotted against CLI
  2.1.287.

## [2.1.287] - 2026-10-01

Re-baseline against Claude CLI **2.1.286**. Models the 2.1.285 → 2.1.286
stream-json drift: three optional timing fields, all additive. No removals,
no new subtypes, and `--help` is unchanged. The bundle's model-id set lost
only a stray `claude-haiku-3-55` string that the crate never modeled.

### Added

- `UserMessage::server_received_wall_ms` — epoch ms on the session server's
  clock when it received the HTTP request that delivered the message. Stamped
  by the session server, not settable by clients.
- `ResultMessage::first_request_input_tokens` — input + cache-read +
  cache-creation tokens of the model call sent at `request_sent_wall_ms`, for
  grouping turn latency by prompt size.
- `ResultMessage::user_message_server_received_wall_ms` — the triggering user
  message's `server_received_wall_ms`, copied onto the result so the session
  server can measure from its own receipt to `request_sent_wall_ms`.

### Changed

- `tests/schemas/claude_stream_json_snapshot.txt` re-snapshotted against CLI
  2.1.286.

## [2.1.286] - 2026-09-30

Re-baseline against Claude CLI **2.1.285**. Models the 2.1.284 → 2.1.285
stream-json drift, all additive: eight new `system` subtypes (the SDK output
union grew 49 → 57 members), eight optional fields on `result`, one on usage,
one on the compaction metadata and one new startup failure reason. No
removals. The set of model ids in the bundle is unchanged from 2.1.284.

### Added

- `system/session_title_changed` — `SessionTitleChangedMessage`
  (`SystemSubtype::SessionTitleChanged`, `KnownSystemEvent` variant,
  `is_`/`as_` accessors). The session's name for a host that shows it: sent
  at startup when the session already has one and after each rename. It can
  arrive before the `system/init` that first names its `session_id`.
- Seven plugin-UI pushes a headless session sends for a remote surface to
  draw, each with its `SystemSubtype` / `KnownSystemEvent` variant and
  `is_`/`as_` accessors:
  - `system/ui_log` (`UiLogMessage`), `system/ui_toast` (`UiToastMessage`)
    and `system/ui_status` (`UiStatusMessage`, whose `text` is `None` when
    the plugin clears its pinned line) — the plugin's `$.ui.log` /
    `$.ui.toast` / `$.ui.status` text.
  - `system/ui_invalidate` (`UiInvalidateMessage`, with the optional
    `UiInvalidatedInstance` list) — drawn trees may be stale.
  - `system/ui_panes` (`UiPanesMessage` / `UiPane`) — the placed-pane roster
    with its shown, focused and focus-requested pane ids.
  - `system/ui_scroll` (`UiScrollMessage`) and `system/ui_focus`
    (`UiFocusMessage`) — a plugin moved a site's window or focus ring; the
    site is named by the open-set `UiSiteComponent` (`Pane`, `AbovePrompt`).
- `ResultMessage` request-timing fields, present on `CLAUDE_CODE_REMOTE`
  sessions and hosts that set `CLAUDE_CODE_EMIT_STARTUP_TIMING`:
  `time_to_request_phases_ms` (open-set phase → ms map summing to
  `time_to_request_ms`), `process_turn_index`, `time_to_request_cpu_ms`,
  `time_to_request_loop_lag_ms` and `time_to_request_major_faults`.
- `ResultMessage::turn_start_phases_ms` (open-set step → ms map covering
  `frame_enqueued_wall_ms` → `turn_started_wall_ms`) and
  `::turn_start_control_requests_ms`.
- `ResultMessage::api_error` — the typed kind of the API error that ended
  the turn, mirroring `AssistantMessage::api_error`.
- `UsageInfo::fallback_credit` and `AssistantUsage::fallback_credit` — the
  outcome of a fallback-credit token the request presented (opaque JSON;
  `null` on the wire when none was presented, which a live 2.1.285 result
  does emit).
- `CompactMetadata::pre_compact_artifact_read_versions`
  (`ArtifactReadVersion { slug, ver }`) — the artifact versions the
  conversation held before the compaction.
- `StartupFailureReason::ProviderNotAllowed` (`provider_not_allowed`):
  managed settings' `allowedProviders` does not list the session's API
  provider.

### Changed

- `StartupFailureReason::ManagedSettingsInvalid` doc: also reported when
  managed model settings block the default model and leave none allowed.
- `lib.rs` tested-version doc line caught up (was stuck at 2.1.283).

### Not modeled

- 2.1.285 also added a family of host → CLI control request subtypes for the
  same plugin-UI surface (`ui_attach`, `ui_detach`, `ui_render`, `ui_press`,
  `ui_input`, `ui_select`, `ui_close`, `ui_copy`, `ui_message`,
  `ui_pane_show`, `ui_pane_focus`, `ui_prompt_*`, `ui_client_*`) plus
  `list_directory`. `ControlRequestPayload` covers only tool permission,
  hook, MCP, initialize and interrupt requests, so these stay out.
- `claude --help` gained `--desktop` (open the session in the Claude Desktop
  app). Interactive-only, like `--cloud` and `--tmux`; not mirrored on
  `CliFlag`.

## [2.1.285] - 2026-09-28

### Added

- **`ClaudeModel::Sonnet55`** (`claude-sonnet-5-5`, "Sonnet 5.5",
  released 2026-09-28) — and the `sonnet` floating alias now resolves
  there first-party (staged rollout in the 2.1.284 bundle, new table
  active). The only registry addition since the 2.1.280 catalog refresh.

### Changed

- Re-baseline the tested pin to Claude CLI **2.1.284**: full integration
  tier green, drift script clean (no wire changes).

## [2.1.284] - 2026-09-26

Re-baseline against Claude CLI **2.1.283**. Models the 2.1.282 → 2.1.283
stream-json drift: one additive optional field on `stream_event`, no removals,
no new subtypes, and no enum or literal changes anywhere in the extracted
schemas (nested objects included). The model registry is byte-identical to
2.1.282. `claude --help` gained one flag, mirrored on `CliFlag`.

### Added

- `StreamEventMessage::thinking_display: Option<String>` — the
  `thinking.display` value Claude Code put in the API request body for the
  response the event belongs to, i.e. which display the thinking text was
  requested with (not how to show it). Set on `message_start`, on a thinking
  block's `content_block_start` and on `thinking_delta` events when the body
  had a display; absent on every other event, on Remote Control sessions and
  from older CLIs. Upstream marks it `@internal`, so the value set is open.
- `CliFlag::ClientDataUrl` (`--client-data-url <url>`) — URL of a signed
  configuration document; the CLI exits if it cannot load it or it does not
  cover the selected model. The `CLAUDE_CODE_CLIENT_DATA_URL` environment
  variable is the equivalent that keeps the URL out of the process list.

## [2.1.283] - 2026-09-25

### Added

- **`LoginFlow::poll_outcome`** — poll for login completion WITHOUT
  submitting a code. Investigated on CLI 2.1.282: the sign-in flow now
  opens the browser and polls the authorization session in the
  background (device-code grant, PKCE authorize URL redirecting to the
  hosted `platform.claude.com/oauth/code/callback` page); credentials
  are persisted by the CLI when the user approves, and the on-screen
  "Paste code here if prompted" box is only the fallback for machines
  where the recommended sign-in isn't available. Drivers should
  interleave `poll_outcome` with their own paste handling and treat
  either as completion — wirecheck's login card now does exactly that
  (previously it parked on the paste and would time out on logins that
  had already succeeded).

## [2.1.282] - 2026-09-25

Re-baseline against Claude CLI **2.1.282**. Models the 2.1.281 → 2.1.282
stream-json drift: one additive optional field on `result`, no removals and
no new subtypes. The model registry and `claude --help` are byte-identical
to 2.1.281. The only other schema change is a new `session-inbox` value in
the task-notification provenance `subkind`, which `MessageOrigin` already
carries verbatim through its open `kind` + flattened `extra` shape.

### Added

- `ResultMessage::frame_intake_phases_ms: Option<BTreeMap<String, u64>>` —
  how the triggering send's frame spent the time between
  `frame_received_wall_ms` and `frame_enqueued_wall_ms`, in integer
  milliseconds per intake step (`before_read`, `dedup`, `flag_settle`,
  `receive_hook`, `attachments`, `admission_wait`, `admit_check`, `other`).
  Steps that did not run are absent; `before_read` and `other` are always
  present; unrecognized step names are kept. Present exactly when
  `frame_received_wall_ms` is, except from older producers.

### Changed

- Re-snapshot `tests/schemas/claude_stream_json_snapshot.txt` from CLI
  2.1.282 and re-pin `TESTED_VERSION`.

## [2.1.281] - 2026-09-24

Re-baseline against Claude CLI **2.1.281**. Models the 2.1.280 → 2.1.281
stream-json drift: one new `system` subtype and seven additive optional
fields across three wire types, no removals. The model registry is
byte-identical to 2.1.280; the only CLI surface change is `--agents`
accepting a file path under `--print`.

### Added

- `system/per_turn_effort_changed` (`SystemSubtype::PerTurnEffortChanged`,
  `PerTurnEffortChangedMessage`, `KnownSystemEvent::PerTurnEffortChanged`,
  `SystemMessage::{is,as}_per_turn_effort_changed`) — the conversation
  stopped sending effort per turn after the server refused it; always
  carries `per_turn_effort_active: false`.
- `InitMessage::per_turn_effort_active` — whether per-turn effort is active
  for the init frame's model (`true` only ever arrives on an init).
- `InitMessage::view_mode` (`ViewMode`: `focus` | `default`, open-set) —
  the `/focus` transcript view, on Remote Control bridge and headless
  stream-json inits.
- `ConversationResetMessage::trigger` (`ConversationResetTrigger`: `clear`
  | `plan_mode_exit` | `fresh_session` | `onboarding`, open-set),
  `::user_message_uuid` (trigger `clear` only: the `/clear` message's uuid)
  and `::timestamp` (ISO 8601 UTC, display only).
- `AssistantMessage::local_command_outcome` (`LocalCommandOutcome` with
  `LocalCommandOutcomeKind`: `unavailable_headless` | `unknown` | `failed`
  | `restart_required`, open-set, plus the `unknown` kind's closest-command
  `suggestion`) — what a local-command row reports, so a host can offer a
  fix instead of relaying the text.

### Changed

- Re-baseline the tested pin to Claude CLI **2.1.281** and refresh the drift
  snapshot (49 union members). `CliFlag::Agents` docs note that `--print`
  also accepts a file path.

## [2.1.280] - 2026-09-22

### Added

- **`ClaudeModel`**: `Opus55` (`claude-opus-5-5`, "Opus 5.5", released
  2026-09-22 — the `opus` floating alias now resolves here first-party),
  plus `Fable51` (`claude-fable-5-1`) and `Mythos51`
  (`claude-mythos-5-1`) from the 2.1.280 binary's model registry.
- `AssistantMessage.narration_hint` — optional `"hidden"`/`"faint"`
  renderer preference for the frame's narration blocks (2.1.280+),
  companion to `narration_block_indexes`; kept open for future values.

### Changed

- Re-baseline the tested pin to Claude CLI **2.1.280**: full integration
  tier green (28 live incl. the strict stream audit) and the drift
  snapshot refreshed (`assistant` + `narration_hint` was the only wire
  change from 2.1.278).

## [2.1.278] - 2026-09-19

Re-baseline against Claude CLI **2.1.278**. Models the 2.1.276 → 2.1.278
stream-json drift: all additive, all optional. Every enum value set and wire
literal is unchanged, and `claude --help` and the model catalog are
byte-identical to 2.1.276.

### Added

- `InitMessage::scratchpad_path` — absolute path of the session's scratchpad
  directory; omitted when the scratchpad is disabled and from the redacted
  init sent over the Remote Control bridge.
- `TurnHandoffAvailableMessage::staged_files` and `::no_query_first` — two
  more worker capability markers, previously landing in `extra`.
- `UserMessage::initiator` — the host's label for what started the turn
  (`scheduled-task`, `sleep-auto-resume`, …); usage attribution only.
- `UserMessage::pasted_content` — content the user pasted rather than typed,
  each entry a string or an array of content blocks. The top-level drift
  check does not see this one (it sits on the nested SDK user-message
  schema).
- `CommandInfo::builtin` — marks Claude Code's own slash commands on
  `system/commands_changed` rows. Also nested-only.
- `ResultMessage` timing instrumentation:
  `first_stream_post_queue_wait_ms`, `first_stream_post_queued_behind`
  (`StreamPostQueuedBehind`, open-set over `durable_post`, `ephemeral_post`,
  `retry_backoff`, `hold`, `none`), `frame_received_wall_ms`,
  `frame_enqueued_wall_ms`, `turn_started_wall_ms`, `first_text_post_ms`,
  `first_text_post_wall_ms`.

## [2.1.276] - 2026-09-18

Re-baseline against Claude CLI **2.1.276**. Models the 2.1.274 → 2.1.276
stream-json drift: two additive fields and one required→optional flip. The
model catalog, alias table, and CLI argument surface are byte-identical to
2.1.274 apart from minified renames.

### Added

- `PermissionDeniedMessage::decision_reason_code`
  (`PermissionDeniedReasonCode`, open-set over
  `classifier_transcript_too_long`, `outside_reads_blocked`,
  `memory_paused`) — a code a host can act on beside the unchanged
  `decision_reason_type`; absent for every other reason.
- `ToolResultMeta::remedy` (`ToolResultRemedy`) — the fix a host can offer
  for what a `tool_result` reports, stamped from structured producer state:
  `kind` (`ToolResultRemedyKind`, 14 known kinds from `mcp_needs_auth` to
  `staged_for_review`) plus the parameters that kind needs — `servers`,
  `provider` (`RemedyLoginProvider`: `claude_ai`, `claude_design`), `path`,
  `managed`, `feature` (`RemedyFeature`: `workflows`, `artifacts`), `cause`
  (`RemedyFeatureCause`), and `policy_kind` (`RemedyPolicyKind`). Every enum
  is open-set with an `Unknown(String)` arm.

### Changed

- **Breaking:** `ToolResultMeta::non_execution_kind` is now
  `Option<String>`. The CLI made it optional because an entry may carry a
  `remedy` alone, on a result that did run (a staged settings write, a
  sandbox violation on exit 0).
- Re-snapshot `tests/schemas/claude_stream_json_snapshot.txt`.

Not modeled: the bundle's new MCP-server error-code list
(`CLAUDEAI_BEARER_REJECTED`, `FIRST_PARTY_AUTH_REJECTED`,
`APPROVAL_REQUIRED`) is host-side telemetry routing and appears in no
stream-json schema.

## [2.1.274] - 2026-09-17

Re-baseline against Claude CLI **2.1.274**. Models the 2.1.273 → 2.1.274
stream-json drift, all additive (no removals or required/optional flips).
The model catalog and CLI argument surface are byte-identical to 2.1.273.

### Added

- `AssistantMessage::api_error_code` — the server's `error.details.error_code`
  for a synthetic API-error message, copied through when it is an identifier
  (`^[a-z][a-z0-9_]{0,63}$`). Carries server gate codes the CLI build has no
  `api_error` value for, so a host can key on a new gate without a Claude
  Code release.
- `AssistantMessage::api_error_params` (`ApiErrorParams { effort, provider,
  remedy }`) — parameters of `api_error`, present only for the kinds that
  have any: the refused effort level (`effort_requires_thinking`), the
  provider whose credentials failed, and how to repair them
  (`provider_credentials`, `gateway_session_expired`). Open-set enums
  `ApiErrorProvider` (`bedrock`, `anthropicAws`, `mantle`,
  `anthropicGoogleCloud`, `vertex`, `foundry`, `gateway`) and
  `ApiErrorRemedy` (`refresh_command`, `refresh_credentials`, `adc`,
  `gateway_token`, `host_managed`, `model_access`).
- `ResultMessage::api_error_code` — the `api_error_code` of the API error
  that ended the turn, on a `success` result with `is_error: true`.
- `ResultMessage::startup_failure_reason` (`StartupFailureReason`, open-set
  over the 16 known causes such as `cwd_unavailable`, `cli_version_too_old`,
  `worktree_resume_refused`, `bypass_root`) — set on the zeroed
  `error_during_execution` result a stream-json run writes before exiting on
  a known startup failure; `errors` carries the same text as stderr. Paths
  that used to end with stderr alone write that result only when the host
  sets `CLAUDE_CODE_STARTUP_FAILURE_RESULTS`.

### Changed

- `AssistantMessage::api_error` is documented as the open set it always was;
  2.1.274 grows it from 3 to 24 kinds (`effort_requires_thinking`,
  `provider_credentials`, `gateway_session_expired`,
  `model_requires_usage_credits`, `field_not_granted`, ...). No type change.
- `system/init` `mcp_servers` entries gained an optional `source`; the crate
  carries them as untyped `Value`s, so nothing changes.
- Re-snapshot `tests/schemas/claude_stream_json_snapshot.txt`.

## [2.1.273] - 2026-09-16

Re-baseline against Claude CLI **2.1.273**. Models the 2.1.270 → 2.1.273
stream-json drift, all additive (no removals or required/optional flips).

### Added

- Three `system` subtypes, each with a `SystemSubtype` variant, a typed
  struct, `is_*`/`as_*` accessors on `SystemMessage`, a `KnownSystemEvent`
  variant, and wrap-audit coverage:
  - `turn_handoff_available` → `TurnHandoffAvailableMessage`: a cloud
    worker that accepts the `turn_handoff` control request announces the
    contract version, the tools it would accept, its `worker_epoch`, and
    `relay_marker`.
  - `turn_preempted` → `TurnPreemptedMessage`: the CLI stopped the running
    turn so a rapid follow-up (`reason: rapid_followup`) is answered at once;
    carries `preempted_by_uuid` and `preempted_message_uuids`. Precedes the
    stopped turn's `result` frame. Only sent to consumers that declared
    `rapidFollowupPreempt` on initialize.
  - `peer_message_hold` → `PeerMessageHoldMessage`: a cross-session message
    the receive-side policy held rather than queued, and how the hold
    resolved. Open-set enums `PeerMessageHoldState` (`held`/`released`/
    `dropped`), `PeerMessageLane` (`bridge`/`stdin`/`socket`),
    `PeerMessageHoldCause`, and `PeerMessageHoldOutcome`.
- `AssistantMessage.usage_report` (`Option<Box<UsageReport>>`) — structured
  twin of the `/usage` report carried on the synthetic assistant message
  that delivers its text: the session totals (reusing `UsageSession`), the
  server's usage rows (`UsageReportRateLimits` / `UsageReportLimit` /
  `UsageReportScope`), and extra-usage spend (`UsageReportExtraUsage`).
  Boxed to keep `ClaudeOutput` within clippy's variant-size budget.
- `AssistantMessage.local_command_run` (`LocalCommandRun { command, args }`)
  — on the local-command twin, the run that printed the row.
- `TaskNotificationMessage.reason` (`TaskEndReason`, currently only
  `worker_restart`) — set when a task did not end through an ordinary
  completion, failure, or stop.
- `DevIntentMessage.trigger` (`DevIntentTrigger`, open set of eleven
  values incl. `project_scan`) — the evidence a `dev_intent` detection fired
  on, and `DevIntentKind::AndroidApp`. The CLI now also scans the session's
  git repository in print mode and reports project evidence at startup,
  after each `conversation_reset`, and at the first message of a
  conversation whose earlier scan did not find every kind.

### Changed

- `audit_frame` compares JSON numbers numerically: a whole-valued float the
  CLI serialized as an integer (`40`) no longer counts as changed when an
  `f64` field re-serializes it as `40.0`. This was a false positive, not a
  data loss, and `/usage` frames trip it routinely.
- `CodeChangePublishedMessage.branch` is now also sent with `created` on any
  provider (the branch the create was opened from). Doc-only.
- Re-pin to Claude CLI **2.1.273**; snapshot refreshed. The live
  integration suite passes unchanged.

## [2.1.270] - 2026-09-13

### Changed

- Re-pin to Claude CLI **2.1.270**. No stream-json wire drift from
  2.1.269: the fingerprint check is clean against the committed snapshot,
  and a field-level diff of all 108 extracted zod schemas is empty after
  normalizing minified aliases (no doc-string edits this time either). The
  model registry, alias table and accepted `--model` argument list are
  byte-identical between the two binaries. The live integration suite
  passes unchanged. Pin-only release.

## [2.1.269] - 2026-09-12

### Changed

- Re-pin to Claude CLI **2.1.269**. No stream-json wire drift from
  2.1.268: the fingerprint check is clean against the committed snapshot,
  and a field-level diff of the extracted zod schemas shows only minified
  alias churn and doc-string edits. The one substantive doc change is that
  `user_message_uuid` is now stamped on both the turn's first non-ping
  stream event and its first complete assistant message, independently, so
  the same uuid may appear on both frames. The live integration suite
  passes unchanged. Pin-only release.

## [2.1.268] - 2026-09-11

Re-baseline against Claude CLI **2.1.268**. Models the 2.1.267 → 2.1.268
stream-json drift, all additive (no removals or required/optional flips).

### Added

- `resume_reason` on `AssistantMessage`, `StreamEventMessage`, and
  `ResultMessage` — why the turn is the automatic re-run of a turn a worker
  restart interrupted (`CLAUDE_CODE_RESUME_INTERRUPTED_TURN`): the host's
  `CLAUDE_CODE_RESUME_REASON` when it set one (`host_draining`,
  `checkpoint_restore`, `container_recreated`, ...), else `interrupted_turn`.
  Stamped on the same reply frames as `user_message_uuid`, and on the
  re-run's result whether success or error.
- `ResultMessage.result_index` — delivery sequence of the result within the
  run, numbered by the hosting process in write order starting at 0. A
  result whose write fails still consumes its number, so a gap means a
  result was lost.
- `ResultMessage.local_command` — the local slash command that produced a
  success result when the query loop was bypassed.

### Changed

- Re-pin to Claude CLI **2.1.268**; snapshot refreshed. The live
  integration suite passes unchanged.

## [2.1.267] - 2026-09-10

### Changed

- Re-pin to Claude CLI **2.1.267**. No stream-json wire drift from
  2.1.266: the fingerprint check is clean against the committed snapshot,
  and the live integration suite passes unchanged. Pin-only release.

## [2.1.266] - 2026-09-09

Re-baseline against Claude CLI **2.1.266**. Models the 2.1.263 → 2.1.266
stream-json drift, all additive (no removals or required/optional flips).

### Added

- `SystemSubtype::DevIntent` / `DevIntentMessage` / `DevIntentKind` /
  `KnownSystemEvent::DevIntent`, plus `SystemMessage::is_dev_intent` /
  `as_dev_intent` — the new `system/dev_intent` frame the CLI emits once per
  recognized kind of development work per conversation (e.g. `ios_app`, which
  Claude Code Desktop keys its iOS Simulator entry point on). `kind` is an
  open enum with an `Unknown` fallback, matching the CLI's "ignore a kind you
  do not recognize" contract.
- `AssistantMessage.historical` and `AssistantMessage.wire_ingest_context` —
  the Remote Control replay flag and the input-normalization provenance map
  that rides alongside `wire_tool_inputs`.
- `ResultMessage.runner_exit` (`RunnerExit` with an open `RunnerExitPhase`
  enum) — set by the self-hosted runner on a synthesized failure result when
  the session process fails to start or exits abnormally.
- `InitMessage.startup_timing` — cold-start telemetry (named phases and
  resume-hydration counters) on hosted-session init frames, stored as raw
  JSON.
- `CompactBoundaryMessage.historical` and `UserMessage.historical` — the same
  Remote Control replay flag on those two frames.

### Changed

- The committed schema snapshot moves to 2.1.266. Between 2.1.263 and 2.1.266
  the CLI also tightened the assistant/user `message` field from `z.unknown()`
  to a concrete Anthropic-Messages-API schema, so the extractor now reaches
  the API content-block schemas (`text`, `image`, `thinking`, `tool_use`,
  `tool_result`, `document`, `search_result`, `redacted_thinking`,
  `tool_reference`, `file`, `url`, `message`) and the snapshot records them.
  These are passthrough shapes the crate already types via `ContentBlock`
  (with an `Unknown` fallback), so no new content-block modeling is required;
  the wire JSON is unchanged. The full live integration suite passes against
  2.1.266.

## [2.1.263] - 2026-09-07

### Changed

- Re-baseline the tested pin to Claude CLI **2.1.263**: `TESTED_VERSION`,
  both README `Tested against:` lines and the crate version move to 2.1.263.
  A full field-by-field diff of the extracted stream-json schemas
  (2.1.261 → 2.1.263) shows only renamed minified identifiers; every wire
  label, field and enum is unchanged, and the committed snapshot is
  byte-identical after `--update`. The integration suite passes unchanged
  against 2.1.263. No code changes beyond the pin.

## [2.1.261] - 2026-09-05

Re-baseline against Claude CLI **2.1.261**: the tested pin (`TESTED_VERSION`,
both README `Tested against:` lines) and the crate version move to 2.1.261.
Models the 2.1.259 → 2.1.261 stream-json drift (all additive) reported by the
nightly drift check on issue #361, plus the one field 2.1.261 added on top of
the 2.1.260 report.

### Added

- `SystemSubtype::CloudSessionDelta` / `CloudSessionDeltaMessage` /
  `KnownSystemEvent::CloudSessionDelta` — the new `system/cloud_session_delta`
  frame the headless client of a cloud-hosted session writes when the
  session's status changes between two inits (`seq`, `changed`, and the
  whole `cloud_session` block as raw JSON, matching `InitMessage`).
- `AssistantMessage.narration_block_indexes` — indexes into
  `message.content` of the thinking blocks the server tagged as narration.
- `ResultMessage.first_content_frame_ms`, `.first_stream_post_ms`,
  `.first_stream_post_ack_ms`, `.first_stream_post_wall_ms` — stream-timing
  instrumentation on `result/success`.
- `ApiRetryMessage.no_response` (`ApiRetryNoResponse`) — present only on
  first-byte-timeout retries, with how long the attempt waited and how long
  the retry will wait.
- `ThinkingTokensMessage.user_message_uuid` — the client uuid of the send the
  thinking progress belongs to.

### Fixed

- `scripts/extract_claude_sdk_schemas.py` now resolves schema references only
  inside the bundle chunk that holds the SDK output union. On 2.1.261 the
  assistant schema calls a plain `se()` helper (`z.unknown()`) whose only
  lazy definition lives in another chunk; following it crawled that chunk's
  module graph and inflated the extraction to 667 "schemas" (and the drift
  report to 89 phantom wire labels). The 2.1.239 and 2.1.259 extractions are
  byte-identical before and after.

## [2.1.259] - 2026-09-03

Re-baseline against Claude CLI **2.1.259**: the full live integration suite
(28 tests) plus the live subagent-wrapping audit pass, so the tested pin
(`TESTED_VERSION`, both README `Tested against:` lines) and the crate version
move to 2.1.259. Models the 2.1.239 → 2.1.259 stream-json drift (all additive)
reported by the nightly drift check on issue #354.

### Added

- `AssistantMessage.user_message_uuid` / `.user_message_uuids` — the client
  uuid(s) that triggered the turn, stamped on the first reply frame so a
  consumer can bind the reply to its send. Also added to `StreamEventMessage`
  and `ResultMessage`.
- `AssistantMessage.wire_tool_inputs` and `.local_command_source` —
  round-trip-only wrapper siblings the CLI carries for history replay.
- `ResultMessage.queued_turn_count` — user-initiated sends still queued when
  the result was produced.
- `UserMessage.client_composed` — the client composed the turn from content
  the user did not type.
- `McpMeta.resource_links` and `TaskNotificationMessage.resource_links`
  (`ResourceLink`) — files an MCP tool returned by reference.
- `TaskStartedMessage.ambient`, `TaskNotificationMessage.ambient`, and
  `BackgroundTaskInfo.ambient` — housekeeping tasks hosts should hide from
  activity indicators.
- `CodeChangePublishedMessage.branch` — the working branch for providers
  (`gerrit`) whose changes have no head branch of their own.
- `InitMessage.footer_indicator` (`FooterIndicator`), `.worker_epoch`, and
  `.powershell_path`.
- `RateLimitInfo.unified_windows` (`UnifiedRateLimitWindows`,
  `UnifiedWindowUsage`) and `.rate_limit_grace_active` — per-window
  subscription usage and the latched grace signal. The nested
  `unified_windows` drop was surfaced by the live subagent-wrapping audit,
  not the coarse (top-level-only) drift checker.
- `AssistantErrorKind::AccountOnHold` — a wire error value the enum was
  missing.

### Fixed

- The schema drift checker's key scanner now skips backtick template literals
  (and their `${...}` interpolations) in `.describe()` prose, which 2.1.259's
  `system/init.footer_indicator` describe introduced — previously they
  desynced the top-level field scan.

## [2.1.240] - 2026-09-02

### Fixed

- Login flows: the login child exiting with `Login failed: <reason>`
  (CLI >= 2.1.25x behavior after a rejected token exchange) now
  classifies as `CodeRejected` instead of the `LoginChildExited`
  forensics blob. Reproduced live on claude 2.1.259 by the agent-portal
  consumer.

## [2.1.239] - 2026-09-01

Re-baseline against Claude CLI **2.1.239**: the full live integration suite
(28 tests) passes, so the tested pin (`TESTED_VERSION`, both README
`Tested against:` lines) and the crate version move to 2.1.239. Models the
2.1.232 → 2.1.239 stream-json drift (all additive) and repairs the schema
extractor against the CLI's new bundle style.

### Added

- `AssistantMessage.context_usage` (`ContextUsage` + `ContextCategory`,
  `ContextOverLimit`, `ContextMcpTool`, `ContextMemoryFile`, `ContextAgent`,
  `ContextSkill`) — the structured twin of the `/context` report — and
  `AssistantMessage.batch_tool_uses` (`BatchToolUse`).
- `ResultMessage.subagent_stats` (`SubagentStats` + `SubagentSpawnRequests`,
  `SubagentKillCounts`, `SubagentRefusalCounts`) — running totals for
  subagents started through the Agent tool.
- `InitMessage.effort` (the session's resolved effort level) and
  `InitMessage.cloud_session` (raw JSON snapshot on cloud-hosted sessions).
- `TaskStartedMessage.{is_backgrounded, spawn_depth}`,
  `CodeChangePublishedMessage.action`, `VcsStateChangedMessage.branch`,
  `ModelRefusalFallbackMessage.saw_cyber_refusal`, and
  `UserMessage.seeded_summon`.

### Fixed

- `scripts/extract_claude_sdk_schemas.py` (and with it the nightly drift
  check, which had been soft-skipping since the CLI's bundler switched
  styles): schemas are now also discovered in the free-function zod style
  (`ve(()=>_e({type:Tt("…")}))`) used by CLI 2.1.239+, alongside the
  zod-namespace style (`Se(()=>E.object({type:E.literal("…")}))`) of older
  binaries. The snapshot is re-baselined against 2.1.239 (43 wire labels).

## [2.1.234] - 2026-08-19

### Added

- **`version::tested_cli_version()`** — the tested-against CLI release,
  machine-readable from the published artifact (previously a private
  const only the runtime warning could see). CI keeps it in lockstep
  with the README's `Tested against:` line.

## [2.1.233] - 2026-08-19

### Added

- **`transcript` module** — the CLI's unpublished on-disk transcript
  path rule, exported so consumers stop growing private copies:
  `encode_project_dir` (`/` and `.` → `-`, measured against real
  transcript stores and pinned by tests, documented as lossy) and
  `transcript_path(home, cwd, session_id)`, which takes `home`
  explicitly so a test can never accidentally resolve into a real
  `~/.claude`. Extracted at agent-portal's request to delete its
  duplicate implementation.

## [2.1.232] - 2026-08-14

### Added

- Three fields CLI 2.1.232 added to the wire, each found by wirecheck's
  live subagent wrapping audit (present on the wire, dropped by the typed
  model): **`messaging_socket_path`** and **`terminal_slash_commands`**
  on the `system` init message, and
  **`usage.output_tokens_details.thinking_tokens`** on the `result`
  message (typed as `OutputTokensDetails`).

### Changed

- `TESTED_VERSION` and the crate version move to 2.1.232; the full live
  wirecheck claude tier (curated + cargo) passes against the installed
  CLI.

## [2.1.223] - 2026-08-05

### Changed

- **`LoginOutcome`, `TokenSource`, and `Osc52Status` now derive
  Serialize/Deserialize** — login outcomes are relay-shaped for web/launcher
  surfaces that ship them over the wire (requested by agent-portal's
  login-automation assessment). No wire or behavior change.

## [2.1.222] - 2026-08-04

Catches up to Claude CLI 2.1.222; snapshot baseline and `TESTED_VERSION`
move to 2.1.222, and the full live suite passes against the installed
binary. Also ships the complete login-tooling saga and session forking
(below) that accumulated unreleased.

### Added (2.1.222 drift)

- **`ModelRefusalFallbackMessage.scope`** — new open enum
  `RefusalFallbackScope` (`session` | `local` + `Unknown(String)`):
  `session` means the main thread fell back and the session model is
  swapped (also the meaning when the field is absent, i.e. every CLI
  before 2.1.222); `local` means a subagent / side-question / background
  fork fell back and only that response used the fallback model. (#272)

### Changed

- **Rejected-code retry now follows the CLI's real state machine** —
  recovered from the 2.1.220 binary: the error screen renders **no input
  component**, and its only affordance (Enter) restarts the OAuth flow with
  a **new PKCE challenge**. There is no same-challenge retry; a corrected
  code pasted after a rejection lands nowhere, silently. Accordingly:
  `submit_code` after a `CodeRejected` now returns `InvalidState` instead
  of writing into the void, and the new
  **`LoginFlow::retry_new_url(timeout)`** presses Enter, waits for
  `waiting_for_login` to re-render, and returns the **new** authorize URL
  to show the user (the old URL's code is dead). Live-verified end to end:
  reject → refused re-paste → new URL with rotated challenge in ~1 s →
  fresh input field accepts the next submission. (#270)

### Added

- **Bracketed-paste-mode telemetry**: channel lines now stamp
  `paste-mode@submit=on|off|never-advertised` — the TUI's advertised
  `?2004h/l` state at the moment the code was written, from the raw
  stream. Directly tests the late-write hypothesis (a TUI that drops paste
  mode while the user is off authorizing would receive a late frame as raw
  ESC keypresses) in production, where the 19–22 s human submit latency
  cannot be replicated by harnesses that write in milliseconds.
  `SUBMIT_PATH` → `…+paste-probe/v6`. (#269)

### Fixed

- **Login flows were structurally blind to child death.** `LoginFlow` held
  the PTY *slave* open for its whole life, so when the CLI exited, the
  master never saw EOF and the reader blocked forever — production attempt
  four's child died ~5 s after code submission and the flow spent the
  remaining 85 s polling a corpse, reporting benign absence on every
  channel. The slave is now dropped at spawn (the child owns its own fds),
  making master-side EOF fire on exit. (#267)

### Added

- **Child death is a first-class outcome** (#267):
  - New `Error::LoginChildExited { code, transcript }`, returned within
    ~1 s of death from `auth_url` and `submit_code_and_wait` (EOF-driven,
    with a `try_wait` backstop for a dead parent whose PTY is held open by
    an orphaned grandchild). Replaces the generic `Unknown`/`Protocol`
    errors on the exited-without-outcome paths — downstream match arms
    with a catch-all are unaffected.
  - **Pre-submit liveness check**: a death *before* the paste is reported
    as such ("BEFORE code submission — nothing was written"), timestamping
    death against the write — the discriminator between "the frame killed
    it" and "it was already gone".
  - Channel lines now carry `child=alive|exited(code)`; `SUBMIT_PATH`
    bumps to `…+exit-aware/v5`.
  - An exit that races a credentials write still resolves to success.
  Live-verified: kill-after-submit surfaces `LoginChildExited(code=143)`
  in ~570 ms with the masked input echo intact in the tail; kill-before
  is caught in microseconds; the rejection path is unchanged (~375 ms).

- **Write-path provenance**: `auth::SUBMIT_PATH` identifies the compiled
  code-submission mechanism and is stamped into the `LoginTimeout` channel
  line (`submit-path=bracketed-paste+lone-cr-150ms+term-forced/v3`) — which
  deployed binary is running becomes readable from one log line. Necessary
  because release builds inline the paste-frame bytes into immediates, so
  byte-grepping a binary for `ESC[200~` proves nothing in either
  direction. (#265)

### Changed

- **Login flows scrub session/credential env vars from the spawned CLI**
  (`CLAUDECODE`, `CLAUDE_CODE_ENTRYPOINT`, `CLAUDE_CODE_CHILD_SESSION`,
  `CLAUDE_CODE_SESSION_ID`, `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`,
  `ANTHROPIC_AUTH_TOKEN`), aligning with the session spawn path's
  `CLAUDECODE` scrubbing in `cli.rs` — a login child's purpose is minting
  fresh credentials, so it must not inherit the host's. Measured on 2.1.220
  that none of these break submission when present; this removes the axis,
  not a reproduced failure. `SUBMIT_PATH` bumps to
  `…+env-scrubbed/v4`. (#266)
- **Login flows now force `TERM=xterm-256color` on the spawned CLI.** The
  crate is the terminal on the other side of the PTY (it parses OSC 8/52
  and speaks bracketed paste), so it advertises a deterministic capability
  surface instead of inheriting the host process's TERM (server processes
  often have none). Measured on CLI 2.1.220, submission works under
  `TERM=dumb` and TERM-unset too — this eliminates an environment axis
  rather than fixing a reproduced failure. (#265)

### Fixed

- **Login code submission failed silently for every production-length
  code.** The TUI classifies any single PTY write of **≥ 64 bytes** as a
  paste and swallows a trailing CR into the paste payload instead of
  treating it as Enter (measured on CLI 2.1.220: 62-char code + CR submits;
  63-char + CR sits at the prompt forever). Real authorization codes are
  ~90+ chars, so no unframed single-chunk write could ever submit — this is
  what produced the silent 30s/90s timeouts in production, undetected
  because every test fixture happened to be under 64 bytes. `submit_code`
  now writes the code wrapped in **bracketed-paste framing**
  (`ESC[200~…ESC[201~` — the paste path the CLI explicitly enables via
  `ESC[?2004h`), then sends CR as its own write 150 ms later. Live-verified
  across a 36–120 byte matrix including both previously-silent boundary
  lengths; unit fixtures moved to production lengths (64/92/108/120) and a
  live integration test now submits 92- and 108-char codes, both of which
  any sub-64-byte fixture is structurally incapable of covering. (#263)

### Added

- **Three-source success detection for login flows**, driven by the prod
  finding that every *rejection* is detectable on screen but *success* may
  not be (the TUI masks secret material). `submit_code_and_wait` and
  `finish` now recover the minted token from: (1) visible screen text,
  (2) **OSC 52 clipboard-copy escapes** decoded from raw PTY bytes — exact,
  immune to wrapping/masking; confirmed emitted by CLI 2.1.220 — and
  (3) **credentials-store watching**: `.credentials.json` (under
  `CLAUDE_CONFIG_DIR` or `~/.claude`) created/updated after submission is
  authoritative success even with nothing observable on the PTY.
  `LoginOutcome` gains `token_source: Option<TokenSource>` and
  `credentials_updated: bool`. (#259)
- **Self-diagnosing login timeouts**: new `Error::LoginTimeout { transcript }`
  replaces `Error::Timeout` in `submit_code_and_wait`, carrying a per-channel
  status line (`screen` / `osc52` / `credentials`) plus the post-submission
  screen content — a silent failure now names the blind channel. (#259)

### Changed

- **Success-path OSC 52 telemetry**: `LoginOutcome` gains
  `osc52: Osc52Status` (`Absent` / `Unterminated` / `Undecodable` /
  `PresentNoToken` / `TokenRecovered`) and `copy_nudge_sent: bool`, and the
  `LoginTimeout` channel line gains `copy-nudge=sent|not-sent` — so a
  `token: None` success distinguishes "screen offers no copy affordance"
  from "affordance fired with a non-token payload", crate-side, where the
  PTY bytes are actually observable. The copy nudge is also recorded so any
  unexpected TUI consequence is attributable. (#261)
- **Copy-affordance nudge on confirmed success**: when the credentials store
  updates but no token is yet observable, `submit_code_and_wait` presses `c`
  (the TUI's "c to copy" affordance) once, giving the OSC 52 channel a chance
  to deliver the exact token bytes before the grace window closes — reducing
  the frequency of `token: None` successes that downstream must treat as
  re-login-after-redeploy mode. Best-effort: a stray keypress on screens
  without the affordance, in a flow that has already succeeded. (#260)
- **Login rejection detection widened**: the collapsed `Press Enter to retry`
  prompt now anchors `CodeRejected` as a fallback when the error wording
  doesn't contain `OAuth error` — live capture shows the renderer mangles
  wording (`Requstfailed withstatus code 400`), while the retry prompt is the
  rejection loop's structural constant. (#259)
- **`submit_code` hardening**: the code and its terminating CR are written as
  a single chunk, and an empty-after-trim code is refused with a clear error
  instead of pressing Enter on an empty field (a silent hang). (#259)

- **Login support tooling** behind a new `auth` feature. The CLI's login
  flows are interactive Ink TUIs (they hang forever on a pipe), so
  `auth::LoginFlow` drives them under a pseudo-terminal (`portable-pty`) and
  exposes the flow's human shape as an API: `start(LoginMode)` →
  `auth_url()` (lifted intact from the CLI's OSC 8 hyperlink) → user visits
  the URL and brings back a code → `submit_code()` → `finish()`, which
  returns the minted `sk-ant-oat01-…` token for `LoginMode::SetupToken`.
  Dropping the flow cancels it. `auth::auth_status()` types
  `claude auth status --json`. Live-tested: a real `setup-token` flow yields
  the PKCE authorize URL; `examples/login.rs` walks the full interactive
  loop. (#257)

- **`LoginFlow::submit_code_and_wait`**: outcome-driven completion that
  watches the CLI's *output* instead of its exit. Production use showed two
  hangs `finish()` cannot see: the CLI never exits on a rejected code (it
  prints `OAuth error … Press Enter to retry` and waits), and its Ink TUI
  positions words with cursor-column escapes instead of spaces, so
  ANSI-stripped text runs together (`Pastecodehereifprompted>`) and
  phrase-matching never fires. The new method returns as soon as a minted
  token appears, fails fast with the new `Error::CodeRejected { message }`
  when an OAuth error is printed (scanning only output after the current
  submission, so a retry can't trip on a prior attempt's error), and leaves
  the flow alive on rejection so a corrected code reaches the same PKCE
  session. Presence checks collapse whitespace; token extraction stays
  newline-bounded (collapsing would glue trailing prose onto the token) with
  a loud display-wrap detector instead of silent truncation. PTY width is
  raised to 1000 columns so wrapping is impossible at the source.

- **Session forking**: `ClaudeCliBuilder::fork_from(source_session_id)`
  assembles the full `--resume <src> --fork-session --session-id <new>`
  combination (generating a fresh UUID unless one is chained via
  `session_id`), and `fork_session(bool)` exposes the raw flag — which the
  builder previously could not emit at all. Covered by a live integration
  test proving the fork carries the source's history under the new session
  id. (#227)

### Changed

- **Breaking**: `UsageInfo.iterations` is now `Vec<UsageIteration>` (was
  `Vec<serde_json::Value>`), with typed `input_tokens`, `output_tokens`,
  optional per-iteration cache fields (`cache_read_input_tokens`,
  `cache_creation_input_tokens`, `cache_creation`), and the wire `type`
  exposed as `kind` (`"turn"` and `"message"` observed). (#250)
- **`UsageInfo` docs now state the counters are accumulated roll-ups** across
  the turn's API iterations — correct for cost, wrong for context occupancy
  (`cache_read_input_tokens` can exceed the context window several times over
  on tool-heavy turns). Use the last `iterations` entry for context
  estimates, as the CLI does. (#250)

## [2.1.220] - 2026-07-31

Version jumps 2.1.166 → 2.1.220 to re-align with the tested Claude CLI
version, per the crate's versioning convention.

### Added

- `RawAsyncClient` for newline-framed, undecoded access to the Claude
  stream-json protocol.
- `ClaudeInput::user_message_without_session` for messages associated with the
  current CLI process session.
- `ClaudeCliBuilder::working_directory` for setting the working directory of
  spawned Claude CLI processes.

### Changed

- On Windows, Claude CLI launches and version checks now use
  `CREATE_NO_WINDOW` to avoid opening console windows.

### Contributors

- Thanks to @ozitrance — a first-time contributor to this crate — for the
  entire feature set in this release: `RawAsyncClient`,
  `user_message_without_session`, `working_directory`, and the Windows
  `CREATE_NO_WINDOW` support (#243, #244, #245).

## [2.1.166] - 2026-07-27

### Changed

- **Tested Claude CLI version** bumped to 2.1.220 — no wire drift vs the
  2.1.219 snapshot, and the full integration suite passes against the
  installed binary.
- Shared dependencies and lint policy moved to the workspace root: `serde`,
  `serde_json`, `thiserror`, `tokio`, `log`, `which`, and dev `env_logger` are
  now `{ workspace = true }`, and the crate opts into `[workspace.lints]`
  (`unsafe_code = "deny"`). Aligns previously drifted versions: `tokio`
  1.47.1 → 1.49.0, `log` 0.4.27 → 0.4.29, `env_logger` 0.11.8 → 0.11.9.

## [2.1.165] - 2026-07-25

### Added

- **`SystemMessage::as_code_change_published()` / `as_vcs_state_changed()`**
  (plus `is_*` checks) — dedicated typed accessors following the
  `as_init()` pattern, so consumers reach `CodeChangePublishedMessage` /
  `VcsStateChangedMessage` without matching on `KnownSystemEvent` or poking
  raw JSON (#231). Note: the `vcs_state_changed` SDK frame is flat
  (`kind` + `cwd`) — the CLI's internal git/gh watcher event carries richer
  `commit`/`push`/`branch`/`pr` sections, but the emitter flattens each to
  one-or-more `kind` frames before they cross the wire.

## [2.1.164] - 2026-07-24

Catches up to CLI 2.1.219 — the Opus 5 release. Snapshot baseline and
`TESTED_VERSION` move to 2.1.219; the full integration suite passes against
the installed binary.

### Added

- **`ClaudeModel::Opus5`** — pinned variant for `claude-opus-5` (display name
  "Opus 5", knowledge cutoff May 2026). The floating `opus` alias resolves to
  `claude-opus-5` first-party as of CLI 2.1.219 (noted on the `Opus` variant
  doc). Model table refreshed from the 2.1.219 binary; the accepted floating
  aliases are unchanged.
- **`FastModeDisabledReason`** — open enum (`free`, `preference`,
  `extra_usage_disabled`, `network_error`, `unknown`, `not_first_party`,
  `disabled_by_env`, `model_not_allowed`, `sdk_opt_in_required`, `pending`,
  plus `Unknown(String)`) carried as the new optional
  `fast_mode_disabled_reason` on `ResultMessage` and `InitMessage`: why fast
  mode can't serve right now, complementing the existing `fast_mode_state`.
- **`InitMessage.mcp_server_errors`** — new `McpServerError` struct (`name`,
  `type`, `message`) recording `--mcp-config` entries that failed validation
  and were skipped.
- **`PluginInfo.version`** — installed plugin version on the init plugin
  list (caught by the live wire-fidelity audit, not the drift fingerprint —
  it is a nested field).

## [2.1.163] - 2026-07-22

Models the CLI 2.1.205 → 2.1.218 stream-json drift surfaced by the fixed
schema extractor (#223); the committed snapshot baseline and `TESTED_VERSION`
move to 2.1.218.

### Added

- **`ClaudeOutput::CommandLifecycle`** — typed variant for the new
  `command_lifecycle` wire type (fate of a queued command: queued → started →
  completed/cancelled/discarded), with `CommandLifecycleMessage` and the open
  `CommandLifecycleState` enum (`Unknown(String)` fallback).
- **`system/code_change_published`** — `CodeChangePublishedMessage`
  (`provider`, `url`, `repo`, `identifier`): the session is now associated
  with a published pull/merge request.
- **`system/vcs_state_changed`** — `VcsStateChangedMessage` with the open
  `VcsMutationKind` enum (`commit`/`push`/`merge`/`rebase` +
  `Unknown(String)`): a harness-observed command mutated repository state.
  Both new subtypes are wired through `SystemSubtype`, `KnownSystemEvent`,
  and the `typed_value` wrapping audit.
- **`AssistantMessage.aborted`** — true when the message was truncated by an
  interrupt/abort before the stream completed — and
  **`.resumed_from_incomplete_thinking`** — true when the turn continued a
  truncated thinking block (max-output-tokens recovery).
- **`ResultMessage.request_sent_wall_ms`** (fractional wall-clock ms) and
  **`.user_message_uuid`** (wire uuid of the user message the result answers).
- **`ToolProgressMessage.heartbeat`**, **`.subagent_type`**, and
  **`.subagent_retry`** (new `SubagentRetry` struct: attempt, max_retries,
  retry_delay_ms, error_status, error_category).
- **`UserMessage.tool_result_meta`** — new `ToolResultMeta` struct carrying
  the harness-stamped `non_execution_kind` for error tool results and any
  human-typed `user_feedback` deny comment.

## [2.1.162] - 2026-07-22

### Fixed

- **`ClaudeInput::interrupt()` now actually interrupts** (#218). It previously
  serialized to a bare `{"subtype":"interrupt"}`, which the CLI silently
  ignores (verified against 2.1.205 and 2.1.211) — the in-flight turn ran to
  completion. It now emits the required `control_request` envelope
  `{"type":"control_request","request_id":...,"request":{"subtype":"interrupt"}}`,
  which the CLI acknowledges with a `control_response` and cancels the turn
  immediately (verified live: ack in ~10ms, turn ends with
  `result subtype=error_during_execution`).

### Changed

- **Breaking**: `ClaudeInput::interrupt(request_id)` now takes the unique
  request id for the control envelope. `AsyncClient::interrupt()` /
  `SyncClient::interrupt()` generate an `interrupt-<uuid>` id and now return
  `Result<String>` (the id) so callers can correlate the CLI's
  `control_response` ack.
- **Breaking**: removed `SDKControlInterruptRequest` — its bare wire shape is
  a no-op against the CLI. Use `ClaudeInput::interrupt(request_id)` or the new
  `ControlRequestMessage::interrupt(request_id)` constructor instead.

### Added

- **`ControlRequestPayload::Interrupt`** variant and
  **`ControlRequestMessage::interrupt(request_id)`** constructor (mirroring
  `::initialize`), so the correctly-enveloped interrupt can be built typed.

## [2.1.161] - 2026-07-11

### Added

- **`ClaudeModel`** — convenience enum for every model selector the Claude CLI
  accepts, keyed by human-friendly names. Floating aliases (`Sonnet`, `Opus`,
  `Haiku`, `Fable`, `Best`, `OpusPlan`, and the `[1m]` context variants) plus
  the pinned registry models (`Fable5`, `Mythos5`, `Opus48` … `Haiku35`),
  extracted from the CLI 2.1.205 binary's model registry. `cli_arg()` returns
  the exact `--model` string, `display_name()` the human-readable name, and
  `Custom(String)` passes unknown models through verbatim.
  `ClaudeCliBuilder::model(ClaudeModel::Sonnet5)` works directly via
  `Into<String>`. Re-exported at the crate root.

## [2.1.160] - 2026-07-10

### Added

- **Claude Code CLI 2.1.205 output coverage.** Added typed coverage for new
  top-level SDK frames (`stream_event`, `tool_progress`, `auth_status`,
  `tool_use_summary`, `prompt_suggestion`, `conversation_reset`), 19 newer
  `system` subtypes, richer result/init/status/compact/user/assistant wrapper
  fields, and `get_usage` control-response quota payloads.
- **Rate limit schema updated to CLI 2.1.205.** `RateLimitInfo` now carries the
  full `rate_limit_event` wire schema:
  - New fields: `overage_resets_at`, `overage_in_use`, `surpassed_threshold`,
    `overage_period_monthly` / `overage_period_channel` (new
    `OveragePeriodUtilization` struct), `error_code` (new `RateLimitErrorCode`
    enum), `can_user_purchase_credits`, and
    `has_chargeable_saved_payment_method`.
  - `RateLimitWindow` gains `SevenDayOpus`, `SevenDaySonnet`,
    `SevenDayOverageIncluded`, and `Overage` variants.
  - `OverageStatus` gains `AllowedWarning`.
  - `OverageDisabledReason` expands from 2 to 12 typed variants matching the
    CLI enum.
  - Re-exported `OveragePeriodUtilization` and `RateLimitErrorCode` at the
    crate root.

- **`SubagentUsageRollup`** — session-level accumulator for the subagent
  token rollup the CLI renders as `<subagent_tokens>` / `<agent_count>` in its
  terminal `<usage>` block (resolves #169). Feed every `ClaudeOutput` through
  `observe()`; it gates on genuine `Task` results (`agentId`/`totalTokens`
  present), dedupes replayed frames by `agentId`, and totals tokens, agent
  count, tool uses, and duration. `UsageInfo` now documents explicitly that
  the `result` frame's usage covers the main agent only — the rollup is not
  carried on the wire. Re-exported at the crate root.

### Changed (breaking)

- `ClaudeOutput` gained additional variants, so exhaustive matches must handle
  the new top-level frame types.
- `ResultSubtype`, `TaskStatus`, and `TaskType` are now forward-compatible open
  enums with `Unknown(String)` fallbacks.
- `TaskStartedMessage.task_type`, `TaskStartedMessage.tool_use_id`, and
  `TaskProgressMessage.last_tool_name` are now optional to match CLI 2.1.205
  wire frames.
- `RateLimitInfo::is_using_overage` is now `Option<bool>` — the field is
  optional in the CLI wire schema and events omitting it previously failed to
  deserialize.
- `RateLimitWindow::Hourly` removed; the window no longer exists in the CLI
  schema. An `"hourly"` value now parses as `RateLimitWindow::Unknown`.

### Changed

- **Tested Claude CLI version** bumped to 2.1.205 — the full integration suite
  passes against the installed binary. One suite fix was required: CLI 2.1.205
  only enables `AskUserQuestion` in headless mode when a permission-prompt
  tool is configured (`--permission-prompt-tool`), so the round-trip test now
  spawns with one, matching the existing converge test.

## [2.1.159] - 2026-06-27

### Added

- **Typed subagent token accounting.** `Task`-tool result messages now expose
  the subagent's token / timing / tool-use rollup through typed fields instead
  of raw-JSON poking (resolves #168, #169):
  - New `SubagentResult` struct modeling the `Task` `tool_use_result` —
    `status`, `prompt`, `agent_id`, `agent_type`, `content`, `resolved_model`,
    `total_duration_ms`, `total_tokens`, `total_tool_use_count`, the nested
    per-model `usage` (`UsageInfo`), and an optional `SubagentToolStats`.
  - `UserMessage::subagent_result()` accessor parses the result leniently,
    returning `None` only when `tool_use_result` is absent.
  - `total_tokens` is the per-run `subagent_tokens` line item; summing it across
    a session's `Task` results yields the subagent token rollup the CLI renders
    in its terminal `<usage>` block. (The `stream-json` `result` frame's own
    `usage` does not carry the subagent rollup — the `Task` result is the source
    of truth.)
  - Re-exported `UsageInfo`, `ServerToolUse`, `SubagentResult`, and
    `SubagentToolStats` at the crate root.

### Changed

- Enriched the crate description, `keywords`, and `categories` for crates.io
  discoverability (agent / Claude Code / Anthropic / async terms).

## [2.1.158] - 2026-06-25

### Added

- **Subagent message coverage.** Typed every field a `Task`-tool /
  `local_agent` subagent session emits, verified against real captures:
  - New `system` subtypes `task_updated` (with `TaskUpdatedMessage` /
    `TaskPatch`) and `thinking_tokens` (with `ThinkingTokensMessage`), plus
    `SystemMessage::as_task_updated` / `as_thinking_tokens` accessors.
  - `TaskStartedMessage` and `TaskProgressMessage` gain `subagent_type`;
    `TaskStartedMessage` also gains `prompt`.
  - `AssistantMessage` gains `request_id`, `subagent_type`, `task_description`;
    `AssistantMessageContent` gains `message_type` (the API `type` field);
    `UserMessage` gains `subagent_type` and `task_description`.
  - `ToolUseBlock` gains a typed `caller` (`ToolCaller`).
  - `InitMessage` gains `analytics_disabled` and `product_feedback_disabled`.
  - `ResultMessage` gains `ttft_ms`, `ttft_stream_ms`, and `time_to_request_ms`.
- **Wire-fidelity audit** — `audit_frame` / `assert_fully_wrapped` / `FrameAudit`
  (exported from the crate root and `claude_codes::io`) check that a raw frame
  deserializes, round-trips losslessly, and — for `system` frames — resolves to
  a modeled subtype whose typed view captures every field.
- **`AsyncClient::receive_raw`** — read the next frame as a raw
  `serde_json::Value` before typed parsing, for auditing wire fidelity.
- New `test_cases/subagent_sessions/` captures and a `subagent_wrapping_tests`
  suite that audits every frame (fixtures always; a live subagent run under
  `integration-tests`).

### Changed

- **Tested Claude CLI version** bumped to 2.1.178 — the full integration suite,
  including the new live subagent run, passes against the installed binary.

## [2.1.157] - 2026-06-11

### Changed

- **Tested Claude CLI version** bumped from 2.1.150 to 2.1.170. The full
  integration suite passes against CLI 2.1.172; the pin sits two patch
  versions behind. The newer-than-tested CLI warning now triggers only
  above 2.1.170.

## [2.1.156] - 2026-06-10

### Added

- **`ContentBlock::Fallback`** — typed variant for model-fallback content
  blocks (`{"type": "fallback", "from": {"model": ...}, "to": {"model": ...}}`),
  emitted when the CLI switches the response to a fallback model mid-turn.
  Previously these deserialized as `ContentBlock::Unknown`. The wire shape was
  verified against the claude CLI 2.1.172 binary and real session transcripts;
  it carries no fields beyond the from/to models (no reason field exists).
  New types `FallbackBlock` and `FallbackModel` are exported from the crate
  root and `claude_codes::io`. (#160)

## [2.1.155] - 2026-06-08

### Changed (breaking)

- **`TextBlock::citations`** is now `Vec<Citation>` instead of
  `Vec<serde_json::Value>`. Consumers read typed fields (`citation_type`,
  `url`, `title`, `cited_text`, `document_index`, `document_title`) instead of
  JSON-poking each entry. Unmodeled location fields (start/end indices,
  `encrypted_index`, …) are preserved verbatim in `Citation::extra`, so the
  type round-trips losslessly across all citation shapes.

### Added

- **`Citation`** — typed content-block citation struct (re-exported from
  `claude_codes::io`).

## [2.1.154] - 2026-06-08

### Added

- **`ToolInput::from_named_input(name, input)`** — parses a tool-use `input`
  using the authoritative tool *name* from the `ToolUse` block instead of
  guessing the variant from field shape. Resolves the inherent ambiguity of
  the untagged `Deserialize` impl for structurally-identical inputs — most
  notably `WebSearch` vs `ToolSearch`, both bare `{ "query": String }`, where
  the untagged impl always picked `ToolSearch` and dropped the `WebSearch`
  query. Falls back to `Unknown` on shape mismatch and defers to the untagged
  impl for unmodeled (e.g. MCP) tool names.

## [2.1.153] - 2026-06-08

### Changed (breaking)

- **`ResultMessage::model_usage`** is now
  `Option<BTreeMap<String, ModelUsageEntry>>` instead of
  `Option<serde_json::Value>`. Consumers read typed per-model usage
  (`input_tokens`, `output_tokens`, `cache_read_input_tokens`,
  `cache_creation_input_tokens`, `cost_usd`, `web_search_requests`)
  keyed by model name, instead of poking the `Value`. Unmodeled wire
  keys are preserved in `ModelUsageEntry::extra`.

### Added

- **`ModelUsageEntry`** — typed per-model usage/cost record (re-exported
  from `claude_codes::io`).

## [2.1.152] - 2026-06-08

### Added

- **`CompactBoundaryMessage`** gains optional per-compaction stats:
  `summary: Option<String>` (also accepted under the `content` / `text`
  wire keys), `leaf_message_count: Option<u32>` (also accepted under
  `message_count`), and `duration_ms: Option<u64>`. Consumers can read
  these from the typed struct instead of poking `SystemMessage::data`.

## [2.1.151] - 2026-06-08

### Added

- **Round-trip regression test** — `test_task_messages_roundtrip_through_value`
  pushes `task_started` / `task_progress` / `task_notification` system
  messages through `from_str` → `to_value` → `from_value` → typed accessor,
  covering the proxy/relay path where a dropped or renamed field would
  otherwise surface only as a `None` downstream.

## [2.1.150] - 2026-05-29

### Changed

- Pinned the tested Claude CLI version forward to `2.1.150`. The full integration suite passes against the newer CLI with no protocol changes required.

## [2.1.142] - 2026-05-27

### Changed

- **`ToolPermissionRequest::answer_questions`** — Signature changed from `HashMap<String, String>` (keyed by caller-chosen string) to `&HashMap<usize, String>` (keyed by question index). The previous signature let callers — and the rustdoc example — accidentally key answers by `header` instead of the full question text, which makes the CLI render `"Your questions have been answered: ."` with an empty body and leaves Claude unable to see the choice. The new signature looks each index up in the request's `questions` array and uses `q.question` as the wire key, so the wrong-key footgun is impossible.
- **`answer_questions` now returns `AskUserQuestionResponseError`** instead of a bare `serde_json::Error`, distinguishing `WrongTool` / `ParseInput` / `QuestionIndexOutOfRange { index, total }` failure modes. Also validates that `tool_name == "AskUserQuestion"` before parsing the input.

### Added

- **`AskUserQuestionResponseError`** — Re-exported error type for the helper.

### Migration

If you wired up to the 2.1.141 `answer_questions(HashMap<String, String>)` form, swap to passing a `HashMap<usize, String>` keyed by question index (matches the natural UI shape of "the user picked option X for question 0"). No other callsite changes needed.

## [2.1.141] - 2026-05-17

### Added

- **`ToolPermissionRequest::answer_questions(answers, request_id)`** —
  typed helper for replying to an `AskUserQuestion` permission request.
  Parses `self.input` as `AskUserQuestionInput`, attaches the supplied
  `HashMap<String, String>` answers, and returns a `ControlResponse`
  whose `updatedInput` carries both the original `questions` array AND
  the new `answers`. Eliminates the `"undefined is not an object
  (evaluating 'q.map')"` failure mode in downstream viewers that read
  `tool_use_result.questions` and call `questions.map(...)` — that
  crash happens when the response payload is built by hand and the
  original `questions` are dropped. Existing `allow` / `allow_with`
  continue to work for non-AskUserQuestion approvals.

### Verified

- All 26 integration tests pass against the live Claude CLI, including
  `test_ask_user_question_answered_and_converges` which drives Claude
  through the new helper and confirms the agent's follow-up reply
  references the chosen answer (`"You picked Blue."`).

## [2.1.140] - 2026-05-13

### Added

- **`UserMessage.tool_use_result`** — `Option<serde_json::Value>` capturing the top-level structured tool result the CLI emits alongside `tool_result` content blocks (e.g. `{ questions, answers }` for `AskUserQuestion`, `{ stdout, stderr, exit_code }` for `Bash`). Previously dropped during deserialization, which broke proxies relaying user messages to viewers that read the field.
- **`UserMessage.timestamp`** — `Option<String>` capturing the CLI's ISO-8601 timestamp on echoed tool results.
- **`UserMessage::tool_use_result_as<T>()`** — Typed accessor for parsing `tool_use_result` into a caller-specified type when the tool is known (e.g. `tool_use_result_as::<AskUserQuestionInput>()`).
- **Integration regression test** — `test_ask_user_question_answered_and_converges` drives the full AskUserQuestion round-trip through the permission control protocol and asserts `tool_use_result` survives end-to-end without information loss.

### Changed

- Updated `TESTED_VERSION` to `2.1.140`

## [2.1.117] - 2026-04-15

### Added

- **`ContentBlock::Unknown(Value)`** — Fallback variant for forward compatibility with new content block types from the CLI. Prevents deserialization failures when encountering unknown block types (#104)
- **Typed content block variants** — `ServerToolUse`, `WebSearchToolResult`, `CodeExecutionToolResult`, `McpToolUse`, `McpToolResult`, `ContainerUpload` for the new server-side and MCP content blocks emitted by CLI 2.1.117 (#105, #106, #107)
- **`TextBlock.citations`** — `Vec<Value>` field for web search citations on text blocks (#108)
- **`ResultMessage` fields** — `api_error_status`, `stop_reason`, `terminal_reason`, `fast_mode_state`, `model_usage` (#109)
- **`UsageInfo` fields** — `cache_creation`, `inference_geo`, `iterations`, `speed` (#110)
- **`ServerToolUse.web_fetch_requests`** — Tracks web fetch request count (#110)
- **`InitMessage` fields** — `uuid`, `memory_paths`, `fast_mode_state` (#111)
- **`PluginInfo.source`** — Plugin registry source identifier (#111)
- **`AssistantMessageContent` fields** — `stop_details`, `context_management` (#112)
- **`AssistantUsage.inference_geo`** — Inference geography field (#112)
- **`UserMessage` fields** — `parent_tool_use_id`, `uuid` (#113)
- **New `ToolInput` types** — `MultiEdit`, `LS`, `NotebookRead`, `ScheduleWakeup`, `ToolSearch` with typed structs (#114)
- **`ClaudeCliBuilder.max_thinking_tokens()`** — Builder method and `CliFlag::MaxThinkingTokens` for extended thinking control (#115)
- **`ContentBlock.block_type()`** and **`ContentBlock.is_unknown()`** — Helper methods for content block introspection

### Changed

- Updated `TESTED_VERSION` to `2.1.117`
- `UsageInfo` fields now use `#[serde(default)]` for robustness
- `ServerToolUse` now derives `Default`

## [2.1.53] - 2026-03-17

### Added

- **Binary path resolution via `which`** — `ClaudeCliBuilder::spawn()`, `spawn_sync()`, and `build_command()` now resolve non-absolute binary paths using `which` at spawn time, producing a clear `BinaryNotFound` error instead of an opaque OS "file not found" (#102)
- **`Error::BinaryNotFound`** — New error variant for when the CLI binary isn't found on PATH

### Changed

- **`spawn_sync()` return type** — Now returns `crate::error::Result<Child>` instead of `std::io::Result<Child>` for consistent error handling
- **`build_command()` return type** — Now returns `Result<Command>` instead of `Command` to surface binary resolution errors

## [2.1.52] - 2026-03-12

### Added

- **`SDKControlInterruptRequest`** — Typed struct for the `{ "subtype": "interrupt" }` SDK control message, used to gracefully stop a running Claude session without killing the process
- **`ClaudeInput::interrupt()`** — Constructor for creating interrupt messages
- **`AsyncClient::interrupt()`** and **`SyncClient::interrupt()`** — Convenience methods to send an interrupt to the CLI subprocess

## [2.1.51] - 2026-02-27

### Changed

- **`Error::Deserialization`** now wraps `ParseError` instead of `String`, giving callers structured access to the raw input line, parsed JSON value, and error message
- **`ParseError`** gains a `raw_line: String` field containing the exact stdout line (works even when the input isn't valid JSON)

## [2.1.50] - 2026-02-27

### Fixed

- `RateLimitInfo.resets_at` and `RateLimitInfo.rate_limit_type` are now `Option` — Claude CLI can omit these fields in `rate_limit_event` messages with `status: "allowed"`

## [2.1.49] - 2026-02-25

### Changed

- **`SystemSubtype`** — enum replacing `String` for system message subtypes (`init`, `api_error`, etc.)
- **`ApiErrorType`** — enum replacing `String` for API error types (`authentication_error`, `overloaded_error`, etc.)
- **`RateLimitStatus`** — enum replacing `String` for rate limit statuses (`rate_limited`, `rate_limit_cleared`)
- **`RateLimitWindow`** — enum replacing `String` for rate limit windows (`minutely`, `daily`, etc.)
- **`PermissionType`** — enum replacing `String` for permission types (`addRules`, `setMode`)
- **`PermissionDestination`** — enum replacing `String` for permission destinations (`session`, `project`)
- **`PermissionBehavior`** — enum replacing `String` for permission behaviors (`allow`, `deny`)
- **`PermissionModeName`** — enum replacing `String` for permission mode names (`acceptEdits`, `bypassPermissions`)
- **`MessageRole`** — enum replacing `String` for message roles (`user`, `assistant`)
- **`CompactionTrigger`** — enum replacing `String` for compaction triggers (`auto`, `manual`)
- **`StopReason`** — enum replacing `String` for stop reasons (`end_turn`, `max_tokens`, `tool_use`)
- **`TodoStatus`** — enum replacing `String` for todo statuses (`pending`, `in_progress`, `completed`)
- **`OverageStatus`** — enum replacing `String` for overage billing status (`allowed`, `rejected`)
- **`OverageDisabledReason`** — enum replacing `String` for overage disabled reason (`org_level_disabled`, `out_of_credits`)
- **`ImageSourceType`** — enum replacing `String` for image encoding type (`base64`)
- **`MediaType`** — enum replacing `String` for image MIME types (`image/jpeg`, `image/png`, `image/gif`, `image/webp`)
- **`GrepOutputMode`** — enum replacing `String` for grep output mode (`content`, `files_with_matches`, `count`)
- **`SubagentType`** — enum replacing `String` for task subagent types (`Bash`, `Explore`, `Plan`, `general-purpose`)
- **`NotebookCellType`** — enum replacing `String` for notebook cell types (`code`, `markdown`)
- **`NotebookEditMode`** — enum replacing `String` for notebook edit modes (`replace`, `insert`, `delete`)
- **`ApiKeySource`** — enum replacing `String` for API key source in init messages (`none`)
- **`OutputStyle`** — enum replacing `String` for output style in init messages (`default`)
- **`InitPermissionMode`** — enum replacing `String` for permission mode in init messages (`default`)
- **`StatusMessageStatus`** — enum replacing `String` for status message status (`compacting`)

All enums include an `Unknown(String)` fallback variant for forward compatibility, plus `as_str()`, `Display`, and `From<&str>` implementations.

### Breaking

- Struct fields that were `String` are now typed enums — callers using `.as_deref()`, string comparisons, or `.to_string()` on these fields need to update to use the enum variants or `.as_str()` method

## [2.1.47] - 2026-02-24

### Added

- **`TaskStartedMessage`** — Typed struct for `task_started` system messages emitted when a background task (agent or bash) begins
- **`TaskProgressMessage`** — Typed struct for `task_progress` system messages with tool name, description, and cumulative usage stats
- **`TaskNotificationMessage`** — Typed struct for `task_notification` system messages emitted when a background task completes or fails
- **`TaskUsage`** — Cumulative usage statistics (`duration_ms`, `tool_uses`, `total_tokens`)
- **`TaskType`** enum — `LocalAgent` or `LocalBash`
- **`TaskStatus`** enum — `Completed` or `Failed`
- **`SystemMessage` helpers** — `is_task_started()`, `is_task_progress()`, `is_task_notification()` and corresponding `as_task_*()` methods

## [2.1.46] - 2026-02-20

### Fixed

- **`RateLimitInfo.overage_status`** now `Option<String>` — `allowed_warning` events omit this field, previously causing deserialization failures

### Added

- **`RateLimitInfo.utilization`** — `Option<f64>` capturing rate limit usage (0.0–1.0)

## [2.1.45] - 2026-02-18

### Added

- **Expanded `InitMessage` fields** - Added typed fields for `slash_commands`, `agents`, `plugins`, `skills`, `claude_code_version`, `api_key_source`, `output_style`, and `permission_mode`
- **`PluginInfo` struct** - Typed representation of plugin entries with `name` and `path` fields
- **`allow_recursion()` on `ClaudeCliBuilder`** - Enables spawning Claude CLI from within a Claude Code session by unsetting `CLAUDECODE` env var
- **`/clear` integration test** - Verifies session ID resets after `/clear` command

### Changed

- Updated `TESTED_VERSION` to `2.1.47`
- All integration tests now use `allow_recursion()` for reliable execution inside Claude Code sessions

## [2.1.20] - 2026-02-17

### Added

- **`RateLimitEvent` and `RateLimitInfo`** - Support for `rate_limit_event` messages from Claude CLI
- `ClaudeOutput::RateLimitEvent` variant with `is_rate_limit_event()` and `as_rate_limit_event()` helpers

## [2.1.19] - 2026-02-17

### Added

- **`CliFlag` enum** - Comprehensive enum covering all 41 Claude CLI flags for building launcher UIs and advanced configuration
- **`InputFormat` and `OutputFormat` enums** - Typed representations of `--input-format` and `--output-format` options
- **`PermissionMode::Delegate` and `PermissionMode::DontAsk`** - Added missing permission mode variants
- `CliFlag::as_flag()` - Returns the CLI flag string (e.g., `"--add-dir"`)
- `CliFlag::to_args()` - Converts a flag + value into CLI argument strings
- `CliFlag::all_flags()` - Returns all flag names with descriptions for enumeration

## [2.1.18] - 2026-01-26

### Changed

- Increase stdout buffer from 8KB to 10MB to handle large JSON messages

## [2.1.17] - 2026-01-25

### Added

- **Permission struct for "remember this decision" support** - New typed API for building permission responses that support Claude Code's "remember this decision" functionality.

  When responding to tool permission requests, you can now grant permissions so similar actions won't require approval in the future:

  ```rust
  use claude_codes::{ToolPermissionRequest, Permission};

  fn handle_permission(req: &ToolPermissionRequest, request_id: &str) -> ControlResponse {
      // Allow and remember this specific command for the session
      req.allow_and_remember(
          vec![Permission::allow_tool("Bash", "npm test")],
          request_id,
      )
  }
  ```

  Or accept Claude's suggested permission:

  ```rust
  // Use the first permission suggestion if available
  let response = req.allow_and_remember_suggestion(request_id)
      .unwrap_or_else(|| req.allow(request_id));
  ```

  Available `Permission` constructors:
  - `Permission::allow_tool(tool_name, rule_content)` - Allow a specific tool with a pattern (session-scoped)
  - `Permission::allow_tool_with_destination(tool_name, rule_content, destination)` - Allow with custom scope ("session" or "project")
  - `Permission::set_mode(mode, destination)` - Set a permission mode like "acceptEdits"
  - `Permission::from_suggestion(suggestion)` - Convert a `PermissionSuggestion` to a `Permission`

  **Migration from `allow_with_permissions`:**

  Before (manual JSON conversion):
  ```rust
  // Old approach - manually convert to JSON
  let perms_json: Vec<serde_json::Value> = suggestions
      .iter()
      .filter_map(|p| serde_json::to_value(p).ok())
      .collect();
  ControlResponse::from_result(
      &request_id,
      PermissionResult::allow_with_permissions(input, perms_json)
  )
  ```

  After (typed API):
  ```rust
  // New approach - use typed Permission API
  let permissions: Vec<Permission> = suggestions
      .iter()
      .map(Permission::from_suggestion)
      .collect();
  req.allow_and_remember(permissions, request_id)
  ```

- **`decision_reason` and `tool_use_id` fields on `ToolPermissionRequest`** - These fields are now exposed for consumers that need them when building custom permission handling logic. The `tool_use_id` is particularly useful for correlating permission requests with tool uses in the message stream.

- **`ClaudeOutput::Error` variant for Anthropic API errors** - New variant to capture API errors (500, 529 overloaded, rate limits, etc.) that were previously unparsed.

  ```rust
  use claude_codes::ClaudeOutput;

  match output {
      ClaudeOutput::Error(err) => {
          if err.is_overloaded() {
              println!("API overloaded, retrying...");
          } else if err.is_rate_limited() {
              println!("Rate limited: {}", err.error.message);
          } else {
              println!("API error: {}", err.error.message);
          }
      }
      // ... handle other variants
  }
  ```

  Helper methods on `AnthropicError`:
  - `is_overloaded()` - HTTP 529 overloaded error
  - `is_server_error()` - HTTP 500 server error
  - `is_rate_limited()` - HTTP 429 rate limit error
  - `is_authentication_error()` - HTTP 401 auth error
  - `is_invalid_request()` - HTTP 400 invalid request

  Helper methods on `ClaudeOutput`:
  - `is_api_error()` - Check if this is an error variant
  - `as_anthropic_error()` - Get the error if this is one

### Changed

- `allow_with_permissions` method documentation clarified to note it takes raw `Vec<Value>`. For type safety, prefer the new `allow_and_remember` method.

## [2.1.16] - 2026-01-22

### Fixed

- Fixed `PermissionSuggestion` struct to correctly handle both `setMode` and `addRules` suggestion types from Claude CLI.

## [2.1.15] - 2026-01-21

### Added

- Re-export `ContentBlock`, `ToolUseBlock`, and other io types at crate root
- Typed `UsageInfo` on `AssistantMessage` with `input_tokens`, `output_tokens`, and `cache_creation_input_tokens`
- Typed `PermissionSuggestion` for `ToolPermissionRequest` permission suggestions
- Typed `PermissionDenial` for `ResultMessage` permission denial details
- Typed `StatusDetails` and `SuggestionMetadata` for system status responses
- Typed system message subtypes (`init`, `status`, `compact_boundary`)
- Typed `ToolInput` definitions for all built-in tools (Read, Write, Edit, Bash, Glob, Grep, etc.)
- Helper methods on `ClaudeOutput`: `is_assistant_message()`, `is_result()`, `is_error()`, `as_assistant()`, `as_result()`, `as_system()`, `text_content()`, `tool_uses()`
- `errors` field on `ResultMessage` for capturing error details
- Real production message test captures for structured content

## [2.1.4] - 2026-01-10

### Added

- Tool approval protocol support with interactive permission request/response handling
- `ControlRequest` and `ControlResponse` types for the tool permission workflow
- `ToolPermissionRequest` with `allow()`, `deny()`, and `allow_with_permissions()` helpers

### Fixed

- `--session-id` flag no longer incorrectly added when using `--resume` or `--continue`

## [2.1.3] - 2026-01-09

### Changed

- Version sync with Claude CLI 2.1.3
- WASM support documentation for the `types` feature with `wasm32-unknown-unknown`

## [2.0.76] - 2026-01-04

### Changed

- Version sync with Claude CLI 2.0.76
- Fixed content deserialization to handle both string and array formats

### Fixed

- Removed debug `eprintln` statements from output

## [0.3.0] - 2025-08-30

### Changed

- **Breaking:** Reorganized to feature-based architecture with `sync-client`, `async-client`, and `types` features
- **Breaking:** Switched logging from `tracing` to `log` crate
- **Breaking:** Client modules moved to top-level `client_sync` and `client_async`
- `types` feature enables WASM-compatible type definitions without client dependencies

## [0.2.1] - 2025-08-28

### Added

- `ping()` method on `AsyncClient` and `SyncClient` for connectivity testing
- `parse_json_tolerant()` to handle ANSI escape codes in responses
- Integration tests for slash commands (`/help`, `/status`, `/cost`)

### Fixed

- `num_turns` field type to handle `-1` for slash commands

## [0.2.0] - 2025-08-26

### Added

- Image content block support (JPEG, PNG, GIF, WebP) with `user_message_with_image()`
- OAuth token and API key environment variable support (`CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`)

### Changed

- **Breaking:** Session IDs use `UUID` type instead of `String`
- **Breaking:** `ClaudeInput::user_message()` now requires `UUID` for session_id

## [0.1.2] - 2025-08-25

### Added

- `resume_session()` and `resume_session_with_model()` on both clients
- Environment variable support for OAuth tokens and API keys
- Validation warnings for incorrect token/key prefixes

## [0.1.1] - 2025-08-25

### Added

- Session UUID versioning to track Claude Code sessions
- `session_uuid()` getter on both `AsyncClient` and `SyncClient`
- CLI builder generates UUID v4 by default

## [0.1.0] - 2025-08-25

### Added

- Comprehensive crate and module-level documentation
- `AsyncClient` and `SyncClient` API docs

### Changed

- Simplified licensing to Apache-2.0 only

## [0.0.5] - 2025-08-24

### Added

- `AsyncClient` with `query()` and `query_stream()` methods
- `SyncClient` for non-async contexts
- `ResponseStream` and `ResponseIterator` for iterative response processing
- `ResultMessage` with `UsageInfo` for token usage and cost tracking
- Claude CLI version checking with compatibility warnings
- Example programs: `basic_repl`, `async_client`, `sync_client`

### Changed

- Message types restructured to match Claude Code SDK (System, User, Assistant, Result)

## [0.0.1] - 2025-08-23

### Added

- Initial implementation of `claude-codes` crate
- `ClaudeInput` and `ClaudeOutput` enums for typed protocol messages
- `ClaudeCliBuilder` for streaming JSON mode
- Interactive testing binary for protocol debugging
- Automatic test case capture for failed deserializations