chore(deps): bump actions/setup-python from 6 to 7 - #8
Open
dependabot[bot] wants to merge 89 commits into
Open
Conversation
Replace the 8954-line vanilla JS single-page app with a Vue 3 (CDN, no
build step) component tree. Each pane is a self-contained ES module
imported directly by the browser, keeping the original <style>+<script>
delivery model so users still don't need npm.
Structure:
- serve/static/index.html 13-line shell that boots Vue and mounts App
- serve/static/css/app.css extracted legacy stylesheet (99KB)
- serve/static/css/layout.css new app-shell + Dashboard + drawer styles
- serve/static/i18n/{en,zh}.json 472→517 keys (added 45 Vue-component keys)
- serve/static/js/main.js createApp + mount entrypoint
- serve/static/js/App.js top-level shell, owns polling, dispatches events
- serve/static/js/api.js fetchJSON wrapper + domain endpoints
- serve/static/js/store.js reactive global state (lang/theme/i18n/toasts)
- serve/static/js/components/ TopBar, Sidebar, Tabs, Timeline, Dashboard,
Wiki, WikiEditor, KnowledgeGraph, Settings, RunStrip, Toast, Diagnostic
Bugfixes that shipped with the migration:
- Tabs nav visible (was crashing on .graph.split() because graph was 0)
- v-show drives tab visibility (was blocked by !important on .tab-pane)
- Drawer opens (was blocked by display:none on .drawer)
- llmConfig unwraps {config, warnings} envelope
- All Vue components' i18n keys present in en+zh
Dashboard now renders the full /api/insights payload:
- 10-tile KPI grid (total/today/active/links/entities/clusters/avg/decay/wiki/recall24h)
- 6-stage data-flow pipeline (extract→active→decayed→merged→archived→forgotten)
- Memory type and status distribution bars
- 24h ingest sparkline + per-source health
- Wiki health metrics + top compression candidates
- Animated live-status pulse dot
API-key UX: the input now shows a green '已配置 + fingerprint' pill when
a key is saved, so the cleared input field after Save is no longer
misleading. Tested: all 224 pytest cases pass; ruff clean.
The Wiki new-page button silently did nothing because Wiki.js referenced
<WikiEditor> in its template but never imported the component. Vue 3 then
treated the tag as a generic custom element and rendered an empty shell.
Fix: import { WikiEditor } from './WikiEditor.js' and register it in
the components: {} map. The new-page editor now opens correctly with
all form fields, and the test (createWiki / updateWiki / cancel) flow
works end-to-end.
Also switched Diagnostic's fetch from /api/health (404) to /api/diag
(200) so the modal now shows real subsystem status instead of an empty
error block.
- Dashboard.js: 10 KPI tiles + 3 ring meters + lifecycle + pulse + compression + granularity + distribution + sources + pipeline + health/weekly + LLM audit + write-guard + architecture loop. All sections guarded against missing fields so partial /api/insights still renders. - Settings.js: remove stray </transition> closing tag that broke App template compilation, which prevented Dashboard's onMounted hook from firing on initial mount. - layout.css: dashboard styles for KPI/rings/lifecycle/pulse/compression/ granularity/distribution/sources/pipeline/health/audit/guard/arch.
… menu KnowledgeGraph: - Add mode selector (wiki vs memory) wired to /api/admin/graph/rebuild - Add zoom +/- and Fit buttons in the toolbar - Make the rotate toggle (⟳) toggleable - Expand kind filter to all 7 kinds (concept, acronym, cjk, tag, url, path, wiki_page) - Add side panel — title, kind badge, mention count, connected edges, evidence memories - Add bottom legend strip (Wiki/Tag/Concept/Acronym) - Add wiki_page dblclick → switch to Wiki tab and open that page - Track mention count via api.graphEntityMemories helper TopBar kebab menu: add Rebuild graph, Run doctor (Cmd+D), Consolidate items. App.js wires the new events and listens for the global Cmd+D shortcut. Wiki.js listens for loop-memory:open-wiki to open the matching editor. i18n: action.consolidate, topbar.rebuildGraph, topbar.doctor, common.close api.js: graphEntityMemories endpoint app.css: stat-pill, graph-side, graph-legend, evidence-row, rel-row styles
layout.css: add 'display: flex; flex-direction: column' to .tab-panes so the inner .tab-pane children size to the available viewport height. Without it, the dashboard pane stretched to its natural content height (~5000px) and overflow:auto never triggered a scrollbar. store.js: smarter initial-language selection. Respect any explicit user choice (localStorage), then fall back to navigator.language — zh-* locales default to 'zh', en-* locales default to 'en', everything else falls back to 'zh'. Browsers that report a non-zh locale no longer get an unwanted English UI on first visit.
store.js: change _i18n from a plain object to a reactive({}) so the t()
function's dict lookups are tracked. loadI18n populates the dictionaries
via Object.assign on the reactive proxy, which fires per-key setters and
invalidates every computed that calls t() — fixing the 100-200ms window
during initial load where the tab labels and component text flashed raw
keys like 'tab.wiki' before the dictionaries arrived. Previously the
first render of every component that uses t() in its template would show
those raw keys until something else triggered a re-render (such as the
next stats poll), so on slow connections the wiki tab click could feel
unresponsive until a manual reload.
Wiki.js: watch store.activeTab. When the tab becomes 'wiki', call
refresh() so returning to the tab always shows fresh data (distillation
runs may have added/removed pages while the user was elsewhere). Falls
back to the onMounted-first-fetch path on the very first open.
Topbar (TopBar.js): - Remove kebab three-dot overflow; promote Ingest / AI 整理 / 立即运行 into primary command group - Add named '工具' dropdown caret instead of generic kebab, grouping maintenance actions (重打分 / 整理 / 重建图谱 / 运行 Doctor) - Add language segmented control (中 / EN) and theme segmented control (A / ☀ / ☾) into a single utility row; settings cog remains Dashboard (Dashboard.js + i18n): - 统计概览 split into 4 primary KPIs + 7 compact KPIs; new side accent bar and dot-prefixed labels - 生命周期 re-rendered as 6 icon stages with staggered 3-particle flow between gaps, breathing active-stage node - 矛盾检测: clicking 合并 / 保留A / 保留B / 忽略 now removes the pair immediately (no double-click required) with toast feedback - 衰减分数分布: rebuild SVG with 360x214 viewBox, grid lines, gradient, range labels below the bar, count value above; totals and peak range summary at the top - Pulse titles: icon rendered via .label-icon so it does not double up with translated text emoji - i18n: strip leading ⚠ 📊 🗜 🔬 📈 📡 📖 📥 🔍 ⇩ + duplicates from zh/en Layout (layout.css): - New topbar command group, tools dropdown, utility segmented controls - Stat card tone-driven box-shadow, lifecycle stages/flow keyframes - Decay chart SVG helpers, .resolving opacity for handled items - Wiki masonry: columns: 3 320px with break-inside: avoid 224 tests still pass; ruff clean.
… restore lifecycle flow
Dashboard (Dashboard.js):
- refresh() no longer ends with await loadWeekly(): the weekly report is
intentionally heavy and previously flickered its markdown on every
6-second poll. Weekly is now only fetched:
1. on initial mount (once)
2. when the user clicks a different window pill (3d / 7d / 14d)
3. when the user clicks the explicit \u21bb refresh button
- barsFor(): redesigned decay chart geometry (h 214 -> 230, plot.bottom
38 -> 56). Returns shortLabel ('.1','.3',\u2026) + showShortLabel flag so
the chart only renders every other axis label, eliminating the
crowded-overlap problem at narrow widths. Adds a peak flag for the
highest-count bar and a <title> tooltip carrying the full range + count.
- ingestBars(): 24 hourly buckets now expose tickY / lblY / tick / label
so the template only paints major hour ticks (every 4h) plus a small
idle-vs-active class plus a peak flag for the busy hour.
- ingestPeakHour() helper exposed to the template; renders a pill on
the ingest card showing the peak hour and share of daily volume.
- Layout: 1.2 kB of template changes around the three charts above.
Pulled <line> axis and per-bar <rect> tick into the SVG so the chart
looks like an axis plot, not a tiny floating bar set.
Layout (layout.css):
- Decay chart: .ins-decay-axis, .ins-decay-tick, .ins-decay-bar.peak
(with accent drop-shadow), and a slightly stronger .ins-decay-label
opacity. Card SVG min-height bumped from 246 to keep proportions.
- Ingest chart: full set of .ins-ingest-* rules: grid lines, axis line,
ticks, idle/peak bar states, hover brightness, .ins-src-peak pill.
- Lifecycle: connector pseudo-element switched from border-top dashed
to a 12px segmented linear-gradient that animates background-position
(lc-flow-dash 1.4s linear infinite) so the dashed line 'marching ants'
toward the next stage. Particles bumped from 5 to 8 px with stacked
glow shadows. Icon z-index raised to 3 and background set to opaque
var(--surface) so the dashed line does not leak through.
224 tests still pass; ruff clean.
The contradiction 'merge' button previously just deleted the lower-scored
memory and surfaced a '已删除 1 条重复' toast. That contradicted the label
and surprised the user: '选择合并,为何也提示删除一条,不应该是两条
融合一下吗'.
Storage (sqlite_store.py):
- New merge_memories(a_id, b_id) method:
* Winner = higher-scored side (ties go to A).
* Loser's text is appended onto the winner with '\n\n---\n\n' as the
separator, but the append is skipped when the loser's text is
already a substring of the winner's (avoids duplication).
* Winner's importance and score are bumped to the max of the pair so
the fused memory carries the strongest signal.
* Loser is deleted in the same transaction.
* Pair is recorded in contradiction_ignored so it does not resurface.
* Returns a small dict describing what changed (merged, kept, lost,
appended, new_length, winner_was_a).
API (app.py):
- /api/contradictions/resolve action='merge' now calls merge_memories
instead of doing a plain delete. Response shape moved from the old
{deleted: [{id, kept}]} to {merged, winner, loser, appended, new_length}.
Docstring updated to reflect true-fusion semantics.
Dashboard (Dashboard.js + i18n):
- Toast for 'merge' now reads '已合并两条记忆' / 'Merged both memories
into one'. When the loser's text was actually appended, the toast
also shows the new combined length ('已合并 · 新文本长度 N'). When only
one side survived (the other was already gone) the toast says '已
合并 · 一条记录已不存在'.
- keepA / keepB still delete the loser (single-side keep), toast text
unchanged.
Tests (tests/test_contradictions.py):
- Replaced the old merge-then-delete test with three tests covering:
* the standard fusion (winner keeps row, loser appended, max(score),
pair hidden).
* the no-op-append path when the loser's text is already inside the
winner's.
* the tie-keep-a path.
- 226 tests pass, ruff clean.
…imeline
User-visible bugs fixed:
- Topbar '导入' button did nothing: frontend posted to /api/ingest
with source=manual, which 404s (the real route is
/api/admin/ingest) and 'manual' is not a registered loader
(loaders are codex/claude/hermes/openclaw). The action now opens a
dedicated IngestPopover with a checkbox per loader; user can tick
one or many and 'Ingest now' runs the per-source requests and shows
the result per row.
- Same frontend/backend mismatch for rescore (/api/rescore vs
/api/admin/rescore) and rebuildGraph (/api/graph/rebuild vs
/api/admin/graph/rebuild). Both now go to the right URL. The
stale /api/health alias is also replaced with /api/diag.
- Sidebar looked cluttered and unreadable: rewritten with source
pills (All / Codex / OpenClaw / Claude / Hermes) at the top,
per-session cards using a colored stripe + glyph icon,
timeAgo reading ended_at (the field the API actually returns)
instead of the missing last_seen, friendlier empty state, raised
limit from 100 to 300 so Codex sessions are not hidden by heavier
OpenClaw traffic, auto-refresh on the ingest event plus a 30s
poller so new sessions show up without a manual reload.
- Clicking a sidebar session set activeSession but nothing read it,
so the timeline ignored the user's choice. Timeline now watches
store.activeSession and re-fetches with session_id; renders an
accent scope banner with the session's title and a clear button;
the recall path also respects the scope.
API / backend:
- /api/memories accepts session_id; works alongside source / kind /
score / date range filters.
- /api/admin/ingest documented in popover tooltips.
UI / i18n:
- New IngestPopover component (Vue SFC inside the JS file) with
per-source checkboxes, per-row result chip, run button, progress
text. Goes under tb-ingest-trigger which now has a caret so the
dropdown affordance is obvious.
- Timeline cards now expose .kind-lbl, .polish-spark, .dot-sep,
.score-val, and a header summary chip + time range
('200 条记忆 · 2026/6/24 22:02:06 → 2026/7/18 20:24:51').
- zh/en i18n gain action.ingest* keys, sidebar.turns /
filteringBySession / clearFilter / emptyHint, timeline.memories /
sessionScope / clearScope / emptyForSession.
226 tests still pass; ruff clean.
- Add logo set (mark + light + horizontal lockup) using interlocking arcs around a memory node; reframe README + pyproject as a general agent memory system - Lifecycle beads: fix the missing toneColor variable so the rolling colour-shifting balls actually render; rewrite the keyframes to use `left` (parent-relative) instead of `translateX %` (which referenced the ball's own width and never moved). Destination bead (p3) is ~1 size larger than the on-line beads and grows visibly when it lands at the next stage.
Clicking a session in the left rail used to only set the active-session filter (which the Timeline already respected), but kept the user on whichever tab was currently shown — so on Dashboard/Wiki/Graph users saw no immediate effect. The session click now also flips store.activeTab to 'timeline' so the right pane always opens to that session's fragment list. Tab↔URL sync moved into App.js (watch on store.activeTab) so any writer — Tab clicks, sidebar session picks, Open-wiki from graph — reaches the URL through one path. Removed the duplicate writer in Tabs.js.
Two UI fixes:
1. The model-config entry in the topbar had background:transparent
and border:1px solid transparent, so it dissolved into the chrome
and was unfindable. Replaced the inline text+lock with a proper
two-line chip (icon + provider + status badge). The badge reflects
api_key_set in real time: green 'API key 已设' or amber 'API key
缺失', so users get immediate visual confirmation that the key
was saved (vs. the 'input cleared, looks unset' bug from earlier
rounds). Clicking the chip still opens Settings.
2. Fit-to-window ('适配窗口') was firing correctly but produced a
zero-pixel diff whenever the graph was already at optimum scale,
so the click looked broken. Rewrote fit() to snapshot the current
transform, compute the new fit, then tween scale/tx/ty over
260ms with an ease-out-cubic. Initial loads and window-resize
still use instant fit (correct UX for those), only the manual
button click animates.
Three fixes on the dashboard 生命周期流转 strip: 1. **Centering**: rail vertical midline now matches icon vertical center. Was 10px below. New top:36px on a 32px icon placed in a stage with padding-top:22 → icon center at y=38, rail mid at y=38. Verified via headless screenshot. 2. **Slim the icons + beads**: icons 42→32px, heavy box-shadow halo replaced by a thin ring; remove the thumping keyframe animation and use a static 'active' glow. Beads shrunk from 6/5/12→4/3/6 (still destination ~1.5× larger than on-line) and the heavy multi-layer glow collapsed into a single soft halo. 3. **Soften the color story**: rail opacity .55 → .45 with smoother fade-in/out at the icon ends (transparent → color → transparent); added a faint ::after blurred haze that suggests flow direction without competing with the beads. Bead keyframe nudged to fade in at 10% and pop at 85% (was 12%/88%) so the visual sits inside the rail, not at its edges. Animation slowed from 2.6s → 3.2s.
The previous fix tried to match the rail midline to the icon vertical center but applied the math to the wrong coordinate space: the flow is absolutely positioned inside `.ins-lc-stage` (not inside `.ins-lifecycle`), so the lifecycle's 22px outer padding does not shift it. The stage itself has padding-top:0, the icon is the first flex child at y=0, icon height 32 → icon center y = 16. Flow height 4 → flow top should be 16 - 2 = 14. Headless measurement before this fix: flow cy = 786.5, icon cy = 764.5, diff = +22px (the entire outer padding of .ins-lifecycle bleeding through). After this fix: diff = 0px on all 5 visible stages.
The chat host returns 2049 'invalid api key' for keys issued against the .com host, even though the docs and the platform.minimaxi.com console both serve .com. Verified with the user's key: POST https://api.minimaxi.chat/v1/chat/completions → 401 {"status_code":2049,"status_msg":"invalid api key"} POST https://api.minimaxi.com/v1/chat/completions → 200 (works, returns proper chat completion) Two call sites updated: - PROVIDERS['MiniMax'].default_base_url - build_provider() fallback when config has no base_url Also migrated the saved user config (which had the old .chat URL baked in from earlier sessions) via PUT /api/admin/llm/config so the dashboard's '测试连接' button now passes against the .com host without requiring the user to re-edit the base URL field. Tests: 226/226 passing.
Symptom: user saved their MiniMax API key while the macos-keychain backend was active, then restarted the server with the local-file backend selected. The active store had no entry, the UI badge showed 'API key missing' and every LLM call went out without an Authorization header → MiniMax returned 1004 'Please carry the API secret key'. Fix: `get_secret(account)` now tries the active backend first; on miss it asks each OS-native backend in turn (macos-keychain on Darwin, linux-secret-service on Linux, windows-cred on Windows) and *migrates* the value into the active store on hit. `has_secret` uses get_secret so the UI badge becomes correct automatically. This keeps the canonical store simple (one 0600 JSON file under ~/.loop_memory) while never losing a key that was written by a different backend. Verified: macOS keychain entry 'llm/minimax/api_key' was copied into ~/.loop_memory/secrets.json on the next config read; the '测试连接' button now returns ok=true without re-pasting the key; all 226 tests still pass.
Four user-visible issues fixed:
1. **Title** 'Settings' / 'LLM Consolidator Settings' → '大模型配置' /
'LLM Configuration' (+ subtitle '一次配置,全局生效' /
'Configure once, used everywhere'). The LLM is shared by every
feature that needs one (consolidation, knowledge graph, weekly
report, connectivity test) — the old name implied consolidation
was the only consumer.
2. **Duplicate 'Run mode' label** in the schedule section. The
inline label next to the 'enabled' checkbox was the same string
as the label on the 'mode' dropdown. Renamed the first to
'启用自动运行' / 'Enable auto-run' so the two controls are
visually distinct (auto-run toggle vs. trigger mode picker).
3. **i18n rendering bug**: 'Next run: {when} ({mode}): Next run:
7/19/2026, 3:00:00 AM (Daily)'. The template emitted
't(settings.nextRun)' (literal template with unsubstituted
{when}/{mode}) followed by ': ' followed by nextRunText()
(already-substituted version). Fixed by emitting nextRunText()
only. Replaced the gray plain-text line with a colored status
pill (green when scheduled, gray when disabled) plus a per-mode
hint line ('Every day at 3:00', 'Every Mon at 03:00', etc.).
4. **Scope clarity**: added a small gray note under each section
header explaining what it covers. 'CONNECTION' lists the
features that share this LLM; 'CONSOLIDATION SCHEDULE' and
'CONSOLIDATION BEHAVIOUR' both say 'only affects consolidation'.
Touched files:
- Settings.js: header, schedule label, nextRunText + scheduleModeHint, scope notes
- app.css: .drawer-subtitle, .sec-scope, .sched-status pill
- i18n/{en,zh}.json: 16 new keys (title, subtitle, sections, scopes, hints)
All 226 tests pass; no server-side changes needed (the config
storage key 'llm_consolidator' stays — every feature already
calls build_provider(cfg) on it, which is the existing global
config).
… settings button
Root cause of the stale 'API key missing' badge:
scheduler.status() never returned api_key_set / last_test_ok /
last_test_at, so App.js always saw api_key_set=false on boot and
the chip said 'API key missing' even though the key was safely in
the keychain.
Fix:
Backend
- scheduler._state: added last_test_ok / last_test_at /
last_test_message + record_test_result() method.
- scheduler.status(): reads api_key_set straight from the secret
backend (so the chip is correct without any frontend round-trip),
plus exposes last_test_ok/at so the chip can distinguish
'verified reachable' from 'configured but unverified'.
- handlers.llm_test(): optional scheduler param; records test
result on success/failure (including URL errors).
- _do_run_safe(): on a successful live run, promotes the dot to
'verified reachable'; on an LLM-flavored exception, demotes it
to 'unreachable' — so the chip stays honest without forcing the
user to re-open Settings.
- /api/admin/llm/test route: passes the scheduler through.
Frontend
- store.modelInfo: extended with reachability ('unset'|'ok'|
'stale'|'fail') + last_test_ok/at/message.
- App.refreshRunStatus: derives reachability from
(api_key_set, last_test_ok, last_test_at) — green-pulsing for
a recent success, green-static for set-but-unverified, amber
for last-failed, red for unset.
- TopBar model chip: redesigned to be a small pill (icon + model
name + 8px dot). The wide 'API key 已配置 / 缺失' status text
is gone — that info now lives only in the :title tooltip, so
the topbar stays narrow.
- TopBar settings button: the sun/gear SVG looked too much like
the theme toggle (☀/☾), so users opened Settings by mistake.
Replaced with a clean cog icon (8-tooth gear) clearly distinct
from the theme controls.
- i18n: 4 new tip templates (ok/stale/fail/unset) + 4 dot labels.
CSS
- .model-chip: compact 34px height, border tinted by reachability.
- .model-chip .m-dot: 8px circle with halo + pulsing keyframe
(2.2s ease-in-out, scale + expanding box-shadow).
- Reduced-motion: disables the pulse for users who request it.
Verified screenshots (zh): ok=绿点脉动, fail=橙点, unset=红点;
all 226 tests pass.
Two issues fixed together:
1. **Two redundant buttons** in the topbar:
- 'AI 整理' (llm-run) and '立即运行' (run-now) called the same
backend endpoint, with the same payload; the 'force: true'
flag on the second was silently dropped because the endpoint
doesn't read it. Merged into one '立即整理' button. Dropped
the now-dead onRunNow handler in App.js, the 'run-now' event
in TopBar, and the obsolete llmRun/runNow i18n keys.
2. **Wiki knowledge base lacked import**:
- GET /api/wiki/export already existed (markdown only); extended
it with format=json so the export is round-trippable.
- New POST /api/wiki/import: accepts either:
* {format: 'json', pages: [...]} (round-trip)
* {format: 'markdown', markdown: '...'} (## title sections)
Upserts by slug (creates new ones, updates matching slugs),
returns {created, updated, skipped, errors, total}.
- Wiki toolbar: added 导出全部 / 导入 buttons. Export triggers
a JSON file download (timestamped). Import opens a file
picker, infers format from filename + first char, POSTs to
the endpoint, refreshes the page list, and shows a toast
with the {created/updated/skipped} counts.
- 4 new tests in tests/test_export_ask.py: format=json export,
json import round-trip (create+update), markdown parsing of
## sections including Chinese titles, unknown-format rejection.
All 230 tests pass (was 226; +4 new import tests). Verified via
headless screenshot: topbar shows one '立即整理' button (no more
redundant pair); wiki toolbar shows 导出全部 / 导入 / 新建页面.
- _CLUSTER_SYSTEM / _WIKI_SYSTEM prompts drop hard char/bullet caps; new policy: every decision/number/name/constraint must survive intact - Default token budgets raised: max_text_chars 1200->4000, max_output_tokens 800->4096, validator ceiling 4096->8192 - Per-call caps raised in evolution.py + llm_consolidate.py - OpenAI/Anthropic adapter timeouts 20s->60s for longer outputs - Wiki.js: bulletsOf no longer slices first 6; bodyPreview removed - CONTRIBUTING.md rewritten (pytest, real layout, secrets, providers) - docs/api.md: canonical HTTP API reference - docs/providers.md: provider registration + key-storage story - README adds docs nav table; CHANGELOG adds Unreleased entry Verified: 230/230 tests pass; stage3+stage4 with new prompts produced pages whose titles/summaries/bullets are no longer truncated.
A. Default watcher fix (治本):
watcher.py: poll loop now tracks last_size_change_at instead of
using st.st_mtime as the idle signal. Codex desktop (and similar
agents) refresh mtime on background metadata flushes even when
no new content is being written. Previously that kept resetting
the idle timer for long active sessions so they never ingested.
Size-stable-for-N-seconds is the correct signal.
B. Manual escape hatch (兜底):
- loop-memory hook --once: process every eligible file once and
exit. Used for ad-hoc batch runs without leaving a watcher.
- loop-memory hook --idle SECONDS: override the default 60s idle.
- watcher.run_once(): standalone single-pass helper used by both
the CLI and the server endpoint.
- POST /api/admin/watcher/force-ingest: in-process ingest with
active_only=true|false to either pick the most-recently-modified
transcript or run a full pass.
- GET /api/admin/watcher/active-session: surfaces the live
'currently active' transcript for the UI to display.
- IngestPopover gets a top 'Force-ingest active session' panel:
source selector + active-file preview + ⚡ button. Wired through
the api.js client (forceIngest + activeSession helpers) and the
i18n bundles (zh + en).
Verified end-to-end: clicking force-ingest against the live Codex
session writes the latest messages into the db (101 → 110 memories)
within a few seconds instead of waiting for the idle window.
The topbar button on the Wiki tab and the editor dialog header both used wiki.new, which read '新建页面' / 'New page'. Renamed to '新建知识' / 'New knowledge' per UX feedback — the tab is called 知识库 (knowledge base) so '新建知识' is consistent. Also aligned the orphan wiki.newPage key (no current callers) so the i18n bundle stays consistent and a future caller won't drift.
Per UX feedback, the Wiki tab is named 知识库 (knowledge base), so its content units should be called 知识 (knowledge) throughout the UI, not 页面 (page) or 词条 (entry). Updated strings: - New button / editor dialog: 新建知识 / New knowledge - Empty state: 暂无知识 / No knowledge yet - Confirmation: 删除这个知识? / Delete this knowledge? - Toast: 已创建知识 / Knowledge created (also saved/deleted) - Import/export tips: ...所有知识... / ...all knowledge... - Import empty / no match: 未识别到任何知识 / No knowledge recognised - Dashboard KPI labels: 条 (was 页) / knowledge items (was pages) - Knowledge-graph node kind label: 知识 / Knowledge - Model chip tooltips: 配置页 → 设置 (UI panel, not a 'page') Did NOT change: - DOM class names (.wiki-card-body etc) — they are CSS hooks - API field names (wiki_pages) — those are server contracts - i18n keys themselves (wiki.field.title, wiki.titlePlaceholder, etc)
The four toggles under 大模型配置 → 'AI 整理' (过滤噪音 / 重新打分 /
提炼合并 / 试运行) were rendering with each label as a full-width
block, stacking the span above the checkbox and producing ~60-70px
of vertical gap between rows.
Root cause: layout.css:129 forced every .drawer-body label to
display:block + margin-bottom:10px, and line 130 forced
.drawer-body label > span to display:block. Both rules are meant
for the input-form labels (batchSize, temperature, etc) but also
fired on the <label class='switch'> wrappers, breaking their
inline-flex layout.
Fix: dedicated .drawer-body .behaviour-switches styles that:
- Lay the four switches out in a 2x2 grid (gap 8px row / 14px col)
so the section is dense instead of running off the page
- Override label.switch to inline-flex so the text sits next to
the checkbox
- Override the inner span to inline + restore normal sizing
- Override the checkbox width:100% inherited from .drawer-body
input so it stays a small native control
- Add a subtle surface background + hover accent tint so each
switch reads as a self-contained unit
Users reported that '新建页面' / 'New page' didn't update to
'新建知识' / 'New knowledge' even after a hard reload. Root cause:
* Starlette's StaticFiles default sets Cache-Control: max-age=3600
on every served file. Safari and Chrome both honour that for an
hour after first hit.
* The i18n JSON is fetched by store.js on app boot. With a 1-hour
max-age the browser silently serves the stale en.json / zh.json
and the new wiki.new='新建知识' value never reaches the Vue
template.
Two-layer fix:
1. Server-side middleware (serve/app.py:65) overrides
Cache-Control per extension on /static/* responses:
- json / html -> no-cache, must-revalidate
- js / css / fonts / images -> max-age=300, must-revalidate
- everything else -> no-cache
ETag/If-Modified-Since 304 still keeps unchanged files cheap.
2. Client-side (store.js:83) adds { cache: 'no-store' } to the
i18n fetch so even the very first hit after a deploy isn't
poisoned by a previously cached response.
Verification:
curl -sI :7767/static/i18n/zh.json -> no-cache, must-revalidate
curl -sI :7767/static/js/store.js -> max-age=300, must-revalidate
…ved) Co-Authored-By: Claude <noreply@anthropic.com>
- Settings and Diagnostic are now defineAsyncComponent — their JS bundles are only fetched when the user first opens the modal (gear icon or Cmd+D). - Dashboard, Wiki, KnowledgeGraph switched from v-show to v-if so they unmount completely when not active, freeing CPU/memory instead of just being hidden. Timeline stays v-show because it is the default tab and benefits from keeping state warm. - WikiEditor now lazy-loaded inside Wiki — only fetched the first time the user clicks 'edit' on a page.
…uted helpers Co-Authored-By: Claude <noreply@anthropic.com>
Also adds confirmClearTitle/confirmClearMsg i18n keys. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes #7 (CI lint failure). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Rename the Chinese dashboard tab from 看板 to 数据看板 for clarity (distinguishes the metric dashboard from other dashboard-ish tabs). Redesign the Settings drawer: - Toggle rows now follow a single .row-toggle pattern across redact / storage / behaviour sections. Layout: label + description on the LEFT, checkbox on the RIGHT — descriptions always sit BELOW the label (never above). - Storage section split into .settings-subsection groups (capacity / cadence). Auto-compact toggle now has a matching hint. - Redaction section split into .settings-subsection groups (rule toggles / live preview). Visual rhythm matches storage. - Behaviour section split into .settings-subsection groups (numeric parameters / processing toggles). All four switches (filter, score, summary, dry-run) gained explanatory hints. - Added matching i18n hint keys to both en.json and zh.json. - Unified the .row-toggle CSS into one rule shared by all three sections; pulled storage meter body into a single source of truth.
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v6...v7) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
smartfind
force-pushed
the
main
branch
2 times, most recently
from
July 29, 2026 14:57
0f8d541 to
24933b9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps actions/setup-python from 6 to 7.
Release notes
Sourced from actions/setup-python's releases.
... (truncated)
Commits
5fda3b9Pin SHA commits and update docs with latest versions (#1338)4ab7e95Merge pull request #1337 from actions/philip-gai/bump-actions-cache-6-2-00f3a009Remove the pip-install input (#1336)f8cf429Migrate to ESM and upgrade dependencies (#1330)54baeeaValidate and retry manifest fetch to prevent silent failures (#1332)c709277Annotation code fix (#1335)6849080remove EOL Python versions and Bumps numpy text fixture (#1333)0903b46Bump certifi from 2020.6.20 to 2024.7.4 in /tests/data (#1328)Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)