@lqc123qwe/car-runtime 1.18.2 → 1.20.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.20.0.tgz +0 -0
- package/dist/car-runtime-1.20.0.tgz.sig.bundle +1 -0
- package/dist/cli.js +11 -0
- package/dist/dx/doctor.js +28 -0
- package/dist/load/config.d.ts +11 -0
- package/dist/load/config.js +25 -0
- package/dist/load/pool.js +3 -0
- package/dist/release-audit.jsonl +4 -4
- package/dist/runtime-core/chatStep.d.ts +15 -0
- package/dist/runtime-core/chatStep.js +16 -4
- package/dist/runtime-core/llm.d.ts +14 -0
- package/dist/runtime-core/llm.js +68 -2
- package/dist/sbom.json +8 -8
- package/package.json +1 -1
- package/src/cli.ts +10 -0
- package/src/dx/doctor.ts +27 -0
- package/src/load/config.ts +35 -0
- package/src/load/pool.ts +3 -0
- package/src/runtime-core/chatStep.ts +26 -3
- package/src/runtime-core/llm.ts +72 -2
- package/dist/car-runtime-1.18.2.tgz +0 -0
- package/dist/car-runtime-1.18.2.tgz.sig.bundle +0 -1
package/dist/SHA256SUMS
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4546ebcc06ccdb9f33754d2d98ee413b25937f61e6bd3fe06a6cff60fa7ae541 car-runtime-1.20.0.tgz
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"base64Signature":"MEYCIQCbYahnJCIA6I3JiR78DqF448xO747Dj5VSDZa+DjrhBwIhAOWOh2Ax0tWvzwsaV+OjU2P/tU5+zZ63vwIRgHQ+gSD4","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc1ekNDQm0yZ0F3SUJBZ0lVQm5MRi9MSmhtbDh6b2p3Q3NIM1Z3aGtzWGJnd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1UQTNNRGMwTXpBMFdoY05Nall4TVRBM01EYzFNekEwV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV2WkJzSDB0WlFzZkh0VzNhZUordVVKaVA2dWJKT0lVSlZnbisKRjFkaWNuK2xlQVo4Uk80UDFUVm5QemFMdlNVNkdaL3dZcGtZWjlPZ0RDOGt2bWk5SDZPQ0JZd3dnZ1dJTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl3TGpBd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLR1l6TUdVME5qaGxNRFEyTkRjNVl6QmxOMkUyWm1VdwpaVFE0WVdNeVpUVmhNbUUzTlRoaVpEa3dGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNakF1TURBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl3TGpBd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENobQpNekJsTkRZNFpUQTBOalEzT1dNd1pUZGhObVpsTUdVME9HRmpNbVUxWVRKaE56VTRZbVE1TUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENobQpNekJsTkRZNFpUQTBOalEzT1dNd1pUZGhObVpsTUdVME9HRmpNbVUxWVRKaE56VTRZbVE1TUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNakF1TURBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpJd0xqQXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2htTXpCbE5EWTRaVEEwTmpRM09XTXdaVGRoTm1abE1HVTBPR0ZqTW1VMVlUSmhOelU0WW1RNU1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXlNelV5TkRJeApPRFl4TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eU1DNHdNSUdLQmdvckJnRUVBZFo1QWdRQ0JId0VlZ0I0QUhZQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2h0UFlEeXdBQUJBTUFSekJGCkFpRUFrQlNnbEJvOThmMVZNMFMyUGhaUnlQWG0yZ1pFWUFSREZnY1JsenlObXprQ0lDUWc2NUgxL1diUjJHUEkKYmk0MlFDVGdhKzlsMDduYU1rTG8zTm5NbDN6eE1Bb0dDQ3FHU000OUJBTURBMmdBTUdVQ01RRGxjdWNsQjhvTwpzVE0reGJ0ems3Z0hEbXhOZ2gzbEEwaHk4NUFaWUdIZG5PSUI5N0JLMGtjcWFWMnhPSUg1SVRnQ01FQTNmU293CkppaUUxbXNVNUlRSGc5V2pDcTB5dkpoNmc1VW5LVDFwY1RFN3FNSCtIYVU0WE0wWHVYK3dHWWpjNGc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEUCIQDB6YqP2KWVy4Fo7LbSrDV7nIEb4z/smHLueDq0QzfuQQIgc0BoLANFB0BT/TS7PHukqcqBjRM0g3tV9AA+Kbq5Jvo=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0NTQ2ZWJjYzA2Y2NkYjlmMzM3NTRkMmQ5OGVlNDEzYjI1OTM3ZjYxZTZiZDNmZTA2YTZjZmY2MGZhN2FlNTQxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNiWWFobkpDSUE2STNKaVI3OERxRjQ0OHhPNzQ3RGo1VlNEWmErRGpyaEJ3SWhBT1dPaDJBeDB0V3Z6d3NhVitPalUyUC90VTUrelo2M3Z3SVJnSFErZ1NENCIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzFla05EUW0weVowRjNTVUpCWjBsVlFtNU1SaTlNU21odGJEaDZiMnAzUTNOSU0xWjNhR3R6V0dKbmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUVROTlJHTXdUWHBCTUZkb1kwNU5hbGw0VFZSQk0wMUVZekZOZWtFd1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVYyV2tKelNEQjBXbEZ6WmtoMFZ6TmhaVW9yZFZWS2FWQTJkV0pLVDBsVlNsWm5iaXNLUmpGa2FXTnVLMnhsUVZvNFVrODBVREZVVm01UWVtRk1kbE5WTmtkYUwzZFpjR3RaV2psUFowUkRPR3QyYldrNVNEWlBRMEpaZDNkbloxZEpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha2wzVEdwQmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUjFsNlRVZFZNRTVxYUd4TlJGRXlUa1JqTlZsNlFteE9Na1V5V20xVmR3cGFWRkUwV1ZkTmVWcFVWbWhOYlVVelRsUm9hVnBFYTNkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOYWtGMVRVUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrbDNUR3BCZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm9iUXBOZWtKc1RrUlpORnBVUVRCT2FsRXpUMWROZDFwVVpHaE9iVnBzVFVkVk1FOUhSbXBOYlZVeFdWUkthRTU2VlRSWmJWRTFUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVOb2JRcE5la0pzVGtSWk5GcFVRVEJPYWxFelQxZE5kMXBVWkdoT2JWcHNUVWRWTUU5SFJtcE5iVlV4V1ZSS2FFNTZWVFJaYlZFMVRVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOYWtGMVRVUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BKZDB4cVFYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJodFRYcENiRTVFV1RSYVZFRXdUbXBSTTA5WFRYZGFWR1JvVG0xYWJFMUhWVEJQUjBacVRXMVZNVmxVU21oT2VsVTBXVzFSTlUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYbE5lbFY1VGtSSmVBcFBSRmw0VERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZVUxRE5IZE5TVWRMUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJZDBWbFowSTBRVWhaUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyaDBVRmxFZVhkQlFVSkJUVUZTZWtKR0NrRnBSVUZyUWxObmJFSnZPVGhtTVZaTk1GTXlVR2hhVW5sUVdHMHlaMXBGV1VGU1JFWm5ZMUpzZW5sT2JYcHJRMGxEVVdjMk5VZ3hMMWRpVWpKSFVFa0tZbWswTWxGRFZHZGhLemxzTURkdVlVMXJURzh6VG01TmJETjZlRTFCYjBkRFEzRkhVMDAwT1VKQlRVUkJNbWRCVFVkVlEwMVJSR3hqZFdOc1FqaHZUd3B6VkUwcmVHSjBlbXMzWjBoRWJYaE9aMmd6YkVFd2FIazROVUZhV1VkSVpHNVBTVUk1TjBKTE1HdGpjV0ZXTW5oUFNVZzFTVlJuUTAxRlFUTm1VMjkzQ2twcGFVVXhiWE5WTlVsUlNHYzVWMnBEY1RCNWRrcG9ObWMxVlc1TFZERndZMVJGTjNGTlNDdElZVlUwV0Uwd1dIVllLM2RIV1dwak5HYzlQUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1794037384,"logIndex":3732640096,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
|
package/dist/cli.js
CHANGED
|
@@ -223,6 +223,17 @@ async function main() {
|
|
|
223
223
|
// 池/单端点两分支通用,须先于适配器创建)
|
|
224
224
|
if (rest.includes('--no-prompt-cache'))
|
|
225
225
|
process.env.CAR_LLM_PROMPT_CACHE = 'off';
|
|
226
|
+
// 1.20-K1(D-48 P2):滚动断点白名单——flag > env > config(env = 适配器每请求求值的
|
|
227
|
+
// 运行时事实源;'off' = 显式清空;池成员身份在 pool 装配处按 m.id 传入,不走本 env 的单端点语义)
|
|
228
|
+
const rollingFlag = flagValue(rest, '--prompt-cache-rolling');
|
|
229
|
+
if (rollingFlag !== undefined)
|
|
230
|
+
process.env.CAR_LLM_PROMPT_CACHE_ROLLING = rollingFlag;
|
|
231
|
+
else if (!process.env.CAR_LLM_PROMPT_CACHE_ROLLING && llm.promptCache?.rolling?.adapters?.length) {
|
|
232
|
+
process.env.CAR_LLM_PROMPT_CACHE_ROLLING = llm.promptCache.rolling.adapters.join(',');
|
|
233
|
+
}
|
|
234
|
+
if (llm.promptCache?.rolling?.breakpoints !== undefined) {
|
|
235
|
+
process.env.CAR_LLM_PROMPT_CACHE_ROLLING_BREAKPOINTS = String(llm.promptCache.rolling.breakpoints);
|
|
236
|
+
}
|
|
226
237
|
const credService = new CredentialService();
|
|
227
238
|
const core = new RuntimeCore(credService);
|
|
228
239
|
core.bindContext(ctx);
|
package/dist/dx/doctor.js
CHANGED
|
@@ -87,12 +87,40 @@ export function doctorModelReadiness(opts = {}) {
|
|
|
87
87
|
catch { /* 全落空 = not-found(登记口径,非错误) */ }
|
|
88
88
|
}
|
|
89
89
|
const configured = !!baseUrl && !!model;
|
|
90
|
+
// 1.20-K1(D-48 P2):提示缓存策略可视(断点 = 传输层注解;rolling = 白名单缺省关闭,
|
|
91
|
+
// 登记前置 = 该端点 rolling 形态探针 VERDICT=A——1.20 设计 §3.5 流程门控)
|
|
92
|
+
const cacheOff = env.CAR_LLM_PROMPT_CACHE === 'off';
|
|
93
|
+
const rollingEnv = env.CAR_LLM_PROMPT_CACHE_ROLLING;
|
|
94
|
+
const cfgRolling = cfg.error ? undefined : cfg.config.llm?.promptCache?.rolling;
|
|
95
|
+
const rollingList = rollingEnv !== undefined
|
|
96
|
+
? rollingEnv.split(',').map(s => s.trim()).filter(Boolean)
|
|
97
|
+
: (cfgRolling?.adapters ?? []);
|
|
98
|
+
const rollingShown = rollingEnv !== undefined
|
|
99
|
+
? rollingList
|
|
100
|
+
: rollingList.length ? rollingList : undefined;
|
|
101
|
+
// 白名单未匹配告警(fail-visible 不阻断——成员可能已删,但登记错误必须可见)
|
|
102
|
+
const knownIdentities = new Set();
|
|
103
|
+
if (!cfg.error && cfg.config.modelPool?.endpoints.length) {
|
|
104
|
+
cfg.config.modelPool.endpoints.forEach((ep, i) => knownIdentities.add(ep.id ?? `ep-${i + 1}`));
|
|
105
|
+
}
|
|
106
|
+
else if (adapterId === 'anthropic') {
|
|
107
|
+
knownIdentities.add('anthropic');
|
|
108
|
+
}
|
|
109
|
+
const unmatched = rollingList.filter(id => !knownIdentities.has(id));
|
|
110
|
+
const cacheParts = [
|
|
111
|
+
`promptCache=${cacheOff ? 'off' : 'on'}`,
|
|
112
|
+
rollingShown === undefined ? 'rolling=缺省关闭(白名单须探针 VERDICT=A 后登记——llm.promptCache.rolling.adapters / CAR_LLM_PROMPT_CACHE_ROLLING)'
|
|
113
|
+
: rollingShown.includes('off') ? 'rolling=off(显式清空)'
|
|
114
|
+
: `rolling=[${rollingShown.join(',')}]${cfgRolling?.breakpoints !== undefined ? ` breakpoints=${cfgRolling.breakpoints}` : ''}`,
|
|
115
|
+
unmatched.length ? `⚠️ 白名单含未匹配身份:${unmatched.join(',')}(无在册单端点 anthropic / 池成员 id 对应——登记错误或成员已删)` : '',
|
|
116
|
+
].filter(Boolean);
|
|
90
117
|
const parts = [
|
|
91
118
|
configured ? `baseUrl=${baseUrl} model=${model}${adapterId ? ` adapter=${adapterId}` : ''}` : 'baseUrl/model 未配置(car run 真路径需 llm.* 配置或 CAR_LLM_BASE_URL/CAR_LLM_MODEL——见 car doctor / 配置指南)',
|
|
92
119
|
credential === 'keychain' ? `凭据=keychain 命中(car-runtime/${provider},值不打印)`
|
|
93
120
|
: credential === 'env' ? '凭据=env 命中(显式开启 fallback,值不打印)'
|
|
94
121
|
: '凭据未就绪(keychain 未命中且 env fallback 未开或变量缺席——A080001 会引导至此)',
|
|
95
122
|
`envFallback=${envFallback}`,
|
|
123
|
+
...cacheParts,
|
|
96
124
|
];
|
|
97
125
|
return { configured, baseUrl: baseUrl ?? '', model: model ?? '', credential, envFallback, detail: parts.join(';') };
|
|
98
126
|
}
|
package/dist/load/config.d.ts
CHANGED
|
@@ -25,6 +25,17 @@ export interface LlmConfig {
|
|
|
25
25
|
compaction?: CompactionConfig;
|
|
26
26
|
/** 1.16-K3(D-K2-2):子 Agent 配置(缺省 = 继承父会话模型锚点 Q-A2 + 宿主护栏常量 Q-A3) */
|
|
27
27
|
subagent?: SubagentConfig;
|
|
28
|
+
/** 1.20-K1(D-48 P2):提示缓存滚动断点白名单(缺省关闭;运行时事实源 = CAR_LLM_PROMPT_CACHE_ROLLING,cli 按 flag > env > config 落 env) */
|
|
29
|
+
promptCache?: PromptCacheConfig;
|
|
30
|
+
}
|
|
31
|
+
/** 1.20-K1(D-48 P2):滚动断点白名单(1.20 设计 §3.4/§3.5——逐条确证,无通配) */
|
|
32
|
+
export interface PromptCacheConfig {
|
|
33
|
+
rolling?: {
|
|
34
|
+
/** 路由身份白名单(单端点 'anthropic' / 池成员 id);空表显式拒绝(禁静默 no-op);登记前置 = 该端点探针 VERDICT=A */
|
|
35
|
+
adapters?: string[];
|
|
36
|
+
/** 滚动断点数(1..3 钳制;缺省 1——多挂对同一前缀重复写 1.25x 缓存,1.20 设计 §3.3) */
|
|
37
|
+
breakpoints?: number;
|
|
38
|
+
};
|
|
28
39
|
}
|
|
29
40
|
/** 1.16-K3(D-K2-2):子 Agent 配置面——宿主编排消费(内核自身不装配编排;schema 只登记键位) */
|
|
30
41
|
export interface SubagentConfig {
|
package/dist/load/config.js
CHANGED
|
@@ -148,6 +148,17 @@ const rawSchema = z.object({
|
|
|
148
148
|
}).strict()
|
|
149
149
|
.refine(v => Object.keys(v).length > 0, { message: '至少登记一个键(model/maxConcurrent/maxCumulative;无子 Agent 配置需求请移除 llm.subagent 键,行为与 1.15.2 一致)' })
|
|
150
150
|
.optional(),
|
|
151
|
+
// 1.20-K1(D-48 P2):滚动断点白名单(路由身份精确匹配;adapters 空表显式拒——禁静默 no-op)
|
|
152
|
+
promptCache: z.object({
|
|
153
|
+
rolling: z.object({
|
|
154
|
+
adapters: z.array(nonEmptyTrimmed(), { invalid_type_error: '必须为字符串数组' })
|
|
155
|
+
.min(1, { message: '至少登记一个路由身份(空表 = 静默 no-op 显式拒绝;不需要滚动断点请移除 llm.promptCache 键)' })
|
|
156
|
+
.optional(),
|
|
157
|
+
breakpoints: positiveInt()
|
|
158
|
+
.refine(v => v <= 3, { message: '必须为 1..3(provider 断点硬限 4,system 占 1)' })
|
|
159
|
+
.optional(),
|
|
160
|
+
}).strict().optional(),
|
|
161
|
+
}).strict().optional(),
|
|
151
162
|
}).strict().optional(),
|
|
152
163
|
// 1.14-S4(K-POOL-1 / D-42):模型池登记(多端点 fallback + health + routing/decide 落链)
|
|
153
164
|
modelPool: modelPoolSchema.optional(),
|
|
@@ -221,6 +232,18 @@ function parseCarConfig(raw) {
|
|
|
221
232
|
sg.maxCumulative = d.llm.subagent.maxCumulative;
|
|
222
233
|
llm.subagent = sg;
|
|
223
234
|
}
|
|
235
|
+
if (d.llm.promptCache) {
|
|
236
|
+
const pc = {};
|
|
237
|
+
if (d.llm.promptCache.rolling) {
|
|
238
|
+
const r = {};
|
|
239
|
+
if (d.llm.promptCache.rolling.adapters !== undefined)
|
|
240
|
+
r.adapters = [...d.llm.promptCache.rolling.adapters];
|
|
241
|
+
if (d.llm.promptCache.rolling.breakpoints !== undefined)
|
|
242
|
+
r.breakpoints = d.llm.promptCache.rolling.breakpoints;
|
|
243
|
+
pc.rolling = r;
|
|
244
|
+
}
|
|
245
|
+
llm.promptCache = pc;
|
|
246
|
+
}
|
|
224
247
|
out.llm = llm;
|
|
225
248
|
}
|
|
226
249
|
if (d.mcp) {
|
|
@@ -372,6 +395,8 @@ export function mergeLlmConfig(env, cfg, flags = {}) {
|
|
|
372
395
|
...(adapterId ? { adapterId } : {}),
|
|
373
396
|
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
374
397
|
allowEnvFallback,
|
|
398
|
+
// 1.20-K1(D-48 P2):滚动断点声明层透传(cli 层按 flag > env > config 落运行时 env)
|
|
399
|
+
...(cfg.llm?.promptCache ? { promptCache: cfg.llm.promptCache } : {}),
|
|
375
400
|
};
|
|
376
401
|
}
|
|
377
402
|
/**
|
package/dist/load/pool.js
CHANGED
|
@@ -226,6 +226,9 @@ export function assembleModelPool(opts) {
|
|
|
226
226
|
? createAnthropicAdapter({
|
|
227
227
|
baseUrl: m.baseUrl,
|
|
228
228
|
credentials: opts.credentials,
|
|
229
|
+
// 1.20-K1(D-48 P2):滚动断点按成员路由身份求值——池成员适配器 id 恒 'anthropic'
|
|
230
|
+
//(装配未透传),禁按其求值(一端点解锁 = 全池解锁,1.20 设计 §3.1)
|
|
231
|
+
rollingIdentity: m.id,
|
|
229
232
|
...(m.keyEnv !== undefined ? { credentialEnvVars: [m.keyEnv] } : {}),
|
|
230
233
|
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
|
|
231
234
|
...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
|
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.20.0.tgz(430265 bytes,树哈希锚定)","ts":1794037383291}
|
|
2
|
+
{"stage":"sha256sums","detail":"SHA256SUMS 4546ebcc06ccdb9f…(裸字节复核通过)","ts":1794037383292}
|
|
3
|
+
{"stage":"sbom","detail":"55 个组件(运行时依赖 0 项)","ts":1794037383299}
|
|
4
|
+
{"stage":"cosign-keyless","detail":"signed car-runtime-1.20.0.tgz (keyless OIDC + Rekor)","ts":1794037385104}
|
|
@@ -43,6 +43,21 @@ export interface ChatStepOptions {
|
|
|
43
43
|
reasoning?: {
|
|
44
44
|
effort: 'off' | 'low' | 'medium' | 'high';
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* 1.19.0(D-S2-1 渲染提示面):流式增量回调——宿主实时上屏数据源(发送后不空等的
|
|
48
|
+
* 流式交互)。语义红线:
|
|
49
|
+
* - textDelta = StreamRedactor.push 的已遮蔽放行(跨 chunk 遮蔽驻留期 UI 略滞后——
|
|
50
|
+
* 粒度换正确性),流尾 flush 残段同样回调;全部 textDelta 之和 + flush 尾巴
|
|
51
|
+
* ≡ 落链 assistant 全文(收敛一致,屏幕无第二事实源);
|
|
52
|
+
* - toolName = 工具名首次可知时机(args 仍在流式聚合——半截 JSON 不解析,ADR-001
|
|
53
|
+
* 同纪律);同请求同名只回调一次;
|
|
54
|
+
* - 纯渲染提示:不落链、不持久、不影响 ModelStep 与 N1(事实本体仍以落盘行为准,
|
|
55
|
+
* assistant 事件到达前 UI 呈现的增量属临时态);缺省(undefined)= 零行为变化。
|
|
56
|
+
*/
|
|
57
|
+
onDelta?: (d: {
|
|
58
|
+
textDelta?: string;
|
|
59
|
+
toolName?: string;
|
|
60
|
+
}) => void;
|
|
46
61
|
}
|
|
47
62
|
/** ModelStep 加法扩展:脱敏计数留痕(S15 secretsRedacted 同名口径) */
|
|
48
63
|
export type ChatStepResult = ModelStep & {
|
|
@@ -46,15 +46,23 @@ export async function chatStep(opts) {
|
|
|
46
46
|
// 1.4-S4(D-19):chunk 间隙取消——此刻 assistant 尚未落链(流完才 append),无半截消息
|
|
47
47
|
if (opts.signal?.aborted)
|
|
48
48
|
throw new TurnAborted();
|
|
49
|
-
if (chunk.delta !== undefined)
|
|
50
|
-
|
|
49
|
+
if (chunk.delta !== undefined) {
|
|
50
|
+
const masked = redactor.push(chunk.delta);
|
|
51
|
+
text += masked;
|
|
52
|
+
if (masked && opts.onDelta)
|
|
53
|
+
opts.onDelta({ textDelta: masked });
|
|
54
|
+
}
|
|
51
55
|
if (chunk.toolCallDelta) {
|
|
52
56
|
const idx = chunk.toolCallDelta.index ?? 0;
|
|
53
57
|
const cur = calls.get(idx) ?? { argsBuf: '' };
|
|
54
58
|
if (chunk.toolCallDelta.id)
|
|
55
59
|
cur.id = chunk.toolCallDelta.id;
|
|
56
|
-
if (chunk.toolCallDelta.name)
|
|
60
|
+
if (chunk.toolCallDelta.name) {
|
|
57
61
|
cur.name = chunk.toolCallDelta.name;
|
|
62
|
+
// 1.19.0:工具名首次可知即回调(args 半截 JSON 不解析——ADR-001);同名一次
|
|
63
|
+
if (opts.onDelta)
|
|
64
|
+
opts.onDelta({ toolName: cur.name });
|
|
65
|
+
}
|
|
58
66
|
if (chunk.toolCallDelta.argumentsDelta)
|
|
59
67
|
cur.argsBuf += chunk.toolCallDelta.argumentsDelta;
|
|
60
68
|
calls.set(idx, cur);
|
|
@@ -74,7 +82,11 @@ export async function chatStep(opts) {
|
|
|
74
82
|
// 适配器层已保证首 finishReason(AL-05);防御性兜底显式化
|
|
75
83
|
throw new Error('CAR-E-LLM-FINISH: 流结束无 finishReason(适配器契约违规)');
|
|
76
84
|
}
|
|
77
|
-
text
|
|
85
|
+
// 1.19.0:flush 残段先回调后并入 text——textDelta 之和 + 尾巴 ≡ 落链 assistant 全文
|
|
86
|
+
const tail = redactor.flush();
|
|
87
|
+
if (tail && opts.onDelta)
|
|
88
|
+
opts.onDelta({ textDelta: tail });
|
|
89
|
+
text += tail;
|
|
78
90
|
// 1.11-S2(D-33 / 计量设计 §3.3):请求级 usage 事实落链(拿到 usage 的最后一个 chunk 即请求
|
|
79
91
|
// 收尾;撞限/异常 turn 照常落——截断前已完成请求的 usage 事实不吞)。缺失 = provider 未回,
|
|
80
92
|
// 禁补造;actor='runtime'、不进模型消息流(deriveMessages 不投影 usage,§2.5 红线)
|
|
@@ -76,7 +76,21 @@ export interface AnthropicAdapterOptions {
|
|
|
76
76
|
defaultMaxTokens?: number;
|
|
77
77
|
/** 1.14-S4(K-POOL-1):成员级凭据 env 变量名引用(modelPool keyEnv——resolve 精确候选;keychain 链保持在前) */
|
|
78
78
|
credentialEnvVars?: string[];
|
|
79
|
+
/**
|
|
80
|
+
* 1.20-K1(D-48 P2):滚动断点求值身份。缺省 = id(单端点 'anthropic');
|
|
81
|
+
* **池成员必须显式传 m.id**——池成员适配器 id 恒 'anthropic'(装配未透传成员标识),
|
|
82
|
+
* 按其求值 = 白名单一端点解锁全池(1.20 设计 §3.1 专项锁,S66/AC-9)。
|
|
83
|
+
*/
|
|
84
|
+
rollingIdentity?: string;
|
|
79
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* 1.20-K1(D-48 P2):滚动断点白名单求值(每请求求值,与 promptCacheOff 同址同时序)。
|
|
88
|
+
* 键 = 路由身份:单端点 = adapter id('anthropic')/ 池成员 = 显式 rollingIdentity(m.id)——
|
|
89
|
+
* **禁按 adapter.id 给池成员求值**(池成员适配器 id 恒 'anthropic'——pool 装配未透传成员标识,
|
|
90
|
+
* 一端点解锁 = 全池解锁,1.20 设计 §3.1)。off 连坐最高优先;清单值 'off' = 显式清空;
|
|
91
|
+
* 无 '*' 通配(通配废掉逐条确证);大小写敏感(归一化会掩盖登记错误)。
|
|
92
|
+
*/
|
|
93
|
+
export declare function rollingCacheAllowed(identity: string): boolean;
|
|
80
94
|
export declare function createAnthropicAdapter(opts: AnthropicAdapterOptions): LlmAdapter & {
|
|
81
95
|
id: string;
|
|
82
96
|
};
|
package/dist/runtime-core/llm.js
CHANGED
|
@@ -535,11 +535,68 @@ function mergeAnthropicUsage(start, delta, model, adapterId) {
|
|
|
535
535
|
* 投影/快照比对,N1 零影响;生效有 provider 侧最小长度门槛(~1024 tokens)——短 system
|
|
536
536
|
* 静默不生效应以 usage.cacheCreationTokens 观测,禁以「配了就该生效」推断。关断 =
|
|
537
537
|
* CAR_LLM_PROMPT_CACHE=off(cli flag --no-prompt-cache 置本 env;A/B 与故障回退通道)。
|
|
538
|
+
* 1.20-K1(D-48 P2):滚动断点——白名单命中时在尾部消息挂第二处断点(缺省 1 个,只块化命中
|
|
539
|
+
* 消息),键 = 路由身份(rollingIdentity,池成员 = m.id);CAR_LLM_PROMPT_CACHE_ROLLING
|
|
540
|
+
* 逗号清单('off' = 显式清空,无通配)+ CAR_LLM_PROMPT_CACHE_ROLLING_BREAKPOINTS(1..3 钳制)。
|
|
538
541
|
*/
|
|
539
542
|
function promptCacheOff() {
|
|
540
543
|
return process.env.CAR_LLM_PROMPT_CACHE === 'off';
|
|
541
544
|
}
|
|
542
|
-
|
|
545
|
+
/**
|
|
546
|
+
* 1.20-K1(D-48 P2):滚动断点白名单求值(每请求求值,与 promptCacheOff 同址同时序)。
|
|
547
|
+
* 键 = 路由身份:单端点 = adapter id('anthropic')/ 池成员 = 显式 rollingIdentity(m.id)——
|
|
548
|
+
* **禁按 adapter.id 给池成员求值**(池成员适配器 id 恒 'anthropic'——pool 装配未透传成员标识,
|
|
549
|
+
* 一端点解锁 = 全池解锁,1.20 设计 §3.1)。off 连坐最高优先;清单值 'off' = 显式清空;
|
|
550
|
+
* 无 '*' 通配(通配废掉逐条确证);大小写敏感(归一化会掩盖登记错误)。
|
|
551
|
+
*/
|
|
552
|
+
export function rollingCacheAllowed(identity) {
|
|
553
|
+
if (promptCacheOff())
|
|
554
|
+
return false;
|
|
555
|
+
const raw = process.env.CAR_LLM_PROMPT_CACHE_ROLLING;
|
|
556
|
+
if (!raw)
|
|
557
|
+
return false;
|
|
558
|
+
const list = raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
559
|
+
if (!list.length || list.includes('off'))
|
|
560
|
+
return false;
|
|
561
|
+
return list.includes(identity);
|
|
562
|
+
}
|
|
563
|
+
// 1.20-K1:滚动断点数——provider 硬限 4,system 断点(P1)恒占 1;缺省 1(多挂 = 对同一前缀
|
|
564
|
+
// 重复写 1.25x 缓存,1.20 设计 §3.3)。env 非法/缺席回落缺省,超限钳制(AC-8)
|
|
565
|
+
const ROLLING_BREAKPOINTS_DEFAULT = 1;
|
|
566
|
+
const ROLLING_BREAKPOINTS_MAX = 3;
|
|
567
|
+
function rollingBreakpoints() {
|
|
568
|
+
const raw = Number(process.env.CAR_LLM_PROMPT_CACHE_ROLLING_BREAKPOINTS);
|
|
569
|
+
if (!Number.isFinite(raw) || raw < 1)
|
|
570
|
+
return ROLLING_BREAKPOINTS_DEFAULT;
|
|
571
|
+
return Math.min(Math.trunc(raw), ROLLING_BREAKPOINTS_MAX);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* 1.20-K1(D-48 P2):滚动断点放置——从尾部往前命中即挂,只块化被挂消息,其余消息维持
|
|
575
|
+
* 字符串(1.20 设计 §3.2:全量块化零收益高风险)。断点挂**末块**(tool_result / tool_use
|
|
576
|
+
* 多块形态不拆不重组,语义不变);空串 / 空数组 / 非对象末块无可挂载体 → 跳过前移(不换位不补造)。
|
|
577
|
+
* 纯传输层注解:只改本请求出站体,不进 SessionLog / 投影(N1 零影响,S59 同红线)。
|
|
578
|
+
*/
|
|
579
|
+
function attachRollingBreakpoints(turns, count) {
|
|
580
|
+
let attached = 0;
|
|
581
|
+
for (let i = turns.length - 1; i >= 0 && attached < count; i--) {
|
|
582
|
+
const t = turns[i];
|
|
583
|
+
if (typeof t.content === 'string') {
|
|
584
|
+
if (!t.content.length)
|
|
585
|
+
continue;
|
|
586
|
+
t.content = [{ type: 'text', text: t.content, cache_control: { type: 'ephemeral' } }];
|
|
587
|
+
attached++;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
if (Array.isArray(t.content) && t.content.length) {
|
|
591
|
+
const last = t.content[t.content.length - 1];
|
|
592
|
+
if (last && typeof last === 'object' && !Array.isArray(last)) {
|
|
593
|
+
last.cache_control = { type: 'ephemeral' };
|
|
594
|
+
attached++;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function toAnthropic(req, apiKey, cache) {
|
|
543
600
|
const system = [];
|
|
544
601
|
const turns = [];
|
|
545
602
|
let pendingToolResults = [];
|
|
@@ -574,6 +631,10 @@ function toAnthropic(req, apiKey) {
|
|
|
574
631
|
}
|
|
575
632
|
}
|
|
576
633
|
flushToolResults();
|
|
634
|
+
// 1.20-K1(D-48 P2):滚动断点在白名单命中时挂尾部消息(缺省 1 个)——system 断点(P1)
|
|
635
|
+
// 与消息断点独立,off 时两者俱灭(rollingCacheAllowed 内已连坐)
|
|
636
|
+
if (cache.rolling)
|
|
637
|
+
attachRollingBreakpoints(turns, cache.rollingBreakpoints);
|
|
577
638
|
// 1.14-S1(K-MP-1 / D-39):思考参数——effort → thinking.budget_tokens(ANTHROPIC_THINKING_BUDGET 表);
|
|
578
639
|
// off/缺席不下发;budget ≥ max_tokens 违反 Messages API 约束(max_tokens 必须大于 budget_tokens)→
|
|
579
640
|
// 显式报错提示调高(fail-visible,禁静默改写 max_tokens)
|
|
@@ -687,6 +748,8 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
|
|
|
687
748
|
}
|
|
688
749
|
export function createAnthropicAdapter(opts) {
|
|
689
750
|
const id = opts.id ?? 'anthropic';
|
|
751
|
+
// 1.20-K1(D-48 P2):滚动断点求值身份(每请求经 rollingCacheAllowed 对 env 求值,非构造期定值)
|
|
752
|
+
const rollingIdentity = opts.rollingIdentity ?? id;
|
|
690
753
|
const baseUrl = opts.baseUrl.replace(/\/+$/, '');
|
|
691
754
|
assertTlsOrLoopback(baseUrl);
|
|
692
755
|
const url = `${baseUrl}/v1/messages`;
|
|
@@ -714,7 +777,10 @@ export function createAnthropicAdapter(opts) {
|
|
|
714
777
|
const ref = opts.credentials.resolve(opts.provider ?? id, credResolveOpts);
|
|
715
778
|
apiKey = opts.credentials.reveal(ref, {});
|
|
716
779
|
}
|
|
717
|
-
return toAnthropic(req, apiKey
|
|
780
|
+
return toAnthropic(req, apiKey, {
|
|
781
|
+
rolling: rollingCacheAllowed(rollingIdentity),
|
|
782
|
+
rollingBreakpoints: rollingBreakpoints(),
|
|
783
|
+
});
|
|
718
784
|
},
|
|
719
785
|
fetchImpl,
|
|
720
786
|
signal: req.signal,
|
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.20.0",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"type": "file",
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
{
|
|
12
12
|
"type": "file",
|
|
13
13
|
"path": "src/cli.ts",
|
|
14
|
-
"sha256": "
|
|
14
|
+
"sha256": "914df86dd59907d6aa24b3d339ee1e4c6bf366a63ac553199ad172760aa29539"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"type": "file",
|
|
18
18
|
"path": "src/dx/doctor.ts",
|
|
19
|
-
"sha256": "
|
|
19
|
+
"sha256": "32ea488aea0dcff1709f0244eff5d417b964e6f2eef5abab5a21da883da2e059"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"type": "file",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
{
|
|
57
57
|
"type": "file",
|
|
58
58
|
"path": "src/load/config.ts",
|
|
59
|
-
"sha256": "
|
|
59
|
+
"sha256": "39f0185b46ed580ee3b9cac6a028fe6a3eddbf6257087bc0406b11f3e1b94d86"
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"type": "file",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
{
|
|
67
67
|
"type": "file",
|
|
68
68
|
"path": "src/load/pool.ts",
|
|
69
|
-
"sha256": "
|
|
69
|
+
"sha256": "08595f3786eec9318644f2b183dad6f5d6db3cf90975a92bd4887a1d6591de62"
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"type": "file",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
{
|
|
162
162
|
"type": "file",
|
|
163
163
|
"path": "src/runtime-core/chatStep.ts",
|
|
164
|
-
"sha256": "
|
|
164
|
+
"sha256": "da87f5e0231a9d0dc8455bac6057ce7f5d45eeffa7534b9ae44be7c0a42e64f4"
|
|
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": "
|
|
179
|
+
"sha256": "ea7d8f9c9e8b79d5f073df06e7e8cc8af1819500222108061ff07b60aa29695a"
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"type": "file",
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
{
|
|
272
272
|
"type": "file",
|
|
273
273
|
"path": "package.json",
|
|
274
|
-
"sha256": "
|
|
274
|
+
"sha256": "29ba6c8cb059bcb1429d80a909ca79573c644ae1429f870a6065903c898d2816"
|
|
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.20.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/cli.ts
CHANGED
|
@@ -211,6 +211,16 @@ async function main(): Promise<number> {
|
|
|
211
211
|
// 1.17-K4(D-48 P1 / V1-A):提示缓存断点关断 flag——flag > env(置 env 后适配器内部决策;
|
|
212
212
|
// 池/单端点两分支通用,须先于适配器创建)
|
|
213
213
|
if (rest.includes('--no-prompt-cache')) process.env.CAR_LLM_PROMPT_CACHE = 'off'
|
|
214
|
+
// 1.20-K1(D-48 P2):滚动断点白名单——flag > env > config(env = 适配器每请求求值的
|
|
215
|
+
// 运行时事实源;'off' = 显式清空;池成员身份在 pool 装配处按 m.id 传入,不走本 env 的单端点语义)
|
|
216
|
+
const rollingFlag = flagValue(rest, '--prompt-cache-rolling')
|
|
217
|
+
if (rollingFlag !== undefined) process.env.CAR_LLM_PROMPT_CACHE_ROLLING = rollingFlag
|
|
218
|
+
else if (!process.env.CAR_LLM_PROMPT_CACHE_ROLLING && llm.promptCache?.rolling?.adapters?.length) {
|
|
219
|
+
process.env.CAR_LLM_PROMPT_CACHE_ROLLING = llm.promptCache.rolling.adapters.join(',')
|
|
220
|
+
}
|
|
221
|
+
if (llm.promptCache?.rolling?.breakpoints !== undefined) {
|
|
222
|
+
process.env.CAR_LLM_PROMPT_CACHE_ROLLING_BREAKPOINTS = String(llm.promptCache.rolling.breakpoints)
|
|
223
|
+
}
|
|
214
224
|
const credService = new CredentialService()
|
|
215
225
|
const core = new RuntimeCore(credService)
|
|
216
226
|
core.bindContext(ctx)
|
package/src/dx/doctor.ts
CHANGED
|
@@ -123,12 +123,39 @@ export function doctorModelReadiness(opts: { env?: NodeJS.ProcessEnv; cwd?: stri
|
|
|
123
123
|
} catch { /* 全落空 = not-found(登记口径,非错误) */ }
|
|
124
124
|
}
|
|
125
125
|
const configured = !!baseUrl && !!model
|
|
126
|
+
// 1.20-K1(D-48 P2):提示缓存策略可视(断点 = 传输层注解;rolling = 白名单缺省关闭,
|
|
127
|
+
// 登记前置 = 该端点 rolling 形态探针 VERDICT=A——1.20 设计 §3.5 流程门控)
|
|
128
|
+
const cacheOff = env.CAR_LLM_PROMPT_CACHE === 'off'
|
|
129
|
+
const rollingEnv = env.CAR_LLM_PROMPT_CACHE_ROLLING
|
|
130
|
+
const cfgRolling = cfg.error ? undefined : cfg.config.llm?.promptCache?.rolling
|
|
131
|
+
const rollingList = rollingEnv !== undefined
|
|
132
|
+
? rollingEnv.split(',').map(s => s.trim()).filter(Boolean)
|
|
133
|
+
: (cfgRolling?.adapters ?? [])
|
|
134
|
+
const rollingShown = rollingEnv !== undefined
|
|
135
|
+
? rollingList
|
|
136
|
+
: rollingList.length ? rollingList : undefined
|
|
137
|
+
// 白名单未匹配告警(fail-visible 不阻断——成员可能已删,但登记错误必须可见)
|
|
138
|
+
const knownIdentities = new Set<string>()
|
|
139
|
+
if (!cfg.error && cfg.config.modelPool?.endpoints.length) {
|
|
140
|
+
cfg.config.modelPool.endpoints.forEach((ep, i) => knownIdentities.add(ep.id ?? `ep-${i + 1}`))
|
|
141
|
+
} else if (adapterId === 'anthropic') {
|
|
142
|
+
knownIdentities.add('anthropic')
|
|
143
|
+
}
|
|
144
|
+
const unmatched = rollingList.filter(id => !knownIdentities.has(id))
|
|
145
|
+
const cacheParts = [
|
|
146
|
+
`promptCache=${cacheOff ? 'off' : 'on'}`,
|
|
147
|
+
rollingShown === undefined ? 'rolling=缺省关闭(白名单须探针 VERDICT=A 后登记——llm.promptCache.rolling.adapters / CAR_LLM_PROMPT_CACHE_ROLLING)'
|
|
148
|
+
: rollingShown.includes('off') ? 'rolling=off(显式清空)'
|
|
149
|
+
: `rolling=[${rollingShown.join(',')}]${cfgRolling?.breakpoints !== undefined ? ` breakpoints=${cfgRolling.breakpoints}` : ''}`,
|
|
150
|
+
unmatched.length ? `⚠️ 白名单含未匹配身份:${unmatched.join(',')}(无在册单端点 anthropic / 池成员 id 对应——登记错误或成员已删)` : '',
|
|
151
|
+
].filter(Boolean)
|
|
126
152
|
const parts = [
|
|
127
153
|
configured ? `baseUrl=${baseUrl} model=${model}${adapterId ? ` adapter=${adapterId}` : ''}` : 'baseUrl/model 未配置(car run 真路径需 llm.* 配置或 CAR_LLM_BASE_URL/CAR_LLM_MODEL——见 car doctor / 配置指南)',
|
|
128
154
|
credential === 'keychain' ? `凭据=keychain 命中(car-runtime/${provider},值不打印)`
|
|
129
155
|
: credential === 'env' ? '凭据=env 命中(显式开启 fallback,值不打印)'
|
|
130
156
|
: '凭据未就绪(keychain 未命中且 env fallback 未开或变量缺席——A080001 会引导至此)',
|
|
131
157
|
`envFallback=${envFallback}`,
|
|
158
|
+
...cacheParts,
|
|
132
159
|
]
|
|
133
160
|
return { configured, baseUrl: baseUrl ?? '', model: model ?? '', credential, envFallback, detail: parts.join(';') }
|
|
134
161
|
}
|
package/src/load/config.ts
CHANGED
|
@@ -56,6 +56,18 @@ export interface LlmConfig {
|
|
|
56
56
|
compaction?: CompactionConfig
|
|
57
57
|
/** 1.16-K3(D-K2-2):子 Agent 配置(缺省 = 继承父会话模型锚点 Q-A2 + 宿主护栏常量 Q-A3) */
|
|
58
58
|
subagent?: SubagentConfig
|
|
59
|
+
/** 1.20-K1(D-48 P2):提示缓存滚动断点白名单(缺省关闭;运行时事实源 = CAR_LLM_PROMPT_CACHE_ROLLING,cli 按 flag > env > config 落 env) */
|
|
60
|
+
promptCache?: PromptCacheConfig
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 1.20-K1(D-48 P2):滚动断点白名单(1.20 设计 §3.4/§3.5——逐条确证,无通配) */
|
|
64
|
+
export interface PromptCacheConfig {
|
|
65
|
+
rolling?: {
|
|
66
|
+
/** 路由身份白名单(单端点 'anthropic' / 池成员 id);空表显式拒绝(禁静默 no-op);登记前置 = 该端点探针 VERDICT=A */
|
|
67
|
+
adapters?: string[]
|
|
68
|
+
/** 滚动断点数(1..3 钳制;缺省 1——多挂对同一前缀重复写 1.25x 缓存,1.20 设计 §3.3) */
|
|
69
|
+
breakpoints?: number
|
|
70
|
+
}
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
/** 1.16-K3(D-K2-2):子 Agent 配置面——宿主编排消费(内核自身不装配编排;schema 只登记键位) */
|
|
@@ -264,6 +276,17 @@ const rawSchema = z.object({
|
|
|
264
276
|
}).strict()
|
|
265
277
|
.refine(v => Object.keys(v).length > 0, { message: '至少登记一个键(model/maxConcurrent/maxCumulative;无子 Agent 配置需求请移除 llm.subagent 键,行为与 1.15.2 一致)' })
|
|
266
278
|
.optional(),
|
|
279
|
+
// 1.20-K1(D-48 P2):滚动断点白名单(路由身份精确匹配;adapters 空表显式拒——禁静默 no-op)
|
|
280
|
+
promptCache: z.object({
|
|
281
|
+
rolling: z.object({
|
|
282
|
+
adapters: z.array(nonEmptyTrimmed(), { invalid_type_error: '必须为字符串数组' })
|
|
283
|
+
.min(1, { message: '至少登记一个路由身份(空表 = 静默 no-op 显式拒绝;不需要滚动断点请移除 llm.promptCache 键)' })
|
|
284
|
+
.optional(),
|
|
285
|
+
breakpoints: positiveInt()
|
|
286
|
+
.refine(v => v <= 3, { message: '必须为 1..3(provider 断点硬限 4,system 占 1)' })
|
|
287
|
+
.optional(),
|
|
288
|
+
}).strict().optional(),
|
|
289
|
+
}).strict().optional(),
|
|
267
290
|
}).strict().optional(),
|
|
268
291
|
// 1.14-S4(K-POOL-1 / D-42):模型池登记(多端点 fallback + health + routing/decide 落链)
|
|
269
292
|
modelPool: modelPoolSchema.optional(),
|
|
@@ -324,6 +347,16 @@ function parseCarConfig(raw: unknown): { config: CarConfig; error?: string } {
|
|
|
324
347
|
if (d.llm.subagent.maxCumulative !== undefined) sg.maxCumulative = d.llm.subagent.maxCumulative
|
|
325
348
|
llm.subagent = sg
|
|
326
349
|
}
|
|
350
|
+
if (d.llm.promptCache) {
|
|
351
|
+
const pc: PromptCacheConfig = {}
|
|
352
|
+
if (d.llm.promptCache.rolling) {
|
|
353
|
+
const r: NonNullable<PromptCacheConfig['rolling']> = {}
|
|
354
|
+
if (d.llm.promptCache.rolling.adapters !== undefined) r.adapters = [...d.llm.promptCache.rolling.adapters]
|
|
355
|
+
if (d.llm.promptCache.rolling.breakpoints !== undefined) r.breakpoints = d.llm.promptCache.rolling.breakpoints
|
|
356
|
+
pc.rolling = r
|
|
357
|
+
}
|
|
358
|
+
llm.promptCache = pc
|
|
359
|
+
}
|
|
327
360
|
out.llm = llm
|
|
328
361
|
}
|
|
329
362
|
if (d.mcp) {
|
|
@@ -469,6 +502,8 @@ export function mergeLlmConfig(env: NodeJS.ProcessEnv, cfg: CarConfig, flags: {
|
|
|
469
502
|
...(adapterId ? { adapterId } : {}),
|
|
470
503
|
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
471
504
|
allowEnvFallback,
|
|
505
|
+
// 1.20-K1(D-48 P2):滚动断点声明层透传(cli 层按 flag > env > config 落运行时 env)
|
|
506
|
+
...(cfg.llm?.promptCache ? { promptCache: cfg.llm.promptCache } : {}),
|
|
472
507
|
}
|
|
473
508
|
}
|
|
474
509
|
|
package/src/load/pool.ts
CHANGED
|
@@ -304,6 +304,9 @@ export function assembleModelPool(opts: ModelPoolAssemblyOptions): {
|
|
|
304
304
|
? createAnthropicAdapter({
|
|
305
305
|
baseUrl: m.baseUrl,
|
|
306
306
|
credentials: opts.credentials,
|
|
307
|
+
// 1.20-K1(D-48 P2):滚动断点按成员路由身份求值——池成员适配器 id 恒 'anthropic'
|
|
308
|
+
//(装配未透传),禁按其求值(一端点解锁 = 全池解锁,1.20 设计 §3.1)
|
|
309
|
+
rollingIdentity: m.id,
|
|
307
310
|
...(m.keyEnv !== undefined ? { credentialEnvVars: [m.keyEnv] } : {}),
|
|
308
311
|
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
|
|
309
312
|
...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
|
|
@@ -43,6 +43,18 @@ export interface ChatStepOptions {
|
|
|
43
43
|
* 模型元数据思考档位判断在宿主侧(models:meta),内核防线 = 未知档位 fail-visible。
|
|
44
44
|
*/
|
|
45
45
|
reasoning?: { effort: 'off' | 'low' | 'medium' | 'high' }
|
|
46
|
+
/**
|
|
47
|
+
* 1.19.0(D-S2-1 渲染提示面):流式增量回调——宿主实时上屏数据源(发送后不空等的
|
|
48
|
+
* 流式交互)。语义红线:
|
|
49
|
+
* - textDelta = StreamRedactor.push 的已遮蔽放行(跨 chunk 遮蔽驻留期 UI 略滞后——
|
|
50
|
+
* 粒度换正确性),流尾 flush 残段同样回调;全部 textDelta 之和 + flush 尾巴
|
|
51
|
+
* ≡ 落链 assistant 全文(收敛一致,屏幕无第二事实源);
|
|
52
|
+
* - toolName = 工具名首次可知时机(args 仍在流式聚合——半截 JSON 不解析,ADR-001
|
|
53
|
+
* 同纪律);同请求同名只回调一次;
|
|
54
|
+
* - 纯渲染提示:不落链、不持久、不影响 ModelStep 与 N1(事实本体仍以落盘行为准,
|
|
55
|
+
* assistant 事件到达前 UI 呈现的增量属临时态);缺省(undefined)= 零行为变化。
|
|
56
|
+
*/
|
|
57
|
+
onDelta?: (d: { textDelta?: string; toolName?: string }) => void
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
/** ModelStep 加法扩展:脱敏计数留痕(S15 secretsRedacted 同名口径) */
|
|
@@ -95,12 +107,20 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
95
107
|
for await (const chunk of stream) {
|
|
96
108
|
// 1.4-S4(D-19):chunk 间隙取消——此刻 assistant 尚未落链(流完才 append),无半截消息
|
|
97
109
|
if (opts.signal?.aborted) throw new TurnAborted()
|
|
98
|
-
if (chunk.delta !== undefined)
|
|
110
|
+
if (chunk.delta !== undefined) {
|
|
111
|
+
const masked = redactor.push(chunk.delta)
|
|
112
|
+
text += masked
|
|
113
|
+
if (masked && opts.onDelta) opts.onDelta({ textDelta: masked })
|
|
114
|
+
}
|
|
99
115
|
if (chunk.toolCallDelta) {
|
|
100
116
|
const idx = chunk.toolCallDelta.index ?? 0
|
|
101
117
|
const cur = calls.get(idx) ?? { argsBuf: '' }
|
|
102
118
|
if (chunk.toolCallDelta.id) cur.id = chunk.toolCallDelta.id
|
|
103
|
-
if (chunk.toolCallDelta.name)
|
|
119
|
+
if (chunk.toolCallDelta.name) {
|
|
120
|
+
cur.name = chunk.toolCallDelta.name
|
|
121
|
+
// 1.19.0:工具名首次可知即回调(args 半截 JSON 不解析——ADR-001);同名一次
|
|
122
|
+
if (opts.onDelta) opts.onDelta({ toolName: cur.name })
|
|
123
|
+
}
|
|
104
124
|
if (chunk.toolCallDelta.argumentsDelta) cur.argsBuf += chunk.toolCallDelta.argumentsDelta
|
|
105
125
|
calls.set(idx, cur)
|
|
106
126
|
}
|
|
@@ -115,7 +135,10 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
115
135
|
// 适配器层已保证首 finishReason(AL-05);防御性兜底显式化
|
|
116
136
|
throw new Error('CAR-E-LLM-FINISH: 流结束无 finishReason(适配器契约违规)')
|
|
117
137
|
}
|
|
118
|
-
text
|
|
138
|
+
// 1.19.0:flush 残段先回调后并入 text——textDelta 之和 + 尾巴 ≡ 落链 assistant 全文
|
|
139
|
+
const tail = redactor.flush()
|
|
140
|
+
if (tail && opts.onDelta) opts.onDelta({ textDelta: tail })
|
|
141
|
+
text += tail
|
|
119
142
|
|
|
120
143
|
// 1.11-S2(D-33 / 计量设计 §3.3):请求级 usage 事实落链(拿到 usage 的最后一个 chunk 即请求
|
|
121
144
|
// 收尾;撞限/异常 turn 照常落——截断前已完成请求的 usage 事实不吞)。缺失 = provider 未回,
|
package/src/runtime-core/llm.ts
CHANGED
|
@@ -520,6 +520,12 @@ export interface AnthropicAdapterOptions {
|
|
|
520
520
|
defaultMaxTokens?: number
|
|
521
521
|
/** 1.14-S4(K-POOL-1):成员级凭据 env 变量名引用(modelPool keyEnv——resolve 精确候选;keychain 链保持在前) */
|
|
522
522
|
credentialEnvVars?: string[]
|
|
523
|
+
/**
|
|
524
|
+
* 1.20-K1(D-48 P2):滚动断点求值身份。缺省 = id(单端点 'anthropic');
|
|
525
|
+
* **池成员必须显式传 m.id**——池成员适配器 id 恒 'anthropic'(装配未透传成员标识),
|
|
526
|
+
* 按其求值 = 白名单一端点解锁全池(1.20 设计 §3.1 专项锁,S66/AC-9)。
|
|
527
|
+
*/
|
|
528
|
+
rollingIdentity?: string
|
|
523
529
|
}
|
|
524
530
|
|
|
525
531
|
/** stop_reason → CAR finishReason(未映射 fail-visible——openai FINISH_MAP 同纪律) */
|
|
@@ -579,12 +585,68 @@ function mergeAnthropicUsage(
|
|
|
579
585
|
* 投影/快照比对,N1 零影响;生效有 provider 侧最小长度门槛(~1024 tokens)——短 system
|
|
580
586
|
* 静默不生效应以 usage.cacheCreationTokens 观测,禁以「配了就该生效」推断。关断 =
|
|
581
587
|
* CAR_LLM_PROMPT_CACHE=off(cli flag --no-prompt-cache 置本 env;A/B 与故障回退通道)。
|
|
588
|
+
* 1.20-K1(D-48 P2):滚动断点——白名单命中时在尾部消息挂第二处断点(缺省 1 个,只块化命中
|
|
589
|
+
* 消息),键 = 路由身份(rollingIdentity,池成员 = m.id);CAR_LLM_PROMPT_CACHE_ROLLING
|
|
590
|
+
* 逗号清单('off' = 显式清空,无通配)+ CAR_LLM_PROMPT_CACHE_ROLLING_BREAKPOINTS(1..3 钳制)。
|
|
582
591
|
*/
|
|
583
592
|
function promptCacheOff(): boolean {
|
|
584
593
|
return process.env.CAR_LLM_PROMPT_CACHE === 'off'
|
|
585
594
|
}
|
|
586
595
|
|
|
587
|
-
|
|
596
|
+
/**
|
|
597
|
+
* 1.20-K1(D-48 P2):滚动断点白名单求值(每请求求值,与 promptCacheOff 同址同时序)。
|
|
598
|
+
* 键 = 路由身份:单端点 = adapter id('anthropic')/ 池成员 = 显式 rollingIdentity(m.id)——
|
|
599
|
+
* **禁按 adapter.id 给池成员求值**(池成员适配器 id 恒 'anthropic'——pool 装配未透传成员标识,
|
|
600
|
+
* 一端点解锁 = 全池解锁,1.20 设计 §3.1)。off 连坐最高优先;清单值 'off' = 显式清空;
|
|
601
|
+
* 无 '*' 通配(通配废掉逐条确证);大小写敏感(归一化会掩盖登记错误)。
|
|
602
|
+
*/
|
|
603
|
+
export function rollingCacheAllowed(identity: string): boolean {
|
|
604
|
+
if (promptCacheOff()) return false
|
|
605
|
+
const raw = process.env.CAR_LLM_PROMPT_CACHE_ROLLING
|
|
606
|
+
if (!raw) return false
|
|
607
|
+
const list = raw.split(',').map(s => s.trim()).filter(Boolean)
|
|
608
|
+
if (!list.length || list.includes('off')) return false
|
|
609
|
+
return list.includes(identity)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// 1.20-K1:滚动断点数——provider 硬限 4,system 断点(P1)恒占 1;缺省 1(多挂 = 对同一前缀
|
|
613
|
+
// 重复写 1.25x 缓存,1.20 设计 §3.3)。env 非法/缺席回落缺省,超限钳制(AC-8)
|
|
614
|
+
const ROLLING_BREAKPOINTS_DEFAULT = 1
|
|
615
|
+
const ROLLING_BREAKPOINTS_MAX = 3
|
|
616
|
+
|
|
617
|
+
function rollingBreakpoints(): number {
|
|
618
|
+
const raw = Number(process.env.CAR_LLM_PROMPT_CACHE_ROLLING_BREAKPOINTS)
|
|
619
|
+
if (!Number.isFinite(raw) || raw < 1) return ROLLING_BREAKPOINTS_DEFAULT
|
|
620
|
+
return Math.min(Math.trunc(raw), ROLLING_BREAKPOINTS_MAX)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* 1.20-K1(D-48 P2):滚动断点放置——从尾部往前命中即挂,只块化被挂消息,其余消息维持
|
|
625
|
+
* 字符串(1.20 设计 §3.2:全量块化零收益高风险)。断点挂**末块**(tool_result / tool_use
|
|
626
|
+
* 多块形态不拆不重组,语义不变);空串 / 空数组 / 非对象末块无可挂载体 → 跳过前移(不换位不补造)。
|
|
627
|
+
* 纯传输层注解:只改本请求出站体,不进 SessionLog / 投影(N1 零影响,S59 同红线)。
|
|
628
|
+
*/
|
|
629
|
+
function attachRollingBreakpoints(turns: Array<{ role: 'user' | 'assistant'; content: unknown }>, count: number): void {
|
|
630
|
+
let attached = 0
|
|
631
|
+
for (let i = turns.length - 1; i >= 0 && attached < count; i--) {
|
|
632
|
+
const t = turns[i]!
|
|
633
|
+
if (typeof t.content === 'string') {
|
|
634
|
+
if (!t.content.length) continue
|
|
635
|
+
t.content = [{ type: 'text', text: t.content, cache_control: { type: 'ephemeral' } }]
|
|
636
|
+
attached++
|
|
637
|
+
continue
|
|
638
|
+
}
|
|
639
|
+
if (Array.isArray(t.content) && t.content.length) {
|
|
640
|
+
const last = t.content[t.content.length - 1] as Record<string, unknown> | undefined
|
|
641
|
+
if (last && typeof last === 'object' && !Array.isArray(last)) {
|
|
642
|
+
last.cache_control = { type: 'ephemeral' }
|
|
643
|
+
attached++
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function toAnthropic(req: LlmRequest, apiKey: string, cache: { rolling: boolean; rollingBreakpoints: number }) {
|
|
588
650
|
const system: string[] = []
|
|
589
651
|
const turns: Array<{ role: 'user' | 'assistant'; content: unknown }> = []
|
|
590
652
|
let pendingToolResults: Array<Record<string, unknown>> = []
|
|
@@ -617,6 +679,9 @@ function toAnthropic(req: LlmRequest, apiKey: string) {
|
|
|
617
679
|
}
|
|
618
680
|
}
|
|
619
681
|
flushToolResults()
|
|
682
|
+
// 1.20-K1(D-48 P2):滚动断点在白名单命中时挂尾部消息(缺省 1 个)——system 断点(P1)
|
|
683
|
+
// 与消息断点独立,off 时两者俱灭(rollingCacheAllowed 内已连坐)
|
|
684
|
+
if (cache.rolling) attachRollingBreakpoints(turns, cache.rollingBreakpoints)
|
|
620
685
|
// 1.14-S1(K-MP-1 / D-39):思考参数——effort → thinking.budget_tokens(ANTHROPIC_THINKING_BUDGET 表);
|
|
621
686
|
// off/缺席不下发;budget ≥ max_tokens 违反 Messages API 约束(max_tokens 必须大于 budget_tokens)→
|
|
622
687
|
// 显式报错提示调高(fail-visible,禁静默改写 max_tokens)
|
|
@@ -718,6 +783,8 @@ async function* parseAnthropicSseStream(body: AsyncIterable<Uint8Array>, model:
|
|
|
718
783
|
|
|
719
784
|
export function createAnthropicAdapter(opts: AnthropicAdapterOptions): LlmAdapter & { id: string } {
|
|
720
785
|
const id = opts.id ?? 'anthropic'
|
|
786
|
+
// 1.20-K1(D-48 P2):滚动断点求值身份(每请求经 rollingCacheAllowed 对 env 求值,非构造期定值)
|
|
787
|
+
const rollingIdentity = opts.rollingIdentity ?? id
|
|
721
788
|
const baseUrl = opts.baseUrl.replace(/\/+$/, '')
|
|
722
789
|
assertTlsOrLoopback(baseUrl)
|
|
723
790
|
const url = `${baseUrl}/v1/messages`
|
|
@@ -746,7 +813,10 @@ export function createAnthropicAdapter(opts: AnthropicAdapterOptions): LlmAdapte
|
|
|
746
813
|
const ref = opts.credentials.resolve(opts.provider ?? id, credResolveOpts)
|
|
747
814
|
apiKey = opts.credentials.reveal(ref, {})
|
|
748
815
|
}
|
|
749
|
-
return toAnthropic(req, apiKey
|
|
816
|
+
return toAnthropic(req, apiKey, {
|
|
817
|
+
rolling: rollingCacheAllowed(rollingIdentity),
|
|
818
|
+
rollingBreakpoints: rollingBreakpoints(),
|
|
819
|
+
})
|
|
750
820
|
},
|
|
751
821
|
fetchImpl,
|
|
752
822
|
signal: req.signal,
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"base64Signature":"MEYCIQCAI26VV32rg4QHnOWZr1ghOdrdbOmOpoV43GQODRDZkgIhAOIaFsTy+ooR6F2HuXsOS0W0V4tYoXFTjfBcVJrLqb5H","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc2VENDQm02Z0F3SUJBZ0lVRkdoL0hYbS9sbXM0ZlRJZ0hRN2xaL1lBWXRJd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1ESTFNRGd5TkRJeFdoY05Nall4TURJMU1EZ3pOREl4V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVHd2tjQnNDdHlMRlVUekVpaTNodG9OajVrNmJrQ3VhQndpR3oKWUl0S0VxUU56bmRHUG0rRXhRVWkrT3dDekc2MG5xOFhZS0ExcHhqbGVOMFNla01nN2FPQ0JZMHdnZ1dKTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakU0TGpJd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLRGt6WVdGa1l6TTNOV0ptTmpVeU5UQXpOREF5TkRBeQpObVJtWXpWbE5UQXdZak01WW1Sak56UXdGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNVGd1TWpBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakU0TGpJd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENnNQpNMkZoWkdNek56VmlaalkxTWpVd016UXdNalF3TWpaa1ptTTFaVFV3TUdJek9XSmtZemMwTUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENnNQpNMkZoWkdNek56VmlaalkxTWpVd016UXdNalF3TWpaa1ptTTFaVFV3TUdJek9XSmtZemMwTUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNVGd1TWpBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpFNExqSXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2c1TTJGaFpHTXpOelZpWmpZMU1qVXdNelF3TWpRd01qWmtabU0xWlRVd01HSXpPV0prWXpjME1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXdNVGMyTkRjeApOREV4TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eE9DNHlNSUdMQmdvckJnRUVBZFo1QWdRQ0JIMEVld0I1QUhjQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2hjaWtrcFFBQUJBTUFTREJHCkFpRUFqUytyOG10em16Tkg1RlZpT0M3cVFRM1BrWUJKbmJ1bTV2R2ZTTERFK3pVQ0lRQzcyZFpqczYzdGFnYW4KWjB2RlFIUzZ3a25SVTl0bXZhRXJtb2RUNXNEaDh6QUtCZ2dxaGtqT1BRUURBd05wQURCbUFqRUEvR2g3TUQ5OQp5NGx2b00zZjg1TkhrMjNJWVE4UUhaQ1hURGZpa0VvUGNhRXo2N3RGVWsrN2Qzd0lrK09iNkNETkFqRUF1NnBjCnlQR1ZDSklHdE9OVFpPQ3o4UVZSQzJsVHArcDZuSTNDeExwZXZSUkRlN0xwa2lZbUcrb0xxbjM1Z3Y1UgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEQCIEV0qK9E/NTL4GPTa7/Jz+8A0fKjmh1aMtETgVIWZJDHAiA0Z22djneT0OnmJCUG7QYeWFTYtvSIXLZpG7LRZM3MMQ==","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiYjBmYzBlMjgwZDNkODE4ZDk2MzJmMzZlYjc1NWVjMDEwNzI4OTY4NTVlMjliZTAxNzQ0ZTQ5ZTM5MWQ4ODllIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNBSTI2VlYzMnJnNFFIbk9XWnIxZ2hPZHJkYk9tT3BvVjQzR1FPRFJEWmtnSWhBT0lhRnNUeStvb1I2RjJIdVhzT1MwVzBWNHRZb1hGVGpmQmNWSnJMcWI1SCIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzJWRU5EUW0wMlowRjNTVUpCWjBsVlJrZG9MMGhZYlM5c2JYTTBabFJKWjBoUk4yeGFMMWxCV1hSSmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFFU1RGTlJHZDVUa1JKZUZkb1kwNU5hbGw0VFVSSk1VMUVaM3BPUkVsNFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZIZDJ0alFuTkRkSGxNUmxWVWVrVnBhVE5vZEc5T2FqVnJObUpyUTNWaFFuZHBSM29LV1VsMFMwVnhVVTU2Ym1SSFVHMHJSWGhSVldrclQzZERla2MyTUc1eE9GaFpTMEV4Y0hocWJHVk9NRk5sYTAxbk4yRlBRMEpaTUhkbloxZEtUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha1UwVEdwSmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkd0NldWZEdhMWw2VFROT1YwcHRUbXBWZVU1VVFYcE9SRUY1VGtSQmVRcE9iVkp0V1hwV2JFNVVRWGRaYWswMVdXMVNhazU2VVhkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOVkdkMVRXcEJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrVTBUR3BKZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5OUXBOTWtab1drZE5lazU2Vm1sYWFsa3hUV3BWZDAxNlVYZE5hbEYzVFdwYWExcHRUVEZhVkZWM1RVZEplazlYU210WmVtTXdUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObk5RcE5Na1pvV2tkTmVrNTZWbWxhYWxreFRXcFZkMDE2VVhkTmFsRjNUV3BhYTFwdFRURmFWRlYzVFVkSmVrOVhTbXRaZW1Nd1RVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOVkdkMVRXcEJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BGTkV4cVNYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJjMVRUSkdhRnBIVFhwT2VsWnBXbXBaTVUxcVZYZE5lbEYzVFdwUmQwMXFXbXRhYlUweFdsUlZkMDFIU1hwUFYwcHJXWHBqTUUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYZE5WR015VGtSamVBcE9SRVY0VERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZUU5RE5IbE5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVWbGQwSTFRVWhqUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyaGphV3RyY0ZGQlFVSkJUVUZUUkVKSENrRnBSVUZxVXl0eU9HMTBlbTE2VGtnMVJsWnBUME0zY1ZGUk0xQnJXVUpLYm1KMWJUVjJSMlpUVEVSRkszcFZRMGxSUXpjeVpGcHFjell6ZEdGbllXNEtXakIyUmxGSVV6WjNhMjVTVlRsMGJYWmhSWEp0YjJSVU5YTkVhRGg2UVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV3UVVSQ2JVRnFSVUV2UjJnM1RVUTVPUXA1Tkd4MmIwMHpaamcxVGtock1qTkpXVkU0VVVoYVExaFVSR1pwYTBWdlVHTmhSWG8yTjNSR1ZXc3JOMlF6ZDBsckswOWlOa05FVGtGcVJVRjFObkJqQ25sUVIxWkRTa2xIZEU5T1ZGcFBRM280VVZaU1F6SnNWSEFyY0RadVNUTkRlRXh3WlhaU1VrUmxOMHh3YTJsWmJVY3JiMHh4YmpNMVozWTFVZ290TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1792916662,"logIndex":3455604018,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
|