@lqc123qwe/car-runtime 1.22.1 → 1.23.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 +1 -1
- package/dist/car-runtime-1.23.0.tgz +0 -0
- package/dist/car-runtime-1.23.0.tgz.sig.bundle +1 -0
- package/dist/loop/stop.d.ts +8 -1
- package/dist/loop/stop.js +18 -5
- package/dist/release-audit.jsonl +4 -4
- package/dist/runtime-core/chatStep.js +25 -10
- package/dist/runtime-core/errors.d.ts +14 -2
- package/dist/runtime-core/errors.js +11 -3
- package/dist/runtime-core/llm.js +43 -12
- package/dist/runtime-core/types.d.ts +10 -1
- package/dist/sbom.json +7 -7
- package/package.json +1 -1
- package/src/loop/stop.ts +14 -5
- package/src/runtime-core/chatStep.ts +33 -10
- package/src/runtime-core/errors.ts +8 -1
- package/src/runtime-core/llm.ts +45 -12
- package/src/runtime-core/types.ts +20 -2
- package/dist/car-runtime-1.22.1.tgz +0 -0
- package/dist/car-runtime-1.22.1.tgz.sig.bundle +0 -1
package/dist/SHA256SUMS
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
6e62dc16fe612b7bb524a8233c60428b1a6f76685dd9d9da75dc7749eadd0cf0 car-runtime-1.23.0.tgz
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"base64Signature":"MEYCIQDW2ZdCjKblGFqrvmJOVOwajIkbcz3Y5rSVCO/xoU0CgwIhALOUpcq1yOaJXrvFJPepFRLaUqz1gAcNINk5Ix81rKrv","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc2VENDQm02Z0F3SUJBZ0lVVUhrY0dRTEVYcnFOWFdNSzN0TnQxaE01R3ljd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1URTFNRGN5TXpBeVdoY05Nall4TVRFMU1EY3pNekF5V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVIWU1COWxHSExQelVFd291cFB1S2s5OExENnpsZzlyUURaY3oKR2g2YzhZY0N0a0VUVnZnN0g2eU5UL3VudjA3VG0raEdSVVNoUHBkVTFxekNCT1A3NWFPQ0JZMHdnZ1dKTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl6TGpBd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLRFUwTURWa1lqbGlaR0U0TURaak0yTXpaV1ZrWVRVMwpOV014WldJMFlXVTBPREE0WWpWaE16SXdGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNak11TURBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl6TGpBd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENnMQpOREExWkdJNVltUmhPREEyWXpOak0yVmxaR0UxTnpWak1XVmlOR0ZsTkRnd09HSTFZVE15TUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENnMQpOREExWkdJNVltUmhPREEyWXpOak0yVmxaR0UxTnpWak1XVmlOR0ZsTkRnd09HSTFZVE15TUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNak11TURBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpJekxqQXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2cxTkRBMVpHSTVZbVJoT0RBMll6TmpNMlZsWkdFMU56VmpNV1ZpTkdGbE5EZ3dPR0kxWVRNeU1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXpOamc1TmpnNApNekV3TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eU15NHdNSUdMQmdvckJnRUVBZFo1QWdRQ0JIMEVld0I1QUhjQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2gzaGFQbmdBQUJBTUFTREJHCkFpRUF2Wkp2ZFdYdHZXc2VxOWR5MGRjWU43L0F3Q1BJZ0dRV3FMME1oTWZZb0xnQ0lRRDdYTk1sVnZ4VGE2ZUQKZVZ3ZEczcGhjZWh4NVhrTTZqVTdyRkY4VmpTdyt6QUtCZ2dxaGtqT1BRUURBd05wQURCbUFqRUFnaUptNlBGQwprWjV5bHNDaW42a0pFQkFHVjZyRW1QV0xxQW5yak5seDRUVnRZSnBHMm5UQThJNFBRRkFrV0ROdUFqRUF6dlJqCmg3dEhTNVQzYWlBVXZvVmlMZmZnSlgzMk52dk5RdFJCWndZQlBya3F2RW40OU0weFBoa0VqQWZ5TVdiQwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEQCIDFX1LK6tAMq5Zen3TbpVZE22INBCyh2O1gxEqUxIE8MAiA6L4cok518P7smZs+PnaPqiaXNB3zTFGx8g3v3hJW4/g==","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2ZTYyZGMxNmZlNjEyYjdiYjUyNGE4MjMzYzYwNDI4YjFhNmY3NjY4NWRkOWQ5ZGE3NWRjNzc0OWVhZGQwY2YwIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUURXMlpkQ2pLYmxHRnFydm1KT1ZPd2FqSWtiY3ozWTVyU1ZDTy94b1UwQ2d3SWhBTE9VcGNxMXlPYUpYcnZGSlBlcEZSTGFVcXoxZ0FjTklOazVJeDgxcktydiIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzJWRU5EUW0wMlowRjNTVUpCWjBsVlZVaHJZMGRSVEVWWWNuRk9XRmROU3pOMFRuUXhhRTAxUjNsamQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUlRGTlJHTjVUWHBCZVZkb1kwNU5hbGw0VFZSRk1VMUVZM3BOZWtGNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZJV1UxQ09XeEhTRXhRZWxWRmQyOTFjRkIxUzJzNU9FeEVObnBzWnpseVVVUmFZM29LUjJnMll6aFpZME4wYTBWVVZuWm5OMGcyZVU1VUwzVnVkakEzVkcwcmFFZFNWVk5vVUhCa1ZURnhla05DVDFBM05XRlBRMEpaTUhkbloxZEtUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha2w2VEdwQmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkZVd1RVUldhMWxxYkdsYVIwVTBUVVJhYWsweVRYcGFWMVpyV1ZSVk13cE9WMDE0V2xkSk1GbFhWVEJQUkVFMFdXcFdhRTE2U1hkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOYWsxMVRVUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrbDZUR3BCZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5NUXBPUkVFeFdrZEpOVmx0VW1oUFJFRXlXWHBPYWsweVZteGFSMFV4VG5wV2FrMVhWbWxPUjBac1RrUm5kMDlIU1RGWlZFMTVUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObk1RcE9SRUV4V2tkSk5WbHRVbWhQUkVFeVdYcE9hazB5Vm14YVIwVXhUbnBXYWsxWFZtbE9SMFpzVGtSbmQwOUhTVEZaVkUxNVRVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOYWsxMVRVUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BKZWt4cVFYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJjeFRrUkJNVnBIU1RWWmJWSm9UMFJCTWxsNlRtcE5NbFpzV2tkRk1VNTZWbXBOVjFacFRrZEdiRTVFWjNkUFIwa3hXVlJOZVUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYcE9hbWMxVG1wbk5BcE5la1YzVERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZVUxNU5IZE5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVWbGQwSTFRVWhqUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyZ3phR0ZRYm1kQlFVSkJUVUZUUkVKSENrRnBSVUYyV2twMlpGZFlkSFpYYzJWeE9XUjVNR1JqV1U0M0wwRjNRMUJKWjBkUlYzRk1NRTFvVFdaWmIweG5RMGxSUkRkWVRrMXNWblo0VkdFMlpVUUtaVlozWkVjemNHaGpaV2g0TlZoclRUWnFWVGR5UmtZNFZtcFRkeXQ2UVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV3UVVSQ2JVRnFSVUZuYVVwdE5sQkdRd3ByV2pWNWJITkRhVzQyYTBwRlFrRkhWalp5UlcxUVYweHhRVzV5YWs1c2VEUlVWblJaU25CSE1tNVVRVGhKTkZCUlJrRnJWMFJPZFVGcVJVRjZkbEpxQ21nM2RFaFROVlF6WVdsQlZYWnZWbWxNWm1ablNsZ3pNazUyZGs1UmRGSkNXbmRaUWxCeWEzRjJSVzQwT1Uwd2VGQm9hMFZxUVdaNVRWZGlRd290TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1794727383,"logIndex":3896882215,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
|
package/dist/loop/stop.d.ts
CHANGED
|
@@ -84,8 +84,15 @@ export interface TurnResult {
|
|
|
84
84
|
reason: TurnEndReason;
|
|
85
85
|
}>;
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* 1.23-W1(D-W1-6):取消时机标注(W-0 审计 F-W0-3——aborted 三路径链上不区分)——
|
|
89
|
+
* 'before-request' = 请求未发出(runTurn 循环头 / chatStep 零 chunk 流结束检查);
|
|
90
|
+
* 'in-flight' = 在途打断(chatStep chunk 间隙 / 有 chunk 后的流结束检查)。
|
|
91
|
+
* 缺省缺席 = 1.22 形态(terminate 型 abort 为工具主动收口语义,非取消,不带键)。
|
|
92
|
+
*/
|
|
87
93
|
export declare class TurnAborted extends Error {
|
|
88
|
-
|
|
94
|
+
readonly abortedAt?: 'before-request' | 'in-flight' | undefined;
|
|
95
|
+
constructor(abortedAt?: 'before-request' | 'in-flight' | undefined);
|
|
89
96
|
}
|
|
90
97
|
export declare function runTurn(opts: {
|
|
91
98
|
log: SessionLog;
|
package/dist/loop/stop.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { CarM8Error } from '../runtime-core/errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* 1.23-W1(D-W1-6):取消时机标注(W-0 审计 F-W0-3——aborted 三路径链上不区分)——
|
|
4
|
+
* 'before-request' = 请求未发出(runTurn 循环头 / chatStep 零 chunk 流结束检查);
|
|
5
|
+
* 'in-flight' = 在途打断(chatStep chunk 间隙 / 有 chunk 后的流结束检查)。
|
|
6
|
+
* 缺省缺席 = 1.22 形态(terminate 型 abort 为工具主动收口语义,非取消,不带键)。
|
|
7
|
+
*/
|
|
2
8
|
export class TurnAborted extends Error {
|
|
3
|
-
|
|
9
|
+
abortedAt;
|
|
10
|
+
constructor(abortedAt) {
|
|
11
|
+
super('CAR-ABORTED: turn cancelled by user');
|
|
12
|
+
this.abortedAt = abortedAt;
|
|
13
|
+
}
|
|
4
14
|
}
|
|
5
15
|
/**
|
|
6
16
|
* 1.11-S2(D-33 / 计量设计 §3.3):turn 级 usage 聚合——扫描本 turn 的 'usage' 事件(事实本体
|
|
@@ -166,7 +176,8 @@ export async function runTurn(opts) {
|
|
|
166
176
|
try {
|
|
167
177
|
while (steps < maxSteps) {
|
|
168
178
|
if (signal?.aborted) {
|
|
169
|
-
|
|
179
|
+
// 1.23-W1(D-W1-6):循环头取消 = 请求未发出(无在途请求)
|
|
180
|
+
const seq = closeTurn({ reason: 'aborted', abortedAt: 'before-request' }).seq;
|
|
170
181
|
trail.push({ seq, reason: 'aborted' });
|
|
171
182
|
return { reason: 'aborted', steps, endReasonTrail: trail };
|
|
172
183
|
}
|
|
@@ -252,17 +263,19 @@ export async function runTurn(opts) {
|
|
|
252
263
|
}
|
|
253
264
|
catch (e) {
|
|
254
265
|
if (e instanceof TurnAborted) {
|
|
255
|
-
|
|
266
|
+
// 1.23-W1(D-W1-6):chatStep 甄别过的取消时机随异常携带(terminate 型 abort 不经此路径)
|
|
267
|
+
const seq = closeTurn({ reason: 'aborted', ...(e.abortedAt ? { abortedAt: e.abortedAt } : {}) }).seq;
|
|
256
268
|
trail.push({ seq, reason: 'aborted' });
|
|
257
269
|
return { reason: 'aborted', steps, endReasonTrail: trail };
|
|
258
270
|
}
|
|
259
271
|
// 1.4-S4:CarM8Error 保形——code/userHint/retryable 进 turnEnd(A080001 引导文案「请运行
|
|
260
|
-
// car doctor」可达用户;非 CarM8Error 维持 String(e)
|
|
272
|
+
// car doctor」可达用户;非 CarM8Error 维持 String(e) 口径不变);
|
|
273
|
+
// 1.23-W1(D-W1-3):+kind 失败细类(加法尾键——B080001 多形态分型,审计 F-W0-2)
|
|
261
274
|
const m8 = e instanceof CarM8Error;
|
|
262
275
|
const detail = m8 ? `${e.slug}: ${e.message}` : String(e);
|
|
263
276
|
const seq = closeTurn({
|
|
264
277
|
reason: 'error', detail,
|
|
265
|
-
...(m8 ? { code: e.code, userHint: e.userHint, retryable: e.retryable } : {}),
|
|
278
|
+
...(m8 ? { code: e.code, userHint: e.userHint, retryable: e.retryable, ...(e.kind ? { kind: e.kind } : {}) } : {}),
|
|
266
279
|
}).seq;
|
|
267
280
|
trail.push({ seq, reason: 'error' });
|
|
268
281
|
return { reason: 'error', steps, endReasonTrail: trail };
|
package/dist/release-audit.jsonl
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{"stage":"archive","detail":"git archive HEAD → car-runtime-1.
|
|
2
|
-
{"stage":"sha256sums","detail":"SHA256SUMS
|
|
3
|
-
{"stage":"sbom","detail":"55 个组件(运行时依赖 0 项)","ts":
|
|
4
|
-
{"stage":"cosign-keyless","detail":"signed car-runtime-1.
|
|
1
|
+
{"stage":"archive","detail":"git archive HEAD → car-runtime-1.23.0.tgz(450442 bytes,树哈希锚定)","ts":1794727381948}
|
|
2
|
+
{"stage":"sha256sums","detail":"SHA256SUMS 6e62dc16fe612b7b…(裸字节复核通过)","ts":1794727381950}
|
|
3
|
+
{"stage":"sbom","detail":"55 个组件(运行时依赖 0 项)","ts":1794727381957}
|
|
4
|
+
{"stage":"cosign-keyless","detail":"signed car-runtime-1.23.0.tgz (keyless OIDC + Rekor)","ts":1794727387719}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TurnAborted } from '../loop/stop.js';
|
|
2
2
|
import { StreamRedactor } from './redaction.js';
|
|
3
3
|
import { scanSecrets } from '../security/secrets.js';
|
|
4
|
-
import { providerUnreachable } from './errors.js';
|
|
4
|
+
import { CarM8Error, providerUnreachable } from './errors.js';
|
|
5
5
|
export async function chatStep(opts) {
|
|
6
6
|
const { core, log, turnId, model, tools, maxTokens, adapterId } = opts;
|
|
7
7
|
// 1. 请求前断言(SQ-07 #2):N1 失守 = 带病请求,显式拒绝
|
|
@@ -49,6 +49,12 @@ export async function chatStep(opts) {
|
|
|
49
49
|
const thinkingRedactor = new StreamRedactor();
|
|
50
50
|
let stopReason;
|
|
51
51
|
let errorDetail;
|
|
52
|
+
// 1.23-W1(D-W1-2):error chunk 细类捕获(最后一条 error chunk 的 kind——providerUnreachable
|
|
53
|
+
// 携带 → runTurn turnEnd meta.kind;缺失 = 1.22 形态)
|
|
54
|
+
let errorKind;
|
|
55
|
+
// 1.23-W1(D-W1-6):零 chunk 甄别——流结束检查抛 TurnAborted 时区分「请求未发出(attempt 前
|
|
56
|
+
// 静默收口)」与「在途打断」;chunk 间隙路径恒 in-flight
|
|
57
|
+
let sawChunk = false;
|
|
52
58
|
let usage;
|
|
53
59
|
const stream = core.chat({
|
|
54
60
|
model,
|
|
@@ -62,9 +68,11 @@ export async function chatStep(opts) {
|
|
|
62
68
|
...(opts.log.sessionId ? { metadata: { sessionId: opts.log.sessionId, turnId, traceId: `${opts.log.sessionId}:${turnId}` } } : {}),
|
|
63
69
|
});
|
|
64
70
|
for await (const chunk of stream) {
|
|
65
|
-
|
|
71
|
+
sawChunk = true;
|
|
72
|
+
// 1.4-S4(D-19):chunk 间隙取消——此刻 assistant 尚未落链(流完才 append),无半截消息;
|
|
73
|
+
// 1.23-W1(D-W1-6):在途打断标注 in-flight
|
|
66
74
|
if (opts.signal?.aborted)
|
|
67
|
-
throw new TurnAborted();
|
|
75
|
+
throw new TurnAborted('in-flight');
|
|
68
76
|
if (chunk.delta !== undefined) {
|
|
69
77
|
const masked = redactor.push(chunk.delta);
|
|
70
78
|
text += masked;
|
|
@@ -109,17 +117,21 @@ export async function chatStep(opts) {
|
|
|
109
117
|
opts.onDelta({ thinkingDelta: masked });
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
|
-
if (chunk.error)
|
|
120
|
+
if (chunk.error) {
|
|
113
121
|
errorDetail = chunk.error.message;
|
|
122
|
+
errorKind = chunk.error.kind; // 1.23-W1(D-W1-2):细类透传(最后一条 error chunk 为准)
|
|
123
|
+
}
|
|
114
124
|
if (chunk.usage)
|
|
115
125
|
usage = chunk.usage; // D-33:附着于最后一个 chunk——后到覆盖
|
|
116
126
|
if (chunk.finishReason !== undefined)
|
|
117
127
|
stopReason = chunk.finishReason === 'aborted' ? 'error' : chunk.finishReason;
|
|
118
128
|
}
|
|
119
129
|
// 1.4-S4(D-19):finish 检查前先查取消(适配器取消路径静默收口无 finishReason——
|
|
120
|
-
//
|
|
130
|
+
// 必须先于此处的契约兜底,否则取消被误判为适配器违规);
|
|
131
|
+
// 1.23-W1(D-W1-6):abortedAt 甄别——有 chunk = 在途打断(in-flight);
|
|
132
|
+
// 零 chunk = attempt 前静默收口,请求未发出(before-request)
|
|
121
133
|
if (opts.signal?.aborted)
|
|
122
|
-
throw new TurnAborted();
|
|
134
|
+
throw new TurnAborted(sawChunk ? 'in-flight' : 'before-request');
|
|
123
135
|
if (stopReason === undefined) {
|
|
124
136
|
// 适配器层已保证首 finishReason(AL-05);防御性兜底显式化
|
|
125
137
|
throw new Error('CAR-E-LLM-FINISH: 流结束无 finishReason(适配器契约违规)');
|
|
@@ -201,16 +213,19 @@ export async function chatStep(opts) {
|
|
|
201
213
|
args = c.argsBuf ? JSON.parse(c.argsBuf) : {};
|
|
202
214
|
}
|
|
203
215
|
catch {
|
|
204
|
-
// toolUse 声明下 args 非法 JSON = 协议违规显式化(非 length 场景无 truncatedTools
|
|
205
|
-
|
|
216
|
+
// toolUse 声明下 args 非法 JSON = 协议违规显式化(非 length 场景无 truncatedTools 口径);
|
|
217
|
+
// 1.23-W1(D-W1-3):转 CarM8Error——turnEnd meta 获得 code/userHint/retryable + kind
|
|
218
|
+
// 'proto-invalid-args'(detail 形态变更登记:CarM8Error 统一格式带(B080001)码后缀)
|
|
219
|
+
throw new CarM8Error('B080001', 'CAR-E-LLM-PROTO', `工具调用 ${c.name} 参数非法 JSON(finishReason=${stopReason},${errorDetail ?? '无附加错误'})`, undefined, false, 'proto-invalid-args');
|
|
206
220
|
}
|
|
207
221
|
toolCalls.push({ id: c.id ?? `tc-${idx}-${c.name}`, tool: c.name, args });
|
|
208
222
|
}
|
|
209
223
|
return { stopReason: 'toolUse', ...(text ? { text } : {}), ...(toolCalls.length ? { toolCalls } : {}), secretsRedacted };
|
|
210
224
|
}
|
|
211
|
-
// error(含 aborted 映射):以 B080001 抛出 → runTurn catch → turnEnd error 携带 detail(BD-04
|
|
225
|
+
// error(含 aborted 映射):以 B080001 抛出 → runTurn catch → turnEnd error 携带 detail(BD-04 收口);
|
|
226
|
+
// 1.23-W1(D-W1-2):细类 kind 透传(error chunk 捕获的 errorKind;缺失 = 1.22 形态)
|
|
212
227
|
if (stopReason === 'error') {
|
|
213
|
-
throw providerUnreachable(errorDetail ?? 'provider 返回 error 终止(无附加说明)');
|
|
228
|
+
throw providerUnreachable(errorDetail ?? 'provider 返回 error 终止(无附加说明)', errorKind);
|
|
214
229
|
}
|
|
215
230
|
return { stopReason: 'stop', ...(text ? { text } : {}), secretsRedacted };
|
|
216
231
|
}
|
|
@@ -16,6 +16,12 @@ export declare class CarM8Error extends Error {
|
|
|
16
16
|
readonly userHint?: string | undefined;
|
|
17
17
|
/** 重试建议(A080001 不重试 / B080001 退避后重试) */
|
|
18
18
|
readonly retryable: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* 1.23-W1(D-W1-3):失败细类(LlmErrorKind 注册表)——B080001 一码多形态的分型标注,
|
|
21
|
+
* 经 runTurn closeTurn 落 turnEnd meta.kind(加法尾键,消费面免字符串反解)。
|
|
22
|
+
* 缺省缺席 = 1.22 形态(A080001 凭据缺失以独立码区分,无需 kind)。
|
|
23
|
+
*/
|
|
24
|
+
readonly kind?: string | undefined;
|
|
19
25
|
constructor(
|
|
20
26
|
/** 六位错误码(§3.5.1 注册表) */
|
|
21
27
|
code: M8ErrorCode,
|
|
@@ -24,7 +30,13 @@ export declare class CarM8Error extends Error {
|
|
|
24
30
|
/** 用户文案(§3.5.1 注册表原文) */
|
|
25
31
|
userHint?: string | undefined,
|
|
26
32
|
/** 重试建议(A080001 不重试 / B080001 退避后重试) */
|
|
27
|
-
retryable?: boolean
|
|
33
|
+
retryable?: boolean,
|
|
34
|
+
/**
|
|
35
|
+
* 1.23-W1(D-W1-3):失败细类(LlmErrorKind 注册表)——B080001 一码多形态的分型标注,
|
|
36
|
+
* 经 runTurn closeTurn 落 turnEnd meta.kind(加法尾键,消费面免字符串反解)。
|
|
37
|
+
* 缺省缺席 = 1.22 形态(A080001 凭据缺失以独立码区分,无需 kind)。
|
|
38
|
+
*/
|
|
39
|
+
kind?: string | undefined);
|
|
28
40
|
}
|
|
29
41
|
export declare function credentialMissing(provider: string, searched: string[]): CarM8Error;
|
|
30
|
-
export declare function providerUnreachable(detail: string): CarM8Error;
|
|
42
|
+
export declare function providerUnreachable(detail: string, kind?: string): CarM8Error;
|
|
@@ -11,6 +11,7 @@ export class CarM8Error extends Error {
|
|
|
11
11
|
slug;
|
|
12
12
|
userHint;
|
|
13
13
|
retryable;
|
|
14
|
+
kind;
|
|
14
15
|
constructor(
|
|
15
16
|
/** 六位错误码(§3.5.1 注册表) */
|
|
16
17
|
code,
|
|
@@ -19,18 +20,25 @@ export class CarM8Error extends Error {
|
|
|
19
20
|
/** 用户文案(§3.5.1 注册表原文) */
|
|
20
21
|
userHint,
|
|
21
22
|
/** 重试建议(A080001 不重试 / B080001 退避后重试) */
|
|
22
|
-
retryable = false
|
|
23
|
+
retryable = false,
|
|
24
|
+
/**
|
|
25
|
+
* 1.23-W1(D-W1-3):失败细类(LlmErrorKind 注册表)——B080001 一码多形态的分型标注,
|
|
26
|
+
* 经 runTurn closeTurn 落 turnEnd meta.kind(加法尾键,消费面免字符串反解)。
|
|
27
|
+
* 缺省缺席 = 1.22 形态(A080001 凭据缺失以独立码区分,无需 kind)。
|
|
28
|
+
*/
|
|
29
|
+
kind) {
|
|
23
30
|
super(`${slug}: ${message}(${code})`);
|
|
24
31
|
this.code = code;
|
|
25
32
|
this.slug = slug;
|
|
26
33
|
this.userHint = userHint;
|
|
27
34
|
this.retryable = retryable;
|
|
35
|
+
this.kind = kind;
|
|
28
36
|
this.name = 'CarM8Error';
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
export function credentialMissing(provider, searched) {
|
|
32
40
|
return new CarM8Error('A080001', 'CAR-E-CRED-MISSING', `未找到模型服务凭据(provider=${provider},已检索:${searched.join('、') || '无可用通道'})`, '未找到模型服务凭据,请运行 car doctor', false);
|
|
33
41
|
}
|
|
34
|
-
export function providerUnreachable(detail) {
|
|
35
|
-
return new CarM8Error('B080001', 'CAR-E-LLM-UNREACHABLE', `LLM Provider 不可达/重试耗尽:${detail}`, '模型服务暂时不可用,本轮已停止', true);
|
|
42
|
+
export function providerUnreachable(detail, kind) {
|
|
43
|
+
return new CarM8Error('B080001', 'CAR-E-LLM-UNREACHABLE', `LLM Provider 不可达/重试耗尽:${detail}`, '模型服务暂时不可用,本轮已停止', true, kind);
|
|
36
44
|
}
|
package/dist/runtime-core/llm.js
CHANGED
|
@@ -228,7 +228,9 @@ async function* streamSsePost(spec, parse) {
|
|
|
228
228
|
if (!res.ok) {
|
|
229
229
|
const text = await res.text().catch(() => '');
|
|
230
230
|
// 4xx 业务错不重试(§3.5.4);错误信息不含凭据(凭据值不进错误路径)
|
|
231
|
-
|
|
231
|
+
// 1.23-W1(D-W1-5):4xx 细类标注——overflow 特征命中 = context-limit-suspect(疑似),否则 transport-4xx
|
|
232
|
+
const kind = contextLimitSuspect(text) ? 'context-limit-suspect' : 'transport-4xx';
|
|
233
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: `provider HTTP ${res.status}${text ? `:${text.slice(0, 200)}` : ''}`, kind } };
|
|
232
234
|
return;
|
|
233
235
|
}
|
|
234
236
|
if (!res.body)
|
|
@@ -252,7 +254,8 @@ async function* streamSsePost(spec, parse) {
|
|
|
252
254
|
// 流自然结束但未给 finishReason = 异常终止(AL-05 口径:缺失显式化,禁吞没);
|
|
253
255
|
// 已携带 finishReason 的流原样收口——兜底不得追加(M8-BUG-1 口径延续)
|
|
254
256
|
if (!sawFinish) {
|
|
255
|
-
|
|
257
|
+
// 1.23-W1:细类 no-finish-reason(审计 F-W0-2 拆型)
|
|
258
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: '流结束未携带 finish_reason(AL-05)', kind: 'no-finish-reason' } };
|
|
256
259
|
}
|
|
257
260
|
return;
|
|
258
261
|
}
|
|
@@ -268,11 +271,11 @@ async function* streamSsePost(spec, parse) {
|
|
|
268
271
|
continue;
|
|
269
272
|
}
|
|
270
273
|
if (beforeFirstByte) {
|
|
271
|
-
// 重试耗尽 → B080001(BD-04:消费方 runTurn 以 error
|
|
272
|
-
throw providerUnreachable(`${e.name}: ${e.message}(尝试 ${attempt + 1}/${spec.retries + 1},${Date.now() - started}ms
|
|
274
|
+
// 重试耗尽 → B080001(BD-04:消费方 runTurn 以 error 收口);1.23-W1 细类 transport-exhausted
|
|
275
|
+
throw providerUnreachable(`${e.name}: ${e.message}(尝试 ${attempt + 1}/${spec.retries + 1},${Date.now() - started}ms)`, 'transport-exhausted');
|
|
273
276
|
}
|
|
274
|
-
// 流中途失败:已消费 chunk 不可重放 → error chunk
|
|
275
|
-
yield { finishReason: 'error', error: { code: 'B080001', message: `流中途失败:${e.message}
|
|
277
|
+
// 流中途失败:已消费 chunk 不可重放 → error chunk 收口;1.23-W1 细类 stream-midbreak
|
|
278
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: `流中途失败:${e.message}`, kind: 'stream-midbreak' } };
|
|
276
279
|
return;
|
|
277
280
|
}
|
|
278
281
|
finally {
|
|
@@ -290,6 +293,22 @@ const FINISH_MAP = {
|
|
|
290
293
|
};
|
|
291
294
|
class Retryable extends Error {
|
|
292
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* 1.23-W1(D-W1-5):context-limit-suspect 确定性关键词表——仅用于 4xx 错误体、只收高置信特征;
|
|
298
|
+
* 标注语义 = **疑似**(消费面文案须带「疑似」,禁估算禁 LLM 判断);未命中 = transport-4xx。
|
|
299
|
+
*/
|
|
300
|
+
const CONTEXT_LIMIT_PATTERNS = [
|
|
301
|
+
/context length/i,
|
|
302
|
+
/maximum context/i,
|
|
303
|
+
/context[_ ]window/i,
|
|
304
|
+
/prompt is too long/i,
|
|
305
|
+
/too many tokens/i,
|
|
306
|
+
/input tokens? exceed/i,
|
|
307
|
+
/exceeds? the (maximum )?(context|token)/i,
|
|
308
|
+
/上下文(长度|超限)/,
|
|
309
|
+
/超出上下文/,
|
|
310
|
+
];
|
|
311
|
+
const contextLimitSuspect = (text) => CONTEXT_LIMIT_PATTERNS.some((p) => p.test(text));
|
|
293
312
|
/**
|
|
294
313
|
* 1.11-S1(D-33 / 计量设计 §1.1):openai usage 归一——prompt_tokens 含 cached,inputTokens
|
|
295
314
|
* 必须做减法(prompt − prompt_tokens_details.cached_tokens);cache_creation 恒 0(无对应字段,
|
|
@@ -382,13 +401,20 @@ async function* parseSseStream(body, model, adapterId) {
|
|
|
382
401
|
yield { usage };
|
|
383
402
|
continue;
|
|
384
403
|
}
|
|
404
|
+
// 1.23-W1(D-W1-4 / D-W1-1):finish 终态附带 error 细类——未映射 = unmapped-finish;
|
|
405
|
+
// content_filter(openai 族唯一映射 'aborted' 的信号)= refusal(与 anthropic stop_reason=refusal 对齐)
|
|
406
|
+
const finishError = finish === 'error'
|
|
407
|
+
? { code: 'B080001', message: `未映射的 finish_reason "${raw}"`, kind: 'unmapped-finish' }
|
|
408
|
+
: finish === 'aborted'
|
|
409
|
+
? { code: 'B080001', message: `provider content_filter (finish_reason="${raw}")`, kind: 'refusal' }
|
|
410
|
+
: undefined;
|
|
385
411
|
const chunks = [];
|
|
386
412
|
if (toolCallDeltas.length === 0) {
|
|
387
413
|
chunks.push({
|
|
388
414
|
...(reasoningContent !== undefined ? { thinkingDelta: reasoningContent } : {}),
|
|
389
415
|
...(text !== undefined ? { delta: text } : {}),
|
|
390
416
|
...(finish !== undefined
|
|
391
|
-
? { finishReason: finish, ...(
|
|
417
|
+
? { finishReason: finish, ...(finishError ? { error: finishError } : {}) }
|
|
392
418
|
: {}),
|
|
393
419
|
});
|
|
394
420
|
}
|
|
@@ -400,7 +426,7 @@ async function* parseSseStream(body, model, adapterId) {
|
|
|
400
426
|
...(i === 0 && text !== undefined ? { delta: text } : {}),
|
|
401
427
|
toolCallDelta: toolCallDeltas[i],
|
|
402
428
|
...(last && finish !== undefined
|
|
403
|
-
? { finishReason: finish, ...(
|
|
429
|
+
? { finishReason: finish, ...(finishError ? { error: finishError } : {}) }
|
|
404
430
|
: {}),
|
|
405
431
|
});
|
|
406
432
|
}
|
|
@@ -800,9 +826,13 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
|
|
|
800
826
|
const usage = mergeAnthropicUsage(startUsage, (evt.usage ?? null), model, adapterId);
|
|
801
827
|
if (raw != null) {
|
|
802
828
|
const finish = ANTHROPIC_STOP_REASON_MAP[String(raw)];
|
|
829
|
+
// 1.23-W1(D-W1-4 / D-W1-1):stop_reason 细类标注——refusal 附加 error chunk(kind 'refusal',
|
|
830
|
+
// 与 openai content_filter 对齐;finishReason 映射不动);未映射 = unmapped-finish
|
|
803
831
|
const chunk = finish
|
|
804
|
-
?
|
|
805
|
-
|
|
832
|
+
? finish === 'aborted'
|
|
833
|
+
? { finishReason: finish, error: { code: 'B080001', message: `provider refusal (stop_reason="${raw}")`, kind: 'refusal' } }
|
|
834
|
+
: { finishReason: finish }
|
|
835
|
+
: { finishReason: 'error', error: { code: 'B080001', message: `未映射的 stop_reason "${raw}"`, kind: 'unmapped-finish' } };
|
|
806
836
|
yield usage ? { ...chunk, usage } : chunk;
|
|
807
837
|
}
|
|
808
838
|
else if (usage) {
|
|
@@ -811,8 +841,9 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
|
|
|
811
841
|
}
|
|
812
842
|
}
|
|
813
843
|
else if (type === 'error') {
|
|
814
|
-
// 流内 error 事件(overloaded_error 等)→ error chunk 收口(后续 message_delta
|
|
815
|
-
|
|
844
|
+
// 流内 error 事件(overloaded_error 等)→ error chunk 收口(后续 message_delta 由守卫拦冲突);
|
|
845
|
+
// 1.23-W1 细类 provider-stream-error
|
|
846
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: `provider 流错误:${evt.error?.message ?? JSON.stringify(evt).slice(0, 200)}`, kind: 'provider-stream-error' } };
|
|
816
847
|
}
|
|
817
848
|
}
|
|
818
849
|
}
|
|
@@ -47,6 +47,13 @@ export interface ToolCallDelta {
|
|
|
47
47
|
/** JSON 增量片段,聚合后整体 JSON.parse(半截 JSON 不解析——ADR-001 同纪律) */
|
|
48
48
|
argumentsDelta?: string;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* 1.23-W1(D-W1-1 / 《W-0 失败分类学对照审计》§5):LLM 失败细类注册表——把 B080001 下压着的
|
|
52
|
+
* 多形态失败拆到可对应处置动作的粒度(审计 F-W0-2)。**加法演进**:可选键,缺失 = 1.22 形态
|
|
53
|
+
* 逐字节兼容。`transport-network` 从审计 8 类剔除(无独立产出点——网络失败只以 exhausted /
|
|
54
|
+
* midbreak 到达消费面);`unmapped-finish` / `provider-stream-error` 为实施中浮出的两形态增补。
|
|
55
|
+
*/
|
|
56
|
+
export type LlmErrorKind = 'transport-4xx' | 'transport-exhausted' | 'stream-midbreak' | 'no-finish-reason' | 'refusal' | 'proto-invalid-args' | 'context-limit-suspect' | 'unmapped-finish' | 'provider-stream-error';
|
|
50
57
|
/**
|
|
51
58
|
* 1.11-S1(D-33 / 计量设计 §3.1):token 四分量归一化用量——跨 provider 可加总的库内口径
|
|
52
59
|
* (§1.1:openai prompt_tokens 含 cached 必须减法归一;anthropic input_tokens 天然不含 cache)。
|
|
@@ -79,10 +86,12 @@ export interface LlmChunk {
|
|
|
79
86
|
delta?: string;
|
|
80
87
|
toolCallDelta?: ToolCallDelta;
|
|
81
88
|
finishReason?: 'stop' | 'length' | 'toolUse' | 'error' | 'aborted';
|
|
82
|
-
/** finishReason='error' 时的结构化原因(消费方落 turnEnd meta
|
|
89
|
+
/** finishReason='error' 时的结构化原因(消费方落 turnEnd meta,不进模型消息流);
|
|
90
|
+
* 1.23-W1(D-W1-2):+kind 失败细类(LlmErrorKind 注册表——B080001 一码多形态的分型标注) */
|
|
83
91
|
error?: {
|
|
84
92
|
code: string;
|
|
85
93
|
message: string;
|
|
94
|
+
kind?: LlmErrorKind;
|
|
86
95
|
};
|
|
87
96
|
/** 1.11-S1(D-33):每次请求最后一个 chunk 附带归一化用量;缺失 = provider 未回,禁补造 */
|
|
88
97
|
usage?: LlmUsage;
|
package/dist/sbom.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CAR-SBOM",
|
|
3
3
|
"specVersion": "0.1",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.23.0",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"type": "file",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
{
|
|
107
107
|
"type": "file",
|
|
108
108
|
"path": "src/loop/stop.ts",
|
|
109
|
-
"sha256": "
|
|
109
|
+
"sha256": "a39751b233cbf42073af08fd9a6ba29f29a47700cd025fa79a3d5418e1116a60"
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
"type": "file",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
{
|
|
162
162
|
"type": "file",
|
|
163
163
|
"path": "src/runtime-core/chatStep.ts",
|
|
164
|
-
"sha256": "
|
|
164
|
+
"sha256": "c95f71f3fbe5a99b2fe02d3a71450efb2bb31b003a30da6eafc39eb57c791f33"
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
"type": "file",
|
|
@@ -171,12 +171,12 @@
|
|
|
171
171
|
{
|
|
172
172
|
"type": "file",
|
|
173
173
|
"path": "src/runtime-core/errors.ts",
|
|
174
|
-
"sha256": "
|
|
174
|
+
"sha256": "b7611e5a1647bb0022a76b9f8211a59f8097c0d70c4747d56600e122ca5198a8"
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
"type": "file",
|
|
178
178
|
"path": "src/runtime-core/llm.ts",
|
|
179
|
-
"sha256": "
|
|
179
|
+
"sha256": "fcab344c1dae5a4fac24dcb8e7be3a82cf0442411b4b9943e558a07f35e86a30"
|
|
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": "
|
|
204
|
+
"sha256": "c11f7fbb38ff1f5567e7a18c87300f3bc3bf802b7d3660ebb30a8191017ad37a"
|
|
205
205
|
},
|
|
206
206
|
{
|
|
207
207
|
"type": "file",
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
{
|
|
272
272
|
"type": "file",
|
|
273
273
|
"path": "package.json",
|
|
274
|
-
"sha256": "
|
|
274
|
+
"sha256": "593002604fb264718099c16d5a3d04b82d1a1073b7c8f8160fc7835f4a695e50"
|
|
275
275
|
},
|
|
276
276
|
{
|
|
277
277
|
"type": "file",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lqc123qwe/car-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.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",
|
package/src/loop/stop.ts
CHANGED
|
@@ -74,8 +74,14 @@ export interface TurnPreset {
|
|
|
74
74
|
|
|
75
75
|
export interface TurnResult { reason: TurnEndReason; steps: number; endReasonTrail: Array<{ seq: number; reason: TurnEndReason }> }
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* 1.23-W1(D-W1-6):取消时机标注(W-0 审计 F-W0-3——aborted 三路径链上不区分)——
|
|
79
|
+
* 'before-request' = 请求未发出(runTurn 循环头 / chatStep 零 chunk 流结束检查);
|
|
80
|
+
* 'in-flight' = 在途打断(chatStep chunk 间隙 / 有 chunk 后的流结束检查)。
|
|
81
|
+
* 缺省缺席 = 1.22 形态(terminate 型 abort 为工具主动收口语义,非取消,不带键)。
|
|
82
|
+
*/
|
|
77
83
|
export class TurnAborted extends Error {
|
|
78
|
-
constructor() { super('CAR-ABORTED: turn cancelled by user') }
|
|
84
|
+
constructor(readonly abortedAt?: 'before-request' | 'in-flight') { super('CAR-ABORTED: turn cancelled by user') }
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
/**
|
|
@@ -231,7 +237,8 @@ export async function runTurn(opts: {
|
|
|
231
237
|
try {
|
|
232
238
|
while (steps < maxSteps) {
|
|
233
239
|
if (signal?.aborted) {
|
|
234
|
-
|
|
240
|
+
// 1.23-W1(D-W1-6):循环头取消 = 请求未发出(无在途请求)
|
|
241
|
+
const seq = closeTurn({ reason: 'aborted', abortedAt: 'before-request' }).seq
|
|
235
242
|
trail.push({ seq, reason: 'aborted' })
|
|
236
243
|
return { reason: 'aborted', steps, endReasonTrail: trail }
|
|
237
244
|
}
|
|
@@ -320,17 +327,19 @@ export async function runTurn(opts: {
|
|
|
320
327
|
return { reason: 'error', steps, endReasonTrail: trail }
|
|
321
328
|
} catch (e) {
|
|
322
329
|
if (e instanceof TurnAborted) {
|
|
323
|
-
|
|
330
|
+
// 1.23-W1(D-W1-6):chatStep 甄别过的取消时机随异常携带(terminate 型 abort 不经此路径)
|
|
331
|
+
const seq = closeTurn({ reason: 'aborted', ...(e.abortedAt ? { abortedAt: e.abortedAt } : {}) }).seq
|
|
324
332
|
trail.push({ seq, reason: 'aborted' })
|
|
325
333
|
return { reason: 'aborted', steps, endReasonTrail: trail }
|
|
326
334
|
}
|
|
327
335
|
// 1.4-S4:CarM8Error 保形——code/userHint/retryable 进 turnEnd(A080001 引导文案「请运行
|
|
328
|
-
// car doctor」可达用户;非 CarM8Error 维持 String(e)
|
|
336
|
+
// car doctor」可达用户;非 CarM8Error 维持 String(e) 口径不变);
|
|
337
|
+
// 1.23-W1(D-W1-3):+kind 失败细类(加法尾键——B080001 多形态分型,审计 F-W0-2)
|
|
329
338
|
const m8 = e instanceof CarM8Error
|
|
330
339
|
const detail = m8 ? `${e.slug}: ${e.message}` : String(e)
|
|
331
340
|
const seq = closeTurn({
|
|
332
341
|
reason: 'error', detail,
|
|
333
|
-
...(m8 ? { code: e.code, userHint: e.userHint, retryable: e.retryable } : {}),
|
|
342
|
+
...(m8 ? { code: e.code, userHint: e.userHint, retryable: e.retryable, ...(e.kind ? { kind: e.kind } : {}) } : {}),
|
|
334
343
|
}).seq
|
|
335
344
|
trail.push({ seq, reason: 'error' })
|
|
336
345
|
return { reason: 'error', steps, endReasonTrail: trail }
|
|
@@ -23,7 +23,7 @@ import type { RuntimeCore } from './llm.ts'
|
|
|
23
23
|
import type { LlmUsage, ToolDefinition } from './types.ts'
|
|
24
24
|
import { StreamRedactor } from './redaction.ts'
|
|
25
25
|
import { scanSecrets } from '../security/secrets.ts'
|
|
26
|
-
import { providerUnreachable } from './errors.ts'
|
|
26
|
+
import { CarM8Error, providerUnreachable } from './errors.ts'
|
|
27
27
|
|
|
28
28
|
export interface ChatStepOptions {
|
|
29
29
|
core: RuntimeCore
|
|
@@ -119,6 +119,12 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
119
119
|
const thinkingRedactor = new StreamRedactor()
|
|
120
120
|
let stopReason: ModelStep['stopReason'] | undefined
|
|
121
121
|
let errorDetail: string | undefined
|
|
122
|
+
// 1.23-W1(D-W1-2):error chunk 细类捕获(最后一条 error chunk 的 kind——providerUnreachable
|
|
123
|
+
// 携带 → runTurn turnEnd meta.kind;缺失 = 1.22 形态)
|
|
124
|
+
let errorKind: string | undefined
|
|
125
|
+
// 1.23-W1(D-W1-6):零 chunk 甄别——流结束检查抛 TurnAborted 时区分「请求未发出(attempt 前
|
|
126
|
+
// 静默收口)」与「在途打断」;chunk 间隙路径恒 in-flight
|
|
127
|
+
let sawChunk = false
|
|
122
128
|
let usage: LlmUsage | undefined
|
|
123
129
|
|
|
124
130
|
const stream = core.chat({
|
|
@@ -133,8 +139,10 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
133
139
|
...(opts.log.sessionId ? { metadata: { sessionId: opts.log.sessionId, turnId, traceId: `${opts.log.sessionId}:${turnId}` } } : {}),
|
|
134
140
|
})
|
|
135
141
|
for await (const chunk of stream) {
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
sawChunk = true
|
|
143
|
+
// 1.4-S4(D-19):chunk 间隙取消——此刻 assistant 尚未落链(流完才 append),无半截消息;
|
|
144
|
+
// 1.23-W1(D-W1-6):在途打断标注 in-flight
|
|
145
|
+
if (opts.signal?.aborted) throw new TurnAborted('in-flight')
|
|
138
146
|
if (chunk.delta !== undefined) {
|
|
139
147
|
const masked = redactor.push(chunk.delta)
|
|
140
148
|
text += masked
|
|
@@ -171,13 +179,18 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
171
179
|
if (masked) opts.onDelta({ thinkingDelta: masked })
|
|
172
180
|
}
|
|
173
181
|
}
|
|
174
|
-
if (chunk.error)
|
|
182
|
+
if (chunk.error) {
|
|
183
|
+
errorDetail = chunk.error.message
|
|
184
|
+
errorKind = chunk.error.kind // 1.23-W1(D-W1-2):细类透传(最后一条 error chunk 为准)
|
|
185
|
+
}
|
|
175
186
|
if (chunk.usage) usage = chunk.usage // D-33:附着于最后一个 chunk——后到覆盖
|
|
176
187
|
if (chunk.finishReason !== undefined) stopReason = chunk.finishReason === 'aborted' ? 'error' : chunk.finishReason
|
|
177
188
|
}
|
|
178
189
|
// 1.4-S4(D-19):finish 检查前先查取消(适配器取消路径静默收口无 finishReason——
|
|
179
|
-
//
|
|
180
|
-
|
|
190
|
+
// 必须先于此处的契约兜底,否则取消被误判为适配器违规);
|
|
191
|
+
// 1.23-W1(D-W1-6):abortedAt 甄别——有 chunk = 在途打断(in-flight);
|
|
192
|
+
// 零 chunk = attempt 前静默收口,请求未发出(before-request)
|
|
193
|
+
if (opts.signal?.aborted) throw new TurnAborted(sawChunk ? 'in-flight' : 'before-request')
|
|
181
194
|
if (stopReason === undefined) {
|
|
182
195
|
// 适配器层已保证首 finishReason(AL-05);防御性兜底显式化
|
|
183
196
|
throw new Error('CAR-E-LLM-FINISH: 流结束无 finishReason(适配器契约违规)')
|
|
@@ -256,16 +269,26 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
256
269
|
if (!c.name) continue
|
|
257
270
|
let args: Record<string, unknown>
|
|
258
271
|
try { args = c.argsBuf ? JSON.parse(c.argsBuf) as Record<string, unknown> : {} } catch {
|
|
259
|
-
// toolUse 声明下 args 非法 JSON = 协议违规显式化(非 length 场景无 truncatedTools
|
|
260
|
-
|
|
272
|
+
// toolUse 声明下 args 非法 JSON = 协议违规显式化(非 length 场景无 truncatedTools 口径);
|
|
273
|
+
// 1.23-W1(D-W1-3):转 CarM8Error——turnEnd meta 获得 code/userHint/retryable + kind
|
|
274
|
+
// 'proto-invalid-args'(detail 形态变更登记:CarM8Error 统一格式带(B080001)码后缀)
|
|
275
|
+
throw new CarM8Error(
|
|
276
|
+
'B080001',
|
|
277
|
+
'CAR-E-LLM-PROTO',
|
|
278
|
+
`工具调用 ${c.name} 参数非法 JSON(finishReason=${stopReason},${errorDetail ?? '无附加错误'})`,
|
|
279
|
+
undefined,
|
|
280
|
+
false,
|
|
281
|
+
'proto-invalid-args',
|
|
282
|
+
)
|
|
261
283
|
}
|
|
262
284
|
toolCalls.push({ id: c.id ?? `tc-${idx}-${c.name}`, tool: c.name, args })
|
|
263
285
|
}
|
|
264
286
|
return { stopReason: 'toolUse', ...(text ? { text } : {}), ...(toolCalls.length ? { toolCalls } : {}), secretsRedacted }
|
|
265
287
|
}
|
|
266
|
-
// error(含 aborted 映射):以 B080001 抛出 → runTurn catch → turnEnd error 携带 detail(BD-04
|
|
288
|
+
// error(含 aborted 映射):以 B080001 抛出 → runTurn catch → turnEnd error 携带 detail(BD-04 收口);
|
|
289
|
+
// 1.23-W1(D-W1-2):细类 kind 透传(error chunk 捕获的 errorKind;缺失 = 1.22 形态)
|
|
267
290
|
if (stopReason === 'error') {
|
|
268
|
-
throw providerUnreachable(errorDetail ?? 'provider 返回 error 终止(无附加说明)')
|
|
291
|
+
throw providerUnreachable(errorDetail ?? 'provider 返回 error 终止(无附加说明)', errorKind)
|
|
269
292
|
}
|
|
270
293
|
return { stopReason: 'stop', ...(text ? { text } : {}), secretsRedacted }
|
|
271
294
|
}
|
|
@@ -20,6 +20,12 @@ export class CarM8Error extends Error {
|
|
|
20
20
|
readonly userHint?: string,
|
|
21
21
|
/** 重试建议(A080001 不重试 / B080001 退避后重试) */
|
|
22
22
|
readonly retryable = false,
|
|
23
|
+
/**
|
|
24
|
+
* 1.23-W1(D-W1-3):失败细类(LlmErrorKind 注册表)——B080001 一码多形态的分型标注,
|
|
25
|
+
* 经 runTurn closeTurn 落 turnEnd meta.kind(加法尾键,消费面免字符串反解)。
|
|
26
|
+
* 缺省缺席 = 1.22 形态(A080001 凭据缺失以独立码区分,无需 kind)。
|
|
27
|
+
*/
|
|
28
|
+
readonly kind?: string,
|
|
23
29
|
) {
|
|
24
30
|
super(`${slug}: ${message}(${code})`)
|
|
25
31
|
this.name = 'CarM8Error'
|
|
@@ -36,12 +42,13 @@ export function credentialMissing(provider: string, searched: string[]): CarM8Er
|
|
|
36
42
|
)
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
export function providerUnreachable(detail: string): CarM8Error {
|
|
45
|
+
export function providerUnreachable(detail: string, kind?: string): CarM8Error {
|
|
40
46
|
return new CarM8Error(
|
|
41
47
|
'B080001',
|
|
42
48
|
'CAR-E-LLM-UNREACHABLE',
|
|
43
49
|
`LLM Provider 不可达/重试耗尽:${detail}`,
|
|
44
50
|
'模型服务暂时不可用,本轮已停止',
|
|
45
51
|
true,
|
|
52
|
+
kind,
|
|
46
53
|
)
|
|
47
54
|
}
|
package/src/runtime-core/llm.ts
CHANGED
|
@@ -244,7 +244,9 @@ async function* streamSsePost(spec: SsePostSpec, parse: (body: AsyncIterable<Uin
|
|
|
244
244
|
if (!res.ok) {
|
|
245
245
|
const text = await res.text().catch(() => '')
|
|
246
246
|
// 4xx 业务错不重试(§3.5.4);错误信息不含凭据(凭据值不进错误路径)
|
|
247
|
-
|
|
247
|
+
// 1.23-W1(D-W1-5):4xx 细类标注——overflow 特征命中 = context-limit-suspect(疑似),否则 transport-4xx
|
|
248
|
+
const kind = contextLimitSuspect(text) ? 'context-limit-suspect' : 'transport-4xx'
|
|
249
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: `provider HTTP ${res.status}${text ? `:${text.slice(0, 200)}` : ''}`, kind } }
|
|
248
250
|
return
|
|
249
251
|
}
|
|
250
252
|
if (!res.body) throw new Retryable('响应无 body 流')
|
|
@@ -260,7 +262,8 @@ async function* streamSsePost(spec: SsePostSpec, parse: (body: AsyncIterable<Uin
|
|
|
260
262
|
// 流自然结束但未给 finishReason = 异常终止(AL-05 口径:缺失显式化,禁吞没);
|
|
261
263
|
// 已携带 finishReason 的流原样收口——兜底不得追加(M8-BUG-1 口径延续)
|
|
262
264
|
if (!sawFinish) {
|
|
263
|
-
|
|
265
|
+
// 1.23-W1:细类 no-finish-reason(审计 F-W0-2 拆型)
|
|
266
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: '流结束未携带 finish_reason(AL-05)', kind: 'no-finish-reason' } }
|
|
264
267
|
}
|
|
265
268
|
return
|
|
266
269
|
} catch (e) {
|
|
@@ -274,11 +277,11 @@ async function* streamSsePost(spec: SsePostSpec, parse: (body: AsyncIterable<Uin
|
|
|
274
277
|
continue
|
|
275
278
|
}
|
|
276
279
|
if (beforeFirstByte) {
|
|
277
|
-
// 重试耗尽 → B080001(BD-04:消费方 runTurn 以 error
|
|
278
|
-
throw providerUnreachable(`${(e as Error).name}: ${(e as Error).message}(尝试 ${attempt + 1}/${spec.retries + 1},${Date.now() - started}ms
|
|
280
|
+
// 重试耗尽 → B080001(BD-04:消费方 runTurn 以 error 收口);1.23-W1 细类 transport-exhausted
|
|
281
|
+
throw providerUnreachable(`${(e as Error).name}: ${(e as Error).message}(尝试 ${attempt + 1}/${spec.retries + 1},${Date.now() - started}ms)`, 'transport-exhausted')
|
|
279
282
|
}
|
|
280
|
-
// 流中途失败:已消费 chunk 不可重放 → error chunk
|
|
281
|
-
yield { finishReason: 'error', error: { code: 'B080001', message: `流中途失败:${(e as Error).message}
|
|
283
|
+
// 流中途失败:已消费 chunk 不可重放 → error chunk 收口;1.23-W1 细类 stream-midbreak
|
|
284
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: `流中途失败:${(e as Error).message}`, kind: 'stream-midbreak' } }
|
|
282
285
|
return
|
|
283
286
|
} finally {
|
|
284
287
|
clearTimeout(firstByteTimer)
|
|
@@ -317,6 +320,23 @@ const FINISH_MAP: Record<string, LlmChunk['finishReason']> = {
|
|
|
317
320
|
|
|
318
321
|
class Retryable extends Error {}
|
|
319
322
|
|
|
323
|
+
/**
|
|
324
|
+
* 1.23-W1(D-W1-5):context-limit-suspect 确定性关键词表——仅用于 4xx 错误体、只收高置信特征;
|
|
325
|
+
* 标注语义 = **疑似**(消费面文案须带「疑似」,禁估算禁 LLM 判断);未命中 = transport-4xx。
|
|
326
|
+
*/
|
|
327
|
+
const CONTEXT_LIMIT_PATTERNS: RegExp[] = [
|
|
328
|
+
/context length/i,
|
|
329
|
+
/maximum context/i,
|
|
330
|
+
/context[_ ]window/i,
|
|
331
|
+
/prompt is too long/i,
|
|
332
|
+
/too many tokens/i,
|
|
333
|
+
/input tokens? exceed/i,
|
|
334
|
+
/exceeds? the (maximum )?(context|token)/i,
|
|
335
|
+
/上下文(长度|超限)/,
|
|
336
|
+
/超出上下文/,
|
|
337
|
+
]
|
|
338
|
+
const contextLimitSuspect = (text: string): boolean => CONTEXT_LIMIT_PATTERNS.some((p) => p.test(text))
|
|
339
|
+
|
|
320
340
|
/**
|
|
321
341
|
* 1.11-S1(D-33 / 计量设计 §1.1):openai usage 归一——prompt_tokens 含 cached,inputTokens
|
|
322
342
|
* 必须做减法(prompt − prompt_tokens_details.cached_tokens);cache_creation 恒 0(无对应字段,
|
|
@@ -399,13 +419,21 @@ async function* parseSseStream(body: AsyncIterable<Uint8Array>, model: string, a
|
|
|
399
419
|
if (usage) yield { usage }
|
|
400
420
|
continue
|
|
401
421
|
}
|
|
422
|
+
// 1.23-W1(D-W1-4 / D-W1-1):finish 终态附带 error 细类——未映射 = unmapped-finish;
|
|
423
|
+
// content_filter(openai 族唯一映射 'aborted' 的信号)= refusal(与 anthropic stop_reason=refusal 对齐)
|
|
424
|
+
const finishError: LlmChunk['error'] | undefined =
|
|
425
|
+
finish === 'error'
|
|
426
|
+
? { code: 'B080001', message: `未映射的 finish_reason "${raw}"`, kind: 'unmapped-finish' }
|
|
427
|
+
: finish === 'aborted'
|
|
428
|
+
? { code: 'B080001', message: `provider content_filter (finish_reason="${raw}")`, kind: 'refusal' }
|
|
429
|
+
: undefined
|
|
402
430
|
const chunks: LlmChunk[] = []
|
|
403
431
|
if (toolCallDeltas.length === 0) {
|
|
404
432
|
chunks.push({
|
|
405
433
|
...(reasoningContent !== undefined ? { thinkingDelta: reasoningContent } : {}),
|
|
406
434
|
...(text !== undefined ? { delta: text } : {}),
|
|
407
435
|
...(finish !== undefined
|
|
408
|
-
? { finishReason: finish, ...(
|
|
436
|
+
? { finishReason: finish, ...(finishError ? { error: finishError } : {}) }
|
|
409
437
|
: {}),
|
|
410
438
|
})
|
|
411
439
|
} else {
|
|
@@ -416,7 +444,7 @@ async function* parseSseStream(body: AsyncIterable<Uint8Array>, model: string, a
|
|
|
416
444
|
...(i === 0 && text !== undefined ? { delta: text } : {}),
|
|
417
445
|
toolCallDelta: toolCallDeltas[i],
|
|
418
446
|
...(last && finish !== undefined
|
|
419
|
-
? { finishReason: finish, ...(
|
|
447
|
+
? { finishReason: finish, ...(finishError ? { error: finishError } : {}) }
|
|
420
448
|
: {}),
|
|
421
449
|
})
|
|
422
450
|
}
|
|
@@ -835,17 +863,22 @@ async function* parseAnthropicSseStream(body: AsyncIterable<Uint8Array>, model:
|
|
|
835
863
|
const usage = mergeAnthropicUsage(startUsage, (evt.usage ?? null) as Record<string, unknown> | null, model, adapterId)
|
|
836
864
|
if (raw != null) {
|
|
837
865
|
const finish = ANTHROPIC_STOP_REASON_MAP[String(raw)]
|
|
866
|
+
// 1.23-W1(D-W1-4 / D-W1-1):stop_reason 细类标注——refusal 附加 error chunk(kind 'refusal',
|
|
867
|
+
// 与 openai content_filter 对齐;finishReason 映射不动);未映射 = unmapped-finish
|
|
838
868
|
const chunk: LlmChunk = finish
|
|
839
|
-
?
|
|
840
|
-
|
|
869
|
+
? finish === 'aborted'
|
|
870
|
+
? { finishReason: finish, error: { code: 'B080001', message: `provider refusal (stop_reason="${raw}")`, kind: 'refusal' } }
|
|
871
|
+
: { finishReason: finish }
|
|
872
|
+
: { finishReason: 'error', error: { code: 'B080001', message: `未映射的 stop_reason "${raw}"`, kind: 'unmapped-finish' } }
|
|
841
873
|
yield usage ? { ...chunk, usage } : chunk
|
|
842
874
|
} else if (usage) {
|
|
843
875
|
// delta 未携 stop_reason 但携 usage(网关形态):usage 不随缺失终态信号丢失
|
|
844
876
|
yield { usage }
|
|
845
877
|
}
|
|
846
878
|
} else if (type === 'error') {
|
|
847
|
-
// 流内 error 事件(overloaded_error 等)→ error chunk 收口(后续 message_delta
|
|
848
|
-
|
|
879
|
+
// 流内 error 事件(overloaded_error 等)→ error chunk 收口(后续 message_delta 由守卫拦冲突);
|
|
880
|
+
// 1.23-W1 细类 provider-stream-error
|
|
881
|
+
yield { finishReason: 'error', error: { code: 'B080001', message: `provider 流错误:${evt.error?.message ?? JSON.stringify(evt).slice(0, 200)}`, kind: 'provider-stream-error' } }
|
|
849
882
|
}
|
|
850
883
|
}
|
|
851
884
|
}
|
|
@@ -40,6 +40,23 @@ export interface ToolCallDelta {
|
|
|
40
40
|
argumentsDelta?: string
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* 1.23-W1(D-W1-1 / 《W-0 失败分类学对照审计》§5):LLM 失败细类注册表——把 B080001 下压着的
|
|
45
|
+
* 多形态失败拆到可对应处置动作的粒度(审计 F-W0-2)。**加法演进**:可选键,缺失 = 1.22 形态
|
|
46
|
+
* 逐字节兼容。`transport-network` 从审计 8 类剔除(无独立产出点——网络失败只以 exhausted /
|
|
47
|
+
* midbreak 到达消费面);`unmapped-finish` / `provider-stream-error` 为实施中浮出的两形态增补。
|
|
48
|
+
*/
|
|
49
|
+
export type LlmErrorKind =
|
|
50
|
+
| 'transport-4xx'
|
|
51
|
+
| 'transport-exhausted'
|
|
52
|
+
| 'stream-midbreak'
|
|
53
|
+
| 'no-finish-reason'
|
|
54
|
+
| 'refusal'
|
|
55
|
+
| 'proto-invalid-args'
|
|
56
|
+
| 'context-limit-suspect'
|
|
57
|
+
| 'unmapped-finish'
|
|
58
|
+
| 'provider-stream-error'
|
|
59
|
+
|
|
43
60
|
/**
|
|
44
61
|
* 1.11-S1(D-33 / 计量设计 §3.1):token 四分量归一化用量——跨 provider 可加总的库内口径
|
|
45
62
|
* (§1.1:openai prompt_tokens 含 cached 必须减法归一;anthropic input_tokens 天然不含 cache)。
|
|
@@ -72,8 +89,9 @@ export interface LlmChunk {
|
|
|
72
89
|
toolCallDelta?: ToolCallDelta
|
|
73
90
|
finishReason?: 'stop' | 'length' | 'toolUse' | 'error' | 'aborted'
|
|
74
91
|
// —— 规格外加法扩展(登记:M8 收口文档 §4 / 计量设计 §3.1,1.11.0)——
|
|
75
|
-
/** finishReason='error' 时的结构化原因(消费方落 turnEnd meta
|
|
76
|
-
|
|
92
|
+
/** finishReason='error' 时的结构化原因(消费方落 turnEnd meta,不进模型消息流);
|
|
93
|
+
* 1.23-W1(D-W1-2):+kind 失败细类(LlmErrorKind 注册表——B080001 一码多形态的分型标注) */
|
|
94
|
+
error?: { code: string; message: string; kind?: LlmErrorKind }
|
|
77
95
|
/** 1.11-S1(D-33):每次请求最后一个 chunk 附带归一化用量;缺失 = provider 未回,禁补造 */
|
|
78
96
|
usage?: LlmUsage
|
|
79
97
|
// —— 1.21(R-TOKENHUB-1 / D-T1)thinking 捕获加法——anthropic SSE thinking 面捕获;
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"base64Signature":"MEQCIG5kpHlRBiuR0C1XTzOWawwa6B3jaeuUCD30RgQpZCPdAiBO9uojSbYcCjrLKnD668Am0dOc0yjRdG7lTZ92TpEwWQ==","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc1akNDQm15Z0F3SUJBZ0lVRkdNdTBsKytWSE1uK0lQbFNTVnNPSEkrbDRJd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1URTBNVEF3T1RFeVdoY05Nall4TVRFME1UQXhPVEV5V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVjcW03aXVPL1ljT21HcTdVYXpCZUtSa0JVQlpPbXZYREdLaXcKQ2J3TFJhMHdKS1lUQW1QQWRwMDRFR2s3ZXB1WDVBeGdIaG03b1pLSFRKK0ZWd0tBY0tPQ0JZc3dnZ1dITUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl5TGpFd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLREF5TjJOa1pUVTRaVFZoWlRZMll6WmlaV1kwWXpVNApPV0l5TUdZeVkyUXhaRFppTVRFMk5EZ3dGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNakl1TVRBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl5TGpFd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENndwpNamRqWkdVMU9HVTFZV1UyTm1NMlltVm1OR00xT0RsaU1qQm1NbU5rTVdRMllqRXhOalE0TUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENndwpNamRqWkdVMU9HVTFZV1UyTm1NMlltVm1OR00xT0RsaU1qQm1NbU5rTVdRMllqRXhOalE0TUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNakl1TVRBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpJeUxqRXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2d3TWpkalpHVTFPR1UxWVdVMk5tTTJZbVZtTkdNMU9EbGlNakJtTW1Oa01XUTJZakV4TmpRNE1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXpOVGd6T0RZMQpOVEl5TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eU1pNHhNSUdKQmdvckJnRUVBZFo1QWdRQ0JIc0VlUUIzQUhVQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2gyWWhTSndBQUJBTUFSakJFCkFpQmtYUkJoenJNKzlRdkpQeUQ1Qk1qNWZZNEhaQzFrdVZPOGM3SE1UejUyZHdJZ2ZqcHdUZC95YnAzTEhRaEoKd01wMUxnQXdyaFlwZFVvZDZjcUFpUDZYTWxrd0NnWUlLb1pJemowRUF3TURhQUF3WlFJd1ZsWUJyeE80d1hWNQozVERDN3F1WDJ4OTlHaFBQR3gwenQ0dUt4WVE4S2R5azhwNityL0VuSkxJSlVRc3F3THFUQWpFQWlvcGZGbWxuCmxGSXl2dnROeWV0SjRJcVBzRkpXeUZSa0xsMzAzTXRKamZNRVNwRmN0dVlrbmhmOVoyZFFVYlB0Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K","rekorBundle":{"SignedEntryTimestamp":"MEQCIEjmbV0RoSQts2UKhH/hILFK0yAWexpkjLbd/Tp/jGXHAiAdgKwePusa2Oju7+xAwaXZ+DBLjv7N/VWl6srH8VpMEQ==","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJkMjg3MzgxNDMxMmM1ZGQ2NTQ4MGRlNDc2ZmIyY2NlYWI3YjBlYzU5YmU5MmYzN2ZiN2E3ZDZmZDRhNjFiZjgyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRzVrcEhsUkJpdVIwQzFYVHpPV2F3d2E2QjNqYWV1VUNEMzBSZ1FwWkNQZEFpQk85dW9qU2JZY0NqckxLbkQ2NjhBbTBkT2MweWpSZEc3bFRaOTJUcEV3V1E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzFha05EUW0xNVowRjNTVUpCWjBsVlJrZE5kVEJzS3l0V1NFMXVLMGxRYkZOVFZuTlBTRWtyYkRSSmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUlRCTlZFRjNUMVJGZVZkb1kwNU5hbGw0VFZSRk1FMVVRWGhQVkVWNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZqY1cwM2FYVlBMMWxqVDIxSGNUZFZZWHBDWlV0U2EwSlZRbHBQYlhaWVJFZExhWGNLUTJKM1RGSmhNSGRLUzFsVVFXMVFRV1J3TURSRlIyczNaWEIxV0RWQmVHZElhRzAzYjFwTFNGUktLMFpXZDB0QlkwdFBRMEpaYzNkbloxZElUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha2w1VEdwRmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkVGNVRqSk9hMXBVVlRSYVZGWm9XbFJaTWxsNldtbGFWMWt3V1hwVk5BcFBWMGw1VFVkWmVWa3lVWGhhUkZwcFRWUkZNazVFWjNkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOYWtsMVRWUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrbDVUR3BGZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5kd3BOYW1ScVdrZFZNVTlIVlRGWlYxVXlUbTFOTWxsdFZtMU9SMDB4VDBSc2FVMXFRbTFOYlU1clRWZFJNbGxxUlhoT2FsRTBUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObmR3cE5hbVJxV2tkVk1VOUhWVEZaVjFVeVRtMU5NbGx0Vm0xT1IwMHhUMFJzYVUxcVFtMU5iVTVyVFZkUk1sbHFSWGhPYWxFMFRVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOYWtsMVRWUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BKZVV4cVJYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJkM1RXcGthbHBIVlRGUFIxVXhXVmRWTWs1dFRUSlpiVlp0VGtkTk1VOUViR2xOYWtKdFRXMU9hMDFYVVRKWmFrVjRUbXBSTkUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYcE9WR2Q2VDBSWk1RcE9WRWw1VERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZVUxcE5IaE5TVWRLUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJYzBWbFVVSXpRVWhWUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyZ3lXV2hUU25kQlFVSkJUVUZTYWtKRkNrRnBRbXRZVWtKb2VuSk5LemxSZGtwUWVVUTFRazFxTldaWk5FaGFRekZyZFZaUE9HTTNTRTFVZWpVeVpIZEpaMlpxY0hkVVpDOTVZbkF6VEVoUmFFb0tkMDF3TVV4blFYZHlhRmx3WkZWdlpEWmpjVUZwVURaWVRXeHJkME5uV1VsTGIxcEplbW93UlVGM1RVUmhRVUYzV2xGSmQxWnNXVUp5ZUU4MGQxaFdOUW96VkVSRE4zRjFXREo0T1RsSGFGQlFSM2d3ZW5RMGRVdDRXVkU0UzJSNWF6aHdOaXR5TDBWdVNreEpTbFZSYzNGM1RIRlVRV3BGUVdsdmNHWkdiV3h1Q214R1NYbDJkblJPZVdWMFNqUkpjVkJ6UmtwWGVVWlNhMHhzTXpBelRYUkthbVpOUlZOd1JtTjBkVmxyYm1obU9Wb3laRkZWWWxCMENpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19","integratedTime":1794650953,"logIndex":3875442005,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
|