dsh-context-memory 0.7.0 → 0.9.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.
@@ -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", "../src/client/ui.tsx"],
4
- "sourcesContent": ["/**\r\n * Browser half of dsh-context-memory: its page inside Settings.\r\n *\r\n * Two services are injected by their runtime identity, not by the packages that provide\r\n * them: the settings shell is `slots`, the dictionary registry is `locale`. The label and\r\n * every string inside the component are translated through this plugin's own namespace,\r\n * which is why the page follows the UI language instead of the plugin author's.\r\n *\r\n * @module client\r\n */\r\n\r\nimport type { ComponentType } from 'react'\r\n\r\nimport { NS, en, zh } from './locales.js'\r\nimport { ContextMemoryPanel } from './view.js'\r\n\r\nexport const inject = ['slots', 'locale']\r\n\r\ninterface SlotsService {\r\n inject(name: string, register: () => void): void\r\n register(\r\n options: { name: string; id: string; order: number; label: () => string; locale: string },\r\n component: ComponentType<{ t: (key: string, params?: Record<string, unknown>) => string }>,\r\n ): unknown\r\n}\r\n\r\ninterface ClientContext {\r\n slots: SlotsService\r\n locale: {\r\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\r\n bind(ns: string): (key: string, params?: Record<string, unknown>) => string\r\n }\r\n effect(callback: () => unknown, label?: string): unknown\r\n}\r\n\r\nexport function apply(ctx: ClientContext): void {\r\n // `zh` is the key-set source of truth, matching the official client plugins.\r\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-context-memory: dictionaries')\r\n\r\n ctx.slots.inject('settings.section', () => ctx.slots.register(\r\n {\r\n name: 'settings.section',\r\n id: 'context-memory',\r\n order: 45,\r\n label: () => ctx.locale.bind(NS)('nav'),\r\n locale: NS,\r\n },\r\n ContextMemoryPanel,\r\n ))\r\n}\r\n", "/**\r\n * Dictionaries for the Context memory page.\r\n *\r\n * `zh` is the key-set source of truth, as in the official client plugins, and `en` is\r\n * typed against it: a key translated in one language but not the other fails the build\r\n * instead of silently rendering the raw key.\r\n *\r\n * @module client/locales\r\n */\r\n\r\n/** Dictionary namespace owned by this plugin. */\r\nexport const NS = 'contextMemory'\r\n\r\n/** Simplified Chinese dictionary (the key-set source of truth). */\r\nexport const zh = {\r\n 'nav': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\r\n 'title': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\r\n 'active': '\u751F\u6548\u4E2D',\r\n 'superseded': '\u5DF2\u5931\u6548',\r\n 'conflicts': '\u51B2\u7A81',\r\n 'noConflicts': '\u6CA1\u6709\u51B2\u7A81\u3002',\r\n 'memory': '\u8BB0\u5FC6',\r\n 'kind': '\u7C7B\u578B',\r\n 'status': '\u6821\u9A8C\u72B6\u6001',\r\n 'from': '\u6765\u6E90',\r\n 'checkNone': '\u65E0\u68C0\u67E5',\r\n 'checkPassed': '\u5DF2\u6838\u5BF9\uFF0C\u6210\u7ACB',\r\n 'checkViolated': '\u5DF2\u6838\u5BF9\uFF0C\u4E0D\u6210\u7ACB',\r\n 'checkBroken': '\u68C0\u67E5\u65E0\u6CD5\u8FD0\u884C',\r\n 'checkPending': '\u5C1A\u672A\u6838\u5BF9',\r\n 'unverified': '\u672A\u9A8C\u8BC1\u6210\u7ACB',\r\n 'empty': '\u8FD8\u6CA1\u6709\u8BB0\u4F4F\u4EFB\u4F55\u5185\u5BB9\u3002',\r\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',\r\n 'refresh': '\u5237\u65B0',\r\n 'loading': '\u6B63\u5728\u52A0\u8F7D\u2026',\r\n 'failed': '\u52A0\u8F7D\u5931\u8D25',\r\n 'retry': '\u91CD\u8BD5',\r\n 'searchPlaceholder': '\u641C\u7D22\u8BB0\u5FC6\u2026',\r\n 'search': '\u641C\u7D22',\r\n 'searchResults': '\u641C\u7D22\u7ED3\u679C',\r\n 'noResults': '\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5FC6\u3002',\r\n 'addMemory': '\u6DFB\u52A0\u8BB0\u5FC6',\r\n 'memoryText': '\u8BB0\u5FC6\u5185\u5BB9',\r\n 'subjectOptional': '\u4E3B\u9898\uFF08\u53EF\u9009\uFF09',\r\n 'kindFact': '\u4E8B\u5B9E',\r\n 'kindDecision': '\u51B3\u7B56',\r\n 'kindPreference': '\u504F\u597D',\r\n 'kindPitfall': '\u9677\u9631',\r\n 'add': '\u6DFB\u52A0',\r\n 'forget': '\u5E9F\u5F03',\r\n 'resolve': '\u89E3\u51B3',\r\n 'integration': '\u96C6\u6210',\r\n 'autoCaptureLabel': '\u81EA\u52A8\u6355\u83B7:\u7528\u6237\u6D88\u606F\u91CC\u7684\u300C\u8BB0\u4F4F\u2026\u300D\u300C\u4EE5\u540E\u2026\u300D\u81EA\u52A8\u5165\u5E93',\r\n 'recallInjectLabel': '\u53EC\u56DE\u6CE8\u5165:\u6BCF\u6B21\u8BF7\u6C42\u524D\u628A\u76F8\u5173\u8BB0\u5FC6\u6298\u8FDB\u4E0A\u4E0B\u6587',\r\n}\r\n\r\n/** English dictionary, checked complete against the zh key set. */\r\nexport const en: typeof zh = {\r\n 'nav': 'Context memory',\r\n 'title': 'Context memory',\r\n 'active': 'active',\r\n 'superseded': 'superseded',\r\n 'conflicts': 'Conflicts',\r\n 'noConflicts': 'No conflicts.',\r\n 'memory': 'Memory',\r\n 'kind': 'Kind',\r\n 'status': 'Check',\r\n 'from': 'From',\r\n 'checkNone': 'no check',\r\n 'checkPassed': 'checked, holds',\r\n 'checkViolated': 'checked, does not hold',\r\n 'checkBroken': 'check unusable',\r\n 'checkPending': 'not yet checked',\r\n 'unverified': 'not verified as holding',\r\n 'empty': 'Nothing is remembered yet.',\r\n 'checkLegend': 'no check = no check could be compiled; check unusable = the check is defective, not the memory.',\r\n 'refresh': 'Refresh',\r\n 'loading': 'Loading\u2026',\r\n 'failed': 'Failed to load',\r\n 'retry': 'Retry',\r\n 'searchPlaceholder': 'Search memories\u2026',\r\n 'search': 'Search',\r\n 'searchResults': 'Search Results',\r\n 'noResults': 'No matching memories.',\r\n 'addMemory': 'Add Memory',\r\n 'memoryText': 'Memory text',\r\n 'subjectOptional': 'Subject (optional)',\r\n 'kindFact': 'Fact',\r\n 'kindDecision': 'Decision',\r\n 'kindPreference': 'Preference',\r\n 'kindPitfall': 'Pitfall',\r\n 'add': 'Add',\r\n 'forget': 'Forget',\r\n 'resolve': 'Resolve',\r\n 'integration': 'Integration',\r\n 'autoCaptureLabel': 'Auto capture: \"remember that\u2026\" statements land in the log',\r\n 'recallInjectLabel': 'Recall injection: fold relevant memories into each request',\r\n}\r\n", "/**\r\n * Pure rendering half of the contextMemory page. Uses shared UI kit.\r\n * @module client/view\r\n */\r\n\r\nimport { useCallback, useEffect, useState } from 'react'\r\nimport type { ReactNode } from 'react'\r\n\r\nimport type { MemoryState, PanelPayload, RecallHit } from '../types.js'\r\nimport {\r\n Badge, Button, Card, ConfirmDialog, EmptyState, Field, Input, Modal,\r\n SectionTitle, Select, Spinner, StatCard, ToastProvider, tableStyles,\r\n usePanel, useToast,\r\n} from './ui.js'\r\n\r\nexport type Translate = (key: string, params?: Record<string, unknown>) => string\r\nexport interface PanelProps { t: Translate }\r\n\r\nconst PANEL_PATH = '/api/context-memory.panel'\r\nconst RECALL_PATH = '/api/context-memory.recall'\r\nconst REMEMBER_PATH = '/api/context-memory.remember'\r\nconst FORGET_PATH = '/api/context-memory.forget'\r\nconst PREFS_PATH = '/api/context-memory.prefs'\r\n\r\nexport function checkLabel(memory: MemoryState, t: Translate): string {\r\n if (!memory.guard) return t('checkNone')\r\n switch (memory.verification?.outcome) {\r\n case 'passed': return t('checkPassed')\r\n case 'violated': return t('checkViolated')\r\n case 'broken': return t('checkBroken')\r\n default: return t('checkPending')\r\n }\r\n}\r\n\r\nexport function provenance(memory: MemoryState): string {\r\n return [memory.source, memory.sessionId !== undefined ? `session ${memory.sessionId}` : null, memory.step !== undefined ? `step ${String(memory.step)}` : null].filter((part) => part !== null).join(' \u00B7 ')\r\n}\r\n\r\nfunction AddMemoryModal({ t, onClose, onAdded }: { t: Translate; onClose: () => void; onAdded: () => void }): ReactNode {\r\n const toast = useToast()\r\n const [text, setText] = useState('')\r\n const [subject, setSubject] = useState('')\r\n const [kind, setKind] = useState('fact')\r\n const [adding, setAdding] = useState(false)\r\n\r\n const handleAdd = useCallback(async () => {\r\n if (!text.trim()) return\r\n setAdding(true)\r\n try {\r\n const r = await fetch(REMEMBER_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text, subject: subject || undefined, kind, source: 'user' }) })\r\n if (r.ok) { toast('success', t('memoryAdded')); onAdded(); onClose() }\r\n } finally { setAdding(false) }\r\n }, [text, subject, kind, t, toast, onAdded, onClose])\r\n\r\n return (\r\n <Modal title={t('addMemory')} onClose={onClose} width={500}\r\n footer={<><Button variant=\"secondary\" onClick={onClose}>{t('cancel')}</Button>\r\n <Button variant=\"primary\" disabled={adding || !text.trim()} onClick={handleAdd}>{adding ? <Spinner size={14} /> : null} {t('add')}</Button></>}>\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>\r\n <Field label={t('memoryText')}><Input value={text} onChange={(e) => setText(e.target.value)} placeholder={t('memoryTextPlaceholder')} /></Field>\r\n <Field label={t('subjectOptional')}><Input value={subject} onChange={(e) => setSubject(e.target.value)} placeholder={t('subjectPlaceholder')} /></Field>\r\n <Field label={t('kind')}>\r\n <Select value={kind} onChange={(e) => setKind(e.target.value)} style={{ width: '100%' }}>\r\n <option value=\"fact\">{t('kindFact')}</option>\r\n <option value=\"decision\">{t('kindDecision')}</option>\r\n <option value=\"preference\">{t('kindPreference')}</option>\r\n <option value=\"pitfall\">{t('kindPitfall')}</option>\r\n </Select>\r\n </Field>\r\n </div>\r\n </Modal>\r\n )\r\n}\r\n\r\nfunction ContextMemoryPanelInner({ t }: PanelProps): ReactNode {\r\n const { payload, error, reload } = usePanel<PanelPayload>(PANEL_PATH)\r\n const toast = useToast()\r\n const [searchQuery, setSearchQuery] = useState('')\r\n const [searchHits, setSearchHits] = useState<RecallHit[] | null>(null)\r\n const [searching, setSearching] = useState(false)\r\n const [prefs, setPrefsState] = useState<{ autoCapture: boolean; recallInject: boolean } | null>(null)\r\n\r\n const togglePref = useCallback(async (key: 'autoCapture' | 'recallInject') => {\r\n const r = await fetch(PREFS_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ [key]: !(prefs?.[key] ?? true) }) })\r\n if (r.ok) setPrefsState(await r.json() as { autoCapture: boolean; recallInject: boolean })\r\n }, [prefs])\r\n\r\n useEffect(() => { void fetch(PREFS_PATH).then(async (r) => { if (r.ok) setPrefsState(await r.json() as { autoCapture: boolean; recallInject: boolean }) }) }, [])\r\n const [addOpen, setAddOpen] = useState(false)\r\n const [forgetTarget, setForgetTarget] = useState<{ id: string; reason: string } | null>(null)\r\n\r\n const handleSearch = useCallback(async () => {\r\n if (!searchQuery.trim()) { setSearchHits(null); return }\r\n setSearching(true)\r\n try { const r = await fetch(`${RECALL_PATH}?q=${encodeURIComponent(searchQuery)}&limit=10`); if (r.ok) { setSearchHits((await r.json() as { hits: RecallHit[] }).hits) } }\r\n finally { setSearching(false) }\r\n }, [searchQuery])\r\n\r\n const handleForget = useCallback(async () => {\r\n if (forgetTarget === null) return\r\n const r = await fetch(FORGET_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(forgetTarget) })\r\n if (r.ok) { toast('success', t('forgotten')); setForgetTarget(null); reload() }\r\n }, [forgetTarget, t, toast, reload])\r\n\r\n const header = (\r\n <header style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4 }}>\r\n <strong style={{ fontSize: 15 }}>\uD83E\uDDE0 {t('title')}</strong>\r\n <span style={{ flex: 1 }} />\r\n <Button variant=\"primary\" size=\"sm\" onClick={() => setAddOpen(true)}>\u2795 {t('addMemory')}</Button>\r\n <Button variant=\"secondary\" size=\"sm\" onClick={reload}>{t('refresh')}</Button>\r\n </header>\r\n )\r\n\r\n if (error !== null) return <div style={{ maxWidth: 820 }}>{header}<Card><p role=\"alert\" style={{ margin: 0, fontSize: 13, color: 'var(--dsw-alias-state-error-primary)' }}>{t('failed')}: {error}</p></Card></div>\r\n if (payload === null) return <div style={{ maxWidth: 820 }}>{header}<div style={{ display: 'flex', justifyContent: 'center', padding: 40 }}><Spinner size={28} /></div></div>\r\n\r\n return (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }}>\r\n {header}\r\n\r\n <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>\r\n <StatCard value={payload.totals.active} label={t('active')} />\r\n <StatCard value={payload.totals.verified} label={t('checkPassed')} />\r\n <StatCard value={payload.totals.unverified} label={t('unverified')} />\r\n <StatCard value={payload.totals.superseded} label={t('superseded')} />\r\n </div>\r\n\r\n {/* Integration switches */}\r\n <Card title={t('integration')} icon=\"\u2699\uFE0F\">\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\r\n {([['autoCapture', 'autoCaptureLabel'], ['recallInject', 'recallInjectLabel']] as const).map(([key, labelKey]) => (\r\n <label key={key} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }}>\r\n <input type=\"checkbox\" checked={prefs?.[key] ?? true} onChange={() => togglePref(key)} />\r\n {t(labelKey)}\r\n </label>\r\n ))}\r\n </div>\r\n </Card>\r\n\r\n {/* Search */}\r\n <Card title={t('search')} icon=\"\uD83D\uDD0D\">\r\n <div style={{ display: 'flex', gap: 8 }}>\r\n <Input value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder={t('searchPlaceholder')}\r\n onKeyDown={(e) => { if (e.key === 'Enter') handleSearch() }} />\r\n <Button variant=\"secondary\" onClick={handleSearch} disabled={searching}>{searching ? <Spinner size={14} /> : null} {t('search')}</Button>\r\n </div>\r\n {searchHits !== null && (\r\n <div style={{ marginTop: 10 }}>\r\n {searchHits.length === 0 ? <EmptyState message={t('noResults')} /> : (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>\r\n {searchHits.map((hit) => (\r\n <div key={hit.memory.id} style={{ display: 'flex', gap: 8, alignItems: 'baseline' }}>\r\n <Badge color=\"info\">{hit.score.toFixed(2)}</Badge>\r\n <span style={{ fontSize: 12 }}>{hit.memory.text}</span>\r\n <span style={{ fontSize: 10, opacity: 0.5 }}>\u2014 {hit.why}</span>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </Card>\r\n\r\n {/* Conflicts */}\r\n {payload.conflicts.length > 0 && (\r\n <>\r\n <SectionTitle icon=\"\u26A0\uFE0F\">{t('conflicts')}</SectionTitle>\r\n {payload.conflicts.map((conflict) => (\r\n <Card key={conflict.subject} title={conflict.subject}>\r\n {conflict.memories.map((memory) => (\r\n <div key={memory.id} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>\r\n <code style={{ fontSize: 11 }}>{memory.id}</code>\r\n <span style={{ fontSize: 12, flex: 1 }}>{memory.text}</span>\r\n <Button variant=\"danger\" size=\"sm\" onClick={() => setForgetTarget({ id: memory.id, reason: `conflict resolved: ${conflict.subject}` })}>{t('resolve')}</Button>\r\n </div>\r\n ))}\r\n </Card>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Active memories */}\r\n <SectionTitle icon=\"\u2705\">{t('active')}</SectionTitle>\r\n {payload.active.length === 0 ? <EmptyState icon=\"\uD83D\uDCED\" message={t('empty')} /> : (\r\n <Card padding={0}>\r\n <table style={tableStyles.table}>\r\n <thead><tr><th style={tableStyles.th}>Id</th><th style={tableStyles.th}>{t('memory')}</th><th style={tableStyles.th}>{t('kind')}</th><th style={tableStyles.th}>{t('status')}</th><th style={tableStyles.th}>{t('from')}</th><th style={tableStyles.th} /></tr></thead>\r\n <tbody>\r\n {payload.active.map((memory) => (\r\n <tr key={memory.id}>\r\n <td style={tableStyles.td}><code style={{ fontSize: 11 }}>{memory.id}</code></td>\r\n <td style={tableStyles.td}>{memory.text}</td>\r\n <td style={tableStyles.td}><Badge>{memory.kind}</Badge></td>\r\n <td style={tableStyles.td}>{checkLabel(memory, t)}</td>\r\n <td style={{ ...tableStyles.td, fontSize: 11, opacity: 0.6 }}>{provenance(memory)}</td>\r\n <td style={tableStyles.td}><Button variant=\"danger\" size=\"sm\" onClick={() => setForgetTarget({ id: memory.id, reason: 'superseded from panel' })}>\uD83D\uDDD1</Button></td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </Card>\r\n )}\r\n\r\n {payload.superseded.length > 0 && (\r\n <>\r\n <SectionTitle icon=\"\uD83D\uDCE6\">{t('superseded')} ({payload.superseded.length})</SectionTitle>\r\n <Card padding={0}>\r\n <table style={tableStyles.table}>\r\n <thead><tr><th style={tableStyles.th}>Id</th><th style={tableStyles.th}>{t('memory')}</th><th style={tableStyles.th}>{t('kind')}</th><th style={tableStyles.th}>{t('status')}</th></tr></thead>\r\n <tbody>\r\n {payload.superseded.map((memory) => (\r\n <tr key={memory.id}><td style={tableStyles.td}><code style={{ fontSize: 11 }}>{memory.id}</code></td><td style={tableStyles.td}>{memory.text}</td><td style={tableStyles.td}><Badge>{memory.kind}</Badge></td><td style={tableStyles.td}>{checkLabel(memory, t)}</td></tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </Card>\r\n </>\r\n )}\r\n\r\n {addOpen && <AddMemoryModal t={t} onClose={() => setAddOpen(false)} onAdded={reload} />}\r\n {forgetTarget !== null && <ConfirmDialog title={t('forget')} message={t('confirmForget')} confirmLabel={t('forget')} danger onConfirm={handleForget} onClose={() => setForgetTarget(null)} />}\r\n </div>\r\n )\r\n}\r\n\r\nexport function ContextMemoryPanel({ t }: PanelProps): ReactNode {\r\n return <ToastProvider><ContextMemoryPanelInner t={t} /></ToastProvider>\r\n}\r\n", "/**\r\n * Shared UI component kit for dsh plugin panels.\r\n *\r\n * Self-contained: imports nothing but React. All styling is inline + CSS custom\r\n * properties so the components follow the host theme. A single <style> tag injects\r\n * keyframes the first time any component mounts.\r\n *\r\n * @module client/ui\r\n */\r\n\r\nimport {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useState,\r\n} from 'react'\r\nimport type {\r\n ButtonHTMLAttributes,\r\n CSSProperties,\r\n InputHTMLAttributes,\r\n ReactNode,\r\n SelectHTMLAttributes,\r\n} from 'react'\r\n\r\n// \u2500\u2500\u2500 Theme tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n/**\r\n * Host theme tokens.\r\n *\r\n * Every name here is a real dsh token, read out of the shipped stylesheets\r\n * (`@deepseek-ai/dsh-client-ui-theme`), which define them on `body`. An earlier\r\n * version used invented names \u2014 `--accent`, `--border`, `--bg-primary`,\r\n * `--text-primary` and friends \u2014 that dsh does not define at all. The\r\n * hardcoded fallbacks then applied in every theme, so each panel rendered its\r\n * own fixed palette instead of following the host, and the modal and input\r\n * surfaces stayed white in dark mode.\r\n *\r\n * No fallbacks on purpose: if a name were ever missing, the declaration becomes\r\n * invalid at computed-value time and the property inherits, which degrades\r\n * gracefully, whereas a hardcoded fallback bakes in a color that is wrong in one\r\n * of the two themes. `tests/ui-tokens.test.mjs` enforces the name list.\r\n */\r\nconst T = {\r\n accent: 'var(--dsw-alias-brand-primary)',\r\n accentHover: 'var(--dsw-alias-button-primary-hover)',\r\n inverted: 'var(--dsw-alias-label-primary-inverted)',\r\n error: 'var(--dsw-alias-state-error-primary)',\r\n success: 'var(--dsw-alias-state-success-primary)',\r\n warning: 'var(--dsw-alias-state-warn-primary)',\r\n bg: 'var(--dsw-alias-bg-layer-2)',\r\n bgHover: 'var(--dsw-alias-interactive-bg-hover)',\r\n border: 'var(--dsw-alias-border-l2)',\r\n text: 'var(--dsw-alias-label-primary)',\r\n muted: 'var(--dsw-alias-label-tertiary)',\r\n surface: 'var(--dsw-alias-bg-layer-1)',\r\n mask: 'var(--dsw-alias-bg-mask-1)',\r\n}\r\n\r\n// \u2500\u2500\u2500 Style injection (once) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nlet injected = false\r\nfunction ensureStyles(): void {\r\n if (injected || typeof document === 'undefined') return\r\n injected = true\r\n const el = document.createElement('style')\r\n el.textContent = `\r\n@keyframes dsh-fade-in{from{opacity:0}to{opacity:1}}\r\n@keyframes dsh-slide-up{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\r\n@keyframes dsh-spin{to{transform:rotate(360deg)}}\r\n.dsh-modal-overlay{animation:dsh-fade-in .15s ease}\r\n.dsh-modal-body{animation:dsh-slide-up .2s ease}\r\n.dsh-toast{animation:dsh-slide-up .2s ease}\r\n.dsh-spin{animation:dsh-spin .6s linear infinite}\r\n `\r\n document.head.appendChild(el)\r\n}\r\n\r\n// \u2500\u2500\u2500 Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Modal({\r\n title,\r\n onClose,\r\n children,\r\n footer,\r\n width = 520,\r\n}: {\r\n title: string\r\n onClose: () => void\r\n children: ReactNode\r\n footer?: ReactNode\r\n width?: number\r\n}): ReactNode {\r\n useEffect(() => {\r\n ensureStyles()\r\n const handler = (e: KeyboardEvent): void => {\r\n if (e.key === 'Escape') onClose()\r\n }\r\n document.addEventListener('keydown', handler)\r\n return () => document.removeEventListener('keydown', handler)\r\n }, [onClose])\r\n\r\n return (\r\n <div\r\n className=\"dsh-modal-overlay\"\r\n style={{\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 1000,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n background: T.mask,\r\n backdropFilter: 'blur(3px)',\r\n WebkitBackdropFilter: 'blur(3px)',\r\n }}\r\n onClick={onClose}\r\n >\r\n <div\r\n className=\"dsh-modal-body\"\r\n style={{\r\n background: T.surface,\r\n borderRadius: 14,\r\n boxShadow: 'var(--dsw-elevation-prominent)',\r\n width: `min(92vw, ${width}px)`,\r\n maxHeight: '85vh',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: '16px 22px',\r\n borderBottom: `1px solid ${T.border}`,\r\n }}\r\n >\r\n <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{title}</h3>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n style={{\r\n background: 'none',\r\n border: 'none',\r\n cursor: 'pointer',\r\n fontSize: 20,\r\n color: T.muted,\r\n padding: '0 4px',\r\n lineHeight: 1,\r\n borderRadius: 4,\r\n }}\r\n aria-label=\"Close\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div style={{ padding: 22, overflow: 'auto', flex: 1 }}>{children}</div>\r\n {footer !== undefined && (\r\n <div\r\n style={{\r\n padding: '14px 22px',\r\n borderTop: `1px solid ${T.border}`,\r\n display: 'flex',\r\n justifyContent: 'flex-end',\r\n gap: 10,\r\n background: T.bg,\r\n }}\r\n >\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 ConfirmDialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function ConfirmDialog({\r\n title,\r\n message,\r\n confirmLabel,\r\n cancelLabel = 'Cancel',\r\n onConfirm,\r\n onClose,\r\n danger,\r\n}: {\r\n title: string\r\n message: string\r\n confirmLabel: string\r\n cancelLabel?: string\r\n onConfirm: () => void\r\n onClose: () => void\r\n danger?: boolean\r\n}): ReactNode {\r\n return (\r\n <Modal\r\n title={title}\r\n onClose={onClose}\r\n width={420}\r\n footer={\r\n <>\r\n <Button variant=\"secondary\" onClick={onClose}>\r\n {cancelLabel}\r\n </Button>\r\n <Button\r\n variant={danger === true ? 'danger' : 'primary'}\r\n onClick={() => {\r\n onConfirm()\r\n onClose()\r\n }}\r\n >\r\n {confirmLabel}\r\n </Button>\r\n </>\r\n }\r\n >\r\n <p style={{ margin: 0, fontSize: 14, lineHeight: 1.6 }}>{message}</p>\r\n </Modal>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Button({\r\n variant = 'secondary',\r\n size = 'md',\r\n children,\r\n style,\r\n ...rest\r\n}: {\r\n variant?: 'primary' | 'secondary' | 'danger' | 'ghost'\r\n size?: 'sm' | 'md'\r\n children: ReactNode\r\n style?: CSSProperties\r\n} & ButtonHTMLAttributes<HTMLButtonElement>): ReactNode {\r\n const base: CSSProperties = {\r\n cursor: 'pointer',\r\n borderRadius: 7,\r\n fontWeight: 500,\r\n transition: 'all 0.15s ease',\r\n border: 'none',\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n fontSize: size === 'sm' ? 12 : 13,\r\n padding: size === 'sm' ? '5px 12px' : '8px 18px',\r\n fontFamily: 'inherit',\r\n }\r\n const variants: Record<string, CSSProperties> = {\r\n primary: { background: T.accent, color: T.inverted },\r\n secondary: {\r\n background: T.bg,\r\n color: T.text,\r\n border: `1px solid ${T.border}`,\r\n },\r\n danger: {\r\n background: 'transparent',\r\n color: T.error,\r\n border: `1px solid ${T.error}`,\r\n },\r\n ghost: { background: 'transparent', color: T.muted, border: 'none' },\r\n }\r\n return (\r\n <button type=\"button\" style={{ ...base, ...variants[variant], ...style }} {...rest}>\r\n {children}\r\n </button>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Card({\r\n title,\r\n icon,\r\n children,\r\n actions,\r\n style,\r\n padding = 14,\r\n}: {\r\n title?: string\r\n icon?: string\r\n children: ReactNode\r\n actions?: ReactNode\r\n style?: CSSProperties\r\n padding?: number\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n borderRadius: 10,\r\n border: `1px solid ${T.border}`,\r\n background: T.bg,\r\n overflow: 'hidden',\r\n ...style,\r\n }}\r\n >\r\n {(title !== undefined || actions !== undefined) && (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: '12px 16px',\r\n borderBottom: title !== undefined ? `1px solid ${T.border}` : 'none',\r\n }}\r\n >\r\n <span style={{ fontSize: 13, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 6 }}>\r\n {icon !== undefined && <span>{icon}</span>}\r\n {title}\r\n </span>\r\n {actions}\r\n </div>\r\n )}\r\n <div style={{ padding }}>{children}</div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 StatCard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function StatCard({\r\n value,\r\n label,\r\n color,\r\n unit,\r\n}: {\r\n value: string | number\r\n label: string\r\n color?: string\r\n unit?: string\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n flex: 1,\r\n minWidth: 90,\r\n textAlign: 'center',\r\n padding: '14px 10px',\r\n borderRadius: 10,\r\n border: `1px solid ${T.border}`,\r\n background: T.bg,\r\n }}\r\n >\r\n <div style={{ fontSize: 24, fontWeight: 700, color: color ?? T.text, lineHeight: 1.2 }}>\r\n {value}\r\n {unit !== undefined && (\r\n <span style={{ fontSize: 13, fontWeight: 500, opacity: 0.7, marginLeft: 2 }}>{unit}</span>\r\n )}\r\n </div>\r\n <div style={{ fontSize: 11, color: T.muted, marginTop: 4 }}>{label}</div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Badge({\r\n children,\r\n color = 'default',\r\n}: {\r\n children: ReactNode\r\n color?: 'default' | 'success' | 'error' | 'warning' | 'info'\r\n}): ReactNode {\r\n const colors: Record<string, { bg: string; fg: string }> = {\r\n default: { bg: T.bgHover, fg: T.muted },\r\n success: { bg: 'var(--dsw-alias-state-success-tertiary)', fg: T.success },\r\n error: { bg: 'var(--dsw-alias-state-error-secondary)', fg: T.error },\r\n warning: { bg: 'var(--dsw-alias-state-warn-tertiary)', fg: T.warning },\r\n info: { bg: 'var(--dsw-alias-state-business-tertiary)', fg: T.accent },\r\n }\r\n const c = colors[color]\r\n return (\r\n <span\r\n style={{\r\n display: 'inline-block',\r\n padding: '2px 9px',\r\n borderRadius: 10,\r\n fontSize: 11,\r\n fontWeight: 600,\r\n background: c.bg,\r\n color: c.fg,\r\n }}\r\n >\r\n {children}\r\n </span>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Input({\r\n style,\r\n ...rest\r\n}: { style?: CSSProperties } & InputHTMLAttributes<HTMLInputElement>): ReactNode {\r\n return (\r\n <input\r\n {...rest}\r\n style={{\r\n fontSize: 13,\r\n padding: '8px 12px',\r\n borderRadius: 7,\r\n border: `1px solid ${T.border}`,\r\n background: T.surface,\r\n color: T.text,\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n outline: 'none',\r\n transition: 'border-color 0.15s',\r\n ...style,\r\n }}\r\n />\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Select({\r\n style,\r\n children,\r\n ...rest\r\n}: {\r\n style?: CSSProperties\r\n children: ReactNode\r\n} & SelectHTMLAttributes<HTMLSelectElement>): ReactNode {\r\n return (\r\n <select\r\n {...rest}\r\n style={{\r\n fontSize: 13,\r\n padding: '8px 12px',\r\n borderRadius: 7,\r\n border: `1px solid ${T.border}`,\r\n background: T.surface,\r\n color: T.text,\r\n cursor: 'pointer',\r\n fontFamily: 'inherit',\r\n outline: 'none',\r\n ...style,\r\n }}\r\n >\r\n {children}\r\n </select>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Textarea \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Textarea({\r\n style,\r\n ...rest\r\n}: { style?: CSSProperties } & React.TextareaHTMLAttributes<HTMLTextAreaElement>): ReactNode {\r\n return (\r\n <textarea\r\n {...rest}\r\n style={{\r\n fontSize: 13,\r\n padding: '8px 12px',\r\n borderRadius: 7,\r\n border: `1px solid ${T.border}`,\r\n background: T.surface,\r\n color: T.text,\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n outline: 'none',\r\n resize: 'vertical',\r\n lineHeight: 1.5,\r\n ...style,\r\n }}\r\n />\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\ninterface ToastItem {\r\n id: number\r\n type: 'success' | 'error' | 'info'\r\n message: string\r\n}\r\n\r\nconst ToastContext = createContext<(type: ToastItem['type'], message: string) => void>(\r\n () => {},\r\n)\r\n\r\nexport function ToastProvider({ children }: { children: ReactNode }): ReactNode {\r\n const [toasts, setToasts] = useState<ToastItem[]>([])\r\n const show = useCallback((type: ToastItem['type'], message: string) => {\r\n const id = Date.now() + Math.random()\r\n setToasts((t) => [...t, { id, type, message }])\r\n setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3500)\r\n }, [])\r\n return (\r\n <ToastContext.Provider value={show}>\r\n {children}\r\n {toasts.length > 0 && (\r\n <div\r\n style={{\r\n position: 'fixed',\r\n bottom: 24,\r\n right: 24,\r\n zIndex: 2000,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 8,\r\n }}\r\n >\r\n {toasts.map((t) => (\r\n <div\r\n key={t.id}\r\n className=\"dsh-toast\"\r\n style={{\r\n padding: '12px 18px',\r\n borderRadius: 8,\r\n fontSize: 13,\r\n fontWeight: 500,\r\n boxShadow: 'var(--dsw-elevation-panel)',\r\n color: T.inverted,\r\n background:\r\n t.type === 'success'\r\n ? T.success\r\n : t.type === 'error'\r\n ? T.error\r\n : T.accent,\r\n maxWidth: 360,\r\n }}\r\n >\r\n {t.message}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </ToastContext.Provider>\r\n )\r\n}\r\n\r\nexport function useToast(): (type: ToastItem['type'], message: string) => void {\r\n return useContext(ToastContext)\r\n}\r\n\r\n// \u2500\u2500\u2500 Spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Spinner({ size = 16 }: { size?: number }): ReactNode {\r\n ensureStyles()\r\n return (\r\n <span\r\n className=\"dsh-spin\"\r\n style={{\r\n display: 'inline-block',\r\n width: size,\r\n height: size,\r\n border: `2px solid ${T.border}`,\r\n borderTopColor: T.accent,\r\n borderRadius: '50%',\r\n }}\r\n />\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 EmptyState \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function EmptyState({\r\n icon,\r\n message,\r\n}: {\r\n icon?: string\r\n message: string\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n textAlign: 'center',\r\n padding: '36px 16px',\r\n color: T.muted,\r\n }}\r\n >\r\n {icon !== undefined && (\r\n <div style={{ fontSize: 36, marginBottom: 10, opacity: 0.4 }}>{icon}</div>\r\n )}\r\n <div style={{ fontSize: 13 }}>{message}</div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 SectionTitle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function SectionTitle({\r\n icon,\r\n children,\r\n actions,\r\n}: {\r\n icon?: string\r\n children: ReactNode\r\n actions?: ReactNode\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n marginTop: 6,\r\n marginBottom: 8,\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontSize: 14,\r\n fontWeight: 600,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 6,\r\n }}\r\n >\r\n {icon !== undefined && <span>{icon}</span>}\r\n {children}\r\n </span>\r\n {actions}\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Table helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport const tableStyles = {\r\n table: {\r\n borderCollapse: 'collapse' as const,\r\n width: '100%' as const,\r\n },\r\n th: {\r\n textAlign: 'left' as const,\r\n padding: '8px 12px 8px 0',\r\n fontWeight: 600 as const,\r\n fontSize: 12 as const,\r\n color: T.muted,\r\n borderBottom: `1px solid ${T.border}`,\r\n },\r\n td: {\r\n padding: '8px 12px 8px 0',\r\n fontSize: 13 as const,\r\n borderBottom: `1px solid ${T.border}`,\r\n },\r\n clickRow: { cursor: 'pointer' as const },\r\n}\r\n\r\n// \u2500\u2500\u2500 CodeBlock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function CodeBlock({\r\n children,\r\n maxHeight = 200,\r\n style,\r\n}: {\r\n children: string\r\n maxHeight?: number\r\n style?: CSSProperties\r\n}): ReactNode {\r\n return (\r\n <pre\r\n style={{\r\n whiteSpace: 'pre-wrap',\r\n wordBreak: 'break-word',\r\n fontSize: 12,\r\n maxHeight,\r\n overflow: 'auto',\r\n padding: 10,\r\n borderRadius: 7,\r\n background: T.bg,\r\n border: `1px solid ${T.border}`,\r\n margin: 0,\r\n ...style,\r\n }}\r\n >\r\n {children}\r\n </pre>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 ProgressBar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function ProgressBar({\r\n pct,\r\n color,\r\n height = 18,\r\n}: {\r\n pct: number\r\n color?: string\r\n height?: number\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n width: '100%',\r\n height,\r\n background: T.bgHover,\r\n borderRadius: 5,\r\n overflow: 'hidden',\r\n position: 'relative',\r\n }}\r\n >\r\n <div\r\n style={{\r\n width: `${Math.min(pct, 100)}%`,\r\n height: '100%',\r\n background: color ?? T.accent,\r\n borderRadius: 5,\r\n transition: 'width 0.3s ease',\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Field label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Field({\r\n label,\r\n children,\r\n hint,\r\n}: {\r\n label: string\r\n children: ReactNode\r\n hint?: string\r\n}): ReactNode {\r\n return (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\r\n <label style={{ fontSize: 12, fontWeight: 600, color: T.text }}>{label}</label>\r\n {children}\r\n {hint !== undefined && (\r\n <span style={{ fontSize: 11, color: T.muted }}>{hint}</span>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 usePanel hook (shared fetch logic) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function usePanel<P>(path: string): {\r\n payload: P | null\r\n error: string | null\r\n reload: () => void\r\n} {\r\n const [payload, setPayload] = useState<P | null>(null)\r\n const [error, setError] = useState<string | null>(null)\r\n const [tick, setTick] = useState(0)\r\n const reload = useCallback(() => setTick((v) => v + 1), [])\r\n\r\n useEffect(() => {\r\n const c = new AbortController()\r\n setError(null)\r\n fetch(path, { signal: c.signal })\r\n .then(async (r) => {\r\n if (!r.ok) throw new Error(String(r.status))\r\n return r.json() as Promise<P>\r\n })\r\n .then((p) => {\r\n if (!c.signal.aborted) setPayload(p)\r\n })\r\n .catch((e: unknown) => {\r\n if (!c.signal.aborted) setPayload(null), setError(e instanceof Error ? e.message : String(e))\r\n })\r\n return () => c.abort()\r\n }, [tick, path])\r\n\r\n return useMemo(() => ({ payload, error, reload }), [payload, error, reload])\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\r\n * Browser half of dsh-context-memory: its page inside Settings.\r\n *\r\n * Two services are injected by their runtime identity, not by the packages that provide\r\n * them: the settings shell is `slots`, the dictionary registry is `locale`. The label and\r\n * every string inside the component are translated through this plugin's own namespace,\r\n * which is why the page follows the UI language instead of the plugin author's.\r\n *\r\n * @module client\r\n */\r\n\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport type { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'\r\nimport type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client'\r\nimport type { SettingsSectionOwnerProps } from '@deepseek-ai/dsh-client-ui-settings/client'\r\n\r\nimport { NS, en, zh } from './locales.js'\r\nimport { ContextMemoryPanel } from './view.js'\r\n\r\nexport const inject = ['slots', 'locale']\r\n\r\ntype ClientContext = Context & { slots: SlotRegistry; locale: LocaleRuntime }\r\n\r\n/**\r\n * Declaring the namespace is what lets the registration below name it: the host's\r\n * `LocaleNamespaceMap` is merge-extensible and `locale: NS` is checked against it,\r\n * so an undeclared namespace does not type-check at all.\r\n */\r\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\r\n interface LocaleNamespaceMap {\r\n contextMemory: keyof typeof zh\r\n }\r\n}\r\n\r\nexport function apply(ctx: ClientContext): void {\r\n // `zh` is the key-set source of truth, matching the official client plugins.\r\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-context-memory: dictionaries')\r\n\r\n ctx.slots.inject('settings.section', () => ctx.slots.register(\r\n {\r\n name: 'settings.section',\r\n id: 'context-memory',\r\n order: 45,\r\n label: () => ctx.locale.bind(NS)('nav'),\r\n locale: NS,\r\n },\r\n ContextMemoryPanel,\r\n ))\r\n}\r\n", "/**\r\n * Dictionaries for the Context memory page.\r\n *\r\n * `zh` is the key-set source of truth, as in the official client plugins, and `en` is\r\n * typed against it: a key translated in one language but not the other fails the build\r\n * instead of silently rendering the raw key.\r\n *\r\n * @module client/locales\r\n */\r\n\r\n/** Dictionary namespace owned by this plugin. */\r\nexport const NS = 'contextMemory'\r\n\r\n/** Simplified Chinese dictionary (the key-set source of truth). */\r\nexport const zh = {\r\n 'nav': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\r\n 'title': '\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6',\r\n 'active': '\u751F\u6548\u4E2D',\r\n 'superseded': '\u5DF2\u5931\u6548',\r\n 'conflicts': '\u51B2\u7A81',\r\n 'noConflicts': '\u6CA1\u6709\u51B2\u7A81\u3002',\r\n 'memory': '\u8BB0\u5FC6',\r\n 'kind': '\u7C7B\u578B',\r\n 'status': '\u6821\u9A8C\u72B6\u6001',\r\n 'from': '\u6765\u6E90',\r\n 'checkNone': '\u65E0\u68C0\u67E5',\r\n 'checkPassed': '\u5DF2\u6838\u5BF9\uFF0C\u6210\u7ACB',\r\n 'checkViolated': '\u5DF2\u6838\u5BF9\uFF0C\u4E0D\u6210\u7ACB',\r\n 'checkBroken': '\u68C0\u67E5\u65E0\u6CD5\u8FD0\u884C',\r\n 'checkPending': '\u5C1A\u672A\u6838\u5BF9',\r\n 'unverified': '\u672A\u9A8C\u8BC1\u6210\u7ACB',\r\n 'empty': '\u8FD8\u6CA1\u6709\u8BB0\u4F4F\u4EFB\u4F55\u5185\u5BB9\u3002',\r\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',\r\n 'refresh': '\u5237\u65B0',\r\n 'loading': '\u6B63\u5728\u52A0\u8F7D\u2026',\r\n 'failed': '\u52A0\u8F7D\u5931\u8D25',\r\n 'retry': '\u91CD\u8BD5',\r\n 'searchPlaceholder': '\u641C\u7D22\u8BB0\u5FC6\u2026',\r\n 'search': '\u641C\u7D22',\r\n 'searchResults': '\u641C\u7D22\u7ED3\u679C',\r\n 'noResults': '\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5FC6\u3002',\r\n 'addMemory': '\u6DFB\u52A0\u8BB0\u5FC6',\r\n 'memoryText': '\u8BB0\u5FC6\u5185\u5BB9',\r\n 'subjectOptional': '\u4E3B\u9898\uFF08\u53EF\u9009\uFF09',\r\n 'kindFact': '\u4E8B\u5B9E',\r\n 'kindDecision': '\u51B3\u7B56',\r\n 'kindPreference': '\u504F\u597D',\r\n 'kindPitfall': '\u9677\u9631',\r\n 'add': '\u6DFB\u52A0',\r\n 'forget': '\u5E9F\u5F03',\r\n 'resolve': '\u89E3\u51B3',\r\n 'integration': '\u96C6\u6210',\r\n 'autoCaptureLabel': '\u81EA\u52A8\u6355\u83B7:\u7528\u6237\u6D88\u606F\u91CC\u7684\u300C\u8BB0\u4F4F\u2026\u300D\u300C\u4EE5\u540E\u2026\u300D\u81EA\u52A8\u5165\u5E93',\r\n 'recallInjectLabel': '\u53EC\u56DE\u6CE8\u5165:\u6BCF\u6B21\u8BF7\u6C42\u524D\u628A\u76F8\u5173\u8BB0\u5FC6\u6298\u8FDB\u4E0A\u4E0B\u6587',\r\n 'memoryAdded': '\u5DF2\u8BB0\u5F55',\r\n 'memoryTextPlaceholder': '\u8981\u8BB0\u4F4F\u7684\u5185\u5BB9\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u6E05',\r\n 'subjectPlaceholder': '\u7559\u7A7A\u5219\u81EA\u52A8\u53D6\u4E3B\u8BED',\r\n 'forgotten': '\u5DF2\u9057\u5FD8',\r\n 'confirmForget': '\u8FD9\u6761\u8BB0\u5FC6\u4F1A\u88AB\u5220\u9664\uFF0C\u4E14\u4E0D\u518D\u53C2\u4E0E\u53EC\u56DE\u3002',\r\n 'candidates': '\u5F85\u786E\u8BA4',\r\n 'candidatesHint': '\u8FD9\u4E9B\u662F\u4F60\u8BF4\u8FC7\u3001\u4F46\u6CA1\u8BA9\u6211\u8BB0\u7684\u8BDD\u3002\u786E\u8BA4\u540E\u624D\u4F1A\u8FDB\u53EC\u56DE\uFF0C\u4E0D\u786E\u8BA4\u5C31\u4E00\u76F4\u7559\u5728\u8FD9\u91CC\u3002',\r\n 'accept': '\u8BB0\u4E0B',\r\n 'dismiss': '\u4E0D\u7528',\r\n 'candidateAccepted': '\u5DF2\u8BB0\u4E0B',\r\n 'candidateDismissed': '\u5DF2\u5FFD\u7565',\r\n}\r\n\r\n/** English dictionary, checked complete against the zh key set. */\r\nexport const en: typeof zh = {\r\n 'nav': 'Context memory',\r\n 'title': 'Context memory',\r\n 'active': 'active',\r\n 'superseded': 'superseded',\r\n 'conflicts': 'Conflicts',\r\n 'noConflicts': 'No conflicts.',\r\n 'memory': 'Memory',\r\n 'kind': 'Kind',\r\n 'status': 'Check',\r\n 'from': 'From',\r\n 'checkNone': 'no check',\r\n 'checkPassed': 'checked, holds',\r\n 'checkViolated': 'checked, does not hold',\r\n 'checkBroken': 'check unusable',\r\n 'checkPending': 'not yet checked',\r\n 'unverified': 'not verified as holding',\r\n 'empty': 'Nothing is remembered yet.',\r\n 'checkLegend': 'no check = no check could be compiled; check unusable = the check is defective, not the memory.',\r\n 'refresh': 'Refresh',\r\n 'loading': 'Loading\u2026',\r\n 'failed': 'Failed to load',\r\n 'retry': 'Retry',\r\n 'searchPlaceholder': 'Search memories\u2026',\r\n 'search': 'Search',\r\n 'searchResults': 'Search Results',\r\n 'noResults': 'No matching memories.',\r\n 'addMemory': 'Add Memory',\r\n 'memoryText': 'Memory text',\r\n 'subjectOptional': 'Subject (optional)',\r\n 'kindFact': 'Fact',\r\n 'kindDecision': 'Decision',\r\n 'kindPreference': 'Preference',\r\n 'kindPitfall': 'Pitfall',\r\n 'add': 'Add',\r\n 'forget': 'Forget',\r\n 'resolve': 'Resolve',\r\n 'integration': 'Integration',\r\n 'autoCaptureLabel': 'Auto capture: \"remember that\u2026\" statements land in the log',\r\n 'recallInjectLabel': 'Recall injection: fold relevant memories into each request',\r\n 'memoryAdded': 'Memory added',\r\n 'memoryTextPlaceholder': 'What to remember, in one sentence',\r\n 'subjectPlaceholder': 'Leave empty to infer the subject',\r\n 'forgotten': 'Forgotten',\r\n 'confirmForget': 'This memory is deleted and no longer recalled.',\r\n 'candidates': 'Suggested',\r\n 'candidatesHint': 'Things you said that were not asked to be remembered. They only enter recall once you accept them.',\r\n 'accept': 'Remember it',\r\n 'dismiss': 'No thanks',\r\n 'candidateAccepted': 'Remembered',\r\n 'candidateDismissed': 'Dismissed',\r\n}\r\n", "/**\r\n * Pure rendering half of the contextMemory page. Uses shared UI kit.\r\n * @module client/view\r\n */\r\n\r\nimport { useCallback, useEffect, useState } from 'react'\r\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\r\nimport type { ReactNode } from 'react'\r\n\r\nimport type { MemoryState, PanelPayload, RecallHit } from '../types.js'\r\nimport {\r\n Badge, Button, Card, ConfirmDialog, EmptyState, Field, Input, Modal,\r\n SectionTitle, Select, Spinner, StatCard, ToastProvider, tableStyles,\r\n usePanel, useToast,\r\n} from './ui.js'\r\n\r\n/**\r\n * The translate seat the host injects for this plugin's namespace, taken from the\r\n * official type so the panel and its registration agree on one key domain.\r\n */\r\nexport type Translate = TranslateNS<'contextMemory'>\r\nexport interface PanelProps { t: Translate }\r\n\r\nconst PANEL_PATH = '/api/context-memory.panel'\r\nconst RECALL_PATH = '/api/context-memory.recall'\r\nconst REMEMBER_PATH = '/api/context-memory.remember'\r\nconst FORGET_PATH = '/api/context-memory.forget'\r\nconst PREFS_PATH = '/api/context-memory.prefs'\r\nconst CANDIDATES_PATH = '/api/context-memory.candidates'\r\n\r\nexport function checkLabel(memory: MemoryState, t: Translate): string {\r\n if (!memory.guard) return t('checkNone')\r\n switch (memory.verification?.outcome) {\r\n case 'passed': return t('checkPassed')\r\n case 'violated': return t('checkViolated')\r\n case 'broken': return t('checkBroken')\r\n default: return t('checkPending')\r\n }\r\n}\r\n\r\nexport function provenance(memory: MemoryState): string {\r\n // Scope first: the same sentence can be held in both logs with the same id, so without\r\n // it two rows would read identically and the \"forget\" button beside them would be a\r\n // coin flip over which log it retires.\r\n return [memory.scope, memory.source, memory.sessionId !== undefined ? `session ${memory.sessionId}` : null, memory.step !== undefined ? `step ${String(memory.step)}` : null].filter((part) => part !== null).join(' \u00B7 ')\r\n}\r\n\r\n/** A row key that is unique across scopes, where the id alone is not. */\r\nexport function memoryKey(memory: MemoryState): string {\r\n return `${memory.scope}:${memory.id}`\r\n}\r\n\r\nfunction AddMemoryModal({ t, onClose, onAdded }: { t: Translate; onClose: () => void; onAdded: () => void }): ReactNode {\r\n const toast = useToast()\r\n const [text, setText] = useState('')\r\n const [subject, setSubject] = useState('')\r\n const [kind, setKind] = useState('fact')\r\n const [adding, setAdding] = useState(false)\r\n\r\n const handleAdd = useCallback(async () => {\r\n if (!text.trim()) return\r\n setAdding(true)\r\n try {\r\n const r = await fetch(REMEMBER_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text, subject: subject || undefined, kind, source: 'user' }) })\r\n if (r.ok) { toast('success', t('memoryAdded')); onAdded(); onClose() }\r\n } finally { setAdding(false) }\r\n }, [text, subject, kind, t, toast, onAdded, onClose])\r\n\r\n return (\r\n <Modal title={t('addMemory')} onClose={onClose} width={500}\r\n footer={<><Button variant=\"secondary\" onClick={onClose}>{t('cancel')}</Button>\r\n <Button variant=\"primary\" disabled={adding || !text.trim()} onClick={handleAdd}>{adding ? <Spinner size={14} /> : null} {t('add')}</Button></>}>\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>\r\n <Field label={t('memoryText')}><Input value={text} onChange={(e) => setText(e.target.value)} placeholder={t('memoryTextPlaceholder')} /></Field>\r\n <Field label={t('subjectOptional')}><Input value={subject} onChange={(e) => setSubject(e.target.value)} placeholder={t('subjectPlaceholder')} /></Field>\r\n <Field label={t('kind')}>\r\n <Select value={kind} onChange={(e) => setKind(e.target.value)} style={{ width: '100%' }}>\r\n <option value=\"fact\">{t('kindFact')}</option>\r\n <option value=\"decision\">{t('kindDecision')}</option>\r\n <option value=\"preference\">{t('kindPreference')}</option>\r\n <option value=\"pitfall\">{t('kindPitfall')}</option>\r\n </Select>\r\n </Field>\r\n </div>\r\n </Modal>\r\n )\r\n}\r\n\r\nfunction ContextMemoryPanelInner({ t }: PanelProps): ReactNode {\r\n const { payload, error, reload } = usePanel<PanelPayload>(PANEL_PATH)\r\n const toast = useToast()\r\n const [searchQuery, setSearchQuery] = useState('')\r\n const [searchHits, setSearchHits] = useState<RecallHit[] | null>(null)\r\n const [searching, setSearching] = useState(false)\r\n const [prefs, setPrefsState] = useState<{ autoCapture: boolean; recallInject: boolean } | null>(null)\r\n\r\n const togglePref = useCallback(async (key: 'autoCapture' | 'recallInject') => {\r\n const r = await fetch(PREFS_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ [key]: !(prefs?.[key] ?? true) }) })\r\n if (r.ok) setPrefsState(await r.json() as { autoCapture: boolean; recallInject: boolean })\r\n }, [prefs])\r\n\r\n useEffect(() => { void fetch(PREFS_PATH).then(async (r) => { if (r.ok) setPrefsState(await r.json() as { autoCapture: boolean; recallInject: boolean }) }) }, [])\r\n const [addOpen, setAddOpen] = useState(false)\r\n const [forgetTarget, setForgetTarget] = useState<{ id: string; reason: string; scope?: string } | null>(null)\r\n\r\n const handleSearch = useCallback(async () => {\r\n if (!searchQuery.trim()) { setSearchHits(null); return }\r\n setSearching(true)\r\n try { const r = await fetch(`${RECALL_PATH}?q=${encodeURIComponent(searchQuery)}&limit=10`); if (r.ok) { setSearchHits((await r.json() as { hits: RecallHit[] }).hits) } }\r\n finally { setSearching(false) }\r\n }, [searchQuery])\r\n\r\n const handleForget = useCallback(async () => {\r\n if (forgetTarget === null) return\r\n const r = await fetch(FORGET_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(forgetTarget) })\r\n if (r.ok) { toast('success', t('forgotten')); setForgetTarget(null); reload() }\r\n }, [forgetTarget, t, toast, reload])\r\n\r\n const decideCandidate = useCallback(async (id: string, action: 'accept' | 'dismiss') => {\r\n const r = await fetch(CANDIDATES_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, action }) })\r\n if (r.ok) { toast('success', action === 'accept' ? t('candidateAccepted') : t('candidateDismissed')); reload() }\r\n }, [t, toast, reload])\r\n\r\n const header = (\r\n <header style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4 }}>\r\n <strong style={{ fontSize: 15 }}>\uD83E\uDDE0 {t('title')}</strong>\r\n <span style={{ flex: 1 }} />\r\n <Button variant=\"primary\" size=\"sm\" onClick={() => setAddOpen(true)}>\u2795 {t('addMemory')}</Button>\r\n <Button variant=\"secondary\" size=\"sm\" onClick={reload}>{t('refresh')}</Button>\r\n </header>\r\n )\r\n\r\n if (error !== null) return <div style={{ maxWidth: 820 }}>{header}<Card><p role=\"alert\" style={{ margin: 0, fontSize: 13, color: 'var(--dsw-alias-state-error-primary)' }}>{t('failed')}: {error}</p></Card></div>\r\n if (payload === null) return <div style={{ maxWidth: 820 }}>{header}<div style={{ display: 'flex', justifyContent: 'center', padding: 40 }}><Spinner size={28} /></div></div>\r\n\r\n return (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }}>\r\n {header}\r\n\r\n <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>\r\n <StatCard value={payload.totals.active} label={t('active')} />\r\n <StatCard value={payload.totals.verified} label={t('checkPassed')} />\r\n <StatCard value={payload.totals.unverified} label={t('unverified')} />\r\n <StatCard value={payload.totals.superseded} label={t('superseded')} />\r\n {payload.totals.candidates > 0 && <StatCard value={payload.totals.candidates} label={t('candidates')} />}\r\n </div>\r\n\r\n {/* Integration switches */}\r\n <Card title={t('integration')} icon=\"\u2699\uFE0F\">\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\r\n {([['autoCapture', 'autoCaptureLabel'], ['recallInject', 'recallInjectLabel']] as const).map(([key, labelKey]) => (\r\n <label key={key} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }}>\r\n <input type=\"checkbox\" checked={prefs?.[key] ?? true} onChange={() => togglePref(key)} />\r\n {t(labelKey)}\r\n </label>\r\n ))}\r\n </div>\r\n </Card>\r\n\r\n {/* Search */}\r\n <Card title={t('search')} icon=\"\uD83D\uDD0D\">\r\n <div style={{ display: 'flex', gap: 8 }}>\r\n <Input value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder={t('searchPlaceholder')}\r\n onKeyDown={(e) => { if (e.key === 'Enter') handleSearch() }} />\r\n <Button variant=\"secondary\" onClick={handleSearch} disabled={searching}>{searching ? <Spinner size={14} /> : null} {t('search')}</Button>\r\n </div>\r\n {searchHits !== null && (\r\n <div style={{ marginTop: 10 }}>\r\n {searchHits.length === 0 ? <EmptyState message={t('noResults')} /> : (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>\r\n {searchHits.map((hit) => (\r\n <div key={hit.memory.id} style={{ display: 'flex', gap: 8, alignItems: 'baseline' }}>\r\n <Badge color=\"info\">{hit.score.toFixed(2)}</Badge>\r\n <span style={{ fontSize: 12 }}>{hit.memory.text}</span>\r\n <span style={{ fontSize: 10, opacity: 0.5 }}>\u2014 {hit.why}</span>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </Card>\r\n\r\n {/* Conflicts */}\r\n {payload.conflicts.length > 0 && (\r\n <>\r\n <SectionTitle icon=\"\u26A0\uFE0F\">{t('conflicts')}</SectionTitle>\r\n {payload.conflicts.map((conflict) => (\r\n <Card key={conflict.subject} title={conflict.subject}>\r\n {conflict.memories.map((memory) => (\r\n <div key={memory.id} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>\r\n <code style={{ fontSize: 11 }}>{memory.id}</code>\r\n <span style={{ fontSize: 12, flex: 1 }}>{memory.text}</span>\r\n <Button variant=\"danger\" size=\"sm\" onClick={() => setForgetTarget({ id: memory.id, reason: `conflict resolved: ${conflict.subject}` })}>{t('resolve')}</Button>\r\n </div>\r\n ))}\r\n </Card>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Candidates \u2014 statements the heuristics noticed, awaiting a decision */}\r\n {payload.candidates.length > 0 && (\r\n <>\r\n <SectionTitle icon=\"\uD83D\uDCA1\">{t('candidates')} ({payload.candidates.length})</SectionTitle>\r\n <Card>\r\n <p style={{ margin: '0 0 10px', fontSize: 12, opacity: 0.7 }}>{t('candidatesHint')}</p>\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\r\n {payload.candidates.map((candidate) => (\r\n <div key={candidate.id} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\r\n <Badge>{candidate.kind}</Badge>\r\n <span style={{ fontSize: 12, flex: 1 }}>{candidate.text}</span>\r\n <Button variant=\"primary\" size=\"sm\" onClick={() => void decideCandidate(candidate.id, 'accept')}>{t('accept')}</Button>\r\n <Button variant=\"secondary\" size=\"sm\" onClick={() => void decideCandidate(candidate.id, 'dismiss')}>{t('dismiss')}</Button>\r\n </div>\r\n ))}\r\n </div>\r\n </Card>\r\n </>\r\n )}\r\n\r\n {/* Active memories */}\r\n <SectionTitle icon=\"\u2705\">{t('active')}</SectionTitle>\r\n {payload.active.length === 0 ? <EmptyState icon=\"\uD83D\uDCED\" message={t('empty')} /> : (\r\n <Card padding={0}>\r\n <table style={tableStyles.table}>\r\n <thead><tr><th style={tableStyles.th}>Id</th><th style={tableStyles.th}>{t('memory')}</th><th style={tableStyles.th}>{t('kind')}</th><th style={tableStyles.th}>{t('status')}</th><th style={tableStyles.th}>{t('from')}</th><th style={tableStyles.th} /></tr></thead>\r\n <tbody>\r\n {payload.active.map((memory) => (\r\n <tr key={memoryKey(memory)}>\r\n <td style={tableStyles.td}><code style={{ fontSize: 11 }}>{memory.id}</code></td>\r\n <td style={tableStyles.td}>{memory.text}</td>\r\n <td style={tableStyles.td}><Badge>{memory.kind}</Badge></td>\r\n <td style={tableStyles.td}>{checkLabel(memory, t)}</td>\r\n <td style={{ ...tableStyles.td, fontSize: 11, opacity: 0.6 }}>{provenance(memory)}</td>\r\n <td style={tableStyles.td}><Button variant=\"danger\" size=\"sm\" onClick={() => setForgetTarget({ id: memory.id, scope: memory.scope, reason: 'superseded from panel' })}>\uD83D\uDDD1</Button></td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </Card>\r\n )}\r\n\r\n {payload.superseded.length > 0 && (\r\n <>\r\n <SectionTitle icon=\"\uD83D\uDCE6\">{t('superseded')} ({payload.superseded.length})</SectionTitle>\r\n <Card padding={0}>\r\n <table style={tableStyles.table}>\r\n <thead><tr><th style={tableStyles.th}>Id</th><th style={tableStyles.th}>{t('memory')}</th><th style={tableStyles.th}>{t('kind')}</th><th style={tableStyles.th}>{t('status')}</th></tr></thead>\r\n <tbody>\r\n {payload.superseded.map((memory) => (\r\n <tr key={memoryKey(memory)}><td style={tableStyles.td}><code style={{ fontSize: 11 }}>{memory.id}</code></td><td style={tableStyles.td}>{memory.text}</td><td style={tableStyles.td}><Badge>{memory.kind}</Badge></td><td style={tableStyles.td}>{checkLabel(memory, t)}</td></tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </Card>\r\n </>\r\n )}\r\n\r\n {addOpen && <AddMemoryModal t={t} onClose={() => setAddOpen(false)} onAdded={reload} />}\r\n {forgetTarget !== null && <ConfirmDialog title={t('forget')} message={t('confirmForget')} confirmLabel={t('forget')} danger onConfirm={handleForget} onClose={() => setForgetTarget(null)} />}\r\n </div>\r\n )\r\n}\r\n\r\nexport function ContextMemoryPanel({ t }: PanelProps): ReactNode {\r\n return <ToastProvider><ContextMemoryPanelInner t={t} /></ToastProvider>\r\n}\r\n", "/**\r\n * Shared UI component kit for dsh plugin panels.\r\n *\r\n * Self-contained: imports nothing but React. All styling is inline + CSS custom\r\n * properties so the components follow the host theme. A single <style> tag injects\r\n * keyframes the first time any component mounts.\r\n *\r\n * @module client/ui\r\n */\r\n\r\nimport {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useState,\r\n} from 'react'\r\nimport type {\r\n ButtonHTMLAttributes,\r\n CSSProperties,\r\n InputHTMLAttributes,\r\n ReactNode,\r\n SelectHTMLAttributes,\r\n} from 'react'\r\n\r\n// \u2500\u2500\u2500 Theme tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n/**\r\n * Host theme tokens.\r\n *\r\n * Every name here is a real dsh token, read out of the shipped stylesheets\r\n * (`@deepseek-ai/dsh-client-ui-theme`), which define them on `body`. An earlier\r\n * version used invented names \u2014 `--accent`, `--border`, `--bg-primary`,\r\n * `--text-primary` and friends \u2014 that dsh does not define at all. The\r\n * hardcoded fallbacks then applied in every theme, so each panel rendered its\r\n * own fixed palette instead of following the host, and the modal and input\r\n * surfaces stayed white in dark mode.\r\n *\r\n * No fallbacks on purpose: if a name were ever missing, the declaration becomes\r\n * invalid at computed-value time and the property inherits, which degrades\r\n * gracefully, whereas a hardcoded fallback bakes in a color that is wrong in one\r\n * of the two themes. `tests/ui-tokens.test.mjs` enforces the name list.\r\n */\r\nconst T = {\r\n accent: 'var(--dsw-alias-brand-primary)',\r\n accentHover: 'var(--dsw-alias-button-primary-hover)',\r\n inverted: 'var(--dsw-alias-label-primary-inverted)',\r\n error: 'var(--dsw-alias-state-error-primary)',\r\n success: 'var(--dsw-alias-state-success-primary)',\r\n warning: 'var(--dsw-alias-state-warn-primary)',\r\n bg: 'var(--dsw-alias-bg-layer-2)',\r\n bgHover: 'var(--dsw-alias-interactive-bg-hover)',\r\n border: 'var(--dsw-alias-border-l2)',\r\n text: 'var(--dsw-alias-label-primary)',\r\n muted: 'var(--dsw-alias-label-tertiary)',\r\n surface: 'var(--dsw-alias-bg-layer-1)',\r\n mask: 'var(--dsw-alias-bg-mask-1)',\r\n}\r\n\r\n// \u2500\u2500\u2500 Style injection (once) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nlet injected = false\r\nfunction ensureStyles(): void {\r\n if (injected || typeof document === 'undefined') return\r\n injected = true\r\n const el = document.createElement('style')\r\n el.textContent = `\r\n@keyframes dsh-fade-in{from{opacity:0}to{opacity:1}}\r\n@keyframes dsh-slide-up{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\r\n@keyframes dsh-spin{to{transform:rotate(360deg)}}\r\n.dsh-modal-overlay{animation:dsh-fade-in .15s ease}\r\n.dsh-modal-body{animation:dsh-slide-up .2s ease}\r\n.dsh-toast{animation:dsh-slide-up .2s ease}\r\n.dsh-spin{animation:dsh-spin .6s linear infinite}\r\n `\r\n document.head.appendChild(el)\r\n}\r\n\r\n// \u2500\u2500\u2500 Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Modal({\r\n title,\r\n onClose,\r\n children,\r\n footer,\r\n width = 520,\r\n}: {\r\n title: string\r\n onClose: () => void\r\n children: ReactNode\r\n footer?: ReactNode\r\n width?: number\r\n}): ReactNode {\r\n useEffect(() => {\r\n ensureStyles()\r\n const handler = (e: KeyboardEvent): void => {\r\n if (e.key === 'Escape') onClose()\r\n }\r\n document.addEventListener('keydown', handler)\r\n return () => document.removeEventListener('keydown', handler)\r\n }, [onClose])\r\n\r\n return (\r\n <div\r\n className=\"dsh-modal-overlay\"\r\n style={{\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 1000,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n background: T.mask,\r\n backdropFilter: 'blur(3px)',\r\n WebkitBackdropFilter: 'blur(3px)',\r\n }}\r\n onClick={onClose}\r\n >\r\n <div\r\n className=\"dsh-modal-body\"\r\n style={{\r\n background: T.surface,\r\n borderRadius: 14,\r\n boxShadow: 'var(--dsw-elevation-prominent)',\r\n width: `min(92vw, ${width}px)`,\r\n maxHeight: '85vh',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: '16px 22px',\r\n borderBottom: `1px solid ${T.border}`,\r\n }}\r\n >\r\n <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{title}</h3>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n style={{\r\n background: 'none',\r\n border: 'none',\r\n cursor: 'pointer',\r\n fontSize: 20,\r\n color: T.muted,\r\n padding: '0 4px',\r\n lineHeight: 1,\r\n borderRadius: 4,\r\n }}\r\n aria-label=\"Close\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div style={{ padding: 22, overflow: 'auto', flex: 1 }}>{children}</div>\r\n {footer !== undefined && (\r\n <div\r\n style={{\r\n padding: '14px 22px',\r\n borderTop: `1px solid ${T.border}`,\r\n display: 'flex',\r\n justifyContent: 'flex-end',\r\n gap: 10,\r\n background: T.bg,\r\n }}\r\n >\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 ConfirmDialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function ConfirmDialog({\r\n title,\r\n message,\r\n confirmLabel,\r\n cancelLabel = 'Cancel',\r\n onConfirm,\r\n onClose,\r\n danger,\r\n}: {\r\n title: string\r\n message: string\r\n confirmLabel: string\r\n cancelLabel?: string\r\n onConfirm: () => void\r\n onClose: () => void\r\n danger?: boolean\r\n}): ReactNode {\r\n return (\r\n <Modal\r\n title={title}\r\n onClose={onClose}\r\n width={420}\r\n footer={\r\n <>\r\n <Button variant=\"secondary\" onClick={onClose}>\r\n {cancelLabel}\r\n </Button>\r\n <Button\r\n variant={danger === true ? 'danger' : 'primary'}\r\n onClick={() => {\r\n onConfirm()\r\n onClose()\r\n }}\r\n >\r\n {confirmLabel}\r\n </Button>\r\n </>\r\n }\r\n >\r\n <p style={{ margin: 0, fontSize: 14, lineHeight: 1.6 }}>{message}</p>\r\n </Modal>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Button({\r\n variant = 'secondary',\r\n size = 'md',\r\n children,\r\n style,\r\n ...rest\r\n}: {\r\n variant?: 'primary' | 'secondary' | 'danger' | 'ghost'\r\n size?: 'sm' | 'md'\r\n children: ReactNode\r\n style?: CSSProperties\r\n} & ButtonHTMLAttributes<HTMLButtonElement>): ReactNode {\r\n const base: CSSProperties = {\r\n cursor: 'pointer',\r\n borderRadius: 7,\r\n fontWeight: 500,\r\n transition: 'all 0.15s ease',\r\n border: 'none',\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n fontSize: size === 'sm' ? 12 : 13,\r\n padding: size === 'sm' ? '5px 12px' : '8px 18px',\r\n fontFamily: 'inherit',\r\n }\r\n const variants: Record<string, CSSProperties> = {\r\n primary: { background: T.accent, color: T.inverted },\r\n secondary: {\r\n background: T.bg,\r\n color: T.text,\r\n border: `1px solid ${T.border}`,\r\n },\r\n danger: {\r\n background: 'transparent',\r\n color: T.error,\r\n border: `1px solid ${T.error}`,\r\n },\r\n ghost: { background: 'transparent', color: T.muted, border: 'none' },\r\n }\r\n return (\r\n <button type=\"button\" style={{ ...base, ...variants[variant], ...style }} {...rest}>\r\n {children}\r\n </button>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Card({\r\n title,\r\n icon,\r\n children,\r\n actions,\r\n style,\r\n padding = 14,\r\n}: {\r\n title?: string\r\n icon?: string\r\n children: ReactNode\r\n actions?: ReactNode\r\n style?: CSSProperties\r\n padding?: number\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n borderRadius: 10,\r\n border: `1px solid ${T.border}`,\r\n background: T.bg,\r\n overflow: 'hidden',\r\n ...style,\r\n }}\r\n >\r\n {(title !== undefined || actions !== undefined) && (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: '12px 16px',\r\n borderBottom: title !== undefined ? `1px solid ${T.border}` : 'none',\r\n }}\r\n >\r\n <span style={{ fontSize: 13, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 6 }}>\r\n {icon !== undefined && <span>{icon}</span>}\r\n {title}\r\n </span>\r\n {actions}\r\n </div>\r\n )}\r\n <div style={{ padding }}>{children}</div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 StatCard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function StatCard({\r\n value,\r\n label,\r\n color,\r\n unit,\r\n}: {\r\n value: string | number\r\n label: string\r\n color?: string\r\n unit?: string\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n flex: 1,\r\n minWidth: 90,\r\n textAlign: 'center',\r\n padding: '14px 10px',\r\n borderRadius: 10,\r\n border: `1px solid ${T.border}`,\r\n background: T.bg,\r\n }}\r\n >\r\n <div style={{ fontSize: 24, fontWeight: 700, color: color ?? T.text, lineHeight: 1.2 }}>\r\n {value}\r\n {unit !== undefined && (\r\n <span style={{ fontSize: 13, fontWeight: 500, opacity: 0.7, marginLeft: 2 }}>{unit}</span>\r\n )}\r\n </div>\r\n <div style={{ fontSize: 11, color: T.muted, marginTop: 4 }}>{label}</div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Badge({\r\n children,\r\n color = 'default',\r\n}: {\r\n children: ReactNode\r\n color?: 'default' | 'success' | 'error' | 'warning' | 'info'\r\n}): ReactNode {\r\n const colors: Record<string, { bg: string; fg: string }> = {\r\n default: { bg: T.bgHover, fg: T.muted },\r\n success: { bg: 'var(--dsw-alias-state-success-tertiary)', fg: T.success },\r\n error: { bg: 'var(--dsw-alias-state-error-secondary)', fg: T.error },\r\n warning: { bg: 'var(--dsw-alias-state-warn-tertiary)', fg: T.warning },\r\n info: { bg: 'var(--dsw-alias-state-business-tertiary)', fg: T.accent },\r\n }\r\n const c = colors[color]\r\n return (\r\n <span\r\n style={{\r\n display: 'inline-block',\r\n padding: '2px 9px',\r\n borderRadius: 10,\r\n fontSize: 11,\r\n fontWeight: 600,\r\n background: c.bg,\r\n color: c.fg,\r\n }}\r\n >\r\n {children}\r\n </span>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Input({\r\n style,\r\n ...rest\r\n}: { style?: CSSProperties } & InputHTMLAttributes<HTMLInputElement>): ReactNode {\r\n return (\r\n <input\r\n {...rest}\r\n style={{\r\n fontSize: 13,\r\n padding: '8px 12px',\r\n borderRadius: 7,\r\n border: `1px solid ${T.border}`,\r\n background: T.surface,\r\n color: T.text,\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n outline: 'none',\r\n transition: 'border-color 0.15s',\r\n ...style,\r\n }}\r\n />\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Select({\r\n style,\r\n children,\r\n ...rest\r\n}: {\r\n style?: CSSProperties\r\n children: ReactNode\r\n} & SelectHTMLAttributes<HTMLSelectElement>): ReactNode {\r\n return (\r\n <select\r\n {...rest}\r\n style={{\r\n fontSize: 13,\r\n padding: '8px 12px',\r\n borderRadius: 7,\r\n border: `1px solid ${T.border}`,\r\n background: T.surface,\r\n color: T.text,\r\n cursor: 'pointer',\r\n fontFamily: 'inherit',\r\n outline: 'none',\r\n ...style,\r\n }}\r\n >\r\n {children}\r\n </select>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Textarea \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Textarea({\r\n style,\r\n ...rest\r\n}: { style?: CSSProperties } & React.TextareaHTMLAttributes<HTMLTextAreaElement>): ReactNode {\r\n return (\r\n <textarea\r\n {...rest}\r\n style={{\r\n fontSize: 13,\r\n padding: '8px 12px',\r\n borderRadius: 7,\r\n border: `1px solid ${T.border}`,\r\n background: T.surface,\r\n color: T.text,\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n outline: 'none',\r\n resize: 'vertical',\r\n lineHeight: 1.5,\r\n ...style,\r\n }}\r\n />\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\ninterface ToastItem {\r\n id: number\r\n type: 'success' | 'error' | 'info'\r\n message: string\r\n}\r\n\r\nconst ToastContext = createContext<(type: ToastItem['type'], message: string) => void>(\r\n () => {},\r\n)\r\n\r\nexport function ToastProvider({ children }: { children: ReactNode }): ReactNode {\r\n const [toasts, setToasts] = useState<ToastItem[]>([])\r\n const show = useCallback((type: ToastItem['type'], message: string) => {\r\n const id = Date.now() + Math.random()\r\n setToasts((t) => [...t, { id, type, message }])\r\n setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3500)\r\n }, [])\r\n return (\r\n <ToastContext.Provider value={show}>\r\n {children}\r\n {toasts.length > 0 && (\r\n <div\r\n style={{\r\n position: 'fixed',\r\n bottom: 24,\r\n right: 24,\r\n zIndex: 2000,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 8,\r\n }}\r\n >\r\n {toasts.map((t) => (\r\n <div\r\n key={t.id}\r\n className=\"dsh-toast\"\r\n style={{\r\n padding: '12px 18px',\r\n borderRadius: 8,\r\n fontSize: 13,\r\n fontWeight: 500,\r\n boxShadow: 'var(--dsw-elevation-panel)',\r\n color: T.inverted,\r\n background:\r\n t.type === 'success'\r\n ? T.success\r\n : t.type === 'error'\r\n ? T.error\r\n : T.accent,\r\n maxWidth: 360,\r\n }}\r\n >\r\n {t.message}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </ToastContext.Provider>\r\n )\r\n}\r\n\r\nexport function useToast(): (type: ToastItem['type'], message: string) => void {\r\n return useContext(ToastContext)\r\n}\r\n\r\n// \u2500\u2500\u2500 Spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Spinner({ size = 16 }: { size?: number }): ReactNode {\r\n ensureStyles()\r\n return (\r\n <span\r\n className=\"dsh-spin\"\r\n style={{\r\n display: 'inline-block',\r\n width: size,\r\n height: size,\r\n border: `2px solid ${T.border}`,\r\n borderTopColor: T.accent,\r\n borderRadius: '50%',\r\n }}\r\n />\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 EmptyState \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function EmptyState({\r\n icon,\r\n message,\r\n}: {\r\n icon?: string\r\n message: string\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n textAlign: 'center',\r\n padding: '36px 16px',\r\n color: T.muted,\r\n }}\r\n >\r\n {icon !== undefined && (\r\n <div style={{ fontSize: 36, marginBottom: 10, opacity: 0.4 }}>{icon}</div>\r\n )}\r\n <div style={{ fontSize: 13 }}>{message}</div>\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 SectionTitle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function SectionTitle({\r\n icon,\r\n children,\r\n actions,\r\n}: {\r\n icon?: string\r\n children: ReactNode\r\n actions?: ReactNode\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n marginTop: 6,\r\n marginBottom: 8,\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontSize: 14,\r\n fontWeight: 600,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 6,\r\n }}\r\n >\r\n {icon !== undefined && <span>{icon}</span>}\r\n {children}\r\n </span>\r\n {actions}\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Table helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport const tableStyles = {\r\n table: {\r\n borderCollapse: 'collapse' as const,\r\n width: '100%' as const,\r\n },\r\n th: {\r\n textAlign: 'left' as const,\r\n padding: '8px 12px 8px 0',\r\n fontWeight: 600 as const,\r\n fontSize: 12 as const,\r\n color: T.muted,\r\n borderBottom: `1px solid ${T.border}`,\r\n },\r\n td: {\r\n padding: '8px 12px 8px 0',\r\n fontSize: 13 as const,\r\n borderBottom: `1px solid ${T.border}`,\r\n },\r\n clickRow: { cursor: 'pointer' as const },\r\n}\r\n\r\n// \u2500\u2500\u2500 CodeBlock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function CodeBlock({\r\n children,\r\n maxHeight = 200,\r\n style,\r\n}: {\r\n children: string\r\n maxHeight?: number\r\n style?: CSSProperties\r\n}): ReactNode {\r\n return (\r\n <pre\r\n style={{\r\n whiteSpace: 'pre-wrap',\r\n wordBreak: 'break-word',\r\n fontSize: 12,\r\n maxHeight,\r\n overflow: 'auto',\r\n padding: 10,\r\n borderRadius: 7,\r\n background: T.bg,\r\n border: `1px solid ${T.border}`,\r\n margin: 0,\r\n ...style,\r\n }}\r\n >\r\n {children}\r\n </pre>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 ProgressBar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function ProgressBar({\r\n pct,\r\n color,\r\n height = 18,\r\n}: {\r\n pct: number\r\n color?: string\r\n height?: number\r\n}): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n width: '100%',\r\n height,\r\n background: T.bgHover,\r\n borderRadius: 5,\r\n overflow: 'hidden',\r\n position: 'relative',\r\n }}\r\n >\r\n <div\r\n style={{\r\n width: `${Math.min(pct, 100)}%`,\r\n height: '100%',\r\n background: color ?? T.accent,\r\n borderRadius: 5,\r\n transition: 'width 0.3s ease',\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 Field label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function Field({\r\n label,\r\n children,\r\n hint,\r\n}: {\r\n label: string\r\n children: ReactNode\r\n hint?: string\r\n}): ReactNode {\r\n return (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\r\n <label style={{ fontSize: 12, fontWeight: 600, color: T.text }}>{label}</label>\r\n {children}\r\n {hint !== undefined && (\r\n <span style={{ fontSize: 11, color: T.muted }}>{hint}</span>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n// \u2500\u2500\u2500 usePanel hook (shared fetch logic) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport function usePanel<P>(path: string): {\r\n payload: P | null\r\n error: string | null\r\n reload: () => void\r\n} {\r\n const [payload, setPayload] = useState<P | null>(null)\r\n const [error, setError] = useState<string | null>(null)\r\n const [tick, setTick] = useState(0)\r\n const reload = useCallback(() => setTick((v) => v + 1), [])\r\n\r\n useEffect(() => {\r\n const c = new AbortController()\r\n setError(null)\r\n fetch(path, { signal: c.signal })\r\n .then(async (r) => {\r\n if (!r.ok) throw new Error(String(r.status))\r\n return r.json() as Promise<P>\r\n })\r\n .then((p) => {\r\n if (!c.signal.aborted) setPayload(p)\r\n })\r\n .catch((e: unknown) => {\r\n if (!c.signal.aborted) setPayload(null), setError(e instanceof Error ? e.message : String(e))\r\n })\r\n return () => c.abort()\r\n }, [tick, path])\r\n\r\n return useMemo(() => ({ payload, error, reload }), [payload, error, reload])\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_react", "import_jsx_runtime"]
7
7
  }
