dsh-context-memory 0.2.1 → 0.3.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/lib/client/locales.d.ts +14 -0
- package/lib/client/locales.js +28 -0
- package/lib/client/view.d.ts +0 -12
- package/lib/client/view.js +79 -36
- package/lib/memory.web.js +177 -31
- package/lib/memory.web.js.map +2 -2
- package/lib/routes.d.ts +3 -0
- package/lib/routes.js +63 -3
- package/package.json +1 -1
package/lib/client/locales.d.ts
CHANGED
|
@@ -33,6 +33,20 @@ export declare const zh: {
|
|
|
33
33
|
loading: string;
|
|
34
34
|
failed: string;
|
|
35
35
|
retry: string;
|
|
36
|
+
searchPlaceholder: string;
|
|
37
|
+
search: string;
|
|
38
|
+
searchResults: string;
|
|
39
|
+
noResults: string;
|
|
40
|
+
addMemory: string;
|
|
41
|
+
memoryText: string;
|
|
42
|
+
subjectOptional: string;
|
|
43
|
+
kindFact: string;
|
|
44
|
+
kindDecision: string;
|
|
45
|
+
kindPreference: string;
|
|
46
|
+
kindPitfall: string;
|
|
47
|
+
add: string;
|
|
48
|
+
forget: string;
|
|
49
|
+
resolve: string;
|
|
36
50
|
};
|
|
37
51
|
/** English dictionary, checked complete against the zh key set. */
|
|
38
52
|
export declare const en: typeof zh;
|
package/lib/client/locales.js
CHANGED
|
@@ -33,6 +33,20 @@ export const zh = {
|
|
|
33
33
|
'loading': '正在加载…',
|
|
34
34
|
'failed': '加载失败',
|
|
35
35
|
'retry': '重试',
|
|
36
|
+
'searchPlaceholder': '搜索记忆…',
|
|
37
|
+
'search': '搜索',
|
|
38
|
+
'searchResults': '搜索结果',
|
|
39
|
+
'noResults': '没有匹配的记忆。',
|
|
40
|
+
'addMemory': '添加记忆',
|
|
41
|
+
'memoryText': '记忆内容',
|
|
42
|
+
'subjectOptional': '主题(可选)',
|
|
43
|
+
'kindFact': '事实',
|
|
44
|
+
'kindDecision': '决策',
|
|
45
|
+
'kindPreference': '偏好',
|
|
46
|
+
'kindPitfall': '陷阱',
|
|
47
|
+
'add': '添加',
|
|
48
|
+
'forget': '废弃',
|
|
49
|
+
'resolve': '解决',
|
|
36
50
|
};
|
|
37
51
|
/** English dictionary, checked complete against the zh key set. */
|
|
38
52
|
export const en = {
|
|
@@ -58,4 +72,18 @@ export const en = {
|
|
|
58
72
|
'loading': 'Loading…',
|
|
59
73
|
'failed': 'Failed to load',
|
|
60
74
|
'retry': 'Retry',
|
|
75
|
+
'searchPlaceholder': 'Search memories…',
|
|
76
|
+
'search': 'Search',
|
|
77
|
+
'searchResults': 'Search Results',
|
|
78
|
+
'noResults': 'No matching memories.',
|
|
79
|
+
'addMemory': 'Add Memory',
|
|
80
|
+
'memoryText': 'Memory text',
|
|
81
|
+
'subjectOptional': 'Subject (optional)',
|
|
82
|
+
'kindFact': 'Fact',
|
|
83
|
+
'kindDecision': 'Decision',
|
|
84
|
+
'kindPreference': 'Preference',
|
|
85
|
+
'kindPitfall': 'Pitfall',
|
|
86
|
+
'add': 'Add',
|
|
87
|
+
'forget': 'Forget',
|
|
88
|
+
'resolve': 'Resolve',
|
|
61
89
|
};
|
package/lib/client/view.d.ts
CHANGED
|
@@ -9,30 +9,18 @@
|
|
|
9
9
|
* @module client/view
|
|
10
10
|
*/
|
|
11
11
|
import type { MemoryState, PanelPayload } from '../types.js';
|
|
12
|
-
/** The translate seat the renderer binds from this plugin's locale namespace. */
|
|
13
12
|
export type Translate = (key: string, params?: Record<string, unknown>) => string;
|
|
14
13
|
export interface PanelProps {
|
|
15
|
-
/** Bound translate function for this plugin's namespace. */
|
|
16
14
|
t: Translate;
|
|
17
15
|
}
|
|
18
16
|
interface PanelState {
|
|
19
17
|
payload: PanelPayload | null;
|
|
20
18
|
error: string | null;
|
|
21
19
|
}
|
|
22
|
-
/** Fetch the host panel payload; `reload` re-runs the request. */
|
|
23
20
|
export declare function usePanel(): PanelState & {
|
|
24
21
|
reload: () => void;
|
|
25
22
|
};
|
|
26
|
-
/**
|
|
27
|
-
* A memory's verification label.
|
|
28
|
-
*
|
|
29
|
-
* Four states, not two, because "no check exists" and "the check could not run" are
|
|
30
|
-
* different answers from "the check failed" — collapsing them would report an
|
|
31
|
-
* unverifiable memory and a false one identically, which is the failure this plugin
|
|
32
|
-
* exists to avoid.
|
|
33
|
-
*/
|
|
34
23
|
export declare function checkLabel(memory: MemoryState, t: Translate): string;
|
|
35
|
-
/** Where a memory came from: the answer to "why does the agent think it knows this". */
|
|
36
24
|
export declare function provenance(memory: MemoryState): string;
|
|
37
25
|
export declare function ContextMemoryPanel({ t }: PanelProps): import("react").JSX.Element;
|
|
38
26
|
export {};
|
package/lib/client/view.js
CHANGED
|
@@ -10,50 +10,39 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
10
10
|
* @module client/view
|
|
11
11
|
*/
|
|
12
12
|
import { useCallback, useEffect, useState } from 'react';
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
13
|
+
const PANEL_PATH = '/api/context-memory.panel';
|
|
14
|
+
const RECALL_PATH = '/api/context-memory.recall';
|
|
15
|
+
const REMEMBER_PATH = '/api/context-memory.remember';
|
|
16
|
+
const FORGET_PATH = '/api/context-memory.forget';
|
|
17
|
+
const wrap = { display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 820, fontFamily: 'inherit' };
|
|
16
18
|
const head = { display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' };
|
|
17
19
|
const muted = { fontSize: 12, opacity: 0.75 };
|
|
18
20
|
const table = { borderCollapse: 'collapse', width: '100%' };
|
|
19
21
|
const th = { textAlign: 'left', padding: '4px 10px 4px 0', fontWeight: 600, fontSize: 12, opacity: 0.8, borderBottom: '0.5px solid rgba(128,128,128,0.4)' };
|
|
20
22
|
const td = { padding: '6px 10px 6px 0', fontSize: 13, borderBottom: '0.5px solid rgba(128,128,128,0.18)' };
|
|
21
23
|
const list = { margin: 0, paddingLeft: 18, fontSize: 13 };
|
|
22
|
-
|
|
24
|
+
const card = { padding: '8px 12px', borderRadius: 6, border: '1px solid rgba(128,128,128,0.2)', fontSize: 13 };
|
|
25
|
+
const btn = { fontSize: 12, cursor: 'pointer', padding: '3px 10px', borderRadius: 4, border: '0.5px solid rgba(128,128,128,0.4)' };
|
|
26
|
+
const dangerBtn = { ...btn, color: '#e55', borderColor: '#e55' };
|
|
27
|
+
const inputStyle = { fontSize: 13, padding: '4px 8px', borderRadius: 4, border: '0.5px solid rgba(128,128,128,0.4)', flex: 1 };
|
|
23
28
|
export function usePanel() {
|
|
24
29
|
const [state, setState] = useState({ payload: null, error: null });
|
|
25
30
|
const [tick, setTick] = useState(0);
|
|
26
|
-
const reload = useCallback(() => setTick((
|
|
31
|
+
const reload = useCallback(() => setTick((v) => v + 1), []);
|
|
27
32
|
useEffect(() => {
|
|
28
|
-
const
|
|
29
|
-
setState((
|
|
30
|
-
fetch(PANEL_PATH, { signal:
|
|
31
|
-
.then(async (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.
|
|
37
|
-
|
|
38
|
-
setState({ payload, error: null });
|
|
39
|
-
})
|
|
40
|
-
.catch((cause) => {
|
|
41
|
-
if (controller.signal.aborted)
|
|
42
|
-
return;
|
|
43
|
-
setState({ payload: null, error: cause instanceof Error ? cause.message : String(cause) });
|
|
44
|
-
});
|
|
45
|
-
return () => controller.abort();
|
|
33
|
+
const c = new AbortController();
|
|
34
|
+
setState((p) => ({ ...p, error: null }));
|
|
35
|
+
fetch(PANEL_PATH, { signal: c.signal })
|
|
36
|
+
.then(async (r) => { if (!r.ok)
|
|
37
|
+
throw new Error(String(r.status)); return r.json(); })
|
|
38
|
+
.then((payload) => { if (!c.signal.aborted)
|
|
39
|
+
setState({ payload, error: null }); })
|
|
40
|
+
.catch((e) => { if (!c.signal.aborted)
|
|
41
|
+
setState({ payload: null, error: e instanceof Error ? e.message : String(e) }); });
|
|
42
|
+
return () => c.abort();
|
|
46
43
|
}, [tick]);
|
|
47
44
|
return { ...state, reload };
|
|
48
45
|
}
|
|
49
|
-
/**
|
|
50
|
-
* A memory's verification label.
|
|
51
|
-
*
|
|
52
|
-
* Four states, not two, because "no check exists" and "the check could not run" are
|
|
53
|
-
* different answers from "the check failed" — collapsing them would report an
|
|
54
|
-
* unverifiable memory and a false one identically, which is the failure this plugin
|
|
55
|
-
* exists to avoid.
|
|
56
|
-
*/
|
|
57
46
|
export function checkLabel(memory, t) {
|
|
58
47
|
if (!memory.guard)
|
|
59
48
|
return t('checkNone');
|
|
@@ -64,7 +53,6 @@ export function checkLabel(memory, t) {
|
|
|
64
53
|
default: return t('checkPending');
|
|
65
54
|
}
|
|
66
55
|
}
|
|
67
|
-
/** Where a memory came from: the answer to "why does the agent think it knows this". */
|
|
68
56
|
export function provenance(memory) {
|
|
69
57
|
return [
|
|
70
58
|
memory.source,
|
|
@@ -72,15 +60,70 @@ export function provenance(memory) {
|
|
|
72
60
|
memory.step !== undefined ? `step ${String(memory.step)}` : null,
|
|
73
61
|
].filter((part) => part !== null).join(' · ');
|
|
74
62
|
}
|
|
63
|
+
// --- Search bar ---
|
|
64
|
+
function SearchBar({ t, onResult }) {
|
|
65
|
+
const [query, setQuery] = useState('');
|
|
66
|
+
const [loading, setLoading] = useState(false);
|
|
67
|
+
const handleSearch = useCallback(async () => {
|
|
68
|
+
if (!query.trim()) {
|
|
69
|
+
onResult(null);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
setLoading(true);
|
|
73
|
+
try {
|
|
74
|
+
const r = await fetch(`${RECALL_PATH}?q=${encodeURIComponent(query)}&limit=10`);
|
|
75
|
+
if (r.ok) {
|
|
76
|
+
const data = await r.json();
|
|
77
|
+
onResult(data.hits);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
setLoading(false);
|
|
82
|
+
}
|
|
83
|
+
}, [query, onResult]);
|
|
84
|
+
return (_jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx("input", { style: inputStyle, placeholder: t('searchPlaceholder'), value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => { if (e.key === 'Enter')
|
|
85
|
+
handleSearch(); } }), _jsx("button", { type: "button", style: btn, disabled: loading, onClick: handleSearch, children: loading ? '…' : t('search') })] }));
|
|
86
|
+
}
|
|
87
|
+
// --- Add memory form ---
|
|
88
|
+
function AddMemoryForm({ t, onAdded }) {
|
|
89
|
+
const [text, setText] = useState('');
|
|
90
|
+
const [subject, setSubject] = useState('');
|
|
91
|
+
const [kind, setKind] = useState('fact');
|
|
92
|
+
const [adding, setAdding] = useState(false);
|
|
93
|
+
const handleAdd = useCallback(async () => {
|
|
94
|
+
if (!text.trim())
|
|
95
|
+
return;
|
|
96
|
+
setAdding(true);
|
|
97
|
+
try {
|
|
98
|
+
await fetch(REMEMBER_PATH, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: { 'content-type': 'application/json' },
|
|
101
|
+
body: JSON.stringify({ text, subject: subject || undefined, kind, source: 'user' }),
|
|
102
|
+
});
|
|
103
|
+
setText('');
|
|
104
|
+
setSubject('');
|
|
105
|
+
setKind('fact');
|
|
106
|
+
onAdded();
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
setAdding(false);
|
|
110
|
+
}
|
|
111
|
+
}, [text, subject, kind, onAdded]);
|
|
112
|
+
return (_jsxs("div", { style: { ...card, display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsxs("strong", { style: { fontSize: 12 }, children: ["\u2795 ", t('addMemory')] }), _jsx("input", { style: inputStyle, placeholder: t('memoryText'), value: text, onChange: (e) => setText(e.target.value) }), _jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx("input", { style: inputStyle, placeholder: t('subjectOptional'), value: subject, onChange: (e) => setSubject(e.target.value) }), _jsxs("select", { style: { ...btn, flex: '0 0 auto' }, value: kind, onChange: (e) => setKind(e.target.value), children: [_jsx("option", { value: "fact", children: t('kindFact') }), _jsx("option", { value: "decision", children: t('kindDecision') }), _jsx("option", { value: "preference", children: t('kindPreference') }), _jsx("option", { value: "pitfall", children: t('kindPitfall') })] }), _jsx("button", { type: "button", style: btn, disabled: adding, onClick: handleAdd, children: adding ? '…' : t('add') })] })] }));
|
|
113
|
+
}
|
|
75
114
|
export function ContextMemoryPanel({ t }) {
|
|
76
115
|
const { payload, error, reload } = usePanel();
|
|
77
|
-
const
|
|
116
|
+
const [searchHits, setSearchHits] = useState(null);
|
|
117
|
+
const handleForget = useCallback(async (id, reason) => {
|
|
118
|
+
await fetch(FORGET_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, reason }) });
|
|
119
|
+
reload();
|
|
120
|
+
}, [reload]);
|
|
121
|
+
const header = (_jsxs("header", { style: head, children: [_jsxs("strong", { style: { fontSize: 13 }, children: ["\uD83E\uDDE0 ", t('title')] }), _jsx("span", { style: { flex: 1 } }), _jsx("button", { type: "button", style: btn, onClick: reload, children: t('refresh') })] }));
|
|
78
122
|
if (error !== null) {
|
|
79
|
-
return (_jsxs("div", { style: wrap, children: [header, _jsxs("p", { role: "alert", style: { margin: 0, fontSize: 13 }, children: [t('failed'), ": ", error] }), _jsx("button", { type: "button", onClick: reload, style: { alignSelf: 'flex-start'
|
|
123
|
+
return (_jsxs("div", { style: wrap, children: [header, _jsxs("p", { role: "alert", style: { margin: 0, fontSize: 13 }, children: [t('failed'), ": ", error] }), _jsx("button", { type: "button", onClick: reload, style: { ...btn, alignSelf: 'flex-start' }, children: t('retry') })] }));
|
|
80
124
|
}
|
|
81
125
|
if (payload === null)
|
|
82
126
|
return _jsx("p", { style: muted, "aria-live": "polite", children: t('loading') });
|
|
83
|
-
const rows = (memories) => memories.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: td, children: memory.text }), _jsx("td", { style: td, children: memory.kind }), _jsx("td", { style: td, children: checkLabel(memory, t) }), _jsx("td", { style: td, children: provenance(memory) })] }, memory.id)));
|
|
84
127
|
const { totals } = payload;
|
|
85
|
-
return (_jsxs("div", { style: wrap, children: [header, _jsxs("span", { style: muted, children: [t('active'), " ", totals.active, " \u00B7 ", t('checkPassed'), " ", totals.verified, " \u00B7 ", t('unverified'), " ", totals.unverified, " \u00B7 ", t('superseded'), " ", totals.superseded] }), _jsx("
|
|
128
|
+
return (_jsxs("div", { style: wrap, children: [header, _jsxs("span", { style: muted, children: [t('active'), " ", totals.active, " \u00B7 ", t('checkPassed'), " ", totals.verified, " \u00B7 ", t('unverified'), " ", totals.unverified, " \u00B7 ", t('superseded'), " ", totals.superseded] }), _jsx(SearchBar, { t: t, onResult: setSearchHits }), searchHits !== null && (_jsxs("div", { style: card, children: [_jsxs("strong", { style: { fontSize: 12 }, children: [t('searchResults'), " (", searchHits.length, ")"] }), searchHits.length === 0 ? (_jsx("p", { style: muted, children: t('noResults') })) : (_jsx("ul", { style: list, children: searchHits.map((hit) => (_jsxs("li", { style: { marginBottom: 4 }, children: [_jsx("code", { style: { fontSize: 11 }, children: hit.memory.id }), " (", hit.score.toFixed(2), ") ", hit.memory.text, _jsxs("span", { style: { ...muted, marginLeft: 8 }, children: ["\u2014 ", hit.why] })] }, hit.memory.id))) }))] })), _jsx(AddMemoryForm, { t: t, onAdded: reload }), _jsxs("strong", { style: { fontSize: 13 }, children: ["\u26A0\uFE0F ", t('conflicts')] }), payload.conflicts.length === 0 ? (_jsx("span", { style: muted, children: t('noConflicts') })) : (_jsx("ul", { style: list, children: payload.conflicts.map((conflict) => (_jsxs("li", { children: [_jsx("strong", { children: conflict.subject }), _jsx("ul", { style: { ...list, fontSize: 12, opacity: 0.85 }, children: conflict.memories.map((memory) => (_jsxs("li", { children: [_jsx("code", { style: { fontSize: 11 }, children: memory.id }), " ", memory.text, _jsx("button", { type: "button", style: { ...dangerBtn, marginLeft: 8 }, onClick: () => handleForget(memory.id, `conflict resolved: ${conflict.subject}`), children: t('resolve') })] }, memory.id))) })] }, conflict.subject))) })), _jsxs("strong", { style: { fontSize: 13 }, children: ["\u2705 ", t('active')] }), payload.active.length === 0 ? (_jsx("p", { style: { margin: 0, fontSize: 13, opacity: 0.8 }, children: t('empty') })) : (_jsxs("table", { style: table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: th, children: "Id" }), _jsx("th", { style: th, children: t('memory') }), _jsx("th", { style: th, children: t('kind') }), _jsx("th", { style: th, children: t('status') }), _jsx("th", { style: th, children: t('from') }), _jsx("th", { style: th })] }) }), _jsx("tbody", { children: payload.active.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: td, children: memory.text }), _jsx("td", { style: td, children: memory.kind }), _jsx("td", { style: td, children: checkLabel(memory, t) }), _jsx("td", { style: td, children: provenance(memory) }), _jsx("td", { style: td, children: _jsxs("button", { type: "button", style: dangerBtn, onClick: () => handleForget(memory.id, 'superseded from panel'), children: ["\uD83D\uDDD1 ", t('forget')] }) })] }, memory.id))) })] })), payload.superseded.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("strong", { style: { fontSize: 13 }, children: ["\uD83D\uDCE6 ", t('superseded'), " (", payload.superseded.length, ")"] }), _jsxs("table", { style: table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: th, children: "Id" }), _jsx("th", { style: th, children: t('memory') }), _jsx("th", { style: th, children: t('kind') }), _jsx("th", { style: th, children: t('status') }), _jsx("th", { style: th, children: t('from') })] }) }), _jsx("tbody", { children: payload.superseded.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: td, children: memory.text }), _jsx("td", { style: td, children: memory.kind }), _jsx("td", { style: td, children: checkLabel(memory, t) }), _jsx("td", { style: td, children: provenance(memory) })] }, memory.id))) })] })] }))] }));
|
|
86
129
|
}
|
package/lib/memory.web.js
CHANGED
|
@@ -51,7 +51,21 @@ var zh = {
|
|
|
51
51
|
"refresh": "\u5237\u65B0",
|
|
52
52
|
"loading": "\u6B63\u5728\u52A0\u8F7D\u2026",
|
|
53
53
|
"failed": "\u52A0\u8F7D\u5931\u8D25",
|
|
54
|
-
"retry": "\u91CD\u8BD5"
|
|
54
|
+
"retry": "\u91CD\u8BD5",
|
|
55
|
+
"searchPlaceholder": "\u641C\u7D22\u8BB0\u5FC6\u2026",
|
|
56
|
+
"search": "\u641C\u7D22",
|
|
57
|
+
"searchResults": "\u641C\u7D22\u7ED3\u679C",
|
|
58
|
+
"noResults": "\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5FC6\u3002",
|
|
59
|
+
"addMemory": "\u6DFB\u52A0\u8BB0\u5FC6",
|
|
60
|
+
"memoryText": "\u8BB0\u5FC6\u5185\u5BB9",
|
|
61
|
+
"subjectOptional": "\u4E3B\u9898\uFF08\u53EF\u9009\uFF09",
|
|
62
|
+
"kindFact": "\u4E8B\u5B9E",
|
|
63
|
+
"kindDecision": "\u51B3\u7B56",
|
|
64
|
+
"kindPreference": "\u504F\u597D",
|
|
65
|
+
"kindPitfall": "\u9677\u9631",
|
|
66
|
+
"add": "\u6DFB\u52A0",
|
|
67
|
+
"forget": "\u5E9F\u5F03",
|
|
68
|
+
"resolve": "\u89E3\u51B3"
|
|
55
69
|
};
|
|
56
70
|
var en = {
|
|
57
71
|
"nav": "Context memory",
|
|
@@ -75,37 +89,57 @@ var en = {
|
|
|
75
89
|
"refresh": "Refresh",
|
|
76
90
|
"loading": "Loading\u2026",
|
|
77
91
|
"failed": "Failed to load",
|
|
78
|
-
"retry": "Retry"
|
|
92
|
+
"retry": "Retry",
|
|
93
|
+
"searchPlaceholder": "Search memories\u2026",
|
|
94
|
+
"search": "Search",
|
|
95
|
+
"searchResults": "Search Results",
|
|
96
|
+
"noResults": "No matching memories.",
|
|
97
|
+
"addMemory": "Add Memory",
|
|
98
|
+
"memoryText": "Memory text",
|
|
99
|
+
"subjectOptional": "Subject (optional)",
|
|
100
|
+
"kindFact": "Fact",
|
|
101
|
+
"kindDecision": "Decision",
|
|
102
|
+
"kindPreference": "Preference",
|
|
103
|
+
"kindPitfall": "Pitfall",
|
|
104
|
+
"add": "Add",
|
|
105
|
+
"forget": "Forget",
|
|
106
|
+
"resolve": "Resolve"
|
|
79
107
|
};
|
|
80
108
|
|
|
81
109
|
// src/client/view.tsx
|
|
82
110
|
var import_react = require("react");
|
|
83
111
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
84
112
|
var PANEL_PATH = "/api/context-memory.panel";
|
|
85
|
-
var
|
|
113
|
+
var RECALL_PATH = "/api/context-memory.recall";
|
|
114
|
+
var REMEMBER_PATH = "/api/context-memory.remember";
|
|
115
|
+
var FORGET_PATH = "/api/context-memory.forget";
|
|
116
|
+
var wrap = { display: "flex", flexDirection: "column", gap: 12, maxWidth: 820, fontFamily: "inherit" };
|
|
86
117
|
var head = { display: "flex", alignItems: "baseline", gap: 12, flexWrap: "wrap" };
|
|
87
118
|
var muted = { fontSize: 12, opacity: 0.75 };
|
|
88
119
|
var table = { borderCollapse: "collapse", width: "100%" };
|
|
89
120
|
var th = { textAlign: "left", padding: "4px 10px 4px 0", fontWeight: 600, fontSize: 12, opacity: 0.8, borderBottom: "0.5px solid rgba(128,128,128,0.4)" };
|
|
90
121
|
var td = { padding: "6px 10px 6px 0", fontSize: 13, borderBottom: "0.5px solid rgba(128,128,128,0.18)" };
|
|
91
122
|
var list = { margin: 0, paddingLeft: 18, fontSize: 13 };
|
|
123
|
+
var card = { padding: "8px 12px", borderRadius: 6, border: "1px solid rgba(128,128,128,0.2)", fontSize: 13 };
|
|
124
|
+
var btn = { fontSize: 12, cursor: "pointer", padding: "3px 10px", borderRadius: 4, border: "0.5px solid rgba(128,128,128,0.4)" };
|
|
125
|
+
var dangerBtn = { ...btn, color: "#e55", borderColor: "#e55" };
|
|
126
|
+
var inputStyle = { fontSize: 13, padding: "4px 8px", borderRadius: 4, border: "0.5px solid rgba(128,128,128,0.4)", flex: 1 };
|
|
92
127
|
function usePanel() {
|
|
93
128
|
const [state, setState] = (0, import_react.useState)({ payload: null, error: null });
|
|
94
129
|
const [tick, setTick] = (0, import_react.useState)(0);
|
|
95
|
-
const reload = (0, import_react.useCallback)(() => setTick((
|
|
130
|
+
const reload = (0, import_react.useCallback)(() => setTick((v) => v + 1), []);
|
|
96
131
|
(0, import_react.useEffect)(() => {
|
|
97
|
-
const
|
|
98
|
-
setState((
|
|
99
|
-
fetch(PANEL_PATH, { signal:
|
|
100
|
-
if (!
|
|
101
|
-
return
|
|
132
|
+
const c = new AbortController();
|
|
133
|
+
setState((p) => ({ ...p, error: null }));
|
|
134
|
+
fetch(PANEL_PATH, { signal: c.signal }).then(async (r) => {
|
|
135
|
+
if (!r.ok) throw new Error(String(r.status));
|
|
136
|
+
return r.json();
|
|
102
137
|
}).then((payload) => {
|
|
103
|
-
if (!
|
|
104
|
-
}).catch((
|
|
105
|
-
if (
|
|
106
|
-
setState({ payload: null, error: cause instanceof Error ? cause.message : String(cause) });
|
|
138
|
+
if (!c.signal.aborted) setState({ payload, error: null });
|
|
139
|
+
}).catch((e) => {
|
|
140
|
+
if (!c.signal.aborted) setState({ payload: null, error: e instanceof Error ? e.message : String(e) });
|
|
107
141
|
});
|
|
108
|
-
return () =>
|
|
142
|
+
return () => c.abort();
|
|
109
143
|
}, [tick]);
|
|
110
144
|
return { ...state, reload };
|
|
111
145
|
}
|
|
@@ -129,12 +163,86 @@ function provenance(memory) {
|
|
|
129
163
|
memory.step !== void 0 ? `step ${String(memory.step)}` : null
|
|
130
164
|
].filter((part) => part !== null).join(" \xB7 ");
|
|
131
165
|
}
|
|
166
|
+
function SearchBar({ t, onResult }) {
|
|
167
|
+
const [query, setQuery] = (0, import_react.useState)("");
|
|
168
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
169
|
+
const handleSearch = (0, import_react.useCallback)(async () => {
|
|
170
|
+
if (!query.trim()) {
|
|
171
|
+
onResult(null);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
setLoading(true);
|
|
175
|
+
try {
|
|
176
|
+
const r = await fetch(`${RECALL_PATH}?q=${encodeURIComponent(query)}&limit=10`);
|
|
177
|
+
if (r.ok) {
|
|
178
|
+
const data = await r.json();
|
|
179
|
+
onResult(data.hits);
|
|
180
|
+
}
|
|
181
|
+
} finally {
|
|
182
|
+
setLoading(false);
|
|
183
|
+
}
|
|
184
|
+
}, [query, onResult]);
|
|
185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: inputStyle, placeholder: t("searchPlaceholder"), value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => {
|
|
187
|
+
if (e.key === "Enter") handleSearch();
|
|
188
|
+
} }),
|
|
189
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: btn, disabled: loading, onClick: handleSearch, children: loading ? "\u2026" : t("search") })
|
|
190
|
+
] });
|
|
191
|
+
}
|
|
192
|
+
function AddMemoryForm({ t, onAdded }) {
|
|
193
|
+
const [text, setText] = (0, import_react.useState)("");
|
|
194
|
+
const [subject, setSubject] = (0, import_react.useState)("");
|
|
195
|
+
const [kind, setKind] = (0, import_react.useState)("fact");
|
|
196
|
+
const [adding, setAdding] = (0, import_react.useState)(false);
|
|
197
|
+
const handleAdd = (0, import_react.useCallback)(async () => {
|
|
198
|
+
if (!text.trim()) return;
|
|
199
|
+
setAdding(true);
|
|
200
|
+
try {
|
|
201
|
+
await fetch(REMEMBER_PATH, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: { "content-type": "application/json" },
|
|
204
|
+
body: JSON.stringify({ text, subject: subject || void 0, kind, source: "user" })
|
|
205
|
+
});
|
|
206
|
+
setText("");
|
|
207
|
+
setSubject("");
|
|
208
|
+
setKind("fact");
|
|
209
|
+
onAdded();
|
|
210
|
+
} finally {
|
|
211
|
+
setAdding(false);
|
|
212
|
+
}
|
|
213
|
+
}, [text, subject, kind, onAdded]);
|
|
214
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...card, display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 12 }, children: [
|
|
216
|
+
"\u2795 ",
|
|
217
|
+
t("addMemory")
|
|
218
|
+
] }),
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: inputStyle, placeholder: t("memoryText"), value: text, onChange: (e) => setText(e.target.value) }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: inputStyle, placeholder: t("subjectOptional"), value: subject, onChange: (e) => setSubject(e.target.value) }),
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { style: { ...btn, flex: "0 0 auto" }, value: kind, onChange: (e) => setKind(e.target.value), children: [
|
|
223
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "fact", children: t("kindFact") }),
|
|
224
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "decision", children: t("kindDecision") }),
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "preference", children: t("kindPreference") }),
|
|
226
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "pitfall", children: t("kindPitfall") })
|
|
227
|
+
] }),
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: btn, disabled: adding, onClick: handleAdd, children: adding ? "\u2026" : t("add") })
|
|
229
|
+
] })
|
|
230
|
+
] });
|
|
231
|
+
}
|
|
132
232
|
function ContextMemoryPanel({ t }) {
|
|
133
233
|
const { payload, error, reload } = usePanel();
|
|
234
|
+
const [searchHits, setSearchHits] = (0, import_react.useState)(null);
|
|
235
|
+
const handleForget = (0, import_react.useCallback)(async (id, reason) => {
|
|
236
|
+
await fetch(FORGET_PATH, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ id, reason }) });
|
|
237
|
+
reload();
|
|
238
|
+
}, [reload]);
|
|
134
239
|
const header = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { style: head, children: [
|
|
135
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
|
|
241
|
+
"\u{1F9E0} ",
|
|
242
|
+
t("title")
|
|
243
|
+
] }),
|
|
136
244
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
137
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button",
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: btn, onClick: reload, children: t("refresh") })
|
|
138
246
|
] });
|
|
139
247
|
if (error !== null) {
|
|
140
248
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: wrap, children: [
|
|
@@ -144,17 +252,10 @@ function ContextMemoryPanel({ t }) {
|
|
|
144
252
|
": ",
|
|
145
253
|
error
|
|
146
254
|
] }),
|
|
147
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: reload, style: { alignSelf: "flex-start"
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: reload, style: { ...btn, alignSelf: "flex-start" }, children: t("retry") })
|
|
148
256
|
] });
|
|
149
257
|
}
|
|
150
258
|
if (payload === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: muted, "aria-live": "polite", children: t("loading") });
|
|
151
|
-
const rows = (memories) => memories.map((memory) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
152
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: memory.id }) }),
|
|
153
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: memory.text }),
|
|
154
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: memory.kind }),
|
|
155
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: checkLabel(memory, t) }),
|
|
156
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: provenance(memory) })
|
|
157
|
-
] }, memory.id));
|
|
158
259
|
const { totals } = payload;
|
|
159
260
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: wrap, children: [
|
|
160
261
|
header,
|
|
@@ -175,29 +276,68 @@ function ContextMemoryPanel({ t }) {
|
|
|
175
276
|
" ",
|
|
176
277
|
totals.superseded
|
|
177
278
|
] }),
|
|
178
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
179
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
279
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchBar, { t, onResult: setSearchHits }),
|
|
280
|
+
searchHits !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: card, children: [
|
|
281
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 12 }, children: [
|
|
282
|
+
t("searchResults"),
|
|
283
|
+
" (",
|
|
284
|
+
searchHits.length,
|
|
285
|
+
")"
|
|
286
|
+
] }),
|
|
287
|
+
searchHits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: muted, children: t("noResults") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: list, children: searchHits.map((hit) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { style: { marginBottom: 4 }, children: [
|
|
288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: hit.memory.id }),
|
|
289
|
+
" (",
|
|
290
|
+
hit.score.toFixed(2),
|
|
291
|
+
") ",
|
|
292
|
+
hit.memory.text,
|
|
293
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { ...muted, marginLeft: 8 }, children: [
|
|
294
|
+
"\u2014 ",
|
|
295
|
+
hit.why
|
|
296
|
+
] })
|
|
297
|
+
] }, hit.memory.id)) })
|
|
298
|
+
] }),
|
|
299
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddMemoryForm, { t, onAdded: reload }),
|
|
300
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
|
|
301
|
+
"\u26A0\uFE0F ",
|
|
302
|
+
t("conflicts")
|
|
303
|
+
] }),
|
|
180
304
|
payload.conflicts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: muted, children: t("noConflicts") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: list, children: payload.conflicts.map((conflict) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
181
305
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: conflict.subject }),
|
|
182
306
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: { ...list, fontSize: 12, opacity: 0.85 }, children: conflict.memories.map((memory) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
183
307
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: memory.id }),
|
|
184
308
|
" ",
|
|
185
|
-
memory.text
|
|
309
|
+
memory.text,
|
|
310
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...dangerBtn, marginLeft: 8 }, onClick: () => handleForget(memory.id, `conflict resolved: ${conflict.subject}`), children: t("resolve") })
|
|
186
311
|
] }, memory.id)) })
|
|
187
312
|
] }, conflict.subject)) }),
|
|
188
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
313
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
|
|
314
|
+
"\u2705 ",
|
|
315
|
+
t("active")
|
|
316
|
+
] }),
|
|
189
317
|
payload.active.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, fontSize: 13, opacity: 0.8 }, children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { style: table, children: [
|
|
190
318
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
191
319
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: "Id" }),
|
|
192
320
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("memory") }),
|
|
193
321
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("kind") }),
|
|
194
322
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("status") }),
|
|
195
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("from") })
|
|
323
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("from") }),
|
|
324
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th })
|
|
196
325
|
] }) }),
|
|
197
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children:
|
|
326
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: payload.active.map((memory) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
327
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: memory.id }) }),
|
|
328
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: memory.text }),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: memory.kind }),
|
|
330
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: checkLabel(memory, t) }),
|
|
331
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: provenance(memory) }),
|
|
332
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", style: dangerBtn, onClick: () => handleForget(memory.id, "superseded from panel"), children: [
|
|
333
|
+
"\u{1F5D1} ",
|
|
334
|
+
t("forget")
|
|
335
|
+
] }) })
|
|
336
|
+
] }, memory.id)) })
|
|
198
337
|
] }),
|
|
199
338
|
payload.superseded.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
200
339
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
|
|
340
|
+
"\u{1F4E6} ",
|
|
201
341
|
t("superseded"),
|
|
202
342
|
" (",
|
|
203
343
|
payload.superseded.length,
|
|
@@ -211,7 +351,13 @@ function ContextMemoryPanel({ t }) {
|
|
|
211
351
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("status") }),
|
|
212
352
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("from") })
|
|
213
353
|
] }) }),
|
|
214
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children:
|
|
354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: payload.superseded.map((memory) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
355
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: memory.id }) }),
|
|
356
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: memory.text }),
|
|
357
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: memory.kind }),
|
|
358
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: checkLabel(memory, t) }),
|
|
359
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: provenance(memory) })
|
|
360
|
+
] }, memory.id)) })
|
|
215
361
|
] })
|
|
216
362
|
] })
|
|
217
363
|
] });
|
package/lib/memory.web.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.tsx", "../src/client/locales.ts", "../src/client/view.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Browser half of dsh-context-memory: its page inside Settings.\n *\n * Two services are injected by their runtime identity, not by the packages that provide\n * them: the settings shell is `slots`, the dictionary registry is `locale`. The label and\n * every string inside the component are translated through this plugin's own namespace,\n * which is why the page follows the UI language instead of the plugin author's.\n *\n * @module client\n */\n\nimport type { ComponentType } from 'react'\n\nimport { NS, en, zh } from './locales.js'\nimport { ContextMemoryPanel } from './view.js'\n\nexport const inject = ['slots', 'locale']\n\ninterface SlotsService {\n inject(name: string, register: () => void): void\n register(\n options: { name: string; id: string; order: number; label: () => string; locale: string },\n component: ComponentType<{ t: (key: string, params?: Record<string, unknown>) => string }>,\n ): unknown\n}\n\ninterface ClientContext {\n slots: SlotsService\n locale: {\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\n bind(ns: string): (key: string, params?: Record<string, unknown>) => string\n }\n effect(callback: () => unknown, label?: string): unknown\n}\n\nexport function apply(ctx: ClientContext): void {\n // `zh` is the key-set source of truth, matching the official client plugins.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-context-memory: dictionaries')\n\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n {\n name: 'settings.section',\n id: 'context-memory',\n order: 46,\n label: () => ctx.locale.bind(NS)('nav'),\n locale: NS,\n },\n ContextMemoryPanel,\n ))\n}\n", "/**\n * Dictionaries for the Context memory page.\n *\n * `zh` is the key-set source of truth, as in the official client plugins, and `en` is\n * typed against it: a key translated in one language but not the other fails the build\n * instead of silently rendering the raw key.\n *\n * @module client/locales\n */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'contextMemory'\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'nav': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\n 'title': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\n 'active': '\u751F\u6548\u4E2D',\n 'superseded': '\u5DF2\u5931\u6548',\n 'conflicts': '\u51B2\u7A81',\n 'noConflicts': '\u6CA1\u6709\u51B2\u7A81\u3002',\n 'memory': '\u8BB0\u5FC6',\n 'kind': '\u7C7B\u578B',\n 'status': '\u6821\u9A8C\u72B6\u6001',\n 'from': '\u6765\u6E90',\n 'checkNone': '\u65E0\u68C0\u67E5',\n 'checkPassed': '\u5DF2\u6838\u5BF9\uFF0C\u6210\u7ACB',\n 'checkViolated': '\u5DF2\u6838\u5BF9\uFF0C\u4E0D\u6210\u7ACB',\n 'checkBroken': '\u68C0\u67E5\u65E0\u6CD5\u8FD0\u884C',\n 'checkPending': '\u5C1A\u672A\u6838\u5BF9',\n 'unverified': '\u672A\u9A8C\u8BC1\u6210\u7ACB',\n 'empty': '\u8FD8\u6CA1\u6709\u8BB0\u4F4F\u4EFB\u4F55\u5185\u5BB9\u3002',\n 'checkLegend': '\u65E0\u68C0\u67E5\uFF1D\u65E0\u6CD5\u7F16\u8BD1\u68C0\u67E5\uFF1B\u68C0\u67E5\u65E0\u6CD5\u8FD0\u884C\uFF1D\u68C0\u67E5\u672C\u8EAB\u6709\u95EE\u9898\uFF0C\u4E0D\u4EE3\u8868\u8BB0\u5FC6\u9519\u8BEF\u3002',\n 'refresh': '\u5237\u65B0',\n 'loading': '\u6B63\u5728\u52A0\u8F7D\u2026',\n 'failed': '\u52A0\u8F7D\u5931\u8D25',\n 'retry': '\u91CD\u8BD5',\n}\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: typeof zh = {\n 'nav': 'Context memory',\n 'title': 'Context memory',\n 'active': 'active',\n 'superseded': 'superseded',\n 'conflicts': 'Conflicts',\n 'noConflicts': 'No conflicts.',\n 'memory': 'Memory',\n 'kind': 'Kind',\n 'status': 'Check',\n 'from': 'From',\n 'checkNone': 'no check',\n 'checkPassed': 'checked, holds',\n 'checkViolated': 'checked, does not hold',\n 'checkBroken': 'check unusable',\n 'checkPending': 'not yet checked',\n 'unverified': 'not verified as holding',\n 'empty': 'Nothing is remembered yet.',\n 'checkLegend': 'no check = no check could be compiled; check unusable = the check is defective, not the memory.',\n 'refresh': 'Refresh',\n 'loading': 'Loading\u2026',\n 'failed': 'Failed to load',\n 'retry': 'Retry',\n}\n", "/**\n * Pure rendering half of the contextMemory page.\n *\n * Separate from `index.tsx` so a static render can assert in Node what the page draws \u2014\n * the shipped bundle is a loader factory only a browser can run. Every user-visible\n * string comes from the `t` seat the renderer binds from this plugin's namespace, so the\n * page follows the UI language; no copy is hardcoded here.\n *\n * @module client/view\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { CSSProperties } from 'react'\n\nimport type { MemoryState, PanelPayload } from '../types.js'\n\n/** The translate seat the renderer binds from this plugin's locale namespace. */\nexport type Translate = (key: string, params?: Record<string, unknown>) => string\n\nexport interface PanelProps {\n /** Bound translate function for this plugin's namespace. */\n t: Translate\n}\n\n/** Panel route registered by the host half on the web connection. */\nconst PANEL_PATH = \"/api/context-memory.panel\"\n\nconst wrap: CSSProperties = { display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 760, fontFamily: 'inherit' }\nconst head: CSSProperties = { display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' }\nconst muted: CSSProperties = { fontSize: 12, opacity: 0.75 }\nconst table: CSSProperties = { borderCollapse: 'collapse', width: '100%' }\nconst th: CSSProperties = { textAlign: 'left', padding: '4px 10px 4px 0', fontWeight: 600, fontSize: 12, opacity: 0.8, borderBottom: '0.5px solid rgba(128,128,128,0.4)' }\nconst td: CSSProperties = { padding: '6px 10px 6px 0', fontSize: 13, borderBottom: '0.5px solid rgba(128,128,128,0.18)' }\nconst list: CSSProperties = { margin: 0, paddingLeft: 18, fontSize: 13 }\n\ninterface PanelState {\n payload: PanelPayload | null\n error: string | null\n}\n\n/** Fetch the host panel payload; `reload` re-runs the request. */\nexport function usePanel(): PanelState & { reload: () => void } {\n const [state, setState] = useState<PanelState>({ payload: null, error: null })\n const [tick, setTick] = useState(0)\n const reload = useCallback(() => setTick((value) => value + 1), [])\n\n useEffect(() => {\n const controller = new AbortController()\n setState((previous) => ({ ...previous, error: null }))\n fetch(PANEL_PATH, { signal: controller.signal })\n .then(async (response) => {\n if (!response.ok) throw new Error(String(response.status))\n return response.json() as Promise<PanelPayload>\n })\n .then((payload) => {\n if (!controller.signal.aborted) setState({ payload, error: null })\n })\n .catch((cause: unknown) => {\n if (controller.signal.aborted) return\n setState({ payload: null, error: cause instanceof Error ? cause.message : String(cause) })\n })\n return () => controller.abort()\n }, [tick])\n\n return { ...state, reload }\n}\n\n/**\n * A memory's verification label.\n *\n * Four states, not two, because \"no check exists\" and \"the check could not run\" are\n * different answers from \"the check failed\" \u2014 collapsing them would report an\n * unverifiable memory and a false one identically, which is the failure this plugin\n * exists to avoid.\n */\nexport function checkLabel(memory: MemoryState, t: Translate): string {\n if (!memory.guard) return t('checkNone')\n switch (memory.verification?.outcome) {\n case 'passed': return t('checkPassed')\n case 'violated': return t('checkViolated')\n case 'broken': return t('checkBroken')\n default: return t('checkPending')\n }\n}\n\n/** Where a memory came from: the answer to \"why does the agent think it knows this\". */\nexport function provenance(memory: MemoryState): string {\n return [\n memory.source,\n memory.sessionId !== undefined ? `session ${memory.sessionId}` : null,\n memory.step !== undefined ? `step ${String(memory.step)}` : null,\n ].filter((part) => part !== null).join(' \u00B7 ')\n}\n\nexport function ContextMemoryPanel({ t }: PanelProps) {\n const { payload, error, reload } = usePanel()\n const header = (\n <header style={head}>\n <strong style={{ fontSize: 13 }}>{t('title')}</strong>\n <span style={{ flex: 1 }} />\n <button type=\"button\" onClick={reload} style={{ fontSize: 12 }}>{t('refresh')}</button>\n </header>\n )\n if (error !== null) {\n return (\n <div style={wrap}>\n {header}\n <p role=\"alert\" style={{ margin: 0, fontSize: 13 }}>{t('failed')}: {error}</p>\n <button type=\"button\" onClick={reload} style={{ alignSelf: 'flex-start', fontSize: 12 }}>{t('retry')}</button>\n </div>\n )\n }\n if (payload === null) return <p style={muted} aria-live=\"polite\">{t('loading')}</p>\n const rows = (memories: MemoryState[]) => memories.map((memory) => (\n <tr key={memory.id}>\n <td style={td}><code style={{ fontSize: 11 }}>{memory.id}</code></td>\n <td style={td}>{memory.text}</td>\n <td style={td}>{memory.kind}</td>\n <td style={td}>{checkLabel(memory, t)}</td>\n <td style={td}>{provenance(memory)}</td>\n </tr>\n ))\n const { totals } = payload\n return (\n <div style={wrap}>\n {header}\n <span style={muted}>\n {t('active')} {totals.active} \u00B7 {t('checkPassed')} {totals.verified} \u00B7 {t('unverified')} {totals.unverified} \u00B7 {t('superseded')} {totals.superseded}\n </span>\n <span style={muted}>{t('checkLegend')}</span>\n <strong style={{ fontSize: 13 }}>{t('conflicts')}</strong>\n {payload.conflicts.length === 0 ? (\n <span style={muted}>{t('noConflicts')}</span>\n ) : (\n <ul style={list}>\n {payload.conflicts.map((conflict) => (\n <li key={conflict.subject}>\n <strong>{conflict.subject}</strong>\n <ul style={{ ...list, fontSize: 12, opacity: 0.85 }}>\n {conflict.memories.map((memory) => (\n <li key={memory.id}><code style={{ fontSize: 11 }}>{memory.id}</code> {memory.text}</li>\n ))}\n </ul>\n </li>\n ))}\n </ul>\n )}\n <strong style={{ fontSize: 13 }}>{t('active')}</strong>\n {payload.active.length === 0 ? (\n <p style={{ margin: 0, fontSize: 13, opacity: 0.8 }}>{t('empty')}</p>\n ) : (\n <table style={table}>\n <thead>\n <tr>\n <th style={th}>Id</th><th style={th}>{t('memory')}</th><th style={th}>{t('kind')}</th>\n <th style={th}>{t('status')}</th><th style={th}>{t('from')}</th>\n </tr>\n </thead>\n <tbody>{rows(payload.active)}</tbody>\n </table>\n )}\n {payload.superseded.length > 0 && (\n <>\n <strong style={{ fontSize: 13 }}>{t('superseded')} ({payload.superseded.length})</strong>\n <table style={table}>\n <thead>\n <tr>\n <th style={th}>Id</th><th style={th}>{t('memory')}</th><th style={th}>{t('kind')}</th>\n <th style={th}>{t('status')}</th><th style={th}>{t('from')}</th>\n </tr>\n </thead>\n <tbody>{rows(payload.superseded)}</tbody>\n </table>\n </>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Browser half of dsh-context-memory: its page inside Settings.\n *\n * Two services are injected by their runtime identity, not by the packages that provide\n * them: the settings shell is `slots`, the dictionary registry is `locale`. The label and\n * every string inside the component are translated through this plugin's own namespace,\n * which is why the page follows the UI language instead of the plugin author's.\n *\n * @module client\n */\n\nimport type { ComponentType } from 'react'\n\nimport { NS, en, zh } from './locales.js'\nimport { ContextMemoryPanel } from './view.js'\n\nexport const inject = ['slots', 'locale']\n\ninterface SlotsService {\n inject(name: string, register: () => void): void\n register(\n options: { name: string; id: string; order: number; label: () => string; locale: string },\n component: ComponentType<{ t: (key: string, params?: Record<string, unknown>) => string }>,\n ): unknown\n}\n\ninterface ClientContext {\n slots: SlotsService\n locale: {\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\n bind(ns: string): (key: string, params?: Record<string, unknown>) => string\n }\n effect(callback: () => unknown, label?: string): unknown\n}\n\nexport function apply(ctx: ClientContext): void {\n // `zh` is the key-set source of truth, matching the official client plugins.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-context-memory: dictionaries')\n\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n {\n name: 'settings.section',\n id: 'context-memory',\n order: 46,\n label: () => ctx.locale.bind(NS)('nav'),\n locale: NS,\n },\n ContextMemoryPanel,\n ))\n}\n", "/**\n * Dictionaries for the Context memory page.\n *\n * `zh` is the key-set source of truth, as in the official client plugins, and `en` is\n * typed against it: a key translated in one language but not the other fails the build\n * instead of silently rendering the raw key.\n *\n * @module client/locales\n */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'contextMemory'\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'nav': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\n 'title': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\n 'active': '\u751F\u6548\u4E2D',\n 'superseded': '\u5DF2\u5931\u6548',\n 'conflicts': '\u51B2\u7A81',\n 'noConflicts': '\u6CA1\u6709\u51B2\u7A81\u3002',\n 'memory': '\u8BB0\u5FC6',\n 'kind': '\u7C7B\u578B',\n 'status': '\u6821\u9A8C\u72B6\u6001',\n 'from': '\u6765\u6E90',\n 'checkNone': '\u65E0\u68C0\u67E5',\n 'checkPassed': '\u5DF2\u6838\u5BF9\uFF0C\u6210\u7ACB',\n 'checkViolated': '\u5DF2\u6838\u5BF9\uFF0C\u4E0D\u6210\u7ACB',\n 'checkBroken': '\u68C0\u67E5\u65E0\u6CD5\u8FD0\u884C',\n 'checkPending': '\u5C1A\u672A\u6838\u5BF9',\n 'unverified': '\u672A\u9A8C\u8BC1\u6210\u7ACB',\n 'empty': '\u8FD8\u6CA1\u6709\u8BB0\u4F4F\u4EFB\u4F55\u5185\u5BB9\u3002',\n 'checkLegend': '\u65E0\u68C0\u67E5\uFF1D\u65E0\u6CD5\u7F16\u8BD1\u68C0\u67E5\uFF1B\u68C0\u67E5\u65E0\u6CD5\u8FD0\u884C\uFF1D\u68C0\u67E5\u672C\u8EAB\u6709\u95EE\u9898\uFF0C\u4E0D\u4EE3\u8868\u8BB0\u5FC6\u9519\u8BEF\u3002',\n 'refresh': '\u5237\u65B0',\n 'loading': '\u6B63\u5728\u52A0\u8F7D\u2026',\n 'failed': '\u52A0\u8F7D\u5931\u8D25',\n 'retry': '\u91CD\u8BD5',\n 'searchPlaceholder': '\u641C\u7D22\u8BB0\u5FC6\u2026',\n 'search': '\u641C\u7D22',\n 'searchResults': '\u641C\u7D22\u7ED3\u679C',\n 'noResults': '\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5FC6\u3002',\n 'addMemory': '\u6DFB\u52A0\u8BB0\u5FC6',\n 'memoryText': '\u8BB0\u5FC6\u5185\u5BB9',\n 'subjectOptional': '\u4E3B\u9898\uFF08\u53EF\u9009\uFF09',\n 'kindFact': '\u4E8B\u5B9E',\n 'kindDecision': '\u51B3\u7B56',\n 'kindPreference': '\u504F\u597D',\n 'kindPitfall': '\u9677\u9631',\n 'add': '\u6DFB\u52A0',\n 'forget': '\u5E9F\u5F03',\n 'resolve': '\u89E3\u51B3',\n}\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: typeof zh = {\n 'nav': 'Context memory',\n 'title': 'Context memory',\n 'active': 'active',\n 'superseded': 'superseded',\n 'conflicts': 'Conflicts',\n 'noConflicts': 'No conflicts.',\n 'memory': 'Memory',\n 'kind': 'Kind',\n 'status': 'Check',\n 'from': 'From',\n 'checkNone': 'no check',\n 'checkPassed': 'checked, holds',\n 'checkViolated': 'checked, does not hold',\n 'checkBroken': 'check unusable',\n 'checkPending': 'not yet checked',\n 'unverified': 'not verified as holding',\n 'empty': 'Nothing is remembered yet.',\n 'checkLegend': 'no check = no check could be compiled; check unusable = the check is defective, not the memory.',\n 'refresh': 'Refresh',\n 'loading': 'Loading\u2026',\n 'failed': 'Failed to load',\n 'retry': 'Retry',\n 'searchPlaceholder': 'Search memories\u2026',\n 'search': 'Search',\n 'searchResults': 'Search Results',\n 'noResults': 'No matching memories.',\n 'addMemory': 'Add Memory',\n 'memoryText': 'Memory text',\n 'subjectOptional': 'Subject (optional)',\n 'kindFact': 'Fact',\n 'kindDecision': 'Decision',\n 'kindPreference': 'Preference',\n 'kindPitfall': 'Pitfall',\n 'add': 'Add',\n 'forget': 'Forget',\n 'resolve': 'Resolve',\n}\n", "/**\n * Pure rendering half of the contextMemory page.\n *\n * Separate from `index.tsx` so a static render can assert in Node what the page draws \u2014\n * the shipped bundle is a loader factory only a browser can run. Every user-visible\n * string comes from the `t` seat the renderer binds from this plugin's namespace, so the\n * page follows the UI language; no copy is hardcoded here.\n *\n * @module client/view\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { CSSProperties } from 'react'\n\nimport type { MemoryState, PanelPayload, RecallHit } from '../types.js'\n\nexport type Translate = (key: string, params?: Record<string, unknown>) => string\n\nexport interface PanelProps {\n t: Translate\n}\n\nconst PANEL_PATH = '/api/context-memory.panel'\nconst RECALL_PATH = '/api/context-memory.recall'\nconst REMEMBER_PATH = '/api/context-memory.remember'\nconst FORGET_PATH = '/api/context-memory.forget'\n\nconst wrap: CSSProperties = { display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 820, fontFamily: 'inherit' }\nconst head: CSSProperties = { display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' }\nconst muted: CSSProperties = { fontSize: 12, opacity: 0.75 }\nconst table: CSSProperties = { borderCollapse: 'collapse', width: '100%' }\nconst th: CSSProperties = { textAlign: 'left', padding: '4px 10px 4px 0', fontWeight: 600, fontSize: 12, opacity: 0.8, borderBottom: '0.5px solid rgba(128,128,128,0.4)' }\nconst td: CSSProperties = { padding: '6px 10px 6px 0', fontSize: 13, borderBottom: '0.5px solid rgba(128,128,128,0.18)' }\nconst list: CSSProperties = { margin: 0, paddingLeft: 18, fontSize: 13 }\nconst card: CSSProperties = { padding: '8px 12px', borderRadius: 6, border: '1px solid rgba(128,128,128,0.2)', fontSize: 13 }\nconst btn: CSSProperties = { fontSize: 12, cursor: 'pointer', padding: '3px 10px', borderRadius: 4, border: '0.5px solid rgba(128,128,128,0.4)' }\nconst dangerBtn: CSSProperties = { ...btn, color: '#e55', borderColor: '#e55' }\nconst inputStyle: CSSProperties = { fontSize: 13, padding: '4px 8px', borderRadius: 4, border: '0.5px solid rgba(128,128,128,0.4)', flex: 1 }\n\ninterface PanelState {\n payload: PanelPayload | null\n error: string | null\n}\n\nexport function usePanel(): PanelState & { reload: () => void } {\n const [state, setState] = useState<PanelState>({ payload: null, error: null })\n const [tick, setTick] = useState(0)\n const reload = useCallback(() => setTick((v) => v + 1), [])\n useEffect(() => {\n const c = new AbortController()\n setState((p) => ({ ...p, error: null }))\n fetch(PANEL_PATH, { signal: c.signal })\n .then(async (r) => { if (!r.ok) throw new Error(String(r.status)); return r.json() as Promise<PanelPayload> })\n .then((payload) => { if (!c.signal.aborted) setState({ payload, error: null }) })\n .catch((e: unknown) => { if (!c.signal.aborted) setState({ payload: null, error: e instanceof Error ? e.message : String(e) }) })\n return () => c.abort()\n }, [tick])\n return { ...state, reload }\n}\n\nexport function checkLabel(memory: MemoryState, t: Translate): string {\n if (!memory.guard) return t('checkNone')\n switch (memory.verification?.outcome) {\n case 'passed': return t('checkPassed')\n case 'violated': return t('checkViolated')\n case 'broken': return t('checkBroken')\n default: return t('checkPending')\n }\n}\n\nexport function provenance(memory: MemoryState): string {\n return [\n memory.source,\n memory.sessionId !== undefined ? `session ${memory.sessionId}` : null,\n memory.step !== undefined ? `step ${String(memory.step)}` : null,\n ].filter((part) => part !== null).join(' \u00B7 ')\n}\n\n// --- Search bar ---\nfunction SearchBar({ t, onResult }: { t: Translate; onResult: (hits: RecallHit[] | null) => void }) {\n const [query, setQuery] = useState('')\n const [loading, setLoading] = useState(false)\n const handleSearch = useCallback(async () => {\n if (!query.trim()) { onResult(null); return }\n setLoading(true)\n try {\n const r = await fetch(`${RECALL_PATH}?q=${encodeURIComponent(query)}&limit=10`)\n if (r.ok) { const data = await r.json() as { hits: RecallHit[] }; onResult(data.hits) }\n } finally { setLoading(false) }\n }, [query, onResult])\n return (\n <div style={{ display: 'flex', gap: 8 }}>\n <input style={inputStyle} placeholder={t('searchPlaceholder')} value={query} onChange={(e) => setQuery(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleSearch() }} />\n <button type=\"button\" style={btn} disabled={loading} onClick={handleSearch}>{loading ? '\u2026' : t('search')}</button>\n </div>\n )\n}\n\n// --- Add memory form ---\nfunction AddMemoryForm({ t, onAdded }: { t: Translate; onAdded: () => void }) {\n const [text, setText] = useState('')\n const [subject, setSubject] = useState('')\n const [kind, setKind] = useState('fact')\n const [adding, setAdding] = useState(false)\n const handleAdd = useCallback(async () => {\n if (!text.trim()) return\n setAdding(true)\n try {\n await fetch(REMEMBER_PATH, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ text, subject: subject || undefined, kind, source: 'user' }),\n })\n setText(''); setSubject(''); setKind('fact')\n onAdded()\n } finally { setAdding(false) }\n }, [text, subject, kind, onAdded])\n return (\n <div style={{ ...card, display: 'flex', flexDirection: 'column', gap: 6 }}>\n <strong style={{ fontSize: 12 }}>\u2795 {t('addMemory')}</strong>\n <input style={inputStyle} placeholder={t('memoryText')} value={text} onChange={(e) => setText(e.target.value)} />\n <div style={{ display: 'flex', gap: 8 }}>\n <input style={inputStyle} placeholder={t('subjectOptional')} value={subject} onChange={(e) => setSubject(e.target.value)} />\n <select style={{ ...btn, flex: '0 0 auto' }} value={kind} onChange={(e) => setKind(e.target.value)}>\n <option value=\"fact\">{t('kindFact')}</option>\n <option value=\"decision\">{t('kindDecision')}</option>\n <option value=\"preference\">{t('kindPreference')}</option>\n <option value=\"pitfall\">{t('kindPitfall')}</option>\n </select>\n <button type=\"button\" style={btn} disabled={adding} onClick={handleAdd}>{adding ? '\u2026' : t('add')}</button>\n </div>\n </div>\n )\n}\n\nexport function ContextMemoryPanel({ t }: PanelProps) {\n const { payload, error, reload } = usePanel()\n const [searchHits, setSearchHits] = useState<RecallHit[] | null>(null)\n\n const handleForget = useCallback(async (id: string, reason: string) => {\n await fetch(FORGET_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, reason }) })\n reload()\n }, [reload])\n\n const header = (\n <header style={head}>\n <strong style={{ fontSize: 13 }}>\uD83E\uDDE0 {t('title')}</strong>\n <span style={{ flex: 1 }} />\n <button type=\"button\" style={btn} onClick={reload}>{t('refresh')}</button>\n </header>\n )\n if (error !== null) {\n return (\n <div style={wrap}>\n {header}\n <p role=\"alert\" style={{ margin: 0, fontSize: 13 }}>{t('failed')}: {error}</p>\n <button type=\"button\" onClick={reload} style={{ ...btn, alignSelf: 'flex-start' }}>{t('retry')}</button>\n </div>\n )\n }\n if (payload === null) return <p style={muted} aria-live=\"polite\">{t('loading')}</p>\n const { totals } = payload\n return (\n <div style={wrap}>\n {header}\n <span style={muted}>\n {t('active')} {totals.active} \u00B7 {t('checkPassed')} {totals.verified} \u00B7 {t('unverified')} {totals.unverified} \u00B7 {t('superseded')} {totals.superseded}\n </span>\n\n {/* Search */}\n <SearchBar t={t} onResult={setSearchHits} />\n {searchHits !== null && (\n <div style={card}>\n <strong style={{ fontSize: 12 }}>{t('searchResults')} ({searchHits.length})</strong>\n {searchHits.length === 0 ? (\n <p style={muted}>{t('noResults')}</p>\n ) : (\n <ul style={list}>\n {searchHits.map((hit) => (\n <li key={hit.memory.id} style={{ marginBottom: 4 }}>\n <code style={{ fontSize: 11 }}>{hit.memory.id}</code> ({hit.score.toFixed(2)}) {hit.memory.text}\n <span style={{ ...muted, marginLeft: 8 }}>\u2014 {hit.why}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n\n {/* Add memory */}\n <AddMemoryForm t={t} onAdded={reload} />\n\n {/* Conflicts with resolve buttons */}\n <strong style={{ fontSize: 13 }}>\u26A0\uFE0F {t('conflicts')}</strong>\n {payload.conflicts.length === 0 ? (\n <span style={muted}>{t('noConflicts')}</span>\n ) : (\n <ul style={list}>\n {payload.conflicts.map((conflict) => (\n <li key={conflict.subject}>\n <strong>{conflict.subject}</strong>\n <ul style={{ ...list, fontSize: 12, opacity: 0.85 }}>\n {conflict.memories.map((memory) => (\n <li key={memory.id}>\n <code style={{ fontSize: 11 }}>{memory.id}</code> {memory.text}\n <button type=\"button\" style={{ ...dangerBtn, marginLeft: 8 }} onClick={() => handleForget(memory.id, `conflict resolved: ${conflict.subject}`)}>\n {t('resolve')}\n </button>\n </li>\n ))}\n </ul>\n </li>\n ))}\n </ul>\n )}\n\n {/* Active memories with supersede buttons */}\n <strong style={{ fontSize: 13 }}>\u2705 {t('active')}</strong>\n {payload.active.length === 0 ? (\n <p style={{ margin: 0, fontSize: 13, opacity: 0.8 }}>{t('empty')}</p>\n ) : (\n <table style={table}>\n <thead>\n <tr>\n <th style={th}>Id</th><th style={th}>{t('memory')}</th><th style={th}>{t('kind')}</th>\n <th style={th}>{t('status')}</th><th style={th}>{t('from')}</th><th style={th} />\n </tr>\n </thead>\n <tbody>\n {payload.active.map((memory) => (\n <tr key={memory.id}>\n <td style={td}><code style={{ fontSize: 11 }}>{memory.id}</code></td>\n <td style={td}>{memory.text}</td>\n <td style={td}>{memory.kind}</td>\n <td style={td}>{checkLabel(memory, t)}</td>\n <td style={td}>{provenance(memory)}</td>\n <td style={td}>\n <button type=\"button\" style={dangerBtn} onClick={() => handleForget(memory.id, 'superseded from panel')}>\n \uD83D\uDDD1 {t('forget')}\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n\n {/* Superseded */}\n {payload.superseded.length > 0 && (\n <>\n <strong style={{ fontSize: 13 }}>\uD83D\uDCE6 {t('superseded')} ({payload.superseded.length})</strong>\n <table style={table}>\n <thead>\n <tr>\n <th style={th}>Id</th><th style={th}>{t('memory')}</th><th style={th}>{t('kind')}</th>\n <th style={th}>{t('status')}</th><th style={th}>{t('from')}</th>\n </tr>\n </thead>\n <tbody>\n {payload.superseded.map((memory) => (\n <tr key={memory.id}>\n <td style={td}><code style={{ fontSize: 11 }}>{memory.id}</code></td>\n <td style={td}>{memory.text}</td>\n <td style={td}>{memory.kind}</td>\n <td style={td}>{checkLabel(memory, t)}</td>\n <td style={td}>{provenance(memory)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/lib/routes.d.ts
CHANGED
|
@@ -11,4 +11,7 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
11
11
|
import type { MemoryService } from './index.js';
|
|
12
12
|
/** The panel path, shared with the browser half so the two cannot drift apart. */
|
|
13
13
|
export declare const MEMORY_PANEL_PATH = "/api/context-memory.panel";
|
|
14
|
+
export declare const MEMORY_RECALL_PATH = "/api/context-memory.recall";
|
|
15
|
+
export declare const MEMORY_REMEMBER_PATH = "/api/context-memory.remember";
|
|
16
|
+
export declare const MEMORY_FORGET_PATH = "/api/context-memory.forget";
|
|
14
17
|
export declare function registerMemoryRoutes(ctx: Context, memory: MemoryService): void;
|
package/lib/routes.js
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
/** The panel path, shared with the browser half so the two cannot drift apart. */
|
|
11
11
|
export const MEMORY_PANEL_PATH = '/api/context-memory.panel';
|
|
12
|
+
export const MEMORY_RECALL_PATH = '/api/context-memory.recall';
|
|
13
|
+
export const MEMORY_REMEMBER_PATH = '/api/context-memory.remember';
|
|
14
|
+
export const MEMORY_FORGET_PATH = '/api/context-memory.forget';
|
|
12
15
|
export function registerMemoryRoutes(ctx, memory) {
|
|
13
16
|
// Injecting rather than reading `ctx.connection` directly: a plain property access on
|
|
14
17
|
// a service that was not declared throws, and the web connection is absent in headless
|
|
@@ -30,12 +33,69 @@ export function registerMemoryRoutes(ctx, memory) {
|
|
|
30
33
|
fetch: async () => {
|
|
31
34
|
const summary = memory.verify();
|
|
32
35
|
return Response.json(summary, {
|
|
33
|
-
// The verification is the answer, not an error: a violated memory is reported
|
|
34
|
-
// with 200 and the caller decides what it means. Only a broken guard is a
|
|
35
|
-
// defect in the request path itself, and that is reported in the payload.
|
|
36
36
|
headers: { 'cache-control': 'no-store' },
|
|
37
37
|
});
|
|
38
38
|
},
|
|
39
39
|
});
|
|
40
|
+
// Recall (search) — GET with query param
|
|
41
|
+
connection.fetch.register({
|
|
42
|
+
path: MEMORY_RECALL_PATH,
|
|
43
|
+
methods: ['GET'],
|
|
44
|
+
requestBody: 'buffered',
|
|
45
|
+
fetch: async (request) => {
|
|
46
|
+
const url = new URL(request.url);
|
|
47
|
+
const query = url.searchParams.get('q') ?? '';
|
|
48
|
+
const limit = Number(url.searchParams.get('limit') ?? '10');
|
|
49
|
+
const hits = memory.recall(query, limit);
|
|
50
|
+
return Response.json({ hits }, { headers: { 'cache-control': 'no-store' } });
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
// Remember (add) — POST with body
|
|
54
|
+
connection.fetch.register({
|
|
55
|
+
path: MEMORY_REMEMBER_PATH,
|
|
56
|
+
methods: ['POST'],
|
|
57
|
+
requestBody: 'buffered',
|
|
58
|
+
fetch: async (request) => {
|
|
59
|
+
let body;
|
|
60
|
+
try {
|
|
61
|
+
body = await request.json();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return Response.json({ error: 'invalid JSON' }, { status: 400 });
|
|
65
|
+
}
|
|
66
|
+
if (!body.text)
|
|
67
|
+
return Response.json({ error: 'missing text' }, { status: 400 });
|
|
68
|
+
const input = { text: body.text };
|
|
69
|
+
if (body.subject)
|
|
70
|
+
input.subject = body.subject;
|
|
71
|
+
if (body.kind)
|
|
72
|
+
input.kind = body.kind;
|
|
73
|
+
if (body.source)
|
|
74
|
+
input.source = body.source;
|
|
75
|
+
const mem = memory.remember(input);
|
|
76
|
+
return Response.json(mem);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
// Forget (supersede) — POST with id + reason
|
|
80
|
+
connection.fetch.register({
|
|
81
|
+
path: MEMORY_FORGET_PATH,
|
|
82
|
+
methods: ['POST'],
|
|
83
|
+
requestBody: 'buffered',
|
|
84
|
+
fetch: async (request) => {
|
|
85
|
+
let body;
|
|
86
|
+
try {
|
|
87
|
+
body = await request.json();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return Response.json({ error: 'invalid JSON' }, { status: 400 });
|
|
91
|
+
}
|
|
92
|
+
if (!body.id)
|
|
93
|
+
return Response.json({ error: 'missing id' }, { status: 400 });
|
|
94
|
+
const ok = memory.forget(body.id, body.reason ?? 'superseded from panel', body.replacedBy);
|
|
95
|
+
if (!ok)
|
|
96
|
+
return Response.json({ error: 'not found' }, { status: 404 });
|
|
97
|
+
return Response.json({ ok: true });
|
|
98
|
+
},
|
|
99
|
+
});
|
|
40
100
|
});
|
|
41
101
|
}
|
package/package.json
CHANGED