@lqc123qwe/car-runtime 1.21.1 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/SHA256SUMS CHANGED
@@ -1 +1 @@
1
- 66bbea0d771ff0a416ece86c703d25f39a4e9e78af767610ba336b01bdb1c9ac car-runtime-1.21.1.tgz
1
+ 47b365f175bdb123a4dcf713285a6e4cf28176cb047d99b5891416901769a710 car-runtime-1.22.0.tgz
Binary file
@@ -0,0 +1 @@
1
+ {"base64Signature":"MEUCIQDVLKF0NzvACUkwTxZp7SiwxmQ1Eg+T44sWztixTvbO+QIgAj4PlRzeD3Rz24/J5yq8RnyvmXlLHQfpEYKCUcLCr+g=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc2RENDQm02Z0F3SUJBZ0lVRXFGMng5bWh4Z0FFMXUxMUpHdTZlMzIrWXlJd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1URTBNRGMxTVRRd1doY05Nall4TVRFME1EZ3dNVFF3V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVNUFhxRVRZRTk4Rm5DTG1SS3h5YUhzaWl5bzI1WnNlS2x1QzIKV0FFVTgxVVYwdHZWUnRCWWVkOWNvZ2hjN0NFemZvc2Z1bjZQMDU3Vk0wSmFnQlRyZzZPQ0JZMHdnZ1dKTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl5TGpBd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLRGt6TVdNNVpHTTVZamd6TUdJd1kySXlZemxrTkdObApNakUzTmpkaVpHUTFOVEJrT1RNeU4ySXdGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNakl1TURBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl5TGpBd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENnNQpNekZqT1dSak9XSTRNekJpTUdOaU1tTTVaRFJqWlRJeE56WTNZbVJrTlRVd1pEa3pNamRpTUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENnNQpNekZqT1dSak9XSTRNekJpTUdOaU1tTTVaRFJqWlRJeE56WTNZbVJrTlRVd1pEa3pNamRpTUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNakl1TURBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpJeUxqQXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2c1TXpGak9XUmpPV0k0TXpCaU1HTmlNbU01WkRSalpUSXhOelkzWW1Sa05UVXdaRGt6TWpkaU1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXpOVGN6TURJMwpNVGsyTDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eU1pNHdNSUdMQmdvckJnRUVBZFo1QWdRQ0JIMEVld0I1QUhjQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2gyUXBuaEFBQUJBTUFTREJHCkFpRUE2TU5RWks2bXM4aElQZTFPdXk3RmNwY2FCRnJXci9zNkZJV0RCcE9tUjI0Q0lRRFZxcjR3eVRMT21KTloKWEloSXRYUEo1MFQzVzZ4M29aVU5JaXFmd3gyOW5EQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqQVlFRVkwQUEydQpSUXo0enpRSFg3SENtMnIzT1UzRDRtQ3p3STRETzdVNmVCQXdweFJ5RTQ3QW5aclg1KzNZVVJBQ01RQ2lTaHhrCkdycUJSWVhUdXV0VGhDY09RSTRRTGhqTWtLanNiYWNFU1pLRDBTWmtOTTFEN3U4TWxIVksrWGQ2RThBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEYCIQDlhHKIxkm2Q8f4998THn2ni5scGkUTarkBoBnQm3pdSgIhAK2ypQcND+k8yy70KbMxrz90gusnFZEeaiCU14yJ5WbP","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0N2IzNjVmMTc1YmRiMTIzYTRkY2Y3MTMyODVhNmU0Y2YyODE3NmNiMDQ3ZDk5YjU4OTE0MTY5MDE3NjlhNzEwIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUURWTEtGME56dkFDVWt3VHhacDdTaXd4bVExRWcrVDQ0c1d6dGl4VHZiTytRSWdBajRQbFJ6ZUQzUnoyNC9KNXlxOFJueXZtWGxMSFFmcEVZS0NVY0xDcitnPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzJSRU5EUW0wMlowRjNTVUpCWjBsVlJYRkdNbmc1YldoNFowRkZNWFV4TVVwSGRUWmxNeklyV1hsSmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUlRCTlJHTXhUVlJSZDFkb1kwNU5hbGw0VFZSRk1FMUVaM2ROVkZGM1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZOVUZoeFJWUlpSVGs0Um01RFRHMVNTM2g1WVVoemFXbDViekkxV25ObFMyeDFReklLVjBGRlZUZ3hWVll3ZEhaV1VuUkNXV1ZrT1dOdloyaGpOME5GZW1admMyWjFialpRTURVM1ZrMHdTbUZuUWxSeVp6WlBRMEpaTUhkbloxZEtUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha2w1VEdwQmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkd0NlRWZE5OVnBIVFRWWmFtZDZUVWRKZDFreVNYbFplbXhyVGtkT2JBcE5ha1V6VG1wa2FWcEhVVEZPVkVKclQxUk5lVTR5U1hkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOYWtsMVRVUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrbDVUR3BCZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5OUXBOZWtacVQxZFNhazlYU1RSTmVrSnBUVWRPYVUxdFRUVmFSRkpxV2xSSmVFNTZXVE5aYlZKclRsUlZkMXBFYTNwTmFtUnBUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObk5RcE5la1pxVDFkU2FrOVhTVFJOZWtKcFRVZE9hVTF0VFRWYVJGSnFXbFJKZUU1NldUTlpiVkpyVGxSVmQxcEVhM3BOYW1ScFRVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOYWtsMVRVUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BKZVV4cVFYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJjMVRYcEdhazlYVW1wUFYwazBUWHBDYVUxSFRtbE5iVTAxV2tSU2FscFVTWGhPZWxreldXMVNhMDVVVlhkYVJHdDZUV3BrYVUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYcE9WR042VFVSSk13cE5WR3N5VERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZVUxcE5IZE5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVWbGQwSTFRVWhqUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyZ3lVWEJ1YUVGQlFVSkJUVUZUUkVKSENrRnBSVUUyVFU1UldrczJiWE00YUVsUVpURlBkWGszUm1Od1kyRkNSbkpYY2k5ek5rWkpWMFJDY0U5dFVqSTBRMGxSUkZaeGNqUjNlVlJNVDIxS1Rsb0tXRWxvU1hSWVVFbzFNRlF6VnpaNE0yOWFWVTVKYVhGbWQzZ3lPVzVFUVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV2UVVSQ2JFRnFRVmxGUlZrd1FVRXlkUXBTVVhvMGVucFJTRmczU0VOdE1uSXpUMVV6UkRSdFEzcDNTVFJFVHpkVk5tVkNRWGR3ZUZKNVJUUTNRVzVhY2xnMUt6TlpWVkpCUTAxUlEybFRhSGhyQ2tkeWNVSlNXVmhVZFhWMFZHaERZMDlSU1RSUlRHaHFUV3RMYW5OaVlXTkZVMXBMUkRCVFdtdE9UVEZFTjNVNFRXeElWa3NyV0dRMlJUaEJQUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1794642701,"logIndex":3873143698,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
@@ -1,4 +1,4 @@
1
- {"stage":"archive","detail":"git archive HEAD → car-runtime-1.21.1.tgz(437655 bytes,树哈希锚定)","ts":1794630034700}
2
- {"stage":"sha256sums","detail":"SHA256SUMS 66bbea0d771ff0a4…(裸字节复核通过)","ts":1794630034701}
3
- {"stage":"sbom","detail":"55 个组件(运行时依赖 0 项)","ts":1794630034707}
4
- {"stage":"cosign-keyless","detail":"signed car-runtime-1.21.1.tgz (keyless OIDC + Rekor)","ts":1794630037965}
1
+ {"stage":"archive","detail":"git archive HEAD → car-runtime-1.22.0.tgz(444663 bytes,树哈希锚定)","ts":1794642698888}
2
+ {"stage":"sha256sums","detail":"SHA256SUMS 47b365f175bdb123…(裸字节复核通过)","ts":1794642698889}
3
+ {"stage":"sbom","detail":"55 个组件(运行时依赖 0 项)","ts":1794642698894}
4
+ {"stage":"cosign-keyless","detail":"signed car-runtime-1.22.0.tgz (keyless OIDC + Rekor)","ts":1794642701495}
@@ -53,10 +53,16 @@ export interface ChatStepOptions {
53
53
  * 同纪律);同请求同名只回调一次;
54
54
  * - 纯渲染提示:不落链、不持久、不影响 ModelStep 与 N1(事实本体仍以落盘行为准,
55
55
  * assistant 事件到达前 UI 呈现的增量属临时态);缺省(undefined)= 零行为变化。
56
+ * 1.22(D-C1/D-C2):+thinkingDelta = 思考流式上屏数据源(桌面批消费)——掩蔽流
57
+ * (独立 StreamRedactor 实例,与 assistant 文本同脱敏纪律;链侧 thinking 禁改写不变,
58
+ * 审计面原文唯一事实源 = 链);渲染提示级(同 toolName,无对拍红线——1.19 红线面
59
+ * 不被 thinking 破坏,D-T6 延续);内核 spec 内断言:thinkingDelta 之和 + flush
60
+ * ≡ 掩蔽(thinking 事件 payload 之和)。
56
61
  */
57
62
  onDelta?: (d: {
58
63
  textDelta?: string;
59
64
  toolName?: string;
65
+ thinkingDelta?: string;
60
66
  }) => void;
61
67
  }