package/lib/recall.d.ts CHANGED
@@ -11,10 +11,26 @@
11
11
  * encoding that in one number is what makes "recall the most reliable thing you know"
12
12
  * expressible at all.
13
13
  *
14
+ * A memory a check *contradicted*, however, is not a low-scoring candidate — it is the
15
+ * wrong answer. It is filtered out here rather than down-weighted, because no amount of
16
+ * lexical overlap makes "the build uses yarn" useful once `package.json` disagrees.
17
+ *
14
18
  * @module context-memory/recall
15
19
  */
16
- import type { MemoryState, RecallHit } from './types.js';
17
- /** Split a query into distinct signal-bearing tokens. */
20
+ import type { MemoryState, MemoryTarget, RecallHit } from './types.js';
21
+ /**
22
+ * Split a query into distinct signal-bearing tokens.
23
+ *
24
+ * Latin text is split on word boundaries as before. CJK text has no spaces, so a
25
+ * whitespace tokenizer discards it entirely — which made recall return nothing for every
26
+ * Chinese query while capture happily stored Chinese statements. Each CJK run is instead
27
+ * cut into overlapping 2-grams: two characters is the smallest unit that carries meaning
28
+ * in Chinese ("构建" does, "建" alone does not), and a 2-gram index needs no dictionary,
29
+ * no dependency, and no runtime model.
30
+ *
31
+ * A genuine word longer than two characters overlaps into several grams, so a query and a
32
+ * memory that share one word still match even when the surrounding wording differs.
33
+ */
18
34
  export declare function tokenize(text: string): string[];
