xiaozuoassistant 0.1.44 → 0.1.45

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.
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.top-0{top:0}.z-10{z-index:10}.z-50{z-index:50}.z-\[60\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.flex{display:flex}.table{display:table}.h-16{height:4rem}.h-2{height:.5rem}.h-4{height:1rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[200px\]{max-height:200px}.max-h-\[70vh\]{max-height:70vh}.max-h-\[90vh\]{max-height:90vh}.min-h-\[300px\]{min-height:300px}.min-h-\[50px\]{min-height:50px}.w-16{width:4rem}.w-8{width:2rem}.w-\[260px\]{width:260px}.w-full{width:100%}.w-screen{width:100vw}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-\[46vw\]{max-width:46vw}.max-w-\[85\%\]{max-width:85%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-bl-none{border-bottom-left-radius:0}.rounded-br-none{border-bottom-right-radius:0}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-none{border-style:none}.border-black\/10{border-color:#0000001a}.border-blue-100{--tw-border-opacity: 1;border-color:rgb(219 234 254 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.bg-black\/20{background-color:#0003}.bg-black\/50{background-color:#00000080}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-teal-600{--tw-bg-opacity: 1;background-color:rgb(13 148 136 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/80{background-color:#fffc}.fill-current{fill:currentColor}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[15px\]{font-size:15px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.accent-blue-600{accent-color:#2563eb}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}:root{font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;line-height:1.5;font-weight:400;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.custom-scrollbar::-webkit-scrollbar{width:5px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:#9b9b9b33;border-radius:10px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:#9b9b9b66}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.selection\:bg-blue-100 *::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.selection\:bg-blue-100 *::selection{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.selection\:text-blue-900 *::-moz-selection{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.selection\:text-blue-900 *::selection{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.selection\:bg-blue-100::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.selection\:bg-blue-100::selection{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.selection\:text-blue-900::-moz-selection{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.selection\:text-blue-900::selection{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.placeholder\:text-gray-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.placeholder\:text-gray-400::placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.focus-within\:border-blue-400:focus-within{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.focus-within\:ring-2:focus-within{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-within\:ring-blue-100:focus-within{--tw-ring-opacity: 1;--tw-ring-color: rgb(219 234 254 / var(--tw-ring-opacity, 1))}.hover\:bg-blue-50:hover{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800\/50:hover{background-color:#1f293780}.hover\:bg-teal-700:hover{--tw-bg-opacity: 1;background-color:rgb(15 118 110 / var(--tw-bg-opacity, 1))}.hover\:text-blue-300:hover{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.hover\:text-blue-700:hover{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-0:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-70:disabled{opacity:.7}.group:hover .group-hover\:rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.group:hover .group-hover\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:opacity-100{opacity:1}
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍇</text></svg>" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>xiaozuoAssistant</title>
8
- <script type="module" crossorigin src="/assets/index-BPATxdcV.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-CgL5gMVL.css">
8
+ <script type="module" crossorigin src="/assets/index-BfEuvtsz.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-C72E_nIr.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -16,11 +16,20 @@
16
16
  "title": "xiaozuoAssistant",
17
17
  "newChat": "New chat",
18
18
  "recent": "Recent",
19
- "defaultSession": "Default Session",
20
- "newConversation": "New Conversation",
21
19
  "settings": "Settings",
20
+ "editSession": "Edit session",
22
21
  "deleteConfirm": "Are you sure you want to delete this session?"
23
22
  },
23
+ "sessionMeta": {
24
+ "title": "Session Settings",
25
+ "aliasLabel": "Alias (optional)",
26
+ "aliasPlaceholder": "e.g., Project A / Requirements",
27
+ "aliasHint": "Alias is shown in the list and header. Empty falls back to session ID.",
28
+ "workspaceLabel": "Workspace",
29
+ "workspaceHint": "File tools default to this workspace and are restricted to it.",
30
+ "selectWorkspace": "Select workspace",
31
+ "selectCurrentFolder": "Select current folder"
32
+ },
24
33
  "settings": {
25
34
  "title": "Settings",
26
35
  "tabs": {
@@ -16,11 +16,20 @@
16
16
  "title": "xiaozuoAssistant",
17
17
  "newChat": "新对话",
18
18
  "recent": "最近",
19
- "defaultSession": "默认会话",
20
- "newConversation": "新会话",
21
19
  "settings": "设置",
20
+ "editSession": "编辑会话",
22
21
  "deleteConfirm": "确定要删除这个会话吗?"
23
22
  },
23
+ "sessionMeta": {
24
+ "title": "会话设置",
25
+ "aliasLabel": "会话别名(可选)",
26
+ "aliasPlaceholder": "例如:项目A / 需求讨论",
27
+ "aliasHint": "列表与聊天头部将优先展示别名。留空将回退显示会话 ID。",
28
+ "workspaceLabel": "Workspace(工作目录)",
29
+ "workspaceHint": "文件相关工具默认以该目录为工作区,并限制访问范围。",
30
+ "selectWorkspace": "选择工作目录",
31
+ "selectCurrentFolder": "选择当前目录"
32
+ },
24
33
  "settings": {
25
34
  "title": "设置",
26
35
  "tabs": {
@@ -0,0 +1,24 @@
1
+ import path from 'path';
2
+ export function defaultWorkspacePath() {
3
+ return path.resolve(process.cwd(), 'workspace');
4
+ }
5
+ export function resolveSessionWorkspace(ctx) {
6
+ const fromSession = ctx?.session?.workspace;
7
+ const fromMetadata = ctx?.metadata?.workspace;
8
+ const candidate = typeof fromSession === 'string' ? fromSession : (typeof fromMetadata === 'string' ? fromMetadata : '');
9
+ const trimmed = candidate.trim();
10
+ if (!trimmed)
11
+ return defaultWorkspacePath();
12
+ return path.resolve(trimmed);
13
+ }
14
+ export function resolvePathWithinWorkspace(workspace, targetPath) {
15
+ const ws = path.resolve(workspace);
16
+ const abs = path.isAbsolute(targetPath) ? path.resolve(targetPath) : path.resolve(ws, targetPath);
17
+ const rel = path.relative(ws, abs);
18
+ if (!rel || rel === '.')
19
+ return abs;
20
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
21
+ throw new Error('Path is outside of workspace');
22
+ }
23
+ return abs;
24
+ }
@@ -19,7 +19,7 @@ export class AgentRuntime {
19
19
  getToolsDefinition() {
20
20
  return this.skills.map(skill => skill.toJSON());
21
21
  }
22
- async process(history, newMessage, contextPrompt) {
22
+ async process(history, newMessage, contextPrompt, context) {
23
23
  const finalSystemPrompt = contextPrompt
24
24
  ? `${this.systemPrompt}\n${contextPrompt}`
25
25
  : this.systemPrompt;
@@ -47,7 +47,10 @@ export class AgentRuntime {
47
47
  let toolResult = '';
48
48
  if (skill) {
49
49
  try {
50
- const result = await skill.execute(functionArgs);
50
+ const ctxForTool = context
51
+ ? { ...context, metadata: { ...(context.metadata || {}), toolCall: { id: toolCall.id, name: functionName } } }
52
+ : undefined;
53
+ const result = await skill.execute(functionArgs, ctxForTool);
51
54
  toolResult = JSON.stringify(result);
52
55
  }
53
56
  catch (error) {
@@ -16,7 +16,7 @@ export class Brain {
16
16
  updateClient() {
17
17
  this.openai = createOpenAIClient(config.llm);
18
18
  }
19
- async processMessage(history, newMessage, systemPrompt) {
19
+ async processMessage(history, newMessage, systemPrompt, context) {
20
20
  if (process.env.DEBUG)
21
21
  console.log('[Brain] Processing message:', newMessage);
22
22
  const defaultSystemPrompt = systemPrompt || config.systemPrompt || SYSTEM_PROMPT || 'You are xiaozuoAssistant, a helpful AI assistant. You can use tools to help users.';
@@ -62,7 +62,10 @@ export class Brain {
62
62
  let toolResult = '';
63
63
  if (skill) {
64
64
  try {
65
- const result = await skill.execute(functionArgs);
65
+ const ctxForTool = context
66
+ ? { ...context, metadata: { ...(context.metadata || {}), toolCall: { id: toolCall.id, name: functionName } } }
67
+ : undefined;
68
+ const result = await skill.execute(functionArgs, ctxForTool);
66
69
  toolResult = JSON.stringify(result);
67
70
  }
68
71
  catch (error) {
@@ -15,8 +15,8 @@ export class MemoryManager {
15
15
  return MemoryManager.instance;
16
16
  }
17
17
  // --- Layer 1: Short-term (Session) ---
18
- async createSession() {
19
- return this.shortTerm.createSession();
18
+ async createSession(input) {
19
+ return this.shortTerm.createSession(input);
20
20
  }
21
21
  async getSession(id) {
22
22
  return this.shortTerm.getSession(id);
@@ -24,6 +24,9 @@ export class MemoryManager {
24
24
  async listSessions() {
25
25
  return this.shortTerm.listSessions();
26
26
  }
27
+ async updateSessionMeta(id, patch) {
28
+ return this.shortTerm.updateSessionMeta(id, patch);
29
+ }
27
30
  async deleteSession(id) {
28
31
  return this.shortTerm.deleteSession(id);
29
32
  }
@@ -42,8 +45,7 @@ export class MemoryManager {
42
45
  // but provide the API for the Brain to call explicitly.
43
46
  }
44
47
  async getHistory(sessionId) {
45
- const session = await this.shortTerm.getSession(sessionId);
46
- return session ? session.messages : [];
48
+ return this.shortTerm.getMessages(sessionId);
47
49
  }
48
50
  // --- Retrieval ---
49
51
  async getRelevantContext(query, sessionId) {
@@ -2,11 +2,21 @@ import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  const SESSIONS_DIR = path.resolve(process.cwd(), 'sessions');
5
+ const INDEX_FILE = path.join(SESSIONS_DIR, 'index.json');
6
+ function defaultWorkspacePath() {
7
+ return path.resolve(process.cwd(), 'workspace');
8
+ }
9
+ function normalizeWorkspace(input) {
10
+ const workspace = (input ?? '').trim();
11
+ if (!workspace)
12
+ return defaultWorkspacePath();
13
+ return path.resolve(workspace);
14
+ }
5
15
  export class ShortTermMemory {
6
16
  constructor() {
7
- this.sessionCache = new Map();
17
+ this.metaCache = new Map();
18
+ this.initPromise = null;
8
19
  fs.ensureDirSync(SESSIONS_DIR);
9
- this.ensureDefaultSession();
10
20
  }
11
21
  static getInstance() {
12
22
  if (!ShortTermMemory.instance) {
@@ -14,115 +24,232 @@ export class ShortTermMemory {
14
24
  }
15
25
  return ShortTermMemory.instance;
16
26
  }
17
- async ensureDefaultSession() {
18
- const defaultSessionId = 'default-session';
19
- const filePath = path.join(SESSIONS_DIR, `${defaultSessionId}.json`);
20
- if (!await fs.pathExists(filePath)) {
21
- const session = {
22
- id: defaultSessionId,
23
- createdAt: Date.now(),
24
- updatedAt: Date.now(),
25
- messages: []
26
- };
27
- await this.saveSession(session);
27
+ async ensureReady() {
28
+ if (!this.initPromise) {
29
+ this.initPromise = this.init();
30
+ }
31
+ await this.initPromise;
32
+ }
33
+ async init() {
34
+ fs.ensureDirSync(SESSIONS_DIR);
35
+ const indexExists = await fs.pathExists(INDEX_FILE);
36
+ if (indexExists) {
37
+ await this.warmMetaCache();
38
+ return;
39
+ }
40
+ await this.migrateLegacyIfNeeded();
41
+ const existsAfter = await fs.pathExists(INDEX_FILE);
42
+ if (!existsAfter) {
43
+ await fs.writeJson(INDEX_FILE, [], { spaces: 2 });
44
+ }
45
+ await this.warmMetaCache();
46
+ }
47
+ getSessionDir(sessionId) {
48
+ return path.join(SESSIONS_DIR, sessionId);
49
+ }
50
+ getMessagesFile(sessionId) {
51
+ return path.join(this.getSessionDir(sessionId), 'messages.json');
52
+ }
53
+ async loadIndex() {
54
+ try {
55
+ const data = await fs.readJson(INDEX_FILE);
56
+ if (Array.isArray(data))
57
+ return data;
58
+ return [];
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ }
64
+ async saveIndex(metas) {
65
+ await fs.writeJson(INDEX_FILE, metas, { spaces: 2 });
66
+ }
67
+ async warmMetaCache() {
68
+ const metas = await this.loadIndex();
69
+ this.metaCache.clear();
70
+ for (const meta of metas) {
71
+ if (meta && typeof meta.id === 'string') {
72
+ this.metaCache.set(meta.id, meta);
73
+ }
74
+ }
75
+ }
76
+ async migrateLegacyIfNeeded() {
77
+ const files = await fs.readdir(SESSIONS_DIR);
78
+ const legacyFiles = files.filter(f => f.endsWith('.json') && f !== 'index.json');
79
+ if (legacyFiles.length === 0)
80
+ return;
81
+ const metas = [];
82
+ for (const file of legacyFiles) {
83
+ const filePath = path.join(SESSIONS_DIR, file);
84
+ try {
85
+ const legacy = await fs.readJson(filePath);
86
+ if (!legacy || typeof legacy.id !== 'string')
87
+ continue;
88
+ const id = legacy.id;
89
+ const createdAt = typeof legacy.createdAt === 'number' ? legacy.createdAt : Date.now();
90
+ const updatedAt = typeof legacy.updatedAt === 'number' ? legacy.updatedAt : Date.now();
91
+ const messages = Array.isArray(legacy.messages) ? legacy.messages : [];
92
+ const meta = {
93
+ id,
94
+ createdAt,
95
+ updatedAt,
96
+ lastActiveAt: updatedAt,
97
+ workspace: defaultWorkspacePath()
98
+ };
99
+ const sessionDir = this.getSessionDir(id);
100
+ await fs.ensureDir(sessionDir);
101
+ await fs.writeJson(this.getMessagesFile(id), messages, { spaces: 2 });
102
+ metas.push(meta);
103
+ }
104
+ catch (e) {
105
+ console.error(`[ShortTermMemory] Legacy migration failed for ${file}:`, e);
106
+ }
107
+ }
108
+ if (metas.length > 0) {
109
+ metas.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
110
+ await fs.writeJson(INDEX_FILE, metas, { spaces: 2 });
28
111
  }
29
112
  }
30
- async createSession() {
113
+ async createSession(input) {
114
+ await this.ensureReady();
31
115
  const id = uuidv4();
32
- const session = {
116
+ const now = Date.now();
117
+ const meta = {
33
118
  id,
34
- createdAt: Date.now(),
35
- updatedAt: Date.now(),
36
- messages: []
119
+ alias: (input?.alias ?? '').trim() || undefined,
120
+ workspace: normalizeWorkspace(input?.workspace),
121
+ createdAt: now,
122
+ updatedAt: now,
123
+ lastActiveAt: now
37
124
  };
38
- await this.saveSession(session);
39
- return session;
125
+ await fs.ensureDir(meta.workspace);
126
+ const sessionDir = this.getSessionDir(id);
127
+ await fs.ensureDir(sessionDir);
128
+ await fs.writeJson(this.getMessagesFile(id), [], { spaces: 2 });
129
+ const metas = await this.loadIndex();
130
+ metas.unshift(meta);
131
+ await this.saveIndex(metas);
132
+ this.metaCache.set(id, meta);
133
+ return { meta, messages: [] };
134
+ }
135
+ async listSessions() {
136
+ await this.ensureReady();
137
+ const metas = await this.loadIndex();
138
+ metas.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
139
+ return metas;
140
+ }
141
+ async getSessionMeta(id) {
142
+ await this.ensureReady();
143
+ if (this.metaCache.has(id))
144
+ return this.metaCache.get(id);
145
+ const metas = await this.loadIndex();
146
+ const found = metas.find(m => m.id === id) || null;
147
+ if (found)
148
+ this.metaCache.set(id, found);
149
+ return found;
150
+ }
151
+ async updateSessionMeta(id, patch) {
152
+ await this.ensureReady();
153
+ const metas = await this.loadIndex();
154
+ const idx = metas.findIndex(m => m.id === id);
155
+ if (idx < 0)
156
+ throw new Error('Session not found');
157
+ const current = metas[idx];
158
+ const next = {
159
+ ...current,
160
+ alias: patch.alias === null ? undefined : (patch.alias !== undefined ? (String(patch.alias).trim() || undefined) : current.alias),
161
+ workspace: patch.workspace !== undefined ? normalizeWorkspace(String(patch.workspace)) : current.workspace,
162
+ updatedAt: Date.now()
163
+ };
164
+ await fs.ensureDir(next.workspace);
165
+ metas[idx] = next;
166
+ metas.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
167
+ await this.saveIndex(metas);
168
+ this.metaCache.set(id, next);
169
+ return next;
40
170
  }
41
171
  async getSession(id) {
42
- if (this.sessionCache.has(id)) {
43
- return this.sessionCache.get(id);
172
+ const meta = await this.getSessionMeta(id);
173
+ if (!meta)
174
+ return null;
175
+ const msgFile = this.getMessagesFile(id);
176
+ let messages = [];
177
+ try {
178
+ const data = await fs.readJson(msgFile);
179
+ messages = Array.isArray(data) ? data : [];
44
180
  }
45
- const filePath = path.join(SESSIONS_DIR, `${id}.json`);
46
- if (await fs.pathExists(filePath)) {
47
- const data = await fs.readJson(filePath);
48
- this.sessionCache.set(id, data);
49
- return data;
181
+ catch {
182
+ await fs.ensureDir(this.getSessionDir(id));
183
+ await fs.writeJson(msgFile, [], { spaces: 2 });
184
+ messages = [];
50
185
  }
51
- return null;
186
+ return { meta, messages };
52
187
  }
53
- async saveSession(session) {
54
- const filePath = path.join(SESSIONS_DIR, `${session.id}.json`);
55
- session.updatedAt = Date.now();
56
- this.sessionCache.set(session.id, session);
57
- await fs.writeJson(filePath, session, { spaces: 2 });
188
+ async getMessages(sessionId) {
189
+ const session = await this.getSession(sessionId);
190
+ return session ? session.messages : [];
58
191
  }
59
192
  async addMessage(sessionId, message) {
193
+ await this.ensureReady();
60
194
  const session = await this.getSession(sessionId);
61
- if (session) {
62
- session.messages.push(message);
63
- // Keep only last 50 messages in memory for "immediate" context if needed,
64
- // but here we keep full session history for simplicity as per current design.
65
- // Layer 1 specific requirement: "Recent 20-50 turns".
66
- // We can implement truncation logic here if we want to save space,
67
- // but for now let's just save everything to JSON.
68
- await this.saveSession(session);
69
- }
70
- else {
195
+ if (!session)
71
196
  throw new Error(`Session ${sessionId} not found`);
197
+ const msg = { ...message };
198
+ if (!msg.timestamp)
199
+ msg.timestamp = Date.now();
200
+ session.messages.push(msg);
201
+ await fs.writeJson(this.getMessagesFile(sessionId), session.messages, { spaces: 2 });
202
+ const metas = await this.loadIndex();
203
+ const idx = metas.findIndex(m => m.id === sessionId);
204
+ if (idx >= 0) {
205
+ metas[idx] = {
206
+ ...metas[idx],
207
+ updatedAt: Date.now(),
208
+ lastActiveAt: Date.now()
209
+ };
210
+ metas.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
211
+ await this.saveIndex(metas);
212
+ this.metaCache.set(sessionId, metas.find(m => m.id === sessionId));
72
213
  }
73
214
  }
74
215
  async deleteSession(id) {
75
- if (id === 'default-session') {
76
- throw new Error('Cannot delete default session');
77
- }
78
- const filePath = path.join(SESSIONS_DIR, `${id}.json`);
79
- if (await fs.pathExists(filePath)) {
80
- await fs.remove(filePath);
81
- this.sessionCache.delete(id);
82
- }
83
- }
84
- async listSessions() {
85
- const files = await fs.readdir(SESSIONS_DIR);
86
- const sessions = [];
87
- for (const file of files) {
88
- if (file.endsWith('.json')) {
89
- try {
90
- const data = await fs.readJson(path.join(SESSIONS_DIR, file));
91
- sessions.push(data);
92
- }
93
- catch (e) {
94
- console.error(`Error reading session file ${file}:`, e);
95
- }
96
- }
97
- }
98
- return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
216
+ await this.ensureReady();
217
+ const metas = await this.loadIndex();
218
+ const next = metas.filter(m => m.id !== id);
219
+ if (next.length === metas.length)
220
+ throw new Error('Session not found');
221
+ await this.saveIndex(next);
222
+ this.metaCache.delete(id);
223
+ await fs.remove(this.getSessionDir(id));
99
224
  }
100
225
  async cleanupOldSessions(maxAgeDays) {
101
- const files = await fs.readdir(SESSIONS_DIR);
102
- let deletedCount = 0;
226
+ await this.ensureReady();
227
+ const metas = await this.loadIndex();
103
228
  const now = Date.now();
104
229
  const msPerDay = 24 * 60 * 60 * 1000;
105
- for (const file of files) {
106
- if (file.endsWith('.json')) {
107
- const filePath = path.join(SESSIONS_DIR, file);
108
- try {
109
- const session = await fs.readJson(filePath);
110
- // Skip default session
111
- if (session.id === 'default-session')
112
- continue;
113
- const ageDays = (now - session.updatedAt) / msPerDay;
114
- if (ageDays > maxAgeDays) {
115
- console.log(`[ShortTermMemory] Deleting old session: ${session.id} (Age: ${ageDays.toFixed(1)} days)`);
116
- await fs.remove(filePath);
117
- this.sessionCache.delete(session.id);
118
- deletedCount++;
119
- }
120
- }
121
- catch (error) {
122
- console.error(`[ShortTermMemory] Error processing file ${file} for cleanup:`, error);
123
- }
230
+ const keep = [];
231
+ const toDelete = [];
232
+ for (const meta of metas) {
233
+ const updatedAt = typeof meta.updatedAt === 'number' ? meta.updatedAt : 0;
234
+ const ageDays = (now - updatedAt) / msPerDay;
235
+ if (ageDays > maxAgeDays)
236
+ toDelete.push(meta);
237
+ else
238
+ keep.push(meta);
239
+ }
240
+ for (const meta of toDelete) {
241
+ try {
242
+ await fs.remove(this.getSessionDir(meta.id));
243
+ this.metaCache.delete(meta.id);
124
244
  }
245
+ catch (e) {
246
+ console.error(`[ShortTermMemory] Failed to delete session ${meta.id}:`, e);
247
+ }
248
+ }
249
+ if (toDelete.length > 0) {
250
+ keep.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
251
+ await this.saveIndex(keep);
125
252
  }
126
- return deletedCount;
253
+ return toDelete.length;
127
254
  }
128
255
  }