62
68
  /** ModelStep 加法扩展:脱敏计数留痕(S15 secretsRedacted 同名口径) */
@@ -35,11 +35,18 @@ export async function chatStep(opts) {
35
35
  const redactor = new StreamRedactor();
36
36
  let text = '';
37
37
  const calls = new Map();
38
- // 1.21(R-TOKENHUB-1 / D-T1/D-T2):thinking 捕获累积——禁改写(回传签名完整性 > 脱敏
39
- // 改写),provider 事实字节保真;redacted_thinking = data 全文单发覆盖
40
- let thinkingText = '';
41
- let thinkingSignature = '';
42
- let thinkingRedacted;
38
+ const thinkingBlocks = [];
39
+ let thCurText = '';
40
+ let thCurSig = '';
41
+ const sealThinkingBlock = () => {
42
+ if (thCurText.length)
43
+ thinkingBlocks.push({ text: thCurText, ...(thCurSig ? { signature: thCurSig } : {}) });
44
+ thCurText = '';
45
+ thCurSig = '';
46
+ };
47
+ // 1.22(D-C1):思考掩蔽流——独立 StreamRedactor 实例(与 assistant 文本同脱敏纪律;
48
+ // 链侧禁改写不变,UI 显示面与审计原文面分离)
49
+ const thinkingRedactor = new StreamRedactor();
43
50
  let stopReason;
44
51
  let errorDetail;
45
52
  let usage;
@@ -79,13 +86,29 @@ export async function chatStep(opts) {
79
86
  cur.argsBuf += chunk.toolCallDelta.argumentsDelta;
80
87
  calls.set(idx, cur);
81
88
  }
82
- // 1.21(R-TOKENHUB-1 / D-T1):thinking 三面捕获(text/signature 增量累积、redacted 单发覆盖)
83
- if (chunk.thinkingDelta !== undefined)
84
- thinkingText += chunk.thinkingDelta;
89
+ // 1.21(D-T1)→ 1.22(D-B1 多块):thinking 三面捕获——块数组累积(签名随块独立)+
90
+ // 掩蔽流回调(D-C1:独立 redactor,masked 非空才回调)
91
+ if (chunk.thinkingBlockStart !== undefined)
92
+ sealThinkingBlock();
93
+ if (chunk.thinkingDelta !== undefined) {
94
+ thCurText += chunk.thinkingDelta;
95
+ if (opts.onDelta) {
96
+ const masked = thinkingRedactor.push(chunk.thinkingDelta);
97
+ if (masked)
98
+ opts.onDelta({ thinkingDelta: masked });
99
+ }
100
+ }
85
101
  if (chunk.thinkingSignatureDelta !== undefined)
86
- thinkingSignature += chunk.thinkingSignatureDelta;
87
- if (chunk.thinkingRedacted !== undefined)
88
- thinkingRedacted = chunk.thinkingRedacted;
102
+ thCurSig += chunk.thinkingSignatureDelta;
103
+ if (chunk.thinkingRedacted !== undefined) {
104
+ sealThinkingBlock(); // redacted = 独立块(不与前文本块拼接)
105
+ thinkingBlocks.push({ text: chunk.thinkingRedacted, redacted: true });
106
+ if (opts.onDelta) {
107
+ const masked = thinkingRedactor.push(chunk.thinkingRedacted);
108
+ if (masked)
109
+ opts.onDelta({ thinkingDelta: masked });
110
+ }
111
+ }
89
112
  if (chunk.error)
90
113
  errorDetail = chunk.error.message;
91
114
  if (chunk.usage)
@@ -106,6 +129,13 @@ export async function chatStep(opts) {
106
129
  if (tail && opts.onDelta)
107
130
  opts.onDelta({ textDelta: tail });
108
131
  text += tail;
132
+ // 1.22(D-B1/D-C1):流尾封板末块 + 思考掩蔽流 flush 残段(渲染提示面,先于 finish 兜底——flush 不抛错)
133
+ sealThinkingBlock();
134
+ {
135
+ const thTail = thinkingRedactor.flush();
136
+ if (thTail && opts.onDelta)
137
+ opts.onDelta({ thinkingDelta: thTail });
138
+ }
109
139
  // 1.11-S2(D-33 / 计量设计 §3.3):请求级 usage 事实落链(拿到 usage 的最后一个 chunk 即请求
110
140
  // 收尾;撞限/异常 turn 照常落——截断前已完成请求的 usage 事实不吞)。缺失 = provider 未回,
111
141
  // 禁补造;actor='runtime'、不进模型消息流(deriveMessages 不投影 usage,§2.5 红线)
@@ -122,20 +152,33 @@ export async function chatStep(opts) {
122
152
  providerRaw: usage.providerRaw,
123
153
  });
124
154
  }