19
35
  /**
20
36
  * Score how relevant one memory is to a query.
@@ -31,10 +47,33 @@ export declare function scoreMemory(memory: MemoryState, tokens: string[], now?:
31
47
  why: string;
32
48
  };
33
49
  /**
34
- * Rank active memories against a query.
50
+ * Whether the world has denied this memory.
51
+ *
52
+ * Distinct from both `superseded` (a person retired it) and `broken` (the check could not
53
+ * run). Only a check that ran and failed counts: an unusable check says nothing about the
54
+ * memory, and treating it as a denial is how a typo would erase a true statement.
55
+ */
56
+ export declare function isContradicted(memory: MemoryState): boolean;
57
+ /**
58
+ * Whether a memory belongs to the population `target` names.
59
+ *
60
+ * Exported because two callers need the same answer and must not drift: ranking uses it
61
+ * to filter, and the memory service uses it to answer "what is in this population" for
62
+ * the pull tool. A second definition of "guidance" is how the push and pull paths would
63
+ * end up disagreeing about what the plugin considers offerable.
64
+ */
65
+ export declare function inTarget(memory: MemoryState, target: MemoryTarget): boolean;
66
+ /**
67
+ * Rank memories against a query.
68
+ *
69
+ * The default population is guidance: superseded and contradicted memories are excluded
70
+ * here rather than filtered by the caller, because one was replaced and the other was
71
+ * disproved. Neither is a weaker answer — both are wrong ones, and ranking them anywhere
72
+ * would put them back in front of the agent unasked.
35
73
  *
36
- * Superseded memories are excluded here rather than filtered by the caller: a memory
37
- * that was replaced is not a weaker answer, it is a wrong one, and ranking it anywhere
38
- * would put it back in front of the agent.
74
+ * `target` exists for the opposite caller: a model that explicitly asks what was
75
+ * recorded, or what a check disproved. Widening it is a deliberate, per-call decision
76
+ * and never the default, because the two paths want different answers to "what is
77
+ * true" — the push path wants guidance, the pull path wants the log.
39
78
  */
