memory-eternal 0.9.2 → 0.9.3
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/README.en.md +1 -0
- package/README.md +1 -0
- package/lib/client.js +1 -1
- package/package.json +2 -2
- package/web/app.js +1 -1
package/README.en.md
CHANGED
|
@@ -246,6 +246,7 @@ npm run build # builds lib/client.js (DSH embed) + web/app.js (standalone web
|
|
|
246
246
|
|
|
247
247
|
| Version | Date | Highlights |
|
|
248
248
|
|---|---|---|
|
|
249
|
+
| **v0.9.3** | 2026-09-12 | **Fixed the blank memory page ("no UI")**: v0.9.1 placed the infinite-scroll `useEffect` **before** the `loadCards` definition — React evaluates the dependency array during render, hitting a TDZ error (`Cannot access 'loadCards' before initialization`) that **crashed the whole memory page on first render**. It now comes after the definition. Added a **render smoke test** (`tests/ssr.test.mjs`: renders the built bundle through all four entry points — cards / settings / usage / graph — wired into `npm test`) so "green build, blank page" bugs cannot ship again |
|
|
249
250
|
| **v0.9.2** | 2026-09-12 | **Log visibility + false-alarm fix**: the capture log is now **persisted to disk** (`~/.dsh/memory-eternal-capture.jsonl`, last 500 entries) — the standalone web page no longer says "log only available inside the DSH host" but shows the records, and history survives a host restart (read back on boot); fixed **stall-detector false alarms flooding the panel** (mismatched turn counters are normal: sub-agent turns and cancelled turns emit no `turn-stopping`; it now requires "turns running **and** no capture activity for 15 minutes" to alert once, and the alarm clears as soon as activity resumes — no more one `fail` line every 10 minutes permanently turning health red); when distillation returns nothing / unparseable JSON the content is **saved as a raw card instead of being dropped** |
|
|
250
251
|
| **v0.9.1** | 2026-09-12 | **Performance & UX batch**: memory page loads lazily per view (opening Settings no longer drags in 250KB of cards + 890KB of graph; first paint needs ~0.5KB); graph generation **3232ms → 43ms** (bigram sets computed once + lossless mathematical-bound pruning + tag inverted index) plus a server-side cache with **fingerprint invalidation** (write/audit/delete rebuilds immediately, no stale reads); site-wide **brotli q5** (graph 877KB → **54KB**, 34% smaller than gzip; cards 246KB → 83KB → 20KB with br) and large responses compressed with both br and gzip keeping the smaller, so no response is ever worse than gzip; `/cards` gained **real pagination** (offset/limit/total + server-side title sort, globally ordered across pages) loading 100 cards first and fetching more on scroll; the "Agent cards" panel in Usage/Today filters by agent server-side (30 rows); **audit red line**: the plugin injects "never call approve/reject on the user's behalf" into the agent context; the watchdog now **reaps redundant instances on fallback ports** (a duplicate web process holding 76MB was reaped in testing) |
|
|
251
252
|
| **v0.9.0** | 2026-09-12 | **Fixed silent auto-capture death (6 days without a single card)**: after a DSH upgrade the session event API moved from `events` to `ownEvents()/snapshotEvents()`; the listener read nothing, returned silently and left no trace — now it tries `ownEvents()` → `snapshotEvents()` → `events` and logs a per-session attach entry. Daily quota now counts **real writes only** (previously every "not worth saving" verdict burned quota, exhausting the 24h budget after 40 attempts); `captureCooldownMs` actually applies; a single capture input is tail-capped at 20k chars. New **capture log** panel and **active failure alerts** (red strip + agent-context injection + turn-stopping stall sweep). Removed the automatic "Daily review" digest (timer / `/todayBrief` route / UI button). |
|
package/README.md
CHANGED
|
@@ -259,6 +259,7 @@ npm run build # 构建 lib/client.js(DSH 内嵌)+ web/app.js(独立 web
|
|
|
259
259
|
|
|
260
260
|
| 版本 | 日期 | 关键改动 |
|
|
261
261
|
|---|---|---|
|
|
262
|
+
| **v0.9.3** | 2026-09-12 | **修复记忆页白屏("没画面")**:v0.9.1 把「滚动续拉」的 `useEffect` 写在了 `loadCards` 定义**之前**——React 在渲染期求值依赖数组,触发 TDZ(`Cannot access 'loadCards' before initialization`),**整个记忆页一渲染就崩**;现已移到定义之后。**新增渲染冒烟测试**(`tests/ssr.test.mjs`:真跑打包产物、把卡片/设置/用量/图谱四个入口各渲染一遍,已并入 `npm test`)——「构建全绿、页面白屏」这类 bug 以后发不出去 |
|
|
262
263
|
| **v0.9.2** | 2026-09-12 | **日志可见性 + 误报修复**:自动沉淀日志**落盘持久化**(`~/.dsh/memory-eternal-capture.jsonl`,最多 500 条)——独立 Web 页不再显示「日志仅在 DSH 宿主内可用」而是直接能看到记录,宿主重启后历史也不丢(启动时读回);修复**停滞探测误报刷屏**(轮次计数对不上其实是常态:子代理轮次、被取消的轮次都不发 `turn-stopping`;现在要求「有轮次在跑 **且** 15 分钟无任何沉淀活动」才报一次,活动恢复即自动撤销,不再每 10 分钟刷一条把面板挤满、健康状态永久亮红);蒸馏无输出/JSON 解析失败时**改为原文卡兜底**,内容不再白丢 |
|
|
263
264
|
| **v0.9.1** | 2026-09-12 | **性能与体验批次**:记忆页按视图**懒加载**(打开「设置」不再顺带拉 250KB 卡片 + 890KB 图谱,首屏只需约 0.5KB);知识图谱生成 **3232ms → 43ms**(bigram 集合只算一次 + 数学上界剪枝无损跳过 + 标签倒排),并加**指纹精确失效**的服务端缓存(写卡/审核/删卡立即重建,无脏读);全站 **brotli q5**(图谱 877KB → **54KB**,比 gzip 再小 34%;卡片 246KB → 83KB → br 20KB),大响应体同时压 br + gzip 取更小者,保证任何响应都不比 gzip 差;`/cards` 支持**真分页**(offset/limit/total + 服务端标题排序,跨页全局有序),首屏只拉 100 条、滚到底续拉;「用量/今日」的 Agent 卡清单改为服务端署名过滤(30 条);**审核红线**:插件在 Agent 上下文注入「禁止调用 approve/reject 代用户审批」;watchdog **回收备用端口的多余实例**(实测清掉一个常驻 76MB 的重复 web 进程) |
|
|
264
265
|
| **v0.9.0** | 2026-09-12 | **修复自动沉淀静默失效(一连 6 天不写卡)**:DSH 升级后会话事件接口由 `events` 改为 `ownEvents()/snapshotEvents()`,监听器读不到事件后静默返回、不留任何痕迹 —— 现改为三级自适应取法(`ownEvents()` → `snapshotEvents()` → `events`)+ 每会话接入留痕;日配额改为**只统计真实写卡**(此前「不值得保存」的判定也照样扣配额,40 次即耗尽 24h 配额)、`captureCooldownMs` 冷却真正生效、单次沉淀输入截尾 20000 字;新增**自动沉淀日志**面板与**异常主动提示**(页面红条 + 注入 Agent 上下文 + 轮次收尾停滞巡检);移除「每日回顾」自动简报(定时器 / `/todayBrief` 接口 / UI 按钮) |
|
package/lib/client.js
CHANGED
|
@@ -219,7 +219,7 @@ Option A, because:
|
|
|
219
219
|
.me-graph-ctxmenu button { display: block; width: 100%; text-align: left; padding: 7px 10px; border: none; border-radius: 7px; background: transparent; color: inherit; font: inherit; cursor: pointer; }
|
|
220
220
|
.me-graph-ctxmenu button:hover { background: rgba(255,255,255,0.1); }
|
|
221
221
|
.me-graph-hint { font-size: 11px; opacity: 0.6; margin-left: auto; }
|
|
222
|
-
`;function _t(t){t.effect(()=>t.locale.register(Ge,{zh:Nt,en:zt}),"memory-eternal: locale");let l=t.locale.bind(Ge);t.effect(()=>t.slots.inject("settings.section",()=>t.slots.register({name:"settings.section",id:Ge,order:25,label:()=>l("nav"),locale:Ge,inject:()=>({})},()=>n.default.createElement(Kt,{t:l}))),"memory-eternal: settings section"),t.effect(()=>t.slots.inject("sidebar.footer.action",()=>t.slots.register({name:"sidebar.footer.action",id:`${Ge}:footer`,order:100,label:()=>l("nav"),locale:Ge,inject:()=>({})},d=>n.default.createElement(Yt,{t:l,locale:t.locale,wide:!(d&&d.wide===!1)}))),"memory-eternal: sidebar footer action")}function Ut(){let[t,l]=(0,n.useState)("http://127.0.0.1:7999/");return(0,n.useEffect)(()=>{fetch("/memory-eternal/api/web-info").then(d=>d.json()).then(d=>{d&&d.ok&&d.url&&l(d.url.endsWith("/")?d.url:d.url+"/")}).catch(()=>{})},[]),t}function Kt({t}){return(0,e.jsxs)("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},children:[(0,e.jsx)("style",{children:Ie}),(0,e.jsx)(Tt,{t,onReload:()=>{},version:0})]})}function Yt({t,locale:l,wide:d}){let[v,m]=(0,n.useState)(!1);return(0,e.jsxs)("div",{className:`me-footer${d?"":" rail"}`,children:[(0,e.jsx)("style",{children:Ie}),(0,e.jsxs)("button",{type:"button",className:"me-footer-btn",onClick:()=>m(!0),"aria-label":t("nav"),title:t("nav"),children:[(0,e.jsx)("span",{className:"me-footer-ico","aria-hidden":"true",children:(0,e.jsx)(Lt,{})}),(0,e.jsx)("span",{className:"me-footer-label",children:t("nav")})]}),v&&(0,e.jsx)(qt,{t,locale:l,onClose:()=>m(!1)})]})}function qt({t,locale:l,onClose:d,tab:v}){let[m,i]=(0,n.useState)(!1),A=(0,n.useRef)(null),C=Ut(),L=(0,n.useCallback)(D=>l&&typeof l.subscribe=="function"?l.subscribe(D):()=>{},[l]),S=(0,n.useCallback)(()=>l&&typeof l.getSnapshot=="function"?l.getSnapshot():null,[l]),k=(0,n.useSyncExternalStore)(L,S),y=k&&(k.active==="zh"||k.active==="en")?k.active:null,x=(0,n.useRef)(null);x.current===null&&y&&(x.current=y);let U=(0,n.useMemo)(()=>{let D=x.current?`lang=${x.current}`:"";return v==="config"?`${W}/ui/config?tab=config${D?`&${D}`:""}`:v?`${C.replace(/\/$/,"")}/?tab=${v}${D?`&${D}`:""}`:D?`${C}${C.endsWith("/")?"":"/"}?${D}`:C},[C,v]);return(0,n.useEffect)(()=>{let D=A.current&&A.current.contentWindow;D&&y&&D.postMessage({source:"memory-eternal",type:"locale",lang:y},"*")},[y]),(0,n.useEffect)(()=>{let D=N=>{N.key==="Escape"&&d()};return window.addEventListener("keydown",D),()=>window.removeEventListener("keydown",D)},[d]),(0,e.jsxs)("div",{className:"me-overlay-top",onClick:d,children:[(0,e.jsx)("style",{children:Ie}),(0,e.jsx)("div",{className:"me-modal",onClick:D=>D.stopPropagation(),style:m?{position:"fixed",inset:0,width:"100vw",height:"100vh",maxWidth:"100vw",maxHeight:"100vh",borderRadius:0}:{},children:(0,e.jsxs)("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0,position:"relative"},children:[(0,e.jsxs)("div",{style:{position:"absolute",top:8,right:8,zIndex:10,display:"flex",gap:4,padding:"4px 6px",background:"var(--dsw-alias-bg-overlay, rgba(17,24,39,0.45))",borderRadius:8,backdropFilter:"blur(4px)"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>i(D=>!D),"aria-label":t(m?"exitFullscreen":"enterFullscreen"),title:t(m?"exitFullscreen":"enterFullscreen"),style:{width:26,height:26,borderRadius:6,border:"1px solid rgba(255,255,255,0.18)",background:"rgba(255,255,255,0.08)",color:"#fff",cursor:"pointer",fontSize:14,lineHeight:1,display:"flex",alignItems:"center",justifyContent:"center"},children:m?"\u2921":"\u2922"}),(0,e.jsx)("button",{type:"button",onClick:d,"aria-label":t("close"),title:t("close"),style:{width:26,height:26,borderRadius:6,border:"1px solid rgba(255,255,255,0.18)",background:"rgba(255,255,255,0.08)",color:"#fff",cursor:"pointer",fontSize:16,lineHeight:1,display:"flex",alignItems:"center",justifyContent:"center"},children:"\xD7"})]}),(0,e.jsx)("iframe",{ref:A,src:U,title:"memory-eternal",style:{width:"100%",flex:1,border:0,borderRadius:"inherit",background:"var(--dsw-alias-bg-base, #fff)"}})]})})]})}function Lt(){return(0,e.jsxs)("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:[(0,e.jsx)("defs",{children:(0,e.jsxs)("linearGradient",{id:"mg-brain",x1:"0",y1:"0",x2:"1",y2:"1",children:[(0,e.jsx)("stop",{offset:"0%",stopColor:"#FFC46B"}),(0,e.jsx)("stop",{offset:"50%",stopColor:"#F97316"}),(0,e.jsx)("stop",{offset:"100%",stopColor:"#EA580C"})]})}),(0,e.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 0-6.4.1 4.1 4.1 0 0 0-2.7 5.9 4.1 4.1 0 0 0 .6 6.7A4.2 4.2 0 0 0 12 18.2Z"}),(0,e.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 1 6.4.1 4.1 4.1 0 0 1 2.7 5.9 4.1 4.1 0 0 1-.6 6.7A4.2 4.2 0 0 1 12 18.2Z"}),(0,e.jsx)("path",{d:"M12 9.2c-.8-1.4-1.7-2-3.1-2.2M12 9.2c.8-1.4 1.7-2 3.1-2.2M12 12.4c-1.2 1-3.2 1.6-5 1.4M12 12.4c1.2 1 3.2 1.6 5 1.4M12 13.2v4",fill:"none",stroke:"rgba(255,255,255,0.62)",strokeWidth:"1",strokeLinecap:"round"}),(0,e.jsx)("circle",{cx:"12",cy:"8.6",r:"1.1",fill:"rgba(255,255,255,0.85)"})]})}function Xt({t,inModal:l,onClose:d,onFull:v,full:m}){let[i,A]=(0,n.useState)(null),[C,L]=(0,n.useState)([]),[S,k]=(0,n.useState)(!0),[y,x]=(0,n.useState)(null),[U,D]=(0,n.useState)(""),[N,H]=(0,n.useState)("all"),[w,P]=(0,n.useState)(""),[re,ee]=(0,n.useState)("all"),[E,Le]=(0,n.useState)("all"),[R,be]=(0,n.useState)(()=>{try{let u=new URLSearchParams(typeof location<"u"?location.search:"").get("tab"),j={cards:"cards",graph:"graph",usage:"stats",stats:"stats",audit:"audit",recycle:"optimize",optimize:"optimize",config:"config"};if(u&&j[u])return j[u]}catch{}return"cards"}),[b,ie]=(0,n.useState)(()=>new Set([R])),g=(0,n.useCallback)(u=>{ie(j=>j.has(u)?j:new Set(j).add(u)),be(u)},[]),[I,_]=(0,n.useState)("recent"),[V,F]=(0,n.useState)(null),O=(0,n.useRef)(null),J=(0,n.useRef)(null),[G,ye]=(0,n.useState)(!1),[Re,We]=(0,n.useState)(null),[Me,de]=(0,n.useState)(!1),[Z,c]=(0,n.useState)(!0),[Y,X]=(0,n.useState)(null),[oe,Ne]=(0,n.useState)(0),Ae=(0,n.useCallback)(()=>Ne(u=>u+1),[]),ze=(0,n.useRef)(null),[He,_e]=(0,n.useState)(0),[je,le]=(0,n.useState)(!1),Ke=(0,n.useRef)(null),Se=(0,n.useRef)(null)
|
|
222
|
+
`;function _t(t){t.effect(()=>t.locale.register(Ge,{zh:Nt,en:zt}),"memory-eternal: locale");let l=t.locale.bind(Ge);t.effect(()=>t.slots.inject("settings.section",()=>t.slots.register({name:"settings.section",id:Ge,order:25,label:()=>l("nav"),locale:Ge,inject:()=>({})},()=>n.default.createElement(Kt,{t:l}))),"memory-eternal: settings section"),t.effect(()=>t.slots.inject("sidebar.footer.action",()=>t.slots.register({name:"sidebar.footer.action",id:`${Ge}:footer`,order:100,label:()=>l("nav"),locale:Ge,inject:()=>({})},d=>n.default.createElement(Yt,{t:l,locale:t.locale,wide:!(d&&d.wide===!1)}))),"memory-eternal: sidebar footer action")}function Ut(){let[t,l]=(0,n.useState)("http://127.0.0.1:7999/");return(0,n.useEffect)(()=>{fetch("/memory-eternal/api/web-info").then(d=>d.json()).then(d=>{d&&d.ok&&d.url&&l(d.url.endsWith("/")?d.url:d.url+"/")}).catch(()=>{})},[]),t}function Kt({t}){return(0,e.jsxs)("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},children:[(0,e.jsx)("style",{children:Ie}),(0,e.jsx)(Tt,{t,onReload:()=>{},version:0})]})}function Yt({t,locale:l,wide:d}){let[v,m]=(0,n.useState)(!1);return(0,e.jsxs)("div",{className:`me-footer${d?"":" rail"}`,children:[(0,e.jsx)("style",{children:Ie}),(0,e.jsxs)("button",{type:"button",className:"me-footer-btn",onClick:()=>m(!0),"aria-label":t("nav"),title:t("nav"),children:[(0,e.jsx)("span",{className:"me-footer-ico","aria-hidden":"true",children:(0,e.jsx)(Lt,{})}),(0,e.jsx)("span",{className:"me-footer-label",children:t("nav")})]}),v&&(0,e.jsx)(qt,{t,locale:l,onClose:()=>m(!1)})]})}function qt({t,locale:l,onClose:d,tab:v}){let[m,i]=(0,n.useState)(!1),A=(0,n.useRef)(null),C=Ut(),L=(0,n.useCallback)(D=>l&&typeof l.subscribe=="function"?l.subscribe(D):()=>{},[l]),S=(0,n.useCallback)(()=>l&&typeof l.getSnapshot=="function"?l.getSnapshot():null,[l]),k=(0,n.useSyncExternalStore)(L,S),y=k&&(k.active==="zh"||k.active==="en")?k.active:null,x=(0,n.useRef)(null);x.current===null&&y&&(x.current=y);let U=(0,n.useMemo)(()=>{let D=x.current?`lang=${x.current}`:"";return v==="config"?`${W}/ui/config?tab=config${D?`&${D}`:""}`:v?`${C.replace(/\/$/,"")}/?tab=${v}${D?`&${D}`:""}`:D?`${C}${C.endsWith("/")?"":"/"}?${D}`:C},[C,v]);return(0,n.useEffect)(()=>{let D=A.current&&A.current.contentWindow;D&&y&&D.postMessage({source:"memory-eternal",type:"locale",lang:y},"*")},[y]),(0,n.useEffect)(()=>{let D=N=>{N.key==="Escape"&&d()};return window.addEventListener("keydown",D),()=>window.removeEventListener("keydown",D)},[d]),(0,e.jsxs)("div",{className:"me-overlay-top",onClick:d,children:[(0,e.jsx)("style",{children:Ie}),(0,e.jsx)("div",{className:"me-modal",onClick:D=>D.stopPropagation(),style:m?{position:"fixed",inset:0,width:"100vw",height:"100vh",maxWidth:"100vw",maxHeight:"100vh",borderRadius:0}:{},children:(0,e.jsxs)("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0,position:"relative"},children:[(0,e.jsxs)("div",{style:{position:"absolute",top:8,right:8,zIndex:10,display:"flex",gap:4,padding:"4px 6px",background:"var(--dsw-alias-bg-overlay, rgba(17,24,39,0.45))",borderRadius:8,backdropFilter:"blur(4px)"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>i(D=>!D),"aria-label":t(m?"exitFullscreen":"enterFullscreen"),title:t(m?"exitFullscreen":"enterFullscreen"),style:{width:26,height:26,borderRadius:6,border:"1px solid rgba(255,255,255,0.18)",background:"rgba(255,255,255,0.08)",color:"#fff",cursor:"pointer",fontSize:14,lineHeight:1,display:"flex",alignItems:"center",justifyContent:"center"},children:m?"\u2921":"\u2922"}),(0,e.jsx)("button",{type:"button",onClick:d,"aria-label":t("close"),title:t("close"),style:{width:26,height:26,borderRadius:6,border:"1px solid rgba(255,255,255,0.18)",background:"rgba(255,255,255,0.08)",color:"#fff",cursor:"pointer",fontSize:16,lineHeight:1,display:"flex",alignItems:"center",justifyContent:"center"},children:"\xD7"})]}),(0,e.jsx)("iframe",{ref:A,src:U,title:"memory-eternal",style:{width:"100%",flex:1,border:0,borderRadius:"inherit",background:"var(--dsw-alias-bg-base, #fff)"}})]})})]})}function Lt(){return(0,e.jsxs)("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:[(0,e.jsx)("defs",{children:(0,e.jsxs)("linearGradient",{id:"mg-brain",x1:"0",y1:"0",x2:"1",y2:"1",children:[(0,e.jsx)("stop",{offset:"0%",stopColor:"#FFC46B"}),(0,e.jsx)("stop",{offset:"50%",stopColor:"#F97316"}),(0,e.jsx)("stop",{offset:"100%",stopColor:"#EA580C"})]})}),(0,e.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 0-6.4.1 4.1 4.1 0 0 0-2.7 5.9 4.1 4.1 0 0 0 .6 6.7A4.2 4.2 0 0 0 12 18.2Z"}),(0,e.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 1 6.4.1 4.1 4.1 0 0 1 2.7 5.9 4.1 4.1 0 0 1-.6 6.7A4.2 4.2 0 0 1 12 18.2Z"}),(0,e.jsx)("path",{d:"M12 9.2c-.8-1.4-1.7-2-3.1-2.2M12 9.2c.8-1.4 1.7-2 3.1-2.2M12 12.4c-1.2 1-3.2 1.6-5 1.4M12 12.4c1.2 1 3.2 1.6 5 1.4M12 13.2v4",fill:"none",stroke:"rgba(255,255,255,0.62)",strokeWidth:"1",strokeLinecap:"round"}),(0,e.jsx)("circle",{cx:"12",cy:"8.6",r:"1.1",fill:"rgba(255,255,255,0.85)"})]})}function Xt({t,inModal:l,onClose:d,onFull:v,full:m}){let[i,A]=(0,n.useState)(null),[C,L]=(0,n.useState)([]),[S,k]=(0,n.useState)(!0),[y,x]=(0,n.useState)(null),[U,D]=(0,n.useState)(""),[N,H]=(0,n.useState)("all"),[w,P]=(0,n.useState)(""),[re,ee]=(0,n.useState)("all"),[E,Le]=(0,n.useState)("all"),[R,be]=(0,n.useState)(()=>{try{let u=new URLSearchParams(typeof location<"u"?location.search:"").get("tab"),j={cards:"cards",graph:"graph",usage:"stats",stats:"stats",audit:"audit",recycle:"optimize",optimize:"optimize",config:"config"};if(u&&j[u])return j[u]}catch{}return"cards"}),[b,ie]=(0,n.useState)(()=>new Set([R])),g=(0,n.useCallback)(u=>{ie(j=>j.has(u)?j:new Set(j).add(u)),be(u)},[]),[I,_]=(0,n.useState)("recent"),[V,F]=(0,n.useState)(null),O=(0,n.useRef)(null),J=(0,n.useRef)(null),[G,ye]=(0,n.useState)(!1),[Re,We]=(0,n.useState)(null),[Me,de]=(0,n.useState)(!1),[Z,c]=(0,n.useState)(!0),[Y,X]=(0,n.useState)(null),[oe,Ne]=(0,n.useState)(0),Ae=(0,n.useCallback)(()=>Ne(u=>u+1),[]),ze=(0,n.useRef)(null),[He,_e]=(0,n.useState)(0),[je,le]=(0,n.useState)(!1),Ke=(0,n.useRef)(null),Se=(0,n.useRef)(null),r=(0,n.useCallback)(async(u=N,j=w,K=E,ue={})=>{let pe=ue.append===!0;try{pe?le(!0):(k(!0),D(""));let me=new URLSearchParams;u&&u!=="all"&&me.set("kind",u),j.trim()&&me.set("q",j.trim()),me.set("status",K||"all"),me.set("limit",String($t)),me.set("offset",String(ue.offset||0)),I==="title"&&me.set("sort","title");let Ee=await(await fetch(`${W}/cards?${me.toString()}`)).json();if(Ee.ok){let Fe=Ee.cards||[];L(Ce=>pe?[...Ce,...Fe.filter(Ye=>!Ce.some(qe=>qe.path===Ye.path))]:Fe),_e(Number(Ee.total)||Fe.length)}}catch(me){D(String(me&&me.message?me.message:me))}finally{pe?le(!1):k(!1)}},[N,w,E,I]),o=(0,n.useCallback)(async()=>{try{let[u,j]=await Promise.all([fetch(`${W}/overview`).then(K=>K.json()),fetch(`${W}/capture-log`).then(K=>K.json()).catch(()=>null)]);u.ok&&A(u),x(j&&j.ok&&j.available&&j.health||null)}catch(u){D(String(u&&u.message?u.message:u))}finally{k(!1)}},[]),z=async u=>{try{let K=await(await fetch(`${W}/delete?path=${encodeURIComponent(u)}`)).json();K.ok?(F({ok:!0,msg:t("deleted")}),await o(),Ae()):F({ok:!1,msg:K.error||t("deleteFail")})}catch{F({ok:!1,msg:t("deleteFail")})}O.current&&clearTimeout(O.current),O.current=setTimeout(()=>F(null),2e3)};(0,n.useEffect)(()=>(o(),()=>{ze.current&&clearTimeout(ze.current)}),[o]);let te=u=>{P(u),ze.current&&clearTimeout(ze.current),ze.current=setTimeout(()=>r(N,u,E),280)},ce=u=>{H(u),r(u,w,E)};(0,n.useEffect)(()=>{R==="cards"&&r(N,w,E)},[E,R,oe,I]),(0,n.useEffect)(()=>{if(R!=="cards"||S||je||C.length>=He)return;let u=Ke.current,j=Se.current;if(!u)return;let K=new IntersectionObserver(ue=>{ue[0]&&ue[0].isIntersecting&&r(N,w,E,{append:!0,offset:C.length})},{root:j||null,rootMargin:"400px"});return K.observe(u),()=>K.disconnect()},[R,C,He,S,je,N,w,E,r]);let ke=async u=>{try{let K=await(await fetch(`${W}/card?path=${encodeURIComponent(u.path||u.id)}`)).json();K.ok&&We({path:K.path,title:u.title,text:K.text})}catch(j){D(String(j&&j.message?j.message:j))}},a=async(u,j)=>{ye(!0);try{let K=await fetch(`${W}/export`);if(!K.ok)throw new Error("HTTP "+K.status);let ue=await K.json();if(!ue.ok)throw new Error(ue.error||"export failed");let pe,me,ge;u==="json"?(me="memory-vault.json",ge="application/json",pe=JSON.stringify(ue.cards.map(Ce=>({path:Ce.path,title:Ce.title,kind:Ce.kind,text:Ce.text})),null,2)):(me="memory-vault.md",ge="text/markdown;charset=utf-8",pe=ue.cards.map(Ce=>Ce.text.trim()).filter(Boolean).join(`
|
|
223
223
|
|
|
224
224
|
---
|
|
225
225
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-eternal",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"build": "node build.mjs",
|
|
60
60
|
"build:client": "node build.mjs",
|
|
61
61
|
"prepublishOnly": "node build.mjs",
|
|
62
|
-
"test": "node tests/vault.test.mjs && node tests/capture.test.mjs && node tests/api.test.mjs"
|
|
62
|
+
"test": "node build.mjs && node tests/vault.test.mjs && node tests/capture.test.mjs && node tests/api.test.mjs && node tests/ssr.test.mjs"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@deepseek-ai/schemastery": "^3.18.1"
|
package/web/app.js
CHANGED
|
@@ -221,7 +221,7 @@ Option A, because:
|
|
|
221
221
|
.me-graph-ctxmenu button { display: block; width: 100%; text-align: left; padding: 7px 10px; border: none; border-radius: 7px; background: transparent; color: inherit; font: inherit; cursor: pointer; }
|
|
222
222
|
.me-graph-ctxmenu button:hover { background: rgba(255,255,255,0.1); }
|
|
223
223
|
.me-graph-hint { font-size: 11px; opacity: 0.6; margin-left: auto; }
|
|
224
|
-
`;function dg(){return(0,i.jsxs)("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:[(0,i.jsx)("defs",{children:(0,i.jsxs)("linearGradient",{id:"mg-brain",x1:"0",y1:"0",x2:"1",y2:"1",children:[(0,i.jsx)("stop",{offset:"0%",stopColor:"#FFC46B"}),(0,i.jsx)("stop",{offset:"50%",stopColor:"#F97316"}),(0,i.jsx)("stop",{offset:"100%",stopColor:"#EA580C"})]})}),(0,i.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 0-6.4.1 4.1 4.1 0 0 0-2.7 5.9 4.1 4.1 0 0 0 .6 6.7A4.2 4.2 0 0 0 12 18.2Z"}),(0,i.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 1 6.4.1 4.1 4.1 0 0 1 2.7 5.9 4.1 4.1 0 0 1-.6 6.7A4.2 4.2 0 0 1 12 18.2Z"}),(0,i.jsx)("path",{d:"M12 9.2c-.8-1.4-1.7-2-3.1-2.2M12 9.2c.8-1.4 1.7-2 3.1-2.2M12 12.4c-1.2 1-3.2 1.6-5 1.4M12 12.4c1.2 1 3.2 1.6 5 1.4M12 13.2v4",fill:"none",stroke:"rgba(255,255,255,0.62)",strokeWidth:"1",strokeLinecap:"round"}),(0,i.jsx)("circle",{cx:"12",cy:"8.6",r:"1.1",fill:"rgba(255,255,255,0.85)"})]})}function Tp({t:e,inModal:t,onClose:n,onFull:r,full:l}){let[o,a]=(0,x.useState)(null),[s,u]=(0,x.useState)([]),[p,v]=(0,x.useState)(!0),[y,g]=(0,x.useState)(null),[C,_]=(0,x.useState)(""),[S,B]=(0,x.useState)("all"),[d,c]=(0,x.useState)(""),[m,k]=(0,x.useState)("all"),[N,O]=(0,x.useState)("all"),[E,F]=(0,x.useState)(()=>{try{let P=new URLSearchParams(typeof location<"u"?location.search:"").get("tab"),K={cards:"cards",graph:"graph",usage:"stats",stats:"stats",audit:"audit",recycle:"optimize",optimize:"optimize",config:"config"};if(P&&K[P])return K[P]}catch{}return"cards"}),[I,A]=(0,x.useState)(()=>new Set([E])),L=(0,x.useCallback)(P=>{A(K=>K.has(P)?K:new Set(K).add(P)),F(P)},[]),[W,q]=(0,x.useState)("recent"),[le,Q]=(0,x.useState)(null),X=(0,x.useRef)(null),ae=(0,x.useRef)(null),[ie,$e]=(0,x.useState)(!1),[Ut,Tn]=(0,x.useState)(null),[ut,Ne]=(0,x.useState)(!1),[se,z]=(0,x.useState)(!0),[te,re]=(0,x.useState)(null),[ge,ct]=(0,x.useState)(0),Dt=(0,x.useCallback)(()=>ct(P=>P+1),[]),ht=(0,x.useRef)(null),[_n,Pn]=(0,x.useState)(0),[Ht,xe]=(0,x.useState)(!1),Gn=(0,x.useRef)(null),rt=(0,x.useRef)(null)
|
|
224
|
+
`;function dg(){return(0,i.jsxs)("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:[(0,i.jsx)("defs",{children:(0,i.jsxs)("linearGradient",{id:"mg-brain",x1:"0",y1:"0",x2:"1",y2:"1",children:[(0,i.jsx)("stop",{offset:"0%",stopColor:"#FFC46B"}),(0,i.jsx)("stop",{offset:"50%",stopColor:"#F97316"}),(0,i.jsx)("stop",{offset:"100%",stopColor:"#EA580C"})]})}),(0,i.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 0-6.4.1 4.1 4.1 0 0 0-2.7 5.9 4.1 4.1 0 0 0 .6 6.7A4.2 4.2 0 0 0 12 18.2Z"}),(0,i.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 1 6.4.1 4.1 4.1 0 0 1 2.7 5.9 4.1 4.1 0 0 1-.6 6.7A4.2 4.2 0 0 1 12 18.2Z"}),(0,i.jsx)("path",{d:"M12 9.2c-.8-1.4-1.7-2-3.1-2.2M12 9.2c.8-1.4 1.7-2 3.1-2.2M12 12.4c-1.2 1-3.2 1.6-5 1.4M12 12.4c1.2 1 3.2 1.6 5 1.4M12 13.2v4",fill:"none",stroke:"rgba(255,255,255,0.62)",strokeWidth:"1",strokeLinecap:"round"}),(0,i.jsx)("circle",{cx:"12",cy:"8.6",r:"1.1",fill:"rgba(255,255,255,0.85)"})]})}function Tp({t:e,inModal:t,onClose:n,onFull:r,full:l}){let[o,a]=(0,x.useState)(null),[s,u]=(0,x.useState)([]),[p,v]=(0,x.useState)(!0),[y,g]=(0,x.useState)(null),[C,_]=(0,x.useState)(""),[S,B]=(0,x.useState)("all"),[d,c]=(0,x.useState)(""),[m,k]=(0,x.useState)("all"),[N,O]=(0,x.useState)("all"),[E,F]=(0,x.useState)(()=>{try{let P=new URLSearchParams(typeof location<"u"?location.search:"").get("tab"),K={cards:"cards",graph:"graph",usage:"stats",stats:"stats",audit:"audit",recycle:"optimize",optimize:"optimize",config:"config"};if(P&&K[P])return K[P]}catch{}return"cards"}),[I,A]=(0,x.useState)(()=>new Set([E])),L=(0,x.useCallback)(P=>{A(K=>K.has(P)?K:new Set(K).add(P)),F(P)},[]),[W,q]=(0,x.useState)("recent"),[le,Q]=(0,x.useState)(null),X=(0,x.useRef)(null),ae=(0,x.useRef)(null),[ie,$e]=(0,x.useState)(!1),[Ut,Tn]=(0,x.useState)(null),[ut,Ne]=(0,x.useState)(!1),[se,z]=(0,x.useState)(!0),[te,re]=(0,x.useState)(null),[ge,ct]=(0,x.useState)(0),Dt=(0,x.useCallback)(()=>ct(P=>P+1),[]),ht=(0,x.useRef)(null),[_n,Pn]=(0,x.useState)(0),[Ht,xe]=(0,x.useState)(!1),Gn=(0,x.useRef)(null),rt=(0,x.useRef)(null),w=(0,x.useCallback)(async(P=S,K=d,J=N,De={})=>{let Le=De.append===!0;try{Le?xe(!0):(v(!0),_(""));let Te=new URLSearchParams;P&&P!=="all"&&Te.set("kind",P),K.trim()&&Te.set("q",K.trim()),Te.set("status",J||"all"),Te.set("limit",String(sg)),Te.set("offset",String(De.offset||0)),W==="title"&&Te.set("sort","title");let on=await(await fetch(`${Z}/cards?${Te.toString()}`)).json();if(on.ok){let Vt=on.cards||[];u(Ze=>Le?[...Ze,...Vt.filter(qn=>!Ze.some(Zn=>Zn.path===qn.path))]:Vt),Pn(Number(on.total)||Vt.length)}}catch(Te){_(String(Te&&Te.message?Te.message:Te))}finally{Le?xe(!1):v(!1)}},[S,d,N,W]),h=(0,x.useCallback)(async()=>{try{let[P,K]=await Promise.all([fetch(`${Z}/overview`).then(J=>J.json()),fetch(`${Z}/capture-log`).then(J=>J.json()).catch(()=>null)]);P.ok&&a(P),g(K&&K.ok&&K.available&&K.health||null)}catch(P){_(String(P&&P.message?P.message:P))}finally{v(!1)}},[]),U=async P=>{try{let J=await(await fetch(`${Z}/delete?path=${encodeURIComponent(P)}`)).json();J.ok?(Q({ok:!0,msg:e("deleted")}),await h(),Dt()):Q({ok:!1,msg:J.error||e("deleteFail")})}catch{Q({ok:!1,msg:e("deleteFail")})}X.current&&clearTimeout(X.current),X.current=setTimeout(()=>Q(null),2e3)};(0,x.useEffect)(()=>(h(),()=>{ht.current&&clearTimeout(ht.current)}),[h]);let ce=P=>{c(P),ht.current&&clearTimeout(ht.current),ht.current=setTimeout(()=>w(S,P,N),280)},ze=P=>{B(P),w(P,d,N)};(0,x.useEffect)(()=>{E==="cards"&&w(S,d,N)},[N,E,ge,W]),(0,x.useEffect)(()=>{if(E!=="cards"||p||Ht||s.length>=_n)return;let P=Gn.current,K=rt.current;if(!P)return;let J=new IntersectionObserver(De=>{De[0]&&De[0].isIntersecting&&w(S,d,N,{append:!0,offset:s.length})},{root:K||null,rootMargin:"400px"});return J.observe(P),()=>J.disconnect()},[E,s,_n,p,Ht,S,d,N,w]);let qe=async P=>{try{let J=await(await fetch(`${Z}/card?path=${encodeURIComponent(P.path||P.id)}`)).json();J.ok&&Tn({path:J.path,title:P.title,text:J.text})}catch(K){_(String(K&&K.message?K.message:K))}},f=async(P,K)=>{$e(!0);try{let J=await fetch(`${Z}/export`);if(!J.ok)throw new Error("HTTP "+J.status);let De=await J.json();if(!De.ok)throw new Error(De.error||"export failed");let Le,Te,Oe;P==="json"?(Te="memory-vault.json",Oe="application/json",Le=JSON.stringify(De.cards.map(Ze=>({path:Ze.path,title:Ze.title,kind:Ze.kind,text:Ze.text})),null,2)):(Te="memory-vault.md",Oe="text/markdown;charset=utf-8",Le=De.cards.map(Ze=>Ze.text.trim()).filter(Boolean).join(`
|
|
225
225
|
|
|
226
226
|
---
|
|
227
227
|
|