125
- // 5a. 1.21(R-TOKENHUB-1 / D-T2):thinking 事件落链(actor='model',先于 assistant/toolCall
126
- // ——投影层按同 turn 首 toolCall 附加回传)。payload = thinking 原文禁改写(签名完整性 >
127
- // 脱敏改写——改写即回传校验失败);secretsRedacted 仅计数留痕(审计信号,不复写);
128
- // redacted 形态 payload = data 全文(meta.redacted=true)。无 thinking chunk = 零新事件
129
- // (D-T5 字节兼容,链形态与 1.20 全等)。
130
- if (thinkingText.length || thinkingRedacted !== undefined) {
131
- const thinkingPayload = thinkingRedacted ?? thinkingText;
132
- const secretsInThinking = scanSecrets(thinkingPayload).hits.length;
133
- log.append('model', 'thinking', turnId, thinkingPayload, {
134
- ...(thinkingRedacted !== undefined ? { redacted: true } : {}),
135
- ...(thinkingSignature ? { signature: thinkingSignature } : {}),
155
+ // 5a. 1.21(R-TOKENHUB-1 / D-T2)→ 1.22(D-B1):thinking 事件落链——一块一事件(append-only
156
+ // 最窄面,审计/投影按序聚合自然成立)。actor='model',先于 assistant/toolCall(投影层按同
157
+ // turn 首 toolCall 附加回传)。payload = 块原文禁改写(签名完整性 > 脱敏改写——改写即回传
158
+ // 校验失败);secretsRedacted 仅计数留痕(审计信号,不复写);redacted 形态 payload = data
159
+ // 全文(meta.redacted=true)。**单块链(含 redacted 单块)meta 无 blockIndex = 与 1.21.1
160
+ // 逐字节同形(D-B1 兼容线);多块链每块带 blockIndex(0 起块序)**。无 thinking 块 = 零新
161
+ // 事件(D-T5 字节兼容,链形态与 1.20 全等)。
162
+ if (thinkingBlocks.length === 1) {
163
+ const b = thinkingBlocks[0];
164
+ const secretsInThinking = scanSecrets(b.text).hits.length;
165
+ log.append('model', 'thinking', turnId, b.text, {
166
+ ...(b.redacted ? { redacted: true } : {}),
167
+ ...(b.signature ? { signature: b.signature } : {}),
136
168
  ...(secretsInThinking ? { secretsRedacted: secretsInThinking } : {}),
137
169
  });
138
170
  }
171
+ else if (thinkingBlocks.length > 1) {
172
+ thinkingBlocks.forEach((b, i) => {
173
+ const secretsInThinking = scanSecrets(b.text).hits.length;
174
+ log.append('model', 'thinking', turnId, b.text, {
175
+ ...(b.redacted ? { redacted: true } : {}),
176
+ ...(b.signature ? { signature: b.signature } : {}),
177
+ ...(secretsInThinking ? { secretsRedacted: secretsInThinking } : {}),
178
+ blockIndex: i,
179
+ });
180
+ });
181
+ }
139
182
  // 5. assistant 文本落 M7(脱敏后 + 计数留痕;空文本不产事件)
140
183
  // flush 后兜底复扫只计数不复写:流式遮蔽已生效,>0 即流式边界逃逸的审计信号
141
184
  let secretsRedacted = 0;
@@ -370,8 +370,13 @@ async function* parseSseStream(body, model, adapterId) {
370
370
  }))
371
371
  : [];
372
372
  const text = typeof delta.content === 'string' && delta.content.length ? delta.content : undefined;
373
+ // 1.22(D-A2):openai 形 thinking 捕获——delta.reasoning_content(DeepSeek 官方 + new-api
374
+ // 家族标准形态)→ thinkingDelta chunk(复用 1.21 anthropic 捕获字段,chatStep 零改动)。
375
+ // 回传面不做(D-A1):已知 openai 形上游零需求且 DeepSeek 官方拒绝收回传(输入含
376
+ // reasoning_content → 400);enforce 契约网关真出现时按 1.21 模式另立项。
377
+ const reasoningContent = typeof delta.reasoning_content === 'string' && delta.reasoning_content.length ? delta.reasoning_content : undefined;
373
378
  // LlmChunk 单 toolCallDelta 口径:同事件多条 tool_calls 顺序展开为多 chunk
374
- if (!text && !toolCallDeltas.length && finish === undefined) {
379
+ if (!text && reasoningContent === undefined && !toolCallDeltas.length && finish === undefined) {
375
380
  // 空事件但携 usage(部分 compat 网关形态):usage 不随空事件丢失
376
381
  if (usage)
377
382
  yield { usage };
@@ -380,6 +385,7 @@ async function* parseSseStream(body, model, adapterId) {
380
385
  const chunks = [];
381
386
  if (toolCallDeltas.length === 0) {
382
387
  chunks.push({
388
+ ...(reasoningContent !== undefined ? { thinkingDelta: reasoningContent } : {}),
383
389
  ...(text !== undefined ? { delta: text } : {}),
384
390
  ...(finish !== undefined
385
391
  ? { finishReason: finish, ...(finish === 'error' ? { error: { code: 'B080001', message: `未映射的 finish_reason "${raw}"` } } : {}) }
@@ -390,6 +396,7 @@ async function* parseSseStream(body, model, adapterId) {
390
396
  for (let i = 0; i < toolCallDeltas.length; i++) {
391
397
  const last = i === toolCallDeltas.length - 1;
392
398
  chunks.push({
399
+ ...(i === 0 && reasoningContent !== undefined ? { thinkingDelta: reasoningContent } : {}),
393
400
  ...(i === 0 && text !== undefined ? { delta: text } : {}),
394
401
  toolCallDelta: toolCallDeltas[i],
395
402
  ...(last && finish !== undefined
@@ -618,13 +625,14 @@ function toAnthropic(req, apiKey, cache) {
618
625
  if (!pendingTu)
619
626
  return;
620
627
  const prev = turns[turns.length - 1];
621
- const { thinkingBlock, toolCalls } = pendingTu;
628
+ const { thinkingBlocks, toolCalls } = pendingTu;
629
+ const th = thinkingBlocks ?? [];
622
630
  if (prev && prev.role === 'assistant' && typeof prev.content === 'string') {
623
631
  turns.pop();
624
- turns.push({ role: 'assistant', content: [...(thinkingBlock ? [thinkingBlock] : []), { type: 'text', text: prev.content }, ...toolCalls] });
632
+ turns.push({ role: 'assistant', content: [...th, { type: 'text', text: prev.content }, ...toolCalls] });
625
633
  }
626
634
  else {
627
- turns.push({ role: 'assistant', content: [...(thinkingBlock ? [thinkingBlock] : []), ...toolCalls] });
635
+ turns.push({ role: 'assistant', content: [...th, ...toolCalls] });
628
636
  }
629
637
  pendingTu = null;
630
638
  };
@@ -648,15 +656,26 @@ function toAnthropic(req, apiKey, cache) {
648
656
  const tc = m.content.toolCall;
649
657
  // 1.21(R-TOKENHUB-1 / D-T4):工具环回传——投影层附加的 thinking(同 turn 原始 assistant
650
658
  // 消息的 thinking 块)前置重放。1.21.1:thinking 块随合并缓冲进同轮单条消息(见 flushTu 注记);
651
- // 无 thinking = 1.20 形态。
659
+ // 无 thinking = 1.20 形态。1.22(D-B2/D-B3):投影双形态消费——单块三元组(1.21 形态,字节
660
+ // 兼容线)/ 多块 blocks 数组(每块独立签名按序展开前置;redacted 混合块序保真)。
652
661
  const th = m.content.thinking;
653
- const thinkingBlock = th?.text?.length
654
- ? th.redacted
662
+ const thinkingBlocks = [];
663
+ if (th?.blocks?.length) {
664
+ for (const b of th.blocks) {
665
+ if (!b.text?.length)
666
+ continue;
667
+ thinkingBlocks.push(b.redacted
668
+ ? { type: 'redacted_thinking', data: b.text }
669
+ : { type: 'thinking', thinking: b.text, ...(b.signature ? { signature: b.signature } : {}) });
670
+ }
671
+ }
672
+ else if (th?.text?.length) {
673
+ thinkingBlocks.push(th.redacted
655
674
  ? { type: 'redacted_thinking', data: th.text }
656
- : { type: 'thinking', thinking: th.text, ...(th.signature ? { signature: th.signature } : {}) }
657
- : undefined;
675
+ : { type: 'thinking', thinking: th.text, ...(th.signature ? { signature: th.signature } : {}) });
676
+ }
658
677
  if (!pendingTu)
659
- pendingTu = { toolCalls: [], ...(thinkingBlock ? { thinkingBlock } : {}) };
678
+ pendingTu = { toolCalls: [], ...(thinkingBlocks.length ? { thinkingBlocks } : {}) };
660
679
  pendingTu.toolCalls.push({ type: 'tool_use', id: tc.id, name: tc.tool, input: tc.args ?? {} });
661
680
  continue;
662
681
  }
@@ -741,8 +760,14 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
741
760
  if (block?.type === 'tool_use') {
742
761
  yield { toolCallDelta: { index: evt.index, id: block.id, name: block.name } };
743
762
  }
763
+ else if (block?.type === 'thinking') {
764
+ // 1.22(D-B1 多块):thinking 块开始信号——消费方(chatStep)以累积非空判封板,
765
+ // 单块流亦发此信号但落链形态不变(单块 meta 无 blockIndex)
766
+ yield { thinkingBlockStart: evt.index };
767
+ }
744
768
  else if (block?.type === 'redacted_thinking' && typeof block.data === 'string' && block.data.length) {
745
- // 1.21(R-TOKENHUB-1 / D-T1):redacted_thinking 全文单发(回传形态 {type:'redacted_thinking',data})
769
+ // 1.21(R-TOKENHUB-1 / D-T1):redacted_thinking 全文单发(回传形态 {type:'redacted_thinking',data});
770
+ // 1.22:消费方将 redacted 封为独立块(原「后块覆盖前块」缺陷随多块硬化消除)
746
771
  yield { thinkingRedacted: block.data };
747
772
  }
748
773
  }
@@ -92,6 +92,9 @@ export interface LlmChunk {
92
92
  thinkingSignatureDelta?: string;
93
93
  /** redacted_thinking 块 data 全文(content_block_start 单发;回传形态 {type:'redacted_thinking',data}) */
94
94
  thinkingRedacted?: string;
95
+ /** 1.22(D-B1 多块):thinking 块开始信号(content_block_start type='thinking',值为块 evt.index)——
96
+ * 消费方(chatStep)以「累积非空时封板」判块边界;单块流亦发此信号,落链形态不受影响(单块 meta 无 blockIndex) */
97
+ thinkingBlockStart?: number;
95
98
  }
96
99
  /** Provider 适配器(ACL,C-02):统一签名,消解 Provider 差异 */
97
100
  export interface LlmAdapter {
package/dist/sbom.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bomFormat": "CAR-SBOM",
3
3
  "specVersion": "0.1",
4
- "version": "1.21.1",
4
+ "version": "1.22.0",
5
5
  "components": [
6
6
  {
7
7
  "type": "file",
@@ -161,7 +161,7 @@
161
161
  {
162
162
  "type": "file",
163
163
  "path": "src/runtime-core/chatStep.ts",
164
- "sha256": "b7cf35ba06ebaae339a89da0e4df08b5ae310a60a97b5201de54f092fc8758ab"
164
+ "sha256": "8847c49264abcb451bb9587c1c15abb06c940038be8f95ddcf8a8ba776b58d03"
165
165
  },
166
166
  {
167
167
  "type": "file",
@@ -176,7 +176,7 @@
176
176
  {
177
177
  "type": "file",
178
178
  "path": "src/runtime-core/llm.ts",
179
- "sha256": "9e481456aa224c468ccdf6b577ce1e1d0c2b91aeeaa2c0f195ee40c779b7da02"
179
+ "sha256": "8e8e255b0de192c6118d5972a62e30a4812ce72f8d3acd5641f8241b63c842f6"
180
180
  },
181
181
  {
182
182
  "type": "file",
@@ -201,7 +201,7 @@
201
201
  {
202
202
  "type": "file",
203
203
  "path": "src/runtime-core/types.ts",
204
- "sha256": "d9ff638ea4de9b89d56238f2d7fd65c07dd29323fcb9906e72b6877618a360a6"
204
+ "sha256": "fbe2d03167420169ebe601bd425e641f3df5ad518d109995ff5302b839a82480"
205
205
  },
206
206
  {
207
207
  "type": "file",
@@ -251,7 +251,7 @@
251
251
  {
252
252
  "type": "file",
253
253
  "path": "src/session/log.ts",
254
- "sha256": "9b3a081bfa60e046166cce11203848402f52d4e9b9d0f94ad2e9dc1f0ebadd9c"
254
+ "sha256": "e87fdaaeecb3ad71924664bd565369b8bfb7347500cd2d97c51c22c5c68ab8be"
255
255
  },
256
256
  {
257
257
  "type": "file",
@@ -271,7 +271,7 @@
271
271
  {
272
272
  "type": "file",
273
273
  "path": "package.json",
274
- "sha256": "707102222be862dbc4f2d6d5aa3168472401d186934edf95ef21d6cc950ca998"
274
+ "sha256": "95a85844d77cb6aab1dc9de4cce906fdd447702381f68fa4afda9a521b86c77c"
275
275
  },
276
276
  {
277
277
  "type": "file",
@@ -122,24 +122,27 @@ export class SessionLog {
122
122
  const out = [];
123
123
  if (compactEvent)
124
124
  out.push({ role: 'system', content: (compactEvent.payload.summary) });
125
- // 1.21(R-TOKENHUB-1 / D-T3):thinking 投影——同 turn 内 thinking 事件附加到首个后续
126
- // toolCall 消息(原始 provider assistant 消息 = [thinking, ...tool_use] 的重放形态);
127
- // standalone(无后续 toolCall)/ 被 user·system 隔断 / 跨 turn 不投影(实证:普通两轮
128
- // 历史不要求回传);assistant(text) 同 turn 不隔断(链序 thinking→assistant→toolCall);
129
- // 无 thinking 事件投影逐字节不变(D-T5 兼容线,N1 快照自洽)。
130
- let pendingThinking = null;
125
+ // 1.21(R-TOKENHUB-1 / D-T3)→ 1.22(D-B2 多块):thinking 投影——同 turn 内 thinking 事件
126
+ // (一块一事件)按序聚合为块列表,附加到首个后续 toolCall 消息:单块 = 1.21 三元组形态
127
+ // (text/signature?/redacted?,投影逐字节不变——D-T5 兼容线);多块 = blocks 数组(顶层
128
+ // text/signature 键缺席,消费方 toAnthropic 双形态展开)。standalone(无后续 toolCall)/
129
+ // 被 user·system 隔断 / 跨 turn 不投影(实证:普通两轮历史不要求回传);assistant(text)
130
+ // 同 turn 不隔断(链序 thinking→assistant→toolCall);无 thinking 事件投影逐字节不变
131
+ // (N1 快照自洽)。
132
+ let pendingThinkingTurn = null;
133
+ let pendingBlocks = [];
131
134
  for (const e of projected) {
132
- if (pendingThinking && (e.turnId !== pendingThinking.turnId || e.kind === 'user' || e.kind === 'system'))
133
- pendingThinking = null;
135
+ if (pendingBlocks.length && (e.turnId !== pendingThinkingTurn || e.kind === 'user' || e.kind === 'system'))
136
+ pendingBlocks = [];
134
137
  if (e.kind === 'thinking') {
135
138
  const p = typeof e.payload === 'string' ? e.payload : '';
136
139
  if (p.length || e.meta?.redacted === true) {
137
- pendingThinking = {
138
- turnId: e.turnId,
140
+ pendingThinkingTurn = e.turnId;
141
+ pendingBlocks.push({
139
142
  text: p,
140
143
  ...(typeof e.meta?.signature === 'string' && e.meta.signature ? { signature: e.meta.signature } : {}),
141
144
  ...(e.meta?.redacted === true ? { redacted: true } : {}),
142
- };
145
+ });
143
146
  }
144
147
  continue; // thinking 不单独成消息(无独立回传语义)
145
148
  }
@@ -151,13 +154,18 @@ export class SessionLog {
151
154
  out.push({ role: 'assistant', content: e.payload });
152
155
  else if (e.kind === 'toolCall') {
153
156
  const msg = { role: 'assistant', toolCall: e.payload };
154
- if (pendingThinking) {
157
+ if (pendingBlocks.length === 1) {
158
+ const b = pendingBlocks[0];
155
159
  msg.thinking = {
156
- text: pendingThinking.text,
157
- ...(pendingThinking.signature ? { signature: pendingThinking.signature } : {}),
158
- ...(pendingThinking.redacted ? { redacted: true } : {}),
160
+ text: b.text,
161
+ ...(b.signature ? { signature: b.signature } : {}),
162
+ ...(b.redacted ? { redacted: true } : {}),
159
163
  };
160
- pendingThinking = null; // 首 toolCall 消费即清——同 turn 多 toolCall = 同一原始消息,单块重放
164
+ pendingBlocks = []; // 首 toolCall 消费即清——同 turn 多 toolCall = 同一原始消息,单块重放
165
+ }
166
+ else if (pendingBlocks.length > 1) {
167
+ msg.thinking = { blocks: pendingBlocks }; // 1.22(D-B2):多块形态(provider 原始多块回复的按序重放)
168
+ pendingBlocks = [];
161
169
  }
162
170
  out.push(msg);
163
171
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lqc123qwe/car-runtime",
3
- "version": "1.21.1",
3
+ "version": "1.22.0",
4
4
  "description": "Composable Agent Runtime (CAR) - microkernel plugin system with sandboxed PTC, append-only hashed session log, and MCP host bridge",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -53,8 +53,13 @@ export interface ChatStepOptions {
53
53
  * 同纪律);同请求同名只回调一次;
54
54
  * - 纯渲染提示:不落链、不持久、不影响 ModelStep 与 N1(事实本体仍以落盘行为准,
55
55
  * assistant 事件到达前 UI 呈现的增量属临时态);缺省(undefined)= 零行为变化。
56
+ * 1.22(D-C1/D-C2):+thinkingDelta = 思考流式上屏数据源(桌面批消费)——掩蔽流
57
+ * (独立 StreamRedactor 实例,与 assistant 文本同脱敏纪律;链侧 thinking 禁改写不变,
58
+ * 审计面原文唯一事实源 = 链);渲染提示级(同 toolName,无对拍红线——1.19 红线面
59
+ * 不被 thinking 破坏,D-T6 延续);内核 spec 内断言:thinkingDelta 之和 + flush
60
+ * ≡ 掩蔽(thinking 事件 payload 之和)。
56
61
  */
57
- onDelta?: (d: { textDelta?: string; toolName?: string }) => void
62
+ onDelta?: (d: { textDelta?: string; toolName?: string; thinkingDelta?: string }) => void
58
63
  }
59
64
 
60
65
  /** ModelStep 加法扩展:脱敏计数留痕(S15 secretsRedacted 同名口径) */
@@ -96,11 +101,22 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
96
101
  const redactor = new StreamRedactor()
97
102
  let text = ''
98
103
  const calls = new Map<number, { id?: string; name?: string; argsBuf: string }>()
99
- // 1.21(R-TOKENHUB-1 / D-T1/D-T2):thinking 捕获累积——禁改写(回传签名完整性 > 脱敏
100
- // 改写),provider 事实字节保真;redacted_thinking = data 全文单发覆盖
101
- let thinkingText = ''
102
- let thinkingSignature = ''
103
- let thinkingRedacted: string | undefined
104
+ // 1.21(R-TOKENHUB-1 / D-T1/D-T2)→ 1.22(D-B1 多块硬化):thinking 捕获累积——禁改写
105
+ // (回传签名完整性 > 脱敏改写),provider 事实字节保真。单累加器升级为块数组:
106
+ // thinkingBlockStart 且累积非空 = 封板前块(签名随块独立,不再跨块拼接);redacted =
107
+ // 封板 + 独立块(原「后块覆盖前块」缺陷消除)。单块流形态与 1.21.1 逐字节等价。
108
+ type ThinkingBlock = { text: string; signature?: string; redacted?: boolean }
109
+ const thinkingBlocks: ThinkingBlock[] = []
110
+ let thCurText = ''
111
+ let thCurSig = ''
112
+ const sealThinkingBlock = () => {
113
+ if (thCurText.length) thinkingBlocks.push({ text: thCurText, ...(thCurSig ? { signature: thCurSig } : {}) })
114
+ thCurText = ''
115
+ thCurSig = ''
116
+ }
117
+ // 1.22(D-C1):思考掩蔽流——独立 StreamRedactor 实例(与 assistant 文本同脱敏纪律;
118
+ // 链侧禁改写不变,UI 显示面与审计原文面分离)
119
+ const thinkingRedactor = new StreamRedactor()
104
120
  let stopReason: ModelStep['stopReason'] | undefined
105
121
  let errorDetail: string | undefined
106
122
  let usage: LlmUsage | undefined
@@ -136,10 +152,25 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
136
152
  if (chunk.toolCallDelta.argumentsDelta) cur.argsBuf += chunk.toolCallDelta.argumentsDelta
137
153
  calls.set(idx, cur)
138
154
  }
139
- // 1.21(R-TOKENHUB-1 / D-T1):thinking 三面捕获(text/signature 增量累积、redacted 单发覆盖)
140
- if (chunk.thinkingDelta !== undefined) thinkingText += chunk.thinkingDelta
141
- if (chunk.thinkingSignatureDelta !== undefined) thinkingSignature += chunk.thinkingSignatureDelta
142
- if (chunk.thinkingRedacted !== undefined) thinkingRedacted = chunk.thinkingRedacted
155
+ // 1.21(D-T1)→ 1.22(D-B1 多块):thinking 三面捕获——块数组累积(签名随块独立)+
156
+ // 掩蔽流回调(D-C1:独立 redactor,masked 非空才回调)
157
+ if (chunk.thinkingBlockStart !== undefined) sealThinkingBlock()
158
+ if (chunk.thinkingDelta !== undefined) {
159
+ thCurText += chunk.thinkingDelta
160
+ if (opts.onDelta) {
161
+ const masked = thinkingRedactor.push(chunk.thinkingDelta)
162
+ if (masked) opts.onDelta({ thinkingDelta: masked })
163
+ }
164
+ }
165
+ if (chunk.thinkingSignatureDelta !== undefined) thCurSig += chunk.thinkingSignatureDelta
166
+ if (chunk.thinkingRedacted !== undefined) {
167
+ sealThinkingBlock() // redacted = 独立块(不与前文本块拼接)
168
+ thinkingBlocks.push({ text: chunk.thinkingRedacted, redacted: true })
169
+ if (opts.onDelta) {
170
+ const masked = thinkingRedactor.push(chunk.thinkingRedacted)
171
+ if (masked) opts.onDelta({ thinkingDelta: masked })
172
+ }
173
+ }
143
174
  if (chunk.error) errorDetail = chunk.error.message
144
175
  if (chunk.usage) usage = chunk.usage // D-33:附着于最后一个 chunk——后到覆盖
145
176
  if (chunk.finishReason !== undefined) stopReason = chunk.finishReason === 'aborted' ? 'error' : chunk.finishReason
@@ -155,6 +186,12 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
155
186
  const tail = redactor.flush()
156
187
  if (tail && opts.onDelta) opts.onDelta({ textDelta: tail })
157
188
  text += tail
189
+ // 1.22(D-B1/D-C1):流尾封板末块 + 思考掩蔽流 flush 残段(渲染提示面,先于 finish 兜底——flush 不抛错)
190
+ sealThinkingBlock()
191
+ {
192
+ const thTail = thinkingRedactor.flush()
193
+ if (thTail && opts.onDelta) opts.onDelta({ thinkingDelta: thTail })
194
+ }
158
195
 
159
196
  // 1.11-S2(D-33 / 计量设计 §3.3):请求级 usage 事实落链(拿到 usage 的最后一个 chunk 即请求
160
197
  // 收尾;撞限/异常 turn 照常落——截断前已完成请求的 usage 事实不吞)。缺失 = provider 未回,
@@ -173,19 +210,31 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
173
210
  })
174
211
  }
175
212
 
176
- // 5a. 1.21(R-TOKENHUB-1 / D-T2):thinking 事件落链(actor='model',先于 assistant/toolCall
177
- // ——投影层按同 turn 首 toolCall 附加回传)。payload = thinking 原文禁改写(签名完整性 >
178
- // 脱敏改写——改写即回传校验失败);secretsRedacted 仅计数留痕(审计信号,不复写);
179
- // redacted 形态 payload = data 全文(meta.redacted=true)。无 thinking chunk = 零新事件
180
- // (D-T5 字节兼容,链形态与 1.20 全等)。
181
- if (thinkingText.length || thinkingRedacted !== undefined) {
182
- const thinkingPayload = thinkingRedacted ?? thinkingText
183
- const secretsInThinking = scanSecrets(thinkingPayload).hits.length
184
- log.append('model', 'thinking', turnId, thinkingPayload, {
185
- ...(thinkingRedacted !== undefined ? { redacted: true } : {}),
186
- ...(thinkingSignature ? { signature: thinkingSignature } : {}),
213
+ // 5a. 1.21(R-TOKENHUB-1 / D-T2)→ 1.22(D-B1):thinking 事件落链——一块一事件(append-only
214
+ // 最窄面,审计/投影按序聚合自然成立)。actor='model',先于 assistant/toolCall(投影层按同
215
+ // turn 首 toolCall 附加回传)。payload = 块原文禁改写(签名完整性 > 脱敏改写——改写即回传
216
+ // 校验失败);secretsRedacted 仅计数留痕(审计信号,不复写);redacted 形态 payload = data
217
+ // 全文(meta.redacted=true)。**单块链(含 redacted 单块)meta 无 blockIndex = 与 1.21.1
218
+ // 逐字节同形(D-B1 兼容线);多块链每块带 blockIndex(0 起块序)**。无 thinking 块 = 零新
219
+ // 事件(D-T5 字节兼容,链形态与 1.20 全等)。
220
+ if (thinkingBlocks.length === 1) {
221
+ const b = thinkingBlocks[0]!
222
+ const secretsInThinking = scanSecrets(b.text).hits.length
223
+ log.append('model', 'thinking', turnId, b.text, {
224
+ ...(b.redacted ? { redacted: true } : {}),
225
+ ...(b.signature ? { signature: b.signature } : {}),
187
226
  ...(secretsInThinking ? { secretsRedacted: secretsInThinking } : {}),
188
227
  })
228
+ } else if (thinkingBlocks.length > 1) {
229
+ thinkingBlocks.forEach((b, i) => {
230
+ const secretsInThinking = scanSecrets(b.text).hits.length
231
+ log.append('model', 'thinking', turnId, b.text, {
232
+ ...(b.redacted ? { redacted: true } : {}),
233
+ ...(b.signature ? { signature: b.signature } : {}),
234
+ ...(secretsInThinking ? { secretsRedacted: secretsInThinking } : {}),
235
+ blockIndex: i,
236
+ })
237
+ })
189
238
  }
190
239
 
191
240
  // 5. assistant 文本落 M7(脱敏后 + 计数留痕;空文本不产事件)
@@ -387,8 +387,14 @@ async function* parseSseStream(body: AsyncIterable<Uint8Array>, model: string, a
387
387
  }))
388
388
  : []
389
389
  const text = typeof delta.content === 'string' && delta.content.length ? delta.content : undefined
390
+ // 1.22(D-A2):openai 形 thinking 捕获——delta.reasoning_content(DeepSeek 官方 + new-api
391
+ // 家族标准形态)→ thinkingDelta chunk(复用 1.21 anthropic 捕获字段,chatStep 零改动)。
392
+ // 回传面不做(D-A1):已知 openai 形上游零需求且 DeepSeek 官方拒绝收回传(输入含
393
+ // reasoning_content → 400);enforce 契约网关真出现时按 1.21 模式另立项。
394
+ const reasoningContent =
395
+ typeof delta.reasoning_content === 'string' && delta.reasoning_content.length ? delta.reasoning_content : undefined
390
396
  // LlmChunk 单 toolCallDelta 口径:同事件多条 tool_calls 顺序展开为多 chunk
391
- if (!text && !toolCallDeltas.length && finish === undefined) {
397
+ if (!text && reasoningContent === undefined && !toolCallDeltas.length && finish === undefined) {
392
398
  // 空事件但携 usage(部分 compat 网关形态):usage 不随空事件丢失
393
399
  if (usage) yield { usage }
394
400
  continue
@@ -396,6 +402,7 @@ async function* parseSseStream(body: AsyncIterable<Uint8Array>, model: string, a
396
402
  const chunks: LlmChunk[] = []
397
403
  if (toolCallDeltas.length === 0) {
398
404
  chunks.push({
405
+ ...(reasoningContent !== undefined ? { thinkingDelta: reasoningContent } : {}),
399
406
  ...(text !== undefined ? { delta: text } : {}),
400
407
  ...(finish !== undefined
401
408
  ? { finishReason: finish, ...(finish === 'error' ? { error: { code: 'B080001', message: `未映射的 finish_reason "${raw}"` } } : {}) }
@@ -405,6 +412,7 @@ async function* parseSseStream(body: AsyncIterable<Uint8Array>, model: string, a
405
412
  for (let i = 0; i < toolCallDeltas.length; i++) {
406
413
  const last = i === toolCallDeltas.length - 1
407
414
  chunks.push({
415
+ ...(i === 0 && reasoningContent !== undefined ? { thinkingDelta: reasoningContent } : {}),
408
416
  ...(i === 0 && text !== undefined ? { delta: text } : {}),
409
417
  toolCallDelta: toolCallDeltas[i],
410
418
  ...(last && finish !== undefined
@@ -662,16 +670,17 @@ function toAnthropic(req: LlmRequest, apiKey: string, cache: { rolling: boolean;
662
670
  // - 相邻 assistant(toolCall) 聚合(同 turn 多 tool_use 单条);thinking 取首条附加;
663
671
  // - 紧邻前驱为 assistant 纯文本消息 → 弹回合并为 [thinking?, text, tool_use…];
664
672
  // - 无相邻文本的 toolCall 消息形态不变([thinking?, tool_use])——非工具环链逐字节兼容(D-T5)。
665
- let pendingTu: { toolCalls: Array<{ type: 'tool_use'; id: string; name: string; input: unknown }>; thinkingBlock?: Record<string, unknown> } | null = null
673
+ let pendingTu: { toolCalls: Array<{ type: 'tool_use'; id: string; name: string; input: unknown }>; thinkingBlocks?: Array<Record<string, unknown>> } | null = null
666
674
  const flushTu = () => {
667
675
  if (!pendingTu) return
668
676
  const prev = turns[turns.length - 1]
669
- const { thinkingBlock, toolCalls } = pendingTu
677
+ const { thinkingBlocks, toolCalls } = pendingTu
678
+ const th = thinkingBlocks ?? []
670
679
  if (prev && prev.role === 'assistant' && typeof prev.content === 'string') {
671
680
  turns.pop()
672
- turns.push({ role: 'assistant', content: [...(thinkingBlock ? [thinkingBlock] : []), { type: 'text', text: prev.content }, ...toolCalls] })
681
+ turns.push({ role: 'assistant', content: [...th, { type: 'text', text: prev.content }, ...toolCalls] })
673
682
  } else {
674
- turns.push({ role: 'assistant', content: [...(thinkingBlock ? [thinkingBlock] : []), ...toolCalls] })
683
+ turns.push({ role: 'assistant', content: [...th, ...toolCalls] })
675
684
  }
676
685
  pendingTu = null
677
686
  }
@@ -695,14 +704,29 @@ function toAnthropic(req: LlmRequest, apiKey: string, cache: { rolling: boolean;
695
704
  const tc = (m.content as { toolCall: { id: string; tool: string; args: unknown } }).toolCall
696
705
  // 1.21(R-TOKENHUB-1 / D-T4):工具环回传——投影层附加的 thinking(同 turn 原始 assistant
697
706
  // 消息的 thinking 块)前置重放。1.21.1:thinking 块随合并缓冲进同轮单条消息(见 flushTu 注记);
698
- // 无 thinking = 1.20 形态。
699
- const th = (m.content as { thinking?: { text?: string; signature?: string; redacted?: boolean } }).thinking
700
- const thinkingBlock = th?.text?.length
701
- ? th.redacted
702
- ? { type: 'redacted_thinking', data: th.text }
703
- : { type: 'thinking', thinking: th.text, ...(th.signature ? { signature: th.signature } : {}) }
704
- : undefined
705
- if (!pendingTu) pendingTu = { toolCalls: [], ...(thinkingBlock ? { thinkingBlock } : {}) }
707
+ // 无 thinking = 1.20 形态。1.22(D-B2/D-B3):投影双形态消费——单块三元组(1.21 形态,字节
708
+ // 兼容线)/ 多块 blocks 数组(每块独立签名按序展开前置;redacted 混合块序保真)。
709
+ const th = (m.content as {
710
+ thinking?: { text?: string; signature?: string; redacted?: boolean; blocks?: Array<{ text: string; signature?: string; redacted?: boolean }> }
711
+ }).thinking
712
+ const thinkingBlocks: Array<Record<string, unknown>> = []
713
+ if (th?.blocks?.length) {
714
+ for (const b of th.blocks) {
715
+ if (!b.text?.length) continue
716
+ thinkingBlocks.push(
717
+ b.redacted
718
+ ? { type: 'redacted_thinking', data: b.text }
719
+ : { type: 'thinking', thinking: b.text, ...(b.signature ? { signature: b.signature } : {}) },
720
+ )
721
+ }
722
+ } else if (th?.text?.length) {
723
+ thinkingBlocks.push(
724
+ th.redacted
725
+ ? { type: 'redacted_thinking', data: th.text }
726
+ : { type: 'thinking', thinking: th.text, ...(th.signature ? { signature: th.signature } : {}) },
727
+ )
728
+ }
729
+ if (!pendingTu) pendingTu = { toolCalls: [], ...(thinkingBlocks.length ? { thinkingBlocks } : {}) }
706
730
  pendingTu.toolCalls.push({ type: 'tool_use', id: tc.id, name: tc.tool, input: tc.args ?? {} })
707
731
  continue
708
732
  }
@@ -779,8 +803,13 @@ async function* parseAnthropicSseStream(body: AsyncIterable<Uint8Array>, model:
779
803
  const block = evt.content_block
780
804
  if (block?.type === 'tool_use') {
781
805
  yield { toolCallDelta: { index: evt.index, id: block.id, name: block.name } }
806
+ } else if (block?.type === 'thinking') {
807
+ // 1.22(D-B1 多块):thinking 块开始信号——消费方(chatStep)以累积非空判封板,
808
+ // 单块流亦发此信号但落链形态不变(单块 meta 无 blockIndex)
809
+ yield { thinkingBlockStart: evt.index }
782
810
  } else if (block?.type === 'redacted_thinking' && typeof block.data === 'string' && block.data.length) {
783
- // 1.21(R-TOKENHUB-1 / D-T1):redacted_thinking 全文单发(回传形态 {type:'redacted_thinking',data})
811
+ // 1.21(R-TOKENHUB-1 / D-T1):redacted_thinking 全文单发(回传形态 {type:'redacted_thinking',data});
812
+ // 1.22:消费方将 redacted 封为独立块(原「后块覆盖前块」缺陷随多块硬化消除)
784
813
  yield { thinkingRedacted: block.data }
785
814
  }
786
815
  } else if (type === 'content_block_delta') {
@@ -84,6 +84,9 @@ export interface LlmChunk {
84
84
  thinkingSignatureDelta?: string
85
85
  /** redacted_thinking 块 data 全文(content_block_start 单发;回传形态 {type:'redacted_thinking',data}) */
86
86
  thinkingRedacted?: string
87
+ /** 1.22(D-B1 多块):thinking 块开始信号(content_block_start type='thinking',值为块 evt.index)——
88
+ * 消费方(chatStep)以「累积非空时封板」判块边界;单块流亦发此信号,落链形态不受影响(单块 meta 无 blockIndex) */
89
+ thinkingBlockStart?: number
87
90
  }
88
91
 
89
92
  /** Provider 适配器(ACL,C-02):统一签名,消解 Provider 差异 */
@@ -11,7 +11,7 @@ import { createHash } from 'node:crypto'
11
11
  import { readFileSync } from 'node:fs'
12
12
  import { decodeLogBuffer, splitJsonlLines } from './format.ts'
13
13
 
14
- export type EventKind = 'user' | 'assistant' | 'toolCall' | 'toolResult' | 'turnEnd' | 'goalUpdate' | 'hostRaw' | 'fork' | 'system' | 'usage' | 'compact' | 'routing' | 'memory' | 'thinking' // M3-S11 加法扩展:多宿主归一化降级通道(pattern 未命中留痕,零静默);M5-DEC4 加法扩展:跨宿主 fork 标记事件;1.4-S2 加法扩展:system prompt 落链(§3.5.5:新模型可见输入 = 新事件类型);1.11-S2 加法扩展:usage 计量事实(D-33,actor='runtime'、model_visible=0 不进模型消息流,deriveMessages 不投影);六值 TurnEndReason 不变;1.14-S2 加法扩展(K-MP-2 / D-40):历史压缩事件(actor='runtime',append-only 只追加——payload={summary, compactedUpTo, ...},投影按 compactedUpTo 前缀折叠为摘要,原事件链不改写);1.14-S4 加法扩展(K-POOL-1 / D-42):模型池路由决策(actor='runtime',payload.type='decide',selected/reason/retries/attempts——禁静默换模型;model_visible=0 不进模型消息流,deriveMessages 不投影——同 usage 纪律);1.15-S1 加法扩展(K-MEM-1 / D-46):白盒记忆操作事件(actor='user' 宿主代理用户操作,turnId='memory' 会话级标识同 'compaction' 先例,payload.type='write'|'edit'|'delete'|'rollback'——审计链同源差异化面,链即版本库〔rollback 恢复数据源=链上 payload.before/after 全文〕;model_visible=0 不进模型消息流——记忆注入走宿主装配层 system ctx 行,deriveMessages 不投影);1.21 加法扩展(R-TOKENHUB-1 / D-T2):thinking 事件(actor='model',payload=thinking 原文禁改写,meta={signature?, redacted?, secretsRedacted?}——先于 assistant/toolCall 落链;投影 = 同 turn 首个 toolCall 消息附加回传(工具环 reasoning_content 契约,Anthropic thinking+tool_use 规范同款);standalone/隔断/跨 turn 不投影;无 thinking 事件链投影逐字节不变);
14
+ export type EventKind = 'user' | 'assistant' | 'toolCall' | 'toolResult' | 'turnEnd' | 'goalUpdate' | 'hostRaw' | 'fork' | 'system' | 'usage' | 'compact' | 'routing' | 'memory' | 'thinking' // M3-S11 加法扩展:多宿主归一化降级通道(pattern 未命中留痕,零静默);M5-DEC4 加法扩展:跨宿主 fork 标记事件;1.4-S2 加法扩展:system prompt 落链(§3.5.5:新模型可见输入 = 新事件类型);1.11-S2 加法扩展:usage 计量事实(D-33,actor='runtime'、model_visible=0 不进模型消息流,deriveMessages 不投影);六值 TurnEndReason 不变;1.14-S2 加法扩展(K-MP-2 / D-40):历史压缩事件(actor='runtime',append-only 只追加——payload={summary, compactedUpTo, ...},投影按 compactedUpTo 前缀折叠为摘要,原事件链不改写);1.14-S4 加法扩展(K-POOL-1 / D-42):模型池路由决策(actor='runtime',payload.type='decide',selected/reason/retries/attempts——禁静默换模型;model_visible=0 不进模型消息流,deriveMessages 不投影——同 usage 纪律);1.15-S1 加法扩展(K-MEM-1 / D-46):白盒记忆操作事件(actor='user' 宿主代理用户操作,turnId='memory' 会话级标识同 'compaction' 先例,payload.type='write'|'edit'|'delete'|'rollback'——审计链同源差异化面,链即版本库〔rollback 恢复数据源=链上 payload.before/after 全文〕;model_visible=0 不进模型消息流——记忆注入走宿主装配层 system ctx 行,deriveMessages 不投影);1.21 加法扩展(R-TOKENHUB-1 / D-T2):thinking 事件(actor='model',payload=thinking 原文禁改写,meta={signature?, redacted?, secretsRedacted?}——先于 assistant/toolCall 落链;投影 = 同 turn 首个 toolCall 消息附加回传(工具环 reasoning_content 契约,Anthropic thinking+tool_use 规范同款);standalone/隔断/跨 turn 不投影;无 thinking 事件链投影逐字节不变;1.22 加法扩展(1.22 迭代规划 D-B1):多块语义——一块一事件(多块链每事件 meta.blockIndex=块序,单块链 meta 与 1.21 逐字节同形),投影单块 = 1.21 三元组、多块 = blocks 数组(D-B2,消费方 toAnthropic 双形态展开));
15
15
  export type Actor = 'user' | 'model' | 'plugin' | 'runtime'
16
16
  export interface SessionEvent {
17
17
  seq: number
@@ -146,23 +146,26 @@ export class SessionLog {
146
146
  }
147
147
  const out: Array<{ role: string; [k: string]: unknown }> = []
148
148
  if (compactEvent) out.push({ role: 'system', content: ((compactEvent.payload as { summary: string }).summary) })
149
- // 1.21(R-TOKENHUB-1 / D-T3):thinking 投影——同 turn 内 thinking 事件附加到首个后续
150
- // toolCall 消息(原始 provider assistant 消息 = [thinking, ...tool_use] 的重放形态);
151
- // standalone(无后续 toolCall)/ 被 user·system 隔断 / 跨 turn 不投影(实证:普通两轮
152
- // 历史不要求回传);assistant(text) 同 turn 不隔断(链序 thinking→assistant→toolCall);
153
- // 无 thinking 事件投影逐字节不变(D-T5 兼容线,N1 快照自洽)。
154
- let pendingThinking: { turnId: string; text: string; signature?: string; redacted?: boolean } | null = null
149
+ // 1.21(R-TOKENHUB-1 / D-T3)→ 1.22(D-B2 多块):thinking 投影——同 turn 内 thinking 事件
150
+ // (一块一事件)按序聚合为块列表,附加到首个后续 toolCall 消息:单块 = 1.21 三元组形态
151
+ // (text/signature?/redacted?,投影逐字节不变——D-T5 兼容线);多块 = blocks 数组(顶层
152
+ // text/signature 键缺席,消费方 toAnthropic 双形态展开)。standalone(无后续 toolCall)/
153
+ // 被 user·system 隔断 / 跨 turn 不投影(实证:普通两轮历史不要求回传);assistant(text)
154
+ // 同 turn 不隔断(链序 thinking→assistant→toolCall);无 thinking 事件投影逐字节不变
155
+ // (N1 快照自洽)。
156
+ let pendingThinkingTurn: string | null = null
157
+ let pendingBlocks: Array<{ text: string; signature?: string; redacted?: boolean }> = []
155
158
  for (const e of projected) {
156
- if (pendingThinking && (e.turnId !== pendingThinking.turnId || e.kind === 'user' || e.kind === 'system')) pendingThinking = null
159
+ if (pendingBlocks.length && (e.turnId !== pendingThinkingTurn || e.kind === 'user' || e.kind === 'system')) pendingBlocks = []
157
160
  if (e.kind === 'thinking') {
158
161
  const p = typeof e.payload === 'string' ? e.payload : ''
159
162
  if (p.length || e.meta?.redacted === true) {
160
- pendingThinking = {
161
- turnId: e.turnId,
163
+ pendingThinkingTurn = e.turnId
164
+ pendingBlocks.push({
162
165
  text: p,
163
166
  ...(typeof e.meta?.signature === 'string' && e.meta.signature ? { signature: e.meta.signature } : {}),
164
167
  ...(e.meta?.redacted === true ? { redacted: true } : {}),
165
- }
168
+ })
166
169
  }
167
170
  continue // thinking 不单独成消息(无独立回传语义)
168
171
  }
@@ -171,13 +174,17 @@ export class SessionLog {
171
174
  else if (e.kind === 'assistant') out.push({ role: 'assistant', content: e.payload })
172
175
  else if (e.kind === 'toolCall') {
173
176
  const msg: { role: string; [k: string]: unknown } = { role: 'assistant', toolCall: e.payload }
174
- if (pendingThinking) {
177
+ if (pendingBlocks.length === 1) {
178
+ const b = pendingBlocks[0]!
175
179
  msg.thinking = {
176
- text: pendingThinking.text,
177
- ...(pendingThinking.signature ? { signature: pendingThinking.signature } : {}),
178
- ...(pendingThinking.redacted ? { redacted: true } : {}),
180
+ text: b.text,
181
+ ...(b.signature ? { signature: b.signature } : {}),
182
+ ...(b.redacted ? { redacted: true } : {}),
179
183
  }
180
- pendingThinking = null // 首 toolCall 消费即清——同 turn 多 toolCall = 同一原始消息,单块重放
184
+ pendingBlocks = [] // 首 toolCall 消费即清——同 turn 多 toolCall = 同一原始消息,单块重放
185
+ } else if (pendingBlocks.length > 1) {
186
+ msg.thinking = { blocks: pendingBlocks } // 1.22(D-B2):多块形态(provider 原始多块回复的按序重放)
187
+ pendingBlocks = []
181
188
  }
182
189
  out.push(msg)
183
190
  }
Binary file
@@ -1 +0,0 @@
1
- {"base64Signature":"MEUCIQDKONgEM8eAPTGlklJnk7tc5wNDrs/vZpp39jeG81yWtwIgR+tCPl3Djrl8Nk9gpNSZJ+FPXI4ZFp7773VAdr+Fpck=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc2RENDQm02Z0F3SUJBZ0lVZEJmS0N4OVhLZllnM3QrVExBMlQzQ3M3WHBJd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1URTBNRFF5TURNMldoY05Nall4TVRFME1EUXpNRE0yV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVSTG9jQzRXNFpuV0ZzMjIvYjA3cllRaW5LMHBVQWhLejFidW4KRFM4ZG02QlVhc2FqUHltK0VVcjBNZWQ5aUJlcjF5cXZuNHFiaHRJQWFkYmtpUXk1emFPQ0JZMHdnZ1dKTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl4TGpFd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLRGc1TURObFpEQTJaVEJoTW1KallqYzVPV1V6TkdJMgpPREE0TldZellqUTFObUZoWWpFM09UZ3dGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNakV1TVRBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl4TGpFd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENnNApPVEF6WldRd05tVXdZVEppWTJJM09UbGxNelJpTmpnd09EVm1NMkkwTlRaaFlXSXhOems0TUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENnNApPVEF6WldRd05tVXdZVEppWTJJM09UbGxNelJpTmpnd09EVm1NMkkwTlRaaFlXSXhOems0TUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNakV1TVRBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpJeExqRXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2c0T1RBelpXUXdObVV3WVRKaVkySTNPVGxsTXpSaU5qZ3dPRFZtTTJJME5UWmhZV0l4TnprNE1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXpOVFUyTVRNNQpNak16TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eU1TNHhNSUdMQmdvckJnRUVBZFo1QWdRQ0JIMEVld0I1QUhjQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2gyRWtyQkFBQUJBTUFTREJHCkFpRUE0eUtpR25lWUhUS1JicjZjNHEwYmM4NXU5RU1FUU1obUtSZWprUlo2dytRQ0lRRFhQMGRRTEVjNEFhV1EKMkhXb002aEZKRE15a291aGJIajE3UHZnVHd5UGJEQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqQk8vdU41VlRxQQpkb3YxcFFFQVFkR3hCNDVKZEw2VHRPRmNMbEd5d0RpWWdsT2RCYnZtZkdkdHlqaDF5V0dXVUtZQ01RQzcrSGJTCmttTHREeGNwOU5TMGZMZkFMNTg5eUtiMXNsOEtVR0FBdlFidEVjNVUvVWNWRmJheXp2c3FBSkZFZGNvPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEUCICgbmt4S7KuZIcHFx5zOZBAs/O7cmqRY9neWpULg0ib6AiEA/O0LtpinJBhb30rjXIzOagiD8H59yhnPAcUqQXIJwec=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2NmJiZWEwZDc3MWZmMGE0MTZlY2U4NmM3MDNkMjVmMzlhNGU5ZTc4YWY3Njc2MTBiYTMzNmIwMWJkYjFjOWFjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUURLT05nRU04ZUFQVEdsa2xKbms3dGM1d05EcnMvdlpwcDM5amVHODF5V3R3SWdSK3RDUGwzRGpybDhOazlncE5TWkorRlBYSTRaRnA3NzczVkFkcitGcGNrPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzJSRU5EUW0wMlowRjNTVUpCWjBsVlpFSm1TME40T1ZoTFpsbG5NM1FyVkV4Qk1sUXpRM00zV0hCSmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUlRCTlJGRjVUVVJOTWxkb1kwNU5hbGw0VFZSRk1FMUVVWHBOUkUweVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZTVEc5alF6UlhORnB1VjBaek1qSXZZakEzY2xsUmFXNUxNSEJWUVdoTGVqRmlkVzRLUkZNNFpHMDJRbFZoYzJGcVVIbHRLMFZWY2pCTlpXUTVhVUpsY2pGNWNYWnVOSEZpYUhSSlFXRmtZbXRwVVhrMWVtRlBRMEpaTUhkbloxZEtUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha2w0VEdwRmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkdjMVRVUk9iRnBFUVRKYVZFSm9UVzFLYWxscVl6VlBWMVY2VGtkSk1ncFBSRUUwVGxkWmVsbHFVVEZPYlVab1dXcEZNMDlVWjNkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOYWtWMVRWUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrbDRUR3BGZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5OQXBQVkVGNldsZFJkMDV0VlhkWlZFcHBXVEpKTTA5VWJHeE5lbEpwVG1wbmQwOUVWbTFOTWtrd1RsUmFhRmxYU1hoT2VtczBUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObk5BcFBWRUY2V2xkUmQwNXRWWGRaVkVwcFdUSkpNMDlVYkd4TmVsSnBUbXBuZDA5RVZtMU5Na2t3VGxSYWFGbFhTWGhPZW1zMFRVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOYWtWMVRWUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BKZUV4cVJYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJjMFQxUkJlbHBYVVhkT2JWVjNXVlJLYVZreVNUTlBWR3hzVFhwU2FVNXFaM2RQUkZadFRUSkpNRTVVV21oWlYwbDRUbnByTkUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYcE9WRlV5VFZSTk5RcE5hazE2VERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZVUxVE5IaE5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVWbGQwSTFRVWhqUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyZ3lSV3R5UWtGQlFVSkJUVUZUUkVKSENrRnBSVUUwZVV0cFIyNWxXVWhVUzFKaWNqWmpOSEV3WW1NNE5YVTVSVTFGVVUxb2JVdFNaV3ByVWxvMmR5dFJRMGxSUkZoUU1HUlJURVZqTkVGaFYxRUtNa2hYYjAwMmFFWktSRTE1YTI5MWFHSklhakUzVUhablZIZDVVR0pFUVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV2UVVSQ2JFRnFRazh2ZFU0MVZsUnhRUXBrYjNZeGNGRkZRVkZrUjNoQ05EVktaRXcyVkhSUFJtTk1iRWQ1ZDBScFdXZHNUMlJDWW5adFprZGtkSGxxYURGNVYwZFhWVXRaUTAxUlF6Y3JTR0pUQ210dFRIUkVlR053T1U1VE1HWk1aa0ZNTlRnNWVVdGlNWE5zT0V0VlIwRkJkbEZpZEVWak5WVXZWV05XUm1KaGVYcDJjM0ZCU2taRlpHTnZQUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1794630037,"logIndex":3869485684,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}