40
- export declare function recall(memories: MemoryState[], query: string, limit?: number, now?: number): RecallHit[];
79
+ export declare function recall(memories: MemoryState[], query: string, limit?: number, now?: number, target?: MemoryTarget): RecallHit[];
package/lib/recall.js CHANGED
@@ -11,6 +11,10 @@
11
11
  * encoding that in one number is what makes "recall the most reliable thing you know"
12
12
  * expressible at all.
13
13
  *
14
+ * A memory a check *contradicted*, however, is not a low-scoring candidate — it is the
15
+ * wrong answer. It is filtered out here rather than down-weighted, because no amount of
16
+ * lexical overlap makes "the build uses yarn" useful once `package.json` disagrees.
17
+ *
14
18
  * @module context-memory/recall
15
19
  */
16
20
  import { normaliseSubject } from './identity.js';
@@ -21,14 +25,52 @@ const STOP_WORDS = new Set([
21
25
  'their', 'them', 'they', 'this', 'to', 'was', 'we', 'were', 'what', 'when', 'where',
22
26
  'which', 'who', 'will', 'with', 'you', 'your',
23
27
  ]);
24
- /** Split a query into distinct signal-bearing tokens. */
28
+ /**
29
+ * Characters treated as part of a Latin word.
30
+ *
31
+ * Everything else — including punctuation, whitespace, and every CJK code point — splits
32
+ * a Latin token. CJK is handled by its own branch below rather than here.
33
+ */
34
+ const LATIN_WORD = /[a-z0-9_.-]+/g;
35
+ /**
36
+ * A run of CJK characters.
37
+ *
38
+ * Covers the unified ideographs (the bulk of Chinese), the extension-A block, and the
39
+ * compatibility ideographs. Kana and Hangul are deliberately excluded: they are not the
40
+ * scripts this plugin's second language is written in, and including them would produce
41
+ * 2-grams that match nothing.
42
+ */
43
+ const CJK_RUN = /[㐀-䶿一-鿿豈-﫿]+/g;
44
+ /** Shortest CJK run that still yields a usable 2-gram. */
45
+ const CJK_MIN_RUN = 2;
46
+ /**
47
+ * Split a query into distinct signal-bearing tokens.
48
+ *
49
+ * Latin text is split on word boundaries as before. CJK text has no spaces, so a
50
+ * whitespace tokenizer discards it entirely — which made recall return nothing for every
51
+ * Chinese query while capture happily stored Chinese statements. Each CJK run is instead
52
+ * cut into overlapping 2-grams: two characters is the smallest unit that carries meaning
53
+ * in Chinese ("构建" does, "建" alone does not), and a 2-gram index needs no dictionary,
54
+ * no dependency, and no runtime model.
55
+ *
56
+ * A genuine word longer than two characters overlaps into several grams, so a query and a
57
+ * memory that share one word still match even when the surrounding wording differs.
58
+ */
25
59
  export function tokenize(text) {
60
+ const lowered = text.toLowerCase();
26
61
  const seen = new Set();
27
- for (const raw of text.toLowerCase().split(/[^a-z0-9_.-]+/)) {
62
+ for (const raw of lowered.match(LATIN_WORD) ?? []) {
28
63
  if (raw.length < 2 || STOP_WORDS.has(raw))
29
64
  continue;
30
65
  seen.add(raw);
31
66
  }
67
+ for (const run of lowered.match(CJK_RUN) ?? []) {
68
+ if (run.length < CJK_MIN_RUN)
69
+ continue;
70
+ for (let index = 0; index + CJK_MIN_RUN <= run.length; index += 1) {
71
+ seen.add(run.slice(index, index + CJK_MIN_RUN));
72
+ }
73
+ }
32
74
  return [...seen];
33
75
  }
34
76
  /** How much each part of the score can contribute. Sums to 1. */
@@ -39,6 +81,25 @@ const WEIGHTS = {
39
81
  };
40
82
  /** A memory older than this contributes no recency credit. */
41
83
  const RECENCY_HORIZON_MS = 90 * 24 * 60 * 60 * 1000;
84
+ /**
85
+ * The text a query is matched against, normalised once per memory.
86
+ *
87
+ * Keyed on the memory object, which the fold produces once and then reuses, so this is
88
+ * one normalisation per memory rather than one per memory per query — the lower-casing
89
+ * and three regex passes in `normaliseSubject` were being redone on every call, for
90
+ * every memory, on every step. A `WeakMap` rather than a field on `MemoryState` so the
91
+ * public shape (which the panel renders and the log emits) does not grow a derived
92
+ * value that could be stale for a hand-built object.
93
+ */
94
+ const SEARCHABLE_TEXT = new WeakMap();
95
+ function searchableText(memory) {
96
+ const cached = SEARCHABLE_TEXT.get(memory);
97
+ if (cached !== undefined)
98
+ return cached;
99
+ const value = normaliseSubject(`${memory.subject} ${memory.text}`);
100
+ SEARCHABLE_TEXT.set(memory, value);
101
+ return value;
102
+ }
42
103
  /**
43
104
  * Score how relevant one memory is to a query.
44
105
  *
@@ -49,7 +110,7 @@ const RECENCY_HORIZON_MS = 90 * 24 * 60 * 60 * 1000;
49
110
  * not been rechecked" would reward staleness.
50
111
  */
51
112
  export function scoreMemory(memory, tokens, now = Date.now()) {
52
- const subject = normaliseSubject(`${memory.subject} ${memory.text}`);
113
+ const subject = searchableText(memory);
53
114
  const matched = tokens.filter((token) => subject.includes(token));
54
115
  const overlap = tokens.length === 0 ? 0 : matched.length / tokens.length;
55
116
  const age = Math.max(0, now - memory.createdAt);
@@ -60,18 +121,64 @@ export function scoreMemory(memory, tokens, now = Date.now()) {
60
121
  return { score, matched, why };
61
122
  }
62
123
  /**
63
- * Rank active memories against a query.
124
+ * Whether the world has denied this memory.
125
+ *
126
+ * Distinct from both `superseded` (a person retired it) and `broken` (the check could not
127
+ * run). Only a check that ran and failed counts: an unusable check says nothing about the
128
+ * memory, and treating it as a denial is how a typo would erase a true statement.
129
+ */
130
+ export function isContradicted(memory) {
131
+ return memory.status === 'active' && memory.verification?.outcome === 'violated';
132
+ }
133
+ /**
134
+ * Whether a memory belongs to the population `target` names.
64
135
  *
65
- * Superseded memories are excluded here rather than filtered by the caller: a memory
66
- * that was replaced is not a weaker answer, it is a wrong one, and ranking it anywhere
67
- * would put it back in front of the agent.
136
+ * Exported because two callers need the same answer and must not drift: ranking uses it
137
+ * to filter, and the memory service uses it to answer "what is in this population" for
138
+ * the pull tool. A second definition of "guidance" is how the push and pull paths would
139
+ * end up disagreeing about what the plugin considers offerable.
68
140
  */
69
- export function recall(memories, query, limit = 5, now = Date.now()) {
141
+ export function inTarget(memory, target) {
142
+ switch (target) {
143
+ case 'guidance': return memory.status === 'active' && !isContradicted(memory);
144
+ case 'contradicted': return isContradicted(memory);
145
+ case 'superseded': return memory.status === 'superseded';
146
+ case 'all': return true;
147
+ }
148
+ }
149
+ /**
150
+ * Rank memories against a query.
151
+ *
152
+ * The default population is guidance: superseded and contradicted memories are excluded
153
+ * here rather than filtered by the caller, because one was replaced and the other was
154
+ * disproved. Neither is a weaker answer — both are wrong ones, and ranking them anywhere
155
+ * would put them back in front of the agent unasked.
156
+ *
157
+ * `target` exists for the opposite caller: a model that explicitly asks what was
158
+ * recorded, or what a check disproved. Widening it is a deliberate, per-call decision
159
+ * and never the default, because the two paths want different answers to "what is
160
+ * true" — the push path wants guidance, the pull path wants the log.
161
+ */
162
+ export function recall(memories, query, limit = 5, now = Date.now(), target = 'guidance') {
70
163
  const tokens = tokenize(query);
71
- return memories
72
- .filter((memory) => memory.status === 'active')
164
+ const hits = memories
165
+ .filter((memory) => inTarget(memory, target))
73
166
  .map((memory) => ({ memory, ...scoreMemory(memory, tokens, now) }))
167
+ // A query with no signal-bearing tokens matches nothing rather than everything: an
168
+ // empty query is a question about nothing, and answering it with the whole log would
169
+ // make every ranking unexplainable.
74
170
  .filter((hit) => hit.matched.length > 0)
75
- .sort((a, b) => b.score - a.score || b.memory.createdAt - a.memory.createdAt)
76
- .slice(0, limit);
171
+ .sort((a, b) => b.score - a.score || b.memory.createdAt - a.memory.createdAt);
172
+ // One claim, one hit. The id is derived from the statement, so the same sentence
173
+ // recorded in both scopes arrives twice with the same id and — by construction — the
174
+ // same score and the same text. Handing both to the caller would put the same line into
175
+ // the injected context twice and make `limit` mean "hits" instead of "memories".
176
+ // `scope` is still reported on the survivor, so which log holds it stays visible.
177
+ const seen = new Set();
178
+ return hits.filter((hit) => {
179
+ if (seen.has(hit.memory.id))
180
+ return false;
181
+ seen.add(hit.memory.id);
182
+ return true;
183
+ }).slice(0, limit);
77
184
  }
package/lib/routes.d.ts CHANGED
@@ -15,4 +15,5 @@ export declare const MEMORY_RECALL_PATH = "/api/context-memory.recall";
15
15
  export declare const MEMORY_REMEMBER_PATH = "/api/context-memory.remember";
16
16
  export declare const MEMORY_FORGET_PATH = "/api/context-memory.forget";
17
17
  export declare const MEMORY_PREFS_PATH = "/api/context-memory.prefs";
18
+ export declare const MEMORY_CANDIDATES_PATH = "/api/context-memory.candidates";
18
19
  export declare function registerMemoryRoutes(ctx: Context, memory: MemoryService): void;