@lqc123qwe/car-runtime 1.19.0 → 1.21.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.21.0.tgz +0 -0
- package/dist/car-runtime-1.21.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.js +34 -1
- package/dist/runtime-core/llm.d.ts +14 -0
- package/dist/runtime-core/llm.js +91 -4
- package/dist/runtime-core/types.d.ts +6 -0
- package/dist/sbom.json +10 -10
- package/dist/session/log.d.ts +1 -1
- package/dist/session/log.js +32 -2
- 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 +32 -1
- package/src/runtime-core/llm.ts +92 -4
- package/src/runtime-core/types.ts +8 -0
- package/src/session/log.ts +32 -2
- package/dist/car-runtime-1.19.0.tgz +0 -0
- package/dist/car-runtime-1.19.0.tgz.sig.bundle +0 -1
package/dist/SHA256SUMS
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
8d9bd1fa4d447b255ae668a51bb47338bde8e35ad8ccac97ac72d2c893664115 car-runtime-1.21.0.tgz
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"base64Signature":"MEQCIEr84YFIo6UQdCBqDYqzEn5iofitA9Wtuxx9AqUbuNGZAiBdNvJTODyumJazgvvJunU8HC/jVNGcHjUM9u7quHIyOg==","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc1ekNDQm0yZ0F3SUJBZ0lVYWV0eWY3L29XeUE3RlVhb1RuV0k0QjYzVU1Vd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1URTBNRE16TlRNM1doY05Nall4TVRFME1ETTBOVE0zV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVpdlNPYnBEY1ZHZjd3Vm0yTWhUajlqbTdmQ0Z2ZGFZeGYrVTcKS2ljbzJLbUdNdWRXRUk4KzJuYmxtQmN6Q0tYNWoyWGMrNDRHTTk0bW5EUkF3OTdZT0tPQ0JZd3dnZ1dJTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl4TGpBd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLRFZsWW1Fd1pHTXpNRGhpT1dWak1qSTVNR1l4Tm1GbApabUV5TVdNNVlUVXlPR1UzTkRReU9EQXdGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNakV1TURBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakl4TGpBd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENnMQpaV0poTUdSak16QTRZamxsWXpJeU9UQm1NVFpoWldaaE1qRmpPV0UxTWpobE56UTBNamd3TUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENnMQpaV0poTUdSak16QTRZamxsWXpJeU9UQm1NVFpoWldaaE1qRmpPV0UxTWpobE56UTBNamd3TUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNakV1TURBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpJeExqQXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2cxWldKaE1HUmpNekE0WWpsbFl6SXlPVEJtTVRaaFpXWmhNakZqT1dFMU1qaGxOelEwTWpnd01CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXpOVFV5TkRFMwpPVFE0TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eU1TNHdNSUdLQmdvckJnRUVBZFo1QWdRQ0JId0VlZ0I0QUhZQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2gyQi85K0FBQUJBTUFSekJGCkFpRUE2QlpwRnVTK3Y5WVVqZXNxWk02WGFyTXcydDVNbmUxaldSOStkYXVJQ0dJQ0lBSWhPL2h4b0NUQitCdlgKbkVRRmlmNzZvanVtbHlLeWYvckF3V1Rzc3VPMk1Bb0dDQ3FHU000OUJBTURBMmdBTUdVQ01HK2R4dS9BV2tMOQo4QUtiSlhXSXYzMFlGNjk5UGp5cWFDcVhTSUllY25VRzdPZHFXTzRYK2dwb1RNVEc1bXRJcVFJeEFOT0xZRGFZCndTNUhoU0RNcXVLU1M2ODRjL1U2bTlLc0syWmN2RGFrRlZOL1BiZHhCc1Y5cE1ySzZFeEVMblpEb3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEUCIB04W7cJqnNa2clZD6iGQQPWdN+4NyyrSfjpxuDJ5cWIAiEAlr/UsHUN8IVr0S/5WeyxAYNKIpQUnHcPyToMjqgz+HA=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI4ZDliZDFmYTRkNDQ3YjI1NWFlNjY4YTUxYmI0NzMzOGJkZThlMzVhZDhjY2FjOTdhYzcyZDJjODkzNjY0MTE1In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRXI4NFlGSW82VVFkQ0JxRFlxekVuNWlvZml0QTlXdHV4eDlBcVVidU5HWkFpQmROdkpUT0R5dW1KYXpndnZKdW5VOEhDL2pWTkdjSGpVTTl1N3F1SEl5T2c9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzFla05EUW0weVowRjNTVUpCWjBsVllXVjBlV1kzTDI5WGVVRTNSbFZoYjFSdVYwazBRall6VlUxVmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUlRCTlJFMTZUbFJOTTFkb1kwNU5hbGw0VFZSRk1FMUVUVEJPVkUwelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZwZGxOUFluQkVZMVpIWmpkM1ZtMHlUV2hVYWpscWJUZG1RMFoyWkdGWmVHWXJWVGNLUzJsamJ6SkxiVWROZFdSWFJVazRLekp1WW14dFFtTjZRMHRZTldveVdHTXJORFJIVFRrMGJXNUVVa0YzT1RkWlQwdFBRMEpaZDNkbloxZEpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha2w0VEdwQmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkZac1dXMUZkMXBIVFhwTlJHaHBUMWRXYWsxcVNUVk5SMWw0VG0xR2JBcGFiVVY1VFZkTk5WbFVWWGxQUjFVelRrUlJlVTlFUVhkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOYWtWMVRVUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrbDRUR3BCZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5NUXBhVjBwb1RVZFNhazE2UVRSWmFteHNXWHBKZVU5VVFtMU5WRnBvV2xkYWFFMXFSbXBQVjBVeFRXcG9iRTU2VVRCTmFtZDNUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObk1RcGFWMHBvVFVkU2FrMTZRVFJaYW14c1dYcEplVTlVUW0xTlZGcG9XbGRhYUUxcVJtcFBWMFV4VFdwb2JFNTZVVEJOYW1kM1RVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOYWtWMVRVUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BKZUV4cVFYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJjeFdsZEthRTFIVW1wTmVrRTBXV3BzYkZsNlNYbFBWRUp0VFZSYWFGcFhXbWhOYWtacVQxZEZNVTFxYUd4T2VsRXdUV3BuZDAxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYcE9WRlY1VGtSRk13cFBWRkUwVERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZVUxVE5IZE5TVWRMUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJZDBWbFowSTBRVWhaUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyZ3lRaTg1SzBGQlFVSkJUVUZTZWtKR0NrRnBSVUUyUWxwd1JuVlRLM1k1V1ZWcVpYTnhXazAyV0dGeVRYY3lkRFZOYm1VeGFsZFNPU3RrWVhWSlEwZEpRMGxCU1doUEwyaDRiME5VUWl0Q2RsZ0tia1ZSUm1sbU56WnZhblZ0YkhsTGVXWXZja0YzVjFSemMzVlBNazFCYjBkRFEzRkhVMDAwT1VKQlRVUkJNbWRCVFVkVlEwMUhLMlI0ZFM5QlYydE1PUW80UVV0aVNsaFhTWFl6TUZsR05qazVVR3A1Y1dGRGNWaFRTVWxsWTI1VlJ6ZFBaSEZYVHpSWUsyZHdiMVJOVkVjMWJYUkpjVkZKZUVGT1QweFpSR0ZaQ25kVE5VaG9VMFJOY1hWTFUxTTJPRFJqTDFVMmJUbExjMHN5V21OMlJHRnJSbFpPTDFCaVpIaENjMVk1Y0UxeVN6WkZlRVZNYmxwRWIzYzlQUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1794627338,"logIndex":3868826566,"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.21.0.tgz(436516 bytes,树哈希锚定)","ts":1794627336025}
|
|
2
|
+
{"stage":"sha256sums","detail":"SHA256SUMS 8d9bd1fa4d447b25…(裸字节复核通过)","ts":1794627336026}
|
|
3
|
+
{"stage":"sbom","detail":"55 个组件(运行时依赖 0 项)","ts":1794627336033}
|
|
4
|
+
{"stage":"cosign-keyless","detail":"signed car-runtime-1.21.0.tgz (keyless OIDC + Rekor)","ts":1794627338842}
|
|
@@ -21,13 +21,25 @@ export async function chatStep(opts) {
|
|
|
21
21
|
const messages = log.deriveMessages(undefined, { activeTurnId: turnId }).map(m => ({
|
|
22
22
|
role: m.role,
|
|
23
23
|
content: m.toolCall !== undefined
|
|
24
|
-
? {
|
|
24
|
+
? {
|
|
25
|
+
toolCall: m.toolCall,
|
|
26
|
+
// 1.21(R-TOKENHUB-1 / D-T3/D-T4):投影层附加的同 turn thinking 三元组随 toolCall
|
|
27
|
+
// 载荷透传(适配器回传消费——toAnthropic 前置 thinking 块;openai 形忽略该键)
|
|
28
|
+
...(m.thinking !== undefined
|
|
29
|
+
? { thinking: m.thinking }
|
|
30
|
+
: {}),
|
|
31
|
+
}
|
|
25
32
|
: m.content,
|
|
26
33
|
}));
|
|
27
34
|
// 4. 流式消费(3. 凭据解析在适配器首块前,A080001 由此传播)
|
|
28
35
|
const redactor = new StreamRedactor();
|
|
29
36
|
let text = '';
|
|
30
37
|
const calls = new Map();
|
|
38
|
+
// 1.21(R-TOKENHUB-1 / D-T1/D-T2):thinking 捕获累积——禁改写(回传签名完整性 > 脱敏
|
|
39
|
+
// 改写),provider 事实字节保真;redacted_thinking = data 全文单发覆盖
|
|
40
|
+
let thinkingText = '';
|
|
41
|
+
let thinkingSignature = '';
|
|
42
|
+
let thinkingRedacted;
|
|
31
43
|
let stopReason;
|
|
32
44
|
let errorDetail;
|
|
33
45
|
let usage;
|
|
@@ -67,6 +79,13 @@ export async function chatStep(opts) {
|
|
|
67
79
|
cur.argsBuf += chunk.toolCallDelta.argumentsDelta;
|
|
68
80
|
calls.set(idx, cur);
|
|
69
81
|
}
|
|
82
|
+
// 1.21(R-TOKENHUB-1 / D-T1):thinking 三面捕获(text/signature 增量累积、redacted 单发覆盖)
|
|
83
|
+
if (chunk.thinkingDelta !== undefined)
|
|
84
|
+
thinkingText += chunk.thinkingDelta;
|
|
85
|
+
if (chunk.thinkingSignatureDelta !== undefined)
|
|
86
|
+
thinkingSignature += chunk.thinkingSignatureDelta;
|
|
87
|
+
if (chunk.thinkingRedacted !== undefined)
|
|
88
|
+
thinkingRedacted = chunk.thinkingRedacted;
|
|
70
89
|
if (chunk.error)
|
|
71
90
|
errorDetail = chunk.error.message;
|
|
72
91
|
if (chunk.usage)
|
|
@@ -103,6 +122,20 @@ export async function chatStep(opts) {
|
|
|
103
122
|
providerRaw: usage.providerRaw,
|
|
104
123
|
});
|
|
105
124
|
}
|
|
125
|
+
// 5a. 1.21(R-TOKENHUB-1 / D-T2):thinking 事件落链(actor='model',先于 assistant/toolCall
|
|
126
|
+
// ——投影层按同 turn 首 toolCall 附加回传)。payload = thinking 原文禁改写(签名完整性 >
|
|
127
|
+
// 脱敏改写——改写即回传校验失败);secretsRedacted 仅计数留痕(审计信号,不复写);
|
|
128
|
+
// redacted 形态 payload = data 全文(meta.redacted=true)。无 thinking chunk = 零新事件
|
|
129
|
+
// (D-T5 字节兼容,链形态与 1.20 全等)。
|
|
130
|
+
if (thinkingText.length || thinkingRedacted !== undefined) {
|
|
131
|
+
const thinkingPayload = thinkingRedacted ?? thinkingText;
|
|
132
|
+
const secretsInThinking = scanSecrets(thinkingPayload).hits.length;
|
|
133
|
+
log.append('model', 'thinking', turnId, thinkingPayload, {
|
|
134
|
+
...(thinkingRedacted !== undefined ? { redacted: true } : {}),
|
|
135
|
+
...(thinkingSignature ? { signature: thinkingSignature } : {}),
|
|
136
|
+
...(secretsInThinking ? { secretsRedacted: secretsInThinking } : {}),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
106
139
|
// 5. assistant 文本落 M7(脱敏后 + 计数留痕;空文本不产事件)
|
|
107
140
|
// flush 后兜底复扫只计数不复写:流式遮蔽已生效,>0 即流式边界逃逸的审计信号
|
|
108
141
|
let secretsRedacted = 0;
|
|
@@ -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 = [];
|
|
@@ -567,13 +624,27 @@ function toAnthropic(req, apiKey) {
|
|
|
567
624
|
flushToolResults();
|
|
568
625
|
if (m.role === 'assistant' && m.content != null && typeof m.content === 'object' && 'toolCall' in m.content) {
|
|
569
626
|
const tc = m.content.toolCall;
|
|
570
|
-
|
|
627
|
+
// 1.21(R-TOKENHUB-1 / D-T4):工具环回传——投影层附加的 thinking(同 turn 原始 assistant
|
|
628
|
+
// 消息的 thinking 块)前置重放(Anthropic 规范:thinking 须位于同消息 text/tool_use 之前;
|
|
629
|
+
// tokenhub 等网关对缺回传 400——R-TOKENHUB-1 实证)。无 thinking = 形态与 1.20 逐字节一致
|
|
630
|
+
// (D-T5 兼容线,golden 不动)。
|
|
631
|
+
const th = m.content.thinking;
|
|
632
|
+
const thinkingBlock = th?.text?.length
|
|
633
|
+
? th.redacted
|
|
634
|
+
? { type: 'redacted_thinking', data: th.text }
|
|
635
|
+
: { type: 'thinking', thinking: th.text, ...(th.signature ? { signature: th.signature } : {}) }
|
|
636
|
+
: undefined;
|
|
637
|
+
turns.push({ role: 'assistant', content: [...(thinkingBlock ? [thinkingBlock] : []), { type: 'tool_use', id: tc.id, name: tc.tool, input: tc.args ?? {} }] });
|
|
571
638
|
}
|
|
572
639
|
else {
|
|
573
640
|
turns.push({ role: m.role, content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content) });
|
|
574
641
|
}
|
|
575
642
|
}
|
|
576
643
|
flushToolResults();
|
|
644
|
+
// 1.20-K1(D-48 P2):滚动断点在白名单命中时挂尾部消息(缺省 1 个)——system 断点(P1)
|
|
645
|
+
// 与消息断点独立,off 时两者俱灭(rollingCacheAllowed 内已连坐)
|
|
646
|
+
if (cache.rolling)
|
|
647
|
+
attachRollingBreakpoints(turns, cache.rollingBreakpoints);
|
|
577
648
|
// 1.14-S1(K-MP-1 / D-39):思考参数——effort → thinking.budget_tokens(ANTHROPIC_THINKING_BUDGET 表);
|
|
578
649
|
// off/缺席不下发;budget ≥ max_tokens 违反 Messages API 约束(max_tokens 必须大于 budget_tokens)→
|
|
579
650
|
// 显式报错提示调高(fail-visible,禁静默改写 max_tokens)
|
|
@@ -645,6 +716,10 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
|
|
|
645
716
|
if (block?.type === 'tool_use') {
|
|
646
717
|
yield { toolCallDelta: { index: evt.index, id: block.id, name: block.name } };
|
|
647
718
|
}
|
|
719
|
+
else if (block?.type === 'redacted_thinking' && typeof block.data === 'string' && block.data.length) {
|
|
720
|
+
// 1.21(R-TOKENHUB-1 / D-T1):redacted_thinking 全文单发(回传形态 {type:'redacted_thinking',data})
|
|
721
|
+
yield { thinkingRedacted: block.data };
|
|
722
|
+
}
|
|
648
723
|
}
|
|
649
724
|
else if (type === 'content_block_delta') {
|
|
650
725
|
const d = evt.delta;
|
|
@@ -654,7 +729,14 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
|
|
|
654
729
|
else if (d?.type === 'input_json_delta' && typeof d.partial_json === 'string' && d.partial_json.length) {
|
|
655
730
|
yield { toolCallDelta: { index: evt.index, argumentsDelta: d.partial_json } };
|
|
656
731
|
}
|
|
657
|
-
|
|
732
|
+
else if (d?.type === 'thinking_delta' && typeof d.thinking === 'string' && d.thinking.length) {
|
|
733
|
+
// 1.21(R-TOKENHUB-1 / D-T1):thinking 文本增量捕获(原容错跳过 → 落链 + 工具环回传)
|
|
734
|
+
yield { thinkingDelta: d.thinking };
|
|
735
|
+
}
|
|
736
|
+
else if (d?.type === 'signature_delta' && typeof d.signature === 'string' && d.signature.length) {
|
|
737
|
+
yield { thinkingSignatureDelta: d.signature };
|
|
738
|
+
}
|
|
739
|
+
// citations_delta 等其余非 CAR 消费面:容错跳过
|
|
658
740
|
}
|
|
659
741
|
else if (type === 'message_start') {
|
|
660
742
|
// 1.11-S1(D-33):input 侧采集(usage 嵌套在 message 下)
|
|
@@ -687,6 +769,8 @@ async function* parseAnthropicSseStream(body, model, adapterId) {
|
|
|
687
769
|
}
|
|
688
770
|
export function createAnthropicAdapter(opts) {
|
|
689
771
|
const id = opts.id ?? 'anthropic';
|
|
772
|
+
// 1.20-K1(D-48 P2):滚动断点求值身份(每请求经 rollingCacheAllowed 对 env 求值,非构造期定值)
|
|
773
|
+
const rollingIdentity = opts.rollingIdentity ?? id;
|
|
690
774
|
const baseUrl = opts.baseUrl.replace(/\/+$/, '');
|
|
691
775
|
assertTlsOrLoopback(baseUrl);
|
|
692
776
|
const url = `${baseUrl}/v1/messages`;
|
|
@@ -714,7 +798,10 @@ export function createAnthropicAdapter(opts) {
|
|
|
714
798
|
const ref = opts.credentials.resolve(opts.provider ?? id, credResolveOpts);
|
|
715
799
|
apiKey = opts.credentials.reveal(ref, {});
|
|
716
800
|
}
|
|
717
|
-
return toAnthropic(req, apiKey
|
|
801
|
+
return toAnthropic(req, apiKey, {
|
|
802
|
+
rolling: rollingCacheAllowed(rollingIdentity),
|
|
803
|
+
rollingBreakpoints: rollingBreakpoints(),
|
|
804
|
+
});
|
|
718
805
|
},
|
|
719
806
|
fetchImpl,
|
|
720
807
|
signal: req.signal,
|
|
@@ -86,6 +86,12 @@ export interface LlmChunk {
|
|
|
86
86
|
};
|
|
87
87
|
/** 1.11-S1(D-33):每次请求最后一个 chunk 附带归一化用量;缺失 = provider 未回,禁补造 */
|
|
88
88
|
usage?: LlmUsage;
|
|
89
|
+
/** thinking 块文本增量(content_block_delta.thinking_delta) */
|
|
90
|
+
thinkingDelta?: string;
|
|
91
|
+
/** thinking 块签名增量(signature_delta——真 Anthropic thinking 块回传须携签名) */
|
|
92
|
+
thinkingSignatureDelta?: string;
|
|
93
|
+
/** redacted_thinking 块 data 全文(content_block_start 单发;回传形态 {type:'redacted_thinking',data}) */
|
|
94
|
+
thinkingRedacted?: string;
|
|
89
95
|
}
|
|
90
96
|
/** Provider 适配器(ACL,C-02):统一签名,消解 Provider 差异 */
|
|
91
97
|
export interface LlmAdapter {
|
package/dist/sbom.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CAR-SBOM",
|
|
3
3
|
"specVersion": "0.1",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.21.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": "b7cf35ba06ebaae339a89da0e4df08b5ae310a60a97b5201de54f092fc8758ab"
|
|
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": "71bf8926b162fc8ae05343953405a225b5630ba85196dc71727524de18196cc3"
|
|
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": "d9ff638ea4de9b89d56238f2d7fd65c07dd29323fcb9906e72b6877618a360a6"
|
|
205
205
|
},
|
|
206
206
|
{
|
|
207
207
|
"type": "file",
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
{
|
|
252
252
|
"type": "file",
|
|
253
253
|
"path": "src/session/log.ts",
|
|
254
|
-
"sha256": "
|
|
254
|
+
"sha256": "9b3a081bfa60e046166cce11203848402f52d4e9b9d0f94ad2e9dc1f0ebadd9c"
|
|
255
255
|
},
|
|
256
256
|
{
|
|
257
257
|
"type": "file",
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
{
|
|
272
272
|
"type": "file",
|
|
273
273
|
"path": "package.json",
|
|
274
|
-
"sha256": "
|
|
274
|
+
"sha256": "a6ae52801faedf9b64c49d0be8c5a7c22204697bf22d78c7253494bf28677b9d"
|
|
275
275
|
},
|
|
276
276
|
{
|
|
277
277
|
"type": "file",
|
package/dist/session/log.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type EventKind = 'user' | 'assistant' | 'toolCall' | 'toolResult' | 'turnEnd' | 'goalUpdate' | 'hostRaw' | 'fork' | 'system' | 'usage' | 'compact' | 'routing' | 'memory';
|
|
1
|
+
export type EventKind = 'user' | 'assistant' | 'toolCall' | 'toolResult' | 'turnEnd' | 'goalUpdate' | 'hostRaw' | 'fork' | 'system' | 'usage' | 'compact' | 'routing' | 'memory' | 'thinking';
|
|
2
2
|
export type Actor = 'user' | 'model' | 'plugin' | 'runtime';
|
|
3
3
|
export interface SessionEvent {
|
|
4
4
|
seq: number;
|
package/dist/session/log.js
CHANGED
|
@@ -122,15 +122,45 @@ export class SessionLog {
|
|
|
122
122
|
const out = [];
|
|
123
123
|
if (compactEvent)
|
|
124
124
|
out.push({ role: 'system', content: (compactEvent.payload.summary) });
|
|
125
|
+
// 1.21(R-TOKENHUB-1 / D-T3):thinking 投影——同 turn 内 thinking 事件附加到首个后续
|
|
126
|
+
// toolCall 消息(原始 provider assistant 消息 = [thinking, ...tool_use] 的重放形态);
|
|
127
|
+
// standalone(无后续 toolCall)/ 被 user·system 隔断 / 跨 turn 不投影(实证:普通两轮
|
|
128
|
+
// 历史不要求回传);assistant(text) 同 turn 不隔断(链序 thinking→assistant→toolCall);
|
|
129
|
+
// 无 thinking 事件投影逐字节不变(D-T5 兼容线,N1 快照自洽)。
|
|
130
|
+
let pendingThinking = null;
|
|
125
131
|
for (const e of projected) {
|
|
132
|
+
if (pendingThinking && (e.turnId !== pendingThinking.turnId || e.kind === 'user' || e.kind === 'system'))
|
|
133
|
+
pendingThinking = null;
|
|
134
|
+
if (e.kind === 'thinking') {
|
|
135
|
+
const p = typeof e.payload === 'string' ? e.payload : '';
|
|
136
|
+
if (p.length || e.meta?.redacted === true) {
|
|
137
|
+
pendingThinking = {
|
|
138
|
+
turnId: e.turnId,
|
|
139
|
+
text: p,
|
|
140
|
+
...(typeof e.meta?.signature === 'string' && e.meta.signature ? { signature: e.meta.signature } : {}),
|
|
141
|
+
...(e.meta?.redacted === true ? { redacted: true } : {}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
continue; // thinking 不单独成消息(无独立回传语义)
|
|
145
|
+
}
|
|
126
146
|
if (e.kind === 'system')
|
|
127
147
|
out.push({ role: 'system', content: e.payload });
|
|
128
148
|
else if (e.kind === 'user')
|
|
129
149
|
out.push({ role: 'user', content: e.payload });
|
|
130
150
|
else if (e.kind === 'assistant')
|
|
131
151
|
out.push({ role: 'assistant', content: e.payload });
|
|
132
|
-
else if (e.kind === 'toolCall')
|
|
133
|
-
|
|
152
|
+
else if (e.kind === 'toolCall') {
|
|
153
|
+
const msg = { role: 'assistant', toolCall: e.payload };
|
|
154
|
+
if (pendingThinking) {
|
|
155
|
+
msg.thinking = {
|
|
156
|
+
text: pendingThinking.text,
|
|
157
|
+
...(pendingThinking.signature ? { signature: pendingThinking.signature } : {}),
|
|
158
|
+
...(pendingThinking.redacted ? { redacted: true } : {}),
|
|
159
|
+
};
|
|
160
|
+
pendingThinking = null; // 首 toolCall 消费即清——同 turn 多 toolCall = 同一原始消息,单块重放
|
|
161
|
+
}
|
|
162
|
+
out.push(msg);
|
|
163
|
+
}
|
|
134
164
|
else if (e.kind === 'toolResult') {
|
|
135
165
|
const id = e.payload?.id;
|
|
136
166
|
if (typeof id === 'string' && lastSeqById.get(id) !== e.seq)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lqc123qwe/car-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.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 } : {}),
|
|
@@ -81,7 +81,14 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
81
81
|
const messages = log.deriveMessages(undefined, { activeTurnId: turnId }).map(m => ({
|
|
82
82
|
role: m.role as 'system' | 'user' | 'assistant' | 'toolResult',
|
|
83
83
|
content: (m as unknown as { toolCall?: unknown }).toolCall !== undefined
|
|
84
|
-
? {
|
|
84
|
+
? {
|
|
85
|
+
toolCall: (m as unknown as { toolCall: unknown }).toolCall,
|
|
86
|
+
// 1.21(R-TOKENHUB-1 / D-T3/D-T4):投影层附加的同 turn thinking 三元组随 toolCall
|
|
87
|
+
// 载荷透传(适配器回传消费——toAnthropic 前置 thinking 块;openai 形忽略该键)
|
|
88
|
+
...((m as unknown as { thinking?: unknown }).thinking !== undefined
|
|
89
|
+
? { thinking: (m as unknown as { thinking: unknown }).thinking }
|
|
90
|
+
: {}),
|
|
91
|
+
}
|
|
85
92
|
: m.content,
|
|
86
93
|
}))
|
|
87
94
|
|
|
@@ -89,6 +96,11 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
89
96
|
const redactor = new StreamRedactor()
|
|
90
97
|
let text = ''
|
|
91
98
|
const calls = new Map<number, { id?: string; name?: string; argsBuf: string }>()
|
|
99
|
+
// 1.21(R-TOKENHUB-1 / D-T1/D-T2):thinking 捕获累积——禁改写(回传签名完整性 > 脱敏
|
|
100
|
+
// 改写),provider 事实字节保真;redacted_thinking = data 全文单发覆盖
|
|
101
|
+
let thinkingText = ''
|
|
102
|
+
let thinkingSignature = ''
|
|
103
|
+
let thinkingRedacted: string | undefined
|
|
92
104
|
let stopReason: ModelStep['stopReason'] | undefined
|
|
93
105
|
let errorDetail: string | undefined
|
|
94
106
|
let usage: LlmUsage | undefined
|
|
@@ -124,6 +136,10 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
124
136
|
if (chunk.toolCallDelta.argumentsDelta) cur.argsBuf += chunk.toolCallDelta.argumentsDelta
|
|
125
137
|
calls.set(idx, cur)
|
|
126
138
|
}
|
|
139
|
+
// 1.21(R-TOKENHUB-1 / D-T1):thinking 三面捕获(text/signature 增量累积、redacted 单发覆盖)
|
|
140
|
+
if (chunk.thinkingDelta !== undefined) thinkingText += chunk.thinkingDelta
|
|
141
|
+
if (chunk.thinkingSignatureDelta !== undefined) thinkingSignature += chunk.thinkingSignatureDelta
|
|
142
|
+
if (chunk.thinkingRedacted !== undefined) thinkingRedacted = chunk.thinkingRedacted
|
|
127
143
|
if (chunk.error) errorDetail = chunk.error.message
|
|
128
144
|
if (chunk.usage) usage = chunk.usage // D-33:附着于最后一个 chunk——后到覆盖
|
|
129
145
|
if (chunk.finishReason !== undefined) stopReason = chunk.finishReason === 'aborted' ? 'error' : chunk.finishReason
|
|
@@ -157,6 +173,21 @@ export async function chatStep(opts: ChatStepOptions): Promise<ChatStepResult> {
|
|
|
157
173
|
})
|
|
158
174
|
}
|
|
159
175
|
|
|
176
|
+
// 5a. 1.21(R-TOKENHUB-1 / D-T2):thinking 事件落链(actor='model',先于 assistant/toolCall
|
|
177
|
+
// ——投影层按同 turn 首 toolCall 附加回传)。payload = thinking 原文禁改写(签名完整性 >
|
|
178
|
+
// 脱敏改写——改写即回传校验失败);secretsRedacted 仅计数留痕(审计信号,不复写);
|
|
179
|
+
// redacted 形态 payload = data 全文(meta.redacted=true)。无 thinking chunk = 零新事件
|
|
180
|
+
// (D-T5 字节兼容,链形态与 1.20 全等)。
|
|
181
|
+
if (thinkingText.length || thinkingRedacted !== undefined) {
|
|
182
|
+
const thinkingPayload = thinkingRedacted ?? thinkingText
|
|
183
|
+
const secretsInThinking = scanSecrets(thinkingPayload).hits.length
|
|
184
|
+
log.append('model', 'thinking', turnId, thinkingPayload, {
|
|
185
|
+
...(thinkingRedacted !== undefined ? { redacted: true } : {}),
|
|
186
|
+
...(thinkingSignature ? { signature: thinkingSignature } : {}),
|
|
187
|
+
...(secretsInThinking ? { secretsRedacted: secretsInThinking } : {}),
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
160
191
|
// 5. assistant 文本落 M7(脱敏后 + 计数留痕;空文本不产事件)
|
|
161
192
|
// flush 后兜底复扫只计数不复写:流式遮蔽已生效,>0 即流式边界逃逸的审计信号
|
|
162
193
|
let secretsRedacted = 0
|
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>> = []
|
|
@@ -611,12 +673,25 @@ function toAnthropic(req: LlmRequest, apiKey: string) {
|
|
|
611
673
|
flushToolResults()
|
|
612
674
|
if (m.role === 'assistant' && m.content != null && typeof m.content === 'object' && 'toolCall' in (m.content as object)) {
|
|
613
675
|
const tc = (m.content as { toolCall: { id: string; tool: string; args: unknown } }).toolCall
|
|
614
|
-
|
|
676
|
+
// 1.21(R-TOKENHUB-1 / D-T4):工具环回传——投影层附加的 thinking(同 turn 原始 assistant
|
|
677
|
+
// 消息的 thinking 块)前置重放(Anthropic 规范:thinking 须位于同消息 text/tool_use 之前;
|
|
678
|
+
// tokenhub 等网关对缺回传 400——R-TOKENHUB-1 实证)。无 thinking = 形态与 1.20 逐字节一致
|
|
679
|
+
// (D-T5 兼容线,golden 不动)。
|
|
680
|
+
const th = (m.content as { thinking?: { text?: string; signature?: string; redacted?: boolean } }).thinking
|
|
681
|
+
const thinkingBlock = th?.text?.length
|
|
682
|
+
? th.redacted
|
|
683
|
+
? { type: 'redacted_thinking', data: th.text }
|
|
684
|
+
: { type: 'thinking', thinking: th.text, ...(th.signature ? { signature: th.signature } : {}) }
|
|
685
|
+
: undefined
|
|
686
|
+
turns.push({ role: 'assistant', content: [...(thinkingBlock ? [thinkingBlock] : []), { type: 'tool_use', id: tc.id, name: tc.tool, input: tc.args ?? {} }] })
|
|
615
687
|
} else {
|
|
616
688
|
turns.push({ role: m.role, content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content) })
|
|
617
689
|
}
|
|
618
690
|
}
|
|
619
691
|
flushToolResults()
|
|
692
|
+
// 1.20-K1(D-48 P2):滚动断点在白名单命中时挂尾部消息(缺省 1 个)——system 断点(P1)
|
|
693
|
+
// 与消息断点独立,off 时两者俱灭(rollingCacheAllowed 内已连坐)
|
|
694
|
+
if (cache.rolling) attachRollingBreakpoints(turns, cache.rollingBreakpoints)
|
|
620
695
|
// 1.14-S1(K-MP-1 / D-39):思考参数——effort → thinking.budget_tokens(ANTHROPIC_THINKING_BUDGET 表);
|
|
621
696
|
// off/缺席不下发;budget ≥ max_tokens 违反 Messages API 约束(max_tokens 必须大于 budget_tokens)→
|
|
622
697
|
// 显式报错提示调高(fail-visible,禁静默改写 max_tokens)
|
|
@@ -681,6 +756,9 @@ async function* parseAnthropicSseStream(body: AsyncIterable<Uint8Array>, model:
|
|
|
681
756
|
const block = evt.content_block
|
|
682
757
|
if (block?.type === 'tool_use') {
|
|
683
758
|
yield { toolCallDelta: { index: evt.index, id: block.id, name: block.name } }
|
|
759
|
+
} else if (block?.type === 'redacted_thinking' && typeof block.data === 'string' && block.data.length) {
|
|
760
|
+
// 1.21(R-TOKENHUB-1 / D-T1):redacted_thinking 全文单发(回传形态 {type:'redacted_thinking',data})
|
|
761
|
+
yield { thinkingRedacted: block.data }
|
|
684
762
|
}
|
|
685
763
|
} else if (type === 'content_block_delta') {
|
|
686
764
|
const d = evt.delta
|
|
@@ -688,8 +766,13 @@ async function* parseAnthropicSseStream(body: AsyncIterable<Uint8Array>, model:
|
|
|
688
766
|
yield { delta: d.text }
|
|
689
767
|
} else if (d?.type === 'input_json_delta' && typeof d.partial_json === 'string' && d.partial_json.length) {
|
|
690
768
|
yield { toolCallDelta: { index: evt.index, argumentsDelta: d.partial_json } }
|
|
769
|
+
} else if (d?.type === 'thinking_delta' && typeof d.thinking === 'string' && d.thinking.length) {
|
|
770
|
+
// 1.21(R-TOKENHUB-1 / D-T1):thinking 文本增量捕获(原容错跳过 → 落链 + 工具环回传)
|
|
771
|
+
yield { thinkingDelta: d.thinking }
|
|
772
|
+
} else if (d?.type === 'signature_delta' && typeof d.signature === 'string' && d.signature.length) {
|
|
773
|
+
yield { thinkingSignatureDelta: d.signature }
|
|
691
774
|
}
|
|
692
|
-
//
|
|
775
|
+
// citations_delta 等其余非 CAR 消费面:容错跳过
|
|
693
776
|
} else if (type === 'message_start') {
|
|
694
777
|
// 1.11-S1(D-33):input 侧采集(usage 嵌套在 message 下)
|
|
695
778
|
const u = (evt.message as Record<string, unknown> | undefined)?.usage
|
|
@@ -718,6 +801,8 @@ async function* parseAnthropicSseStream(body: AsyncIterable<Uint8Array>, model:
|
|
|
718
801
|
|
|
719
802
|
export function createAnthropicAdapter(opts: AnthropicAdapterOptions): LlmAdapter & { id: string } {
|
|
720
803
|
const id = opts.id ?? 'anthropic'
|
|
804
|
+
// 1.20-K1(D-48 P2):滚动断点求值身份(每请求经 rollingCacheAllowed 对 env 求值,非构造期定值)
|
|
805
|
+
const rollingIdentity = opts.rollingIdentity ?? id
|
|
721
806
|
const baseUrl = opts.baseUrl.replace(/\/+$/, '')
|
|
722
807
|
assertTlsOrLoopback(baseUrl)
|
|
723
808
|
const url = `${baseUrl}/v1/messages`
|
|
@@ -746,7 +831,10 @@ export function createAnthropicAdapter(opts: AnthropicAdapterOptions): LlmAdapte
|
|
|
746
831
|
const ref = opts.credentials.resolve(opts.provider ?? id, credResolveOpts)
|
|
747
832
|
apiKey = opts.credentials.reveal(ref, {})
|
|
748
833
|
}
|
|
749
|
-
return toAnthropic(req, apiKey
|
|
834
|
+
return toAnthropic(req, apiKey, {
|
|
835
|
+
rolling: rollingCacheAllowed(rollingIdentity),
|
|
836
|
+
rollingBreakpoints: rollingBreakpoints(),
|
|
837
|
+
})
|
|
750
838
|
},
|
|
751
839
|
fetchImpl,
|
|
752
840
|
signal: req.signal,
|
|
@@ -76,6 +76,14 @@ export interface LlmChunk {
|
|
|
76
76
|
error?: { code: string; message: string }
|
|
77
77
|
/** 1.11-S1(D-33):每次请求最后一个 chunk 附带归一化用量;缺失 = provider 未回,禁补造 */
|
|
78
78
|
usage?: LlmUsage
|
|
79
|
+
// —— 1.21(R-TOKENHUB-1 / D-T1)thinking 捕获加法——anthropic SSE thinking 面捕获;
|
|
80
|
+
// 禁改写(回传签名完整性 > 脱敏改写);缺失 = provider 未发 thinking,禁补造
|
|
81
|
+
/** thinking 块文本增量(content_block_delta.thinking_delta) */
|
|
82
|
+
thinkingDelta?: string
|
|
83
|
+
/** thinking 块签名增量(signature_delta——真 Anthropic thinking 块回传须携签名) */
|
|
84
|
+
thinkingSignatureDelta?: string
|
|
85
|
+
/** redacted_thinking 块 data 全文(content_block_start 单发;回传形态 {type:'redacted_thinking',data}) */
|
|
86
|
+
thinkingRedacted?: string
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
/** Provider 适配器(ACL,C-02):统一签名,消解 Provider 差异 */
|
package/src/session/log.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { createHash } from 'node:crypto'
|
|
|
11
11
|
import { readFileSync } from 'node:fs'
|
|
12
12
|
import { decodeLogBuffer, splitJsonlLines } from './format.ts'
|
|
13
13
|
|
|
14
|
-
export type EventKind = 'user' | 'assistant' | 'toolCall' | 'toolResult' | 'turnEnd' | 'goalUpdate' | 'hostRaw' | 'fork' | 'system' | 'usage' | 'compact' | 'routing' | 'memory' // M3-S11 加法扩展:多宿主归一化降级通道(pattern 未命中留痕,零静默);M5-DEC4 加法扩展:跨宿主 fork 标记事件;1.4-S2 加法扩展:system prompt 落链(§3.5.5:新模型可见输入 = 新事件类型);1.11-S2 加法扩展:usage 计量事实(D-33,actor='runtime'、model_visible=0 不进模型消息流,deriveMessages 不投影);六值 TurnEndReason 不变;1.14-S2 加法扩展(K-MP-2 / D-40):历史压缩事件(actor='runtime',append-only 只追加——payload={summary, compactedUpTo, ...},投影按 compactedUpTo 前缀折叠为摘要,原事件链不改写);1.14-S4 加法扩展(K-POOL-1 / D-42):模型池路由决策(actor='runtime',payload.type='decide',selected/reason/retries/attempts——禁静默换模型;model_visible=0 不进模型消息流,deriveMessages 不投影——同 usage 纪律);1.15-S1 加法扩展(K-MEM-1 / D-46):白盒记忆操作事件(actor='user' 宿主代理用户操作,turnId='memory' 会话级标识同 'compaction' 先例,payload.type='write'|'edit'|'delete'|'rollback'——审计链同源差异化面,链即版本库〔rollback 恢复数据源=链上 payload.before/after 全文〕;model_visible=0 不进模型消息流——记忆注入走宿主装配层 system ctx 行,deriveMessages 不投影);
|
|
14
|
+
export type EventKind = 'user' | 'assistant' | 'toolCall' | 'toolResult' | 'turnEnd' | 'goalUpdate' | 'hostRaw' | 'fork' | 'system' | 'usage' | 'compact' | 'routing' | 'memory' | 'thinking' // M3-S11 加法扩展:多宿主归一化降级通道(pattern 未命中留痕,零静默);M5-DEC4 加法扩展:跨宿主 fork 标记事件;1.4-S2 加法扩展:system prompt 落链(§3.5.5:新模型可见输入 = 新事件类型);1.11-S2 加法扩展:usage 计量事实(D-33,actor='runtime'、model_visible=0 不进模型消息流,deriveMessages 不投影);六值 TurnEndReason 不变;1.14-S2 加法扩展(K-MP-2 / D-40):历史压缩事件(actor='runtime',append-only 只追加——payload={summary, compactedUpTo, ...},投影按 compactedUpTo 前缀折叠为摘要,原事件链不改写);1.14-S4 加法扩展(K-POOL-1 / D-42):模型池路由决策(actor='runtime',payload.type='decide',selected/reason/retries/attempts——禁静默换模型;model_visible=0 不进模型消息流,deriveMessages 不投影——同 usage 纪律);1.15-S1 加法扩展(K-MEM-1 / D-46):白盒记忆操作事件(actor='user' 宿主代理用户操作,turnId='memory' 会话级标识同 'compaction' 先例,payload.type='write'|'edit'|'delete'|'rollback'——审计链同源差异化面,链即版本库〔rollback 恢复数据源=链上 payload.before/after 全文〕;model_visible=0 不进模型消息流——记忆注入走宿主装配层 system ctx 行,deriveMessages 不投影);1.21 加法扩展(R-TOKENHUB-1 / D-T2):thinking 事件(actor='model',payload=thinking 原文禁改写,meta={signature?, redacted?, secretsRedacted?}——先于 assistant/toolCall 落链;投影 = 同 turn 首个 toolCall 消息附加回传(工具环 reasoning_content 契约,Anthropic thinking+tool_use 规范同款);standalone/隔断/跨 turn 不投影;无 thinking 事件链投影逐字节不变);
|
|
15
15
|
export type Actor = 'user' | 'model' | 'plugin' | 'runtime'
|
|
16
16
|
export interface SessionEvent {
|
|
17
17
|
seq: number
|
|
@@ -146,11 +146,41 @@ export class SessionLog {
|
|
|
146
146
|
}
|
|
147
147
|
const out: Array<{ role: string; [k: string]: unknown }> = []
|
|
148
148
|
if (compactEvent) out.push({ role: 'system', content: ((compactEvent.payload as { summary: string }).summary) })
|
|
149
|
+
// 1.21(R-TOKENHUB-1 / D-T3):thinking 投影——同 turn 内 thinking 事件附加到首个后续
|
|
150
|
+
// toolCall 消息(原始 provider assistant 消息 = [thinking, ...tool_use] 的重放形态);
|
|
151
|
+
// standalone(无后续 toolCall)/ 被 user·system 隔断 / 跨 turn 不投影(实证:普通两轮
|
|
152
|
+
// 历史不要求回传);assistant(text) 同 turn 不隔断(链序 thinking→assistant→toolCall);
|
|
153
|
+
// 无 thinking 事件投影逐字节不变(D-T5 兼容线,N1 快照自洽)。
|
|
154
|
+
let pendingThinking: { turnId: string; text: string; signature?: string; redacted?: boolean } | null = null
|
|
149
155
|
for (const e of projected) {
|
|
156
|
+
if (pendingThinking && (e.turnId !== pendingThinking.turnId || e.kind === 'user' || e.kind === 'system')) pendingThinking = null
|
|
157
|
+
if (e.kind === 'thinking') {
|
|
158
|
+
const p = typeof e.payload === 'string' ? e.payload : ''
|
|
159
|
+
if (p.length || e.meta?.redacted === true) {
|
|
160
|
+
pendingThinking = {
|
|
161
|
+
turnId: e.turnId,
|
|
162
|
+
text: p,
|
|
163
|
+
...(typeof e.meta?.signature === 'string' && e.meta.signature ? { signature: e.meta.signature } : {}),
|
|
164
|
+
...(e.meta?.redacted === true ? { redacted: true } : {}),
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
continue // thinking 不单独成消息(无独立回传语义)
|
|
168
|
+
}
|
|
150
169
|
if (e.kind === 'system') out.push({ role: 'system', content: e.payload })
|
|
151
170
|
else if (e.kind === 'user') out.push({ role: 'user', content: e.payload })
|
|
152
171
|
else if (e.kind === 'assistant') out.push({ role: 'assistant', content: e.payload })
|
|
153
|
-
else if (e.kind === 'toolCall')
|
|
172
|
+
else if (e.kind === 'toolCall') {
|
|
173
|
+
const msg: { role: string; [k: string]: unknown } = { role: 'assistant', toolCall: e.payload }
|
|
174
|
+
if (pendingThinking) {
|
|
175
|
+
msg.thinking = {
|
|
176
|
+
text: pendingThinking.text,
|
|
177
|
+
...(pendingThinking.signature ? { signature: pendingThinking.signature } : {}),
|
|
178
|
+
...(pendingThinking.redacted ? { redacted: true } : {}),
|
|
179
|
+
}
|
|
180
|
+
pendingThinking = null // 首 toolCall 消费即清——同 turn 多 toolCall = 同一原始消息,单块重放
|
|
181
|
+
}
|
|
182
|
+
out.push(msg)
|
|
183
|
+
}
|
|
154
184
|
else if (e.kind === 'toolResult') {
|
|
155
185
|
const id = (e.payload as { id?: unknown } | null)?.id
|
|
156
186
|
if (typeof id === 'string' && lastSeqById.get(id) !== e.seq) continue // D-23:后续有同 id 最终结果,本条为授权中间记录
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"base64Signature":"MEUCIBP09C8Zaq0IyLxblaFx3lzHcTPG7VbHr9fnhH/1FijbAiEApJUnN0KBsXmmN7YnGoiCSLxATkO1heeQsrlkaPSCh8g=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc1akNDQm15Z0F3SUJBZ0lVUlI0MW81WkdEMy9DajBSdkdLMmhzSnNXV0hFd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1UQXhNRGswTlRNMFdoY05Nall4TVRBeE1EazFOVE0wV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVuZXB0SDdzdGxlL2h4ZHRLZ0l6Mm1MdHBmY1lhQVMrb05tWVAKUTM1WSs4aDQwdTRKcmhQeUIzdU56Y050RnpEWlIyRGU5SHlSYktRL1hBallhYTVwaXFPQ0JZc3dnZ1dITUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakU1TGpBd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLR1JqTm1Sak5EQTBZVGN4T0RkaU9UQXpOamRtWldNMwpaRFk1WXpaaVlqSmlPV0V5T1RObVpEZ3dGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNVGt1TURBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakU1TGpBd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENoawpZelprWXpRd05HRTNNVGczWWprd016WTNabVZqTjJRMk9XTTJZbUl5WWpsaE1qa3pabVE0TUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENoawpZelprWXpRd05HRTNNVGczWWprd016WTNabVZqTjJRMk9XTTJZbUl5WWpsaE1qa3pabVE0TUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNVGt1TURBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpFNUxqQXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2hrWXpaa1l6UXdOR0UzTVRnM1lqa3dNelkzWm1Wak4yUTJPV00yWW1JeVlqbGhNamt6Wm1RNE1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6UXhNamswTkRnMApOakV3TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eE9TNHdNSUdKQmdvckJnRUVBZFo1QWdRQ0JIc0VlUUIzQUhVQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2hsb0FGM2dBQUJBTUFSakJFCkFpQjVjUGZEb0pVN1EvVTFmdHhtWWlGcyt4ZktQRXQ3ZVBhOWU4MFhaamRYMHdJZ1ZVY3E5UE4ydG03QUpMK08KamdmNVpFNVFvVnFSSW03RzE3Y0ZQUjdva0RVd0NnWUlLb1pJemowRUF3TURhQUF3WlFJeEFPMkNia0VTNmlDTQprUk4wbEZqUkxVVVdwZ1FrMkxQc3FmOTVteVZObDJQLzMvQUtXWTJkUmRnSTlLMThUZ2RGWVFJd1BQQ2VhZlRMCi9nNnpNSTc1bXFLY0ZFVmhqVlJJZnRyMHN0RlRTZkRkT1paR2UrcjFXN2dMUC81alREUTVHZ2ZkCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K","rekorBundle":{"SignedEntryTimestamp":"MEUCIQCOsnCzp5zMWX8CrzrAKjTznpryixWK9TKSOJ2EpPJZ9AIgWycT72YXDKqslN0wfQXBnY1Br76C62gxBCtgx5cF/kg=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0OWQxOGVhMmEzZDAwMWQzYzY3YzUzYmQ2M2U0MjIzOTNhOTQ0ZmI0ZGVjZTRmNTJjNzA5YzUwYWRlMmM0MGJlIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQlAwOUM4WmFxMEl5THhibGFGeDNsekhjVFBHN1ZiSHI5Zm5oSC8xRmlqYkFpRUFwSlVuTjBLQnNYbW1ON1luR29pQ1NMeEFUa08xaGVlUXNybGthUFNDaDhnPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzFha05EUW0xNVowRjNTVUpCWjBsVlVsSTBNVzgxV2tkRU15OURhakJTZGtkTE1taHpTbk5YVjBoRmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFVUVhoTlJHc3dUbFJOTUZkb1kwNU5hbGw0VFZSQmVFMUVhekZPVkUwd1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ1WlhCMFNEZHpkR3hsTDJoNFpIUkxaMGw2TW0xTWRIQm1ZMWxoUVZNcmIwNXRXVkFLVVRNMVdTczRhRFF3ZFRSS2NtaFFlVUl6ZFU1NlkwNTBSbnBFV2xJeVJHVTVTSGxTWWt0UkwxaEJhbGxoWVRWd2FYRlBRMEpaYzNkbloxZElUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha1UxVEdwQmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUjFKcVRtMVNhazVFUVRCWlZHTjRUMFJrYVU5VVFYcE9hbVJ0V2xkTk13cGFSRmsxV1hwYWFWbHFTbWxQVjBWNVQxUk9iVnBFWjNkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOVkd0MVRVUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrVTFUR3BCZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm9hd3BaZWxwcldYcFJkMDVIUlROTlZHY3pXV3ByZDAxNldUTmFiVlpxVGpKUk1rOVhUVEpaYlVsNVdXcHNhRTFxYTNwYWJWRTBUVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVOb2F3cFplbHByV1hwUmQwNUhSVE5OVkdjeldXcHJkMDE2V1ROYWJWWnFUakpSTWs5WFRUSlpiVWw1V1dwc2FFMXFhM3BhYlZFMFRVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOVkd0MVRVUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BGTlV4cVFYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJocldYcGFhMWw2VVhkT1IwVXpUVlJuTTFscWEzZE5lbGt6V20xV2FrNHlVVEpQVjAweVdXMUplVmxxYkdoTmFtdDZXbTFSTkUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlVYaE5hbXN3VGtSbk1BcE9ha1YzVERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZUU5VE5IZE5TVWRLUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJYzBWbFVVSXpRVWhWUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyaHNiMEZHTTJkQlFVSkJUVUZTYWtKRkNrRnBRalZqVUdaRWIwcFZOMUV2VlRGbWRIaHRXV2xHY3l0NFprdFFSWFEzWlZCaE9XVTRNRmhhYW1SWU1IZEpaMVpWWTNFNVVFNHlkRzAzUVVwTUswOEthbWRtTlZwRk5WRnZWbkZTU1cwM1J6RTNZMFpRVWpkdmEwUlZkME5uV1VsTGIxcEplbW93UlVGM1RVUmhRVUYzV2xGSmVFRlBNa05pYTBWVE5tbERUUXByVWs0d2JFWnFVa3hWVlZkd1oxRnJNa3hRYzNGbU9UVnRlVlpPYkRKUUx6TXZRVXRYV1RKa1VtUm5TVGxMTVRoVVoyUkdXVkZKZDFCUVEyVmhabFJNQ2k5bk5ucE5TVGMxYlhGTFkwWkZWbWhxVmxKSlpuUnlNSE4wUmxSVFprUmtUMXBhUjJVcmNqRlhOMmRNVUM4MWFsUkVVVFZIWjJaa0NpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19","integratedTime":1793526337,"logIndex":3625869466,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
|