mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 07:01:34 +00:00
fix(ui): rename the Memory Palace browser to Memory Wiki and document it (#115954)
* fix(ui): rename the Memory Palace browser to Memory Wiki and document it * refactor(memory-wiki): drop the old memory-palace module files * docs: refresh generated docs map * test(ui): align dream-diary hub-tab assertion with the wiki sub-tab id * docs: describe memory wiki clustering behavior accurately * docs(memory-wiki): record the wiki.overview rename decision at the registration site * test(ui): query the Agents channels hub tab by tab role after the hub-tabs refactor
This commit is contained in:
committed by
GitHub
parent
a4aaa1cd74
commit
c87e545c88
@@ -283,6 +283,19 @@ When enabled, the Gateway **Dreams** tab shows:
|
||||
- a distinct grounded Scene lane for staged historical replay entries
|
||||
- an expandable Dream Diary reader backed by `doctor.memory.dreamDiary`
|
||||
|
||||
When the bundled [`memory-wiki`](/plugins/memory-wiki) plugin is enabled, the
|
||||
Diary view gains two more sub-tabs next to Dreams:
|
||||
|
||||
- **Imported Insights**: clustered insights surfaced by external-history
|
||||
imports (for example `openclaw wiki chatgpt import`), for review before any
|
||||
of it graduates into durable memory
|
||||
- **Memory Wiki**: the compiled wiki the memory system can search and reason
|
||||
over — synthesis, entity, and concept pages (plus sources and reports that
|
||||
carry claims, open questions, or contradictions) with per-page counts, a
|
||||
full-vault breakdown, and inline page previews
|
||||
|
||||
Both sub-tabs show an enable hint instead when `memory-wiki` is off.
|
||||
|
||||
## Related
|
||||
|
||||
- [Memory](/concepts/memory)
|
||||
|
||||
@@ -216,7 +216,9 @@ dashboards, compiled digests, and wiki-native tools (`wiki_status`,
|
||||
|
||||
`memory-wiki` does not replace the active memory plugin; the active memory
|
||||
plugin still owns recall, promotion, and dreaming. `memory-wiki` adds a
|
||||
provenance-rich knowledge layer beside it.
|
||||
provenance-rich knowledge layer beside it. You can browse the compiled wiki
|
||||
in the Control UI under Memory → Dreams → Diary → **Memory Wiki**
|
||||
([details](/plugins/memory-wiki#browsing-the-wiki-in-the-control-ui)).
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Memory Wiki" icon="book" href="/plugins/memory-wiki">
|
||||
|
||||
@@ -6219,6 +6219,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Dashboards and health reports
|
||||
- H2: Search and retrieval
|
||||
- H2: Agent tools
|
||||
- H2: Browsing the wiki in the Control UI
|
||||
- H2: Prompt and context behavior
|
||||
- H2: Configuration
|
||||
- H3: Per-agent vaults
|
||||
|
||||
@@ -283,6 +283,23 @@ The plugin also registers a non-exclusive memory corpus supplement, so shared
|
||||
`memory_search` and `memory_get` can reach the wiki when the active memory
|
||||
plugin supports corpus selection.
|
||||
|
||||
## Browsing the wiki in the Control UI
|
||||
|
||||
The [Control UI](/web/control-ui) can browse the compiled wiki directly: open
|
||||
the Memory page, then **Dreams → Diary → Memory Wiki**. The tab clusters
|
||||
synthesis, entity, and concept pages — plus source and report pages that
|
||||
carry claims, open questions, or contradictions — with per-page counts and a
|
||||
full-vault page breakdown, and opens full page content inline. Raw sources
|
||||
and reports without that metadata count toward the breakdown but are not
|
||||
listed as cards; open them from the **Imported Insights** sub-tab, which
|
||||
reviews what external-history imports surfaced before promotion.
|
||||
|
||||
Both sub-tabs appear once the plugin is enabled; in agent-scoped vault setups
|
||||
they show the selected agent's own vault. The UI reads through the plugin's
|
||||
gateway methods (`wiki.overview`, `wiki.get`, `wiki.importInsights`); inline
|
||||
page previews use `wiki.get`, the same lookup agents reach through the
|
||||
`wiki_get` tool.
|
||||
|
||||
## Prompt and context behavior
|
||||
|
||||
When `context.includeCompiledDigestPrompt` is enabled, memory prompt sections
|
||||
|
||||
@@ -266,7 +266,7 @@ select it to open the owning Approvals page.
|
||||
- Threads (a workspace page at `/sessions`, with a **Worktrees** tab alongside it): list configured-agent sessions by default, pin frequent sessions, rename them, archive or restore inactive sessions, fall back from stale unconfigured agent session keys, and apply per-session model/thinking/fast/verbose/trace/reasoning overrides (`sessions.list`, `sessions.patch`). A three-way **Active / Archived / All** filter controls both this page and the sidebar; All dims archived rows and labels them explicitly. Archived sessions keep their transcripts, are never auto-pruned, and remain shelved until explicitly unarchived or deleted. Rows show an unread dot for active sessions with activity since they were last read, with mark-unread/mark-read actions (`sessions.patch { unread }`), and a Fork action that branches the transcript into a new session (`sessions.create { parentSessionKey, fork: true }`). Overview tiles above the table summarize the loaded roster (session count, live runs, unread sessions, total tokens, and archived count when available), each row carries a kind glyph with a live-run dot, status renders as a plain dot plus label, and the Tokens column shows a context-window usage meter when the session reports token and context sizes. Row management actions live in a per-row menu (kebab button or right-click) mirroring the sidebar's session menu, and the row drawer carries the agent runtime and run duration alongside the other session details.
|
||||
- Native Claude and Codex sidebar catalogs stream one host at a time, then reconcile after node connectivity changes, on page focus, and at most every 30 seconds while visible. Catalog changes trigger a faster follow-up pass, so sessions created in the native tools appear without reloading the Control UI. Claude Desktop rows also retain their local custom-group label when present; OpenClaw reads that mapping from Desktop's local store and never writes it.
|
||||
- Session grouping: a Group by control organizes the sessions table into sections by custom groups, channel, kind, agent, or date. Custom groups persist per session via `sessions.patch` (`category`), so sessions started from message channels (Discord, Telegram, WhatsApp, ...) can be categorized too; assign groups by dragging rows onto a section, or with the per-row group selector, and create groups with the New group action.
|
||||
- Memory (a tab on the Agents page, scoped to the selected agent): dreaming status, enable/disable toggle, and Dream Diary reader (`doctor.memory.status`, `doctor.memory.dreamDiary`, `config.patch`).
|
||||
- Memory (a tab on the Agents page, scoped to the selected agent): dreaming status, enable/disable toggle, and Dream Diary reader (`doctor.memory.status`, `doctor.memory.dreamDiary`, `config.patch`). When the `memory-wiki` plugin is enabled, the Diary view adds **Imported Insights** and **Memory Wiki** sub-tabs that browse imported source chats and the compiled wiki — clustered synthesis, entity, and concept pages plus annotated sources and reports, with claims, open questions, contradictions, and inline page previews (`wiki.importInsights`, `wiki.overview`, `wiki.get`).
|
||||
- Import Memory (`/memory-import`, reached from the Agents page's Memory tab): preview and copy local Claude Code auto-memory, Codex consolidated memory, or Hermes memory files into the selected agent workspace (`migrations.memory.plan`, `migrations.memory.apply`).
|
||||
- Onboarding memory offer: when the Control UI opens in [onboarding mode](/web/urls#special-documents-and-startup-modes), a one-page dialog offers to import detected memories with the same plan/apply flow; skipping leaves the settings page as the later entry point.
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ describe("memory-wiki plugin", () => {
|
||||
"wiki.status",
|
||||
"wiki.importRuns",
|
||||
"wiki.importInsights",
|
||||
"wiki.palace",
|
||||
"wiki.overview",
|
||||
"wiki.init",
|
||||
"wiki.doctor",
|
||||
"wiki.compile",
|
||||
|
||||
@@ -6,11 +6,11 @@ import { registerMemoryWikiGatewayMethods } from "./gateway.js";
|
||||
import { listMemoryWikiImportInsights } from "./import-insights.js";
|
||||
import { listMemoryWikiImportRuns } from "./import-runs.js";
|
||||
import { ingestMemoryWikiSource } from "./ingest.js";
|
||||
import { listMemoryWikiPalace } from "./memory-palace.js";
|
||||
import { searchMemoryWiki } from "./query.js";
|
||||
import { syncMemoryWikiImportedSources } from "./source-sync.js";
|
||||
import { resolveMemoryWikiStatus } from "./status.js";
|
||||
import { createMemoryWikiTestHarness } from "./test-helpers.js";
|
||||
import { listMemoryWikiOverview } from "./wiki-overview.js";
|
||||
|
||||
type ApplyMemoryWikiMutation = ReturnType<typeof normalizeMemoryWikiMutationInput>;
|
||||
|
||||
@@ -39,8 +39,8 @@ vi.mock("./lint.js", () => ({
|
||||
lintMemoryWikiVault: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./memory-palace.js", () => ({
|
||||
listMemoryWikiPalace: vi.fn(),
|
||||
vi.mock("./wiki-overview.js", () => ({
|
||||
listMemoryWikiOverview: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./obsidian.js", () => ({
|
||||
@@ -108,7 +108,7 @@ const VAULT_BACKED_GATEWAY_CASES = [
|
||||
["wiki.status", {}],
|
||||
["wiki.importRuns", {}],
|
||||
["wiki.importInsights", {}],
|
||||
["wiki.palace", {}],
|
||||
["wiki.overview", {}],
|
||||
["wiki.init", {}],
|
||||
["wiki.doctor", {}],
|
||||
["wiki.compile", {}],
|
||||
@@ -164,7 +164,7 @@ describe("memory-wiki gateway methods", () => {
|
||||
totalClusters: 0,
|
||||
clusters: [],
|
||||
} as never);
|
||||
vi.mocked(listMemoryWikiPalace).mockResolvedValue({
|
||||
vi.mocked(listMemoryWikiOverview).mockResolvedValue({
|
||||
totalItems: 0,
|
||||
totalClaims: 0,
|
||||
totalQuestions: 0,
|
||||
@@ -540,10 +540,10 @@ describe("memory-wiki gateway methods", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("returns memory palace overview over the gateway", async () => {
|
||||
it("returns the wiki overview over the gateway", async () => {
|
||||
const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
|
||||
const { api, registerGatewayMethod } = createPluginApi();
|
||||
vi.mocked(listMemoryWikiPalace).mockResolvedValue({
|
||||
vi.mocked(listMemoryWikiOverview).mockResolvedValue({
|
||||
totalItems: 1,
|
||||
totalPages: 3,
|
||||
pageCounts: {
|
||||
@@ -582,9 +582,9 @@ describe("memory-wiki gateway methods", () => {
|
||||
} as never);
|
||||
|
||||
registerMemoryWikiGatewayMethods({ api, config });
|
||||
const handler = findGatewayHandler(registerGatewayMethod, "wiki.palace");
|
||||
const handler = findGatewayHandler(registerGatewayMethod, "wiki.overview");
|
||||
if (!handler) {
|
||||
throw new Error("wiki.palace handler missing");
|
||||
throw new Error("wiki.overview handler missing");
|
||||
}
|
||||
const respond = vi.fn();
|
||||
|
||||
@@ -594,7 +594,7 @@ describe("memory-wiki gateway methods", () => {
|
||||
});
|
||||
|
||||
expect(syncMemoryWikiImportedSources).toHaveBeenCalledWith({ config, appConfig: undefined });
|
||||
expect(listMemoryWikiPalace).toHaveBeenCalledWith(config);
|
||||
expect(listMemoryWikiOverview).toHaveBeenCalledWith(config);
|
||||
expect(readRespondPayload(respond)).toEqual({
|
||||
totalItems: 1,
|
||||
totalPages: 3,
|
||||
|
||||
@@ -15,7 +15,6 @@ import { listMemoryWikiImportInsights } from "./import-insights.js";
|
||||
import { listMemoryWikiImportRuns } from "./import-runs.js";
|
||||
import { ingestMemoryWikiSource } from "./ingest.js";
|
||||
import { lintMemoryWikiVault } from "./lint.js";
|
||||
import { listMemoryWikiPalace } from "./memory-palace.js";
|
||||
import {
|
||||
probeObsidianCli,
|
||||
runObsidianCommand,
|
||||
@@ -27,6 +26,7 @@ import { getMemoryWikiPage, searchMemoryWiki, WIKI_SEARCH_MODES } from "./query.
|
||||
import { syncMemoryWikiImportedSources } from "./source-sync.js";
|
||||
import { buildMemoryWikiDoctorReport, resolveMemoryWikiStatus } from "./status.js";
|
||||
import { initializeMemoryWikiVault } from "./vault.js";
|
||||
import { listMemoryWikiOverview } from "./wiki-overview.js";
|
||||
|
||||
const READ_SCOPE = "operator.read" as const;
|
||||
const WRITE_SCOPE = "operator.write" as const;
|
||||
@@ -175,13 +175,16 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
{ scope: READ_SCOPE },
|
||||
);
|
||||
|
||||
// Renamed from wiki.palace without an alias by maintainer decision: the method was
|
||||
// undocumented, its only known consumer is the version-locked Control UI, and stale
|
||||
// callers get an explicit unknown-method error rather than a silent failure.
|
||||
api.registerGatewayMethod(
|
||||
"wiki.palace",
|
||||
"wiki.overview",
|
||||
async ({ params: requestParams, respond }) => {
|
||||
try {
|
||||
const { appConfig, config } = resolveRequestContext(requestParams);
|
||||
await syncImportedSourcesIfNeeded(config, appConfig);
|
||||
respond(true, await listMemoryWikiPalace(config));
|
||||
respond(true, await listMemoryWikiOverview(config));
|
||||
} catch (error) {
|
||||
respondError(respond, error);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Memory Wiki tests cover memory palace plugin behavior.
|
||||
// Memory Wiki tests cover the wiki overview plugin behavior.
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { renderWikiMarkdown } from "./markdown.js";
|
||||
import { listMemoryWikiPalace } from "./memory-palace.js";
|
||||
import { createMemoryWikiTestHarness } from "./test-helpers.js";
|
||||
import { listMemoryWikiOverview } from "./wiki-overview.js";
|
||||
|
||||
const { createVault } = createMemoryWikiTestHarness();
|
||||
|
||||
describe("listMemoryWikiPalace", () => {
|
||||
describe("listMemoryWikiOverview", () => {
|
||||
it("groups wiki pages by kind and surfaces claims, questions, and contradictions", async () => {
|
||||
const { rootDir, config } = await createVault({
|
||||
prefix: "memory-wiki-palace-",
|
||||
prefix: "memory-wiki-overview-",
|
||||
initialize: true,
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ describe("listMemoryWikiPalace", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const result = await listMemoryWikiPalace(config);
|
||||
const result = await listMemoryWikiOverview(config);
|
||||
|
||||
expect(result.totalItems).toBe(2);
|
||||
expect(result.totalPages).toBe(3);
|
||||
@@ -1,11 +1,11 @@
|
||||
// Memory Wiki plugin module implements memory palace behavior.
|
||||
// Memory Wiki plugin module implements the memory wiki overview.
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import { parseWikiMarkdown, type WikiPageKind } from "./markdown.js";
|
||||
import { readQueryableWikiPages } from "./query.js";
|
||||
|
||||
const PALACE_KIND_ORDER: WikiPageKind[] = ["synthesis", "entity", "concept", "source", "report"];
|
||||
const PRIMARY_PALACE_KINDS = new Set<WikiPageKind>(["synthesis", "entity", "concept"]);
|
||||
const PALACE_KIND_LABELS: Record<WikiPageKind, string> = {
|
||||
const OVERVIEW_KIND_ORDER: WikiPageKind[] = ["synthesis", "entity", "concept", "source", "report"];
|
||||
const PRIMARY_OVERVIEW_KINDS = new Set<WikiPageKind>(["synthesis", "entity", "concept"]);
|
||||
const OVERVIEW_KIND_LABELS: Record<WikiPageKind, string> = {
|
||||
synthesis: "Syntheses",
|
||||
entity: "Entities",
|
||||
concept: "Concepts",
|
||||
@@ -13,7 +13,7 @@ const PALACE_KIND_LABELS: Record<WikiPageKind, string> = {
|
||||
report: "Reports",
|
||||
};
|
||||
|
||||
type MemoryWikiPalaceItem = {
|
||||
type MemoryWikiOverviewItem = {
|
||||
pagePath: string;
|
||||
title: string;
|
||||
kind: WikiPageKind;
|
||||
@@ -29,7 +29,7 @@ type MemoryWikiPalaceItem = {
|
||||
snippet?: string;
|
||||
};
|
||||
|
||||
type MemoryWikiPalaceCluster = {
|
||||
type MemoryWikiOverviewCluster = {
|
||||
key: WikiPageKind;
|
||||
label: string;
|
||||
itemCount: number;
|
||||
@@ -37,22 +37,22 @@ type MemoryWikiPalaceCluster = {
|
||||
questionCount: number;
|
||||
contradictionCount: number;
|
||||
updatedAt?: string;
|
||||
items: MemoryWikiPalaceItem[];
|
||||
items: MemoryWikiOverviewItem[];
|
||||
};
|
||||
|
||||
type MemoryWikiPalacePageCounts = Record<WikiPageKind, number>;
|
||||
type MemoryWikiOverviewPageCounts = Record<WikiPageKind, number>;
|
||||
|
||||
type MemoryWikiPalaceStatus = {
|
||||
type MemoryWikiOverviewStatus = {
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
pageCounts: MemoryWikiPalacePageCounts;
|
||||
pageCounts: MemoryWikiOverviewPageCounts;
|
||||
totalClaims: number;
|
||||
totalQuestions: number;
|
||||
totalContradictions: number;
|
||||
clusters: MemoryWikiPalaceCluster[];
|
||||
clusters: MemoryWikiOverviewCluster[];
|
||||
};
|
||||
|
||||
function createEmptyPalacePageCounts(): MemoryWikiPalacePageCounts {
|
||||
function createEmptyOverviewPageCounts(): MemoryWikiOverviewPageCounts {
|
||||
return {
|
||||
synthesis: 0,
|
||||
entity: 0,
|
||||
@@ -88,7 +88,7 @@ function extractSnippet(body: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function comparePalaceItems(left: MemoryWikiPalaceItem, right: MemoryWikiPalaceItem): number {
|
||||
function compareOverviewItems(left: MemoryWikiOverviewItem, right: MemoryWikiOverviewItem): number {
|
||||
const leftKey = left.updatedAt ?? "";
|
||||
const rightKey = right.updatedAt ?? "";
|
||||
if (rightKey !== leftKey) {
|
||||
@@ -100,14 +100,14 @@ function comparePalaceItems(left: MemoryWikiPalaceItem, right: MemoryWikiPalaceI
|
||||
return left.title.localeCompare(right.title);
|
||||
}
|
||||
|
||||
export async function listMemoryWikiPalace(
|
||||
export async function listMemoryWikiOverview(
|
||||
config: ResolvedMemoryWikiConfig,
|
||||
): Promise<MemoryWikiPalaceStatus> {
|
||||
): Promise<MemoryWikiOverviewStatus> {
|
||||
const pages = await readQueryableWikiPages(config.vault.path);
|
||||
const pageCounts = pages.reduce<MemoryWikiPalacePageCounts>((counts, page) => {
|
||||
const pageCounts = pages.reduce<MemoryWikiOverviewPageCounts>((counts, page) => {
|
||||
counts[page.kind] += 1;
|
||||
return counts;
|
||||
}, createEmptyPalacePageCounts());
|
||||
}, createEmptyOverviewPageCounts());
|
||||
const totalClaims = pages.reduce((sum, page) => sum + page.claims.length, 0);
|
||||
const totalQuestions = pages.reduce((sum, page) => sum + page.questions.length, 0);
|
||||
const totalContradictions = pages.reduce((sum, page) => sum + page.contradictions.length, 0);
|
||||
@@ -130,18 +130,18 @@ export async function listMemoryWikiPalace(
|
||||
contradictions: page.contradictions.slice(0, 3),
|
||||
},
|
||||
extractSnippet(parsed.body) ? { snippet: extractSnippet(parsed.body) } : {},
|
||||
) satisfies MemoryWikiPalaceItem;
|
||||
) satisfies MemoryWikiOverviewItem;
|
||||
})
|
||||
.filter(
|
||||
(item) =>
|
||||
PRIMARY_PALACE_KINDS.has(item.kind) ||
|
||||
PRIMARY_OVERVIEW_KINDS.has(item.kind) ||
|
||||
item.claimCount > 0 ||
|
||||
item.questionCount > 0 ||
|
||||
item.contradictionCount > 0,
|
||||
)
|
||||
.toSorted(comparePalaceItems);
|
||||
.toSorted(compareOverviewItems);
|
||||
|
||||
const clusters = PALACE_KIND_ORDER.map((kind) => {
|
||||
const clusters = OVERVIEW_KIND_ORDER.map((kind) => {
|
||||
const clusterItems = items.filter((item) => item.kind === kind);
|
||||
if (clusterItems.length === 0) {
|
||||
return null;
|
||||
@@ -149,7 +149,7 @@ export async function listMemoryWikiPalace(
|
||||
return Object.assign(
|
||||
{
|
||||
key: kind,
|
||||
label: PALACE_KIND_LABELS[kind],
|
||||
label: OVERVIEW_KIND_LABELS[kind],
|
||||
itemCount: clusterItems.length,
|
||||
claimCount: clusterItems.reduce((sum, item) => sum + item.claimCount, 0),
|
||||
questionCount: clusterItems.reduce((sum, item) => sum + item.questionCount, 0),
|
||||
@@ -157,8 +157,8 @@ export async function listMemoryWikiPalace(
|
||||
},
|
||||
clusterItems[0]?.updatedAt ? { updatedAt: clusterItems[0].updatedAt } : {},
|
||||
{ items: clusterItems },
|
||||
) satisfies MemoryWikiPalaceCluster;
|
||||
}).filter((entry): entry is MemoryWikiPalaceCluster => entry !== null);
|
||||
) satisfies MemoryWikiOverviewCluster;
|
||||
}).filter((entry): entry is MemoryWikiOverviewCluster => entry !== null);
|
||||
|
||||
return {
|
||||
totalItems: items.length,
|
||||
@@ -97,7 +97,7 @@ describeControlUiE2e("Control UI Agents channel status", () => {
|
||||
try {
|
||||
const response = await page.goto(`${server.baseUrl}agents`);
|
||||
expect(response?.status()).toBe(200);
|
||||
await page.getByRole("button", { name: /^Channels/ }).click();
|
||||
await page.getByRole("tab", { name: /^Channels/ }).click();
|
||||
|
||||
const discordRow = page.locator(".settings-row").filter({ hasText: "Discord" });
|
||||
await expect.poll(() => discordRow.count()).toBe(1);
|
||||
|
||||
@@ -3718,12 +3718,12 @@ export const en: TranslationMap = {
|
||||
loadingPage: "Loading wiki page…",
|
||||
dreamsTab: "Dreams",
|
||||
insightsTab: "Imported Insights",
|
||||
palaceTab: "Memory Palace",
|
||||
wikiTab: "Memory Wiki",
|
||||
dreamsExplainer:
|
||||
"This is the raw dream diary the system writes while replaying and consolidating memory; use it to inspect what the memory system is noticing, and where it still looks noisy or thin.",
|
||||
insightsExplainer:
|
||||
"These are imported insights clustered from external history; use them to review what imports surfaced before any of it graduates into durable memory.",
|
||||
palaceExplainer:
|
||||
wikiExplainer:
|
||||
"This is the compiled memory wiki surface the system can search and reason over; use it to inspect actual memory pages, claims, open questions, and contradictions rather than raw imported source chats.",
|
||||
copyArchivePath: "Copy archive path",
|
||||
loadingInsights: "Loading imported insights…",
|
||||
@@ -3739,9 +3739,9 @@ export const en: TranslationMap = {
|
||||
riskReasons: "Risk reasons:",
|
||||
labels: "Labels:",
|
||||
openSourcePage: "Open source page",
|
||||
loadingPalace: "Loading memory palace…",
|
||||
emptyPalace: "Memory palace is not populated yet",
|
||||
emptyPalaceHint:
|
||||
loadingWiki: "Loading memory wiki…",
|
||||
emptyWiki: "Memory wiki is not populated yet",
|
||||
emptyWikiHint:
|
||||
"Right now the wiki mostly has raw source imports and operational reports. This tab becomes useful once syntheses, entities, or concepts start getting written.",
|
||||
claims: "Claims",
|
||||
openQuestions: "Open questions",
|
||||
@@ -3751,7 +3751,7 @@ export const en: TranslationMap = {
|
||||
id: "Id:",
|
||||
openWikiPage: "Open wiki page",
|
||||
unavailable: "Memory Wiki is not enabled",
|
||||
unavailablePluginPrefix: "Imported Insights and Memory Palace are provided by the bundled",
|
||||
unavailablePluginPrefix: "Imported Insights and Memory Wiki are provided by the bundled",
|
||||
unavailablePluginSuffix: "plugin.",
|
||||
enablePrefix: "Enable",
|
||||
enableSuffix: ", then reload this tab.",
|
||||
@@ -3817,7 +3817,7 @@ export const en: TranslationMap = {
|
||||
tidyingKnowledgeGraph: "tidying the knowledge graph…",
|
||||
replayingConversations: "replaying today's conversations…",
|
||||
weavingShortTerm: "weaving short-term into long-term…",
|
||||
defragmentingMindPalace: "defragmenting the mind palace…",
|
||||
defragmentingMemoryLane: "defragmenting memory lane…",
|
||||
filingLooseThoughts: "filing away loose thoughts…",
|
||||
connectingDots: "connecting distant dots…",
|
||||
compostingContext: "composting old context windows…",
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
loadDreamDiary,
|
||||
loadDreamingStatus,
|
||||
loadWikiImportInsights,
|
||||
loadWikiMemoryPalace,
|
||||
loadWikiOverview,
|
||||
repairDreamingArtifacts,
|
||||
resetGroundedShortTerm,
|
||||
resetDreamDiary,
|
||||
@@ -580,13 +580,13 @@ describe("dreaming controller", () => {
|
||||
expect(state.wikiImportInsightsLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("loads and normalizes the wiki memory palace", async () => {
|
||||
it("loads and normalizes the wiki wiki overview", async () => {
|
||||
const { state, request } = createState();
|
||||
state.hello = {
|
||||
type: "hello-ok",
|
||||
protocol: 4,
|
||||
auth: { role: "operator", scopes: [] },
|
||||
features: { methods: ["wiki.palace"] },
|
||||
features: { methods: ["wiki.overview"] },
|
||||
};
|
||||
state.configSnapshot = {
|
||||
hash: "hash-1",
|
||||
@@ -639,43 +639,41 @@ describe("dreaming controller", () => {
|
||||
],
|
||||
});
|
||||
|
||||
await loadWikiMemoryPalace(state);
|
||||
await loadWikiOverview(state);
|
||||
|
||||
expect(request).toHaveBeenCalledWith("wiki.palace", {});
|
||||
expect(state.wikiMemoryPalace?.totalItems).toBe(1);
|
||||
expect(state.wikiMemoryPalace?.totalPages).toBe(2);
|
||||
expect(state.wikiMemoryPalace?.pageCounts.source).toBe(1);
|
||||
expect(state.wikiMemoryPalace?.pageCounts.synthesis).toBe(1);
|
||||
expect(state.wikiMemoryPalace?.totalClaims).toBe(2);
|
||||
expect(state.wikiMemoryPalace?.clusters).toHaveLength(1);
|
||||
expect(state.wikiMemoryPalace?.clusters[0]?.key).toBe("synthesis");
|
||||
expect(state.wikiMemoryPalace?.clusters[0]?.label).toBe("Syntheses");
|
||||
expect(state.wikiMemoryPalace?.clusters[0]?.items).toHaveLength(1);
|
||||
expect(state.wikiMemoryPalace?.clusters[0]?.items[0]?.title).toBe("Travel system");
|
||||
expect(state.wikiMemoryPalace?.clusters[0]?.items[0]?.claims).toEqual([
|
||||
"prefers direct receipts",
|
||||
]);
|
||||
expect(state.wikiMemoryPalaceError).toBeNull();
|
||||
expect(state.wikiMemoryPalaceLoading).toBe(false);
|
||||
expect(request).toHaveBeenCalledWith("wiki.overview", {});
|
||||
expect(state.wikiOverview?.totalItems).toBe(1);
|
||||
expect(state.wikiOverview?.totalPages).toBe(2);
|
||||
expect(state.wikiOverview?.pageCounts.source).toBe(1);
|
||||
expect(state.wikiOverview?.pageCounts.synthesis).toBe(1);
|
||||
expect(state.wikiOverview?.totalClaims).toBe(2);
|
||||
expect(state.wikiOverview?.clusters).toHaveLength(1);
|
||||
expect(state.wikiOverview?.clusters[0]?.key).toBe("synthesis");
|
||||
expect(state.wikiOverview?.clusters[0]?.label).toBe("Syntheses");
|
||||
expect(state.wikiOverview?.clusters[0]?.items).toHaveLength(1);
|
||||
expect(state.wikiOverview?.clusters[0]?.items[0]?.title).toBe("Travel system");
|
||||
expect(state.wikiOverview?.clusters[0]?.items[0]?.claims).toEqual(["prefers direct receipts"]);
|
||||
expect(state.wikiOverviewError).toBeNull();
|
||||
expect(state.wikiOverviewLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("loads the wiki memory palace for the selected agent", async () => {
|
||||
it("loads the wiki wiki overview for the selected agent", async () => {
|
||||
const { state, request } = createState();
|
||||
state.selectedAgentId = "marketing";
|
||||
state.hello = {
|
||||
type: "hello-ok",
|
||||
protocol: 4,
|
||||
auth: { role: "operator", scopes: [] },
|
||||
features: { methods: ["wiki.palace"] },
|
||||
features: { methods: ["wiki.overview"] },
|
||||
};
|
||||
request.mockResolvedValue({ totalItems: 1, clusters: [] });
|
||||
|
||||
await loadWikiMemoryPalace(state);
|
||||
await loadWikiOverview(state);
|
||||
|
||||
expect(request).toHaveBeenCalledWith("wiki.palace", { agentId: "marketing" });
|
||||
expect(request).toHaveBeenCalledWith("wiki.overview", { agentId: "marketing" });
|
||||
});
|
||||
|
||||
it("starts a new selected-agent palace load and ignores stale completions", async () => {
|
||||
it("starts a new selected-agent wiki overview load and ignores stale completions", async () => {
|
||||
const { state, request } = createState();
|
||||
const agentA = createDeferred<unknown>();
|
||||
const agentB = createDeferred<unknown>();
|
||||
@@ -683,7 +681,7 @@ describe("dreaming controller", () => {
|
||||
type: "hello-ok",
|
||||
protocol: 4,
|
||||
auth: { role: "operator", scopes: [] },
|
||||
features: { methods: ["wiki.palace"] },
|
||||
features: { methods: ["wiki.overview"] },
|
||||
};
|
||||
request.mockImplementation(async (_method: string, payload?: unknown) => {
|
||||
const agentId =
|
||||
@@ -694,29 +692,29 @@ describe("dreaming controller", () => {
|
||||
});
|
||||
|
||||
state.selectedAgentId = "agent-a";
|
||||
const firstLoad = loadWikiMemoryPalace(state);
|
||||
const firstLoad = loadWikiOverview(state);
|
||||
state.selectedAgentId = "agent-b";
|
||||
const secondLoad = loadWikiMemoryPalace(state);
|
||||
const secondLoad = loadWikiOverview(state);
|
||||
|
||||
agentB.resolve({ totalItems: 2, clusters: [] });
|
||||
await secondLoad;
|
||||
agentA.resolve({ totalItems: 1, clusters: [] });
|
||||
await firstLoad;
|
||||
|
||||
expect(request).toHaveBeenCalledWith("wiki.palace", { agentId: "agent-a" });
|
||||
expect(request).toHaveBeenCalledWith("wiki.palace", { agentId: "agent-b" });
|
||||
expect(state.wikiMemoryPalace?.totalItems).toBe(2);
|
||||
expect(state.wikiMemoryPalaceLoading).toBe(false);
|
||||
expect(state.wikiMemoryPalaceError).toBeNull();
|
||||
expect(request).toHaveBeenCalledWith("wiki.overview", { agentId: "agent-a" });
|
||||
expect(request).toHaveBeenCalledWith("wiki.overview", { agentId: "agent-b" });
|
||||
expect(state.wikiOverview?.totalItems).toBe(2);
|
||||
expect(state.wikiOverviewLoading).toBe(false);
|
||||
expect(state.wikiOverviewError).toBeNull();
|
||||
});
|
||||
|
||||
it("derives legacy wiki memory palace page counts from clusters", async () => {
|
||||
it("derives legacy wiki wiki overview page counts from clusters", async () => {
|
||||
const { state, request } = createState();
|
||||
state.hello = {
|
||||
type: "hello-ok",
|
||||
protocol: 4,
|
||||
auth: { role: "operator", scopes: [] },
|
||||
features: { methods: ["wiki.palace"] },
|
||||
features: { methods: ["wiki.overview"] },
|
||||
};
|
||||
state.configSnapshot = {
|
||||
hash: "hash-1",
|
||||
@@ -748,10 +746,10 @@ describe("dreaming controller", () => {
|
||||
],
|
||||
});
|
||||
|
||||
await loadWikiMemoryPalace(state);
|
||||
await loadWikiOverview(state);
|
||||
|
||||
expect(state.wikiMemoryPalace?.totalPages).toBe(1);
|
||||
expect(state.wikiMemoryPalace?.pageCounts).toEqual({
|
||||
expect(state.wikiOverview?.totalPages).toBe(1);
|
||||
expect(state.wikiOverview?.pageCounts).toEqual({
|
||||
synthesis: 1,
|
||||
entity: 0,
|
||||
concept: 0,
|
||||
@@ -760,7 +758,7 @@ describe("dreaming controller", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to config gating for wiki memory palace when methods are not advertised", async () => {
|
||||
it("falls back to config gating for wiki wiki overview when methods are not advertised", async () => {
|
||||
const { state, request } = createState();
|
||||
state.configSnapshot = {
|
||||
hash: "hash-1",
|
||||
@@ -782,24 +780,24 @@ describe("dreaming controller", () => {
|
||||
clusters: [],
|
||||
});
|
||||
|
||||
await loadWikiMemoryPalace(state);
|
||||
await loadWikiOverview(state);
|
||||
|
||||
expect(request).toHaveBeenCalledWith("wiki.palace", {});
|
||||
expect(state.wikiMemoryPalace?.totalItems).toBe(1);
|
||||
expect(state.wikiMemoryPalace?.totalPages).toBe(1);
|
||||
expect(state.wikiMemoryPalace?.pageCounts).toEqual({
|
||||
expect(request).toHaveBeenCalledWith("wiki.overview", {});
|
||||
expect(state.wikiOverview?.totalItems).toBe(1);
|
||||
expect(state.wikiOverview?.totalPages).toBe(1);
|
||||
expect(state.wikiOverview?.pageCounts).toEqual({
|
||||
synthesis: 0,
|
||||
entity: 0,
|
||||
concept: 0,
|
||||
source: 0,
|
||||
report: 0,
|
||||
});
|
||||
expect(state.wikiMemoryPalace?.totalClaims).toBe(2);
|
||||
expect(state.wikiMemoryPalaceError).toBeNull();
|
||||
expect(state.wikiMemoryPalaceLoading).toBe(false);
|
||||
expect(state.wikiOverview?.totalClaims).toBe(2);
|
||||
expect(state.wikiOverviewError).toBeNull();
|
||||
expect(state.wikiOverviewLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("skips wiki memory palace when memory-wiki is not enabled", async () => {
|
||||
it("skips wiki wiki overview when memory-wiki is not enabled", async () => {
|
||||
const { state, request } = createState();
|
||||
state.configSnapshot = {
|
||||
hash: "hash-1",
|
||||
@@ -807,7 +805,7 @@ describe("dreaming controller", () => {
|
||||
plugins: {},
|
||||
},
|
||||
};
|
||||
state.wikiMemoryPalace = {
|
||||
state.wikiOverview = {
|
||||
totalItems: 1,
|
||||
totalPages: 1,
|
||||
pageCounts: {
|
||||
@@ -822,17 +820,17 @@ describe("dreaming controller", () => {
|
||||
totalContradictions: 0,
|
||||
clusters: [],
|
||||
};
|
||||
state.wikiMemoryPalaceError = "unknown method: wiki.palace";
|
||||
state.wikiOverviewError = "unknown method: wiki.overview";
|
||||
|
||||
await loadWikiMemoryPalace(state);
|
||||
await loadWikiOverview(state);
|
||||
|
||||
expect(request).not.toHaveBeenCalled();
|
||||
expect(state.wikiMemoryPalace).toBeNull();
|
||||
expect(state.wikiMemoryPalaceError).toBeNull();
|
||||
expect(state.wikiMemoryPalaceLoading).toBe(false);
|
||||
expect(state.wikiOverview).toBeNull();
|
||||
expect(state.wikiOverviewError).toBeNull();
|
||||
expect(state.wikiOverviewLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("skips wiki memory palace when the gateway does not advertise the method", async () => {
|
||||
it("skips wiki wiki overview when the gateway does not advertise the method", async () => {
|
||||
const { state, request } = createState();
|
||||
state.hello = {
|
||||
type: "hello-ok",
|
||||
@@ -852,7 +850,7 @@ describe("dreaming controller", () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
state.wikiMemoryPalace = {
|
||||
state.wikiOverview = {
|
||||
totalItems: 1,
|
||||
totalPages: 1,
|
||||
pageCounts: {
|
||||
@@ -867,14 +865,14 @@ describe("dreaming controller", () => {
|
||||
totalContradictions: 0,
|
||||
clusters: [],
|
||||
};
|
||||
state.wikiMemoryPalaceError = "unknown method: wiki.palace";
|
||||
state.wikiOverviewError = "unknown method: wiki.overview";
|
||||
|
||||
await loadWikiMemoryPalace(state);
|
||||
await loadWikiOverview(state);
|
||||
|
||||
expect(request).not.toHaveBeenCalled();
|
||||
expect(state.wikiMemoryPalace).toBeNull();
|
||||
expect(state.wikiMemoryPalaceError).toBeNull();
|
||||
expect(state.wikiMemoryPalaceLoading).toBe(false);
|
||||
expect(state.wikiOverview).toBeNull();
|
||||
expect(state.wikiOverviewError).toBeNull();
|
||||
expect(state.wikiOverviewLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("patches config to update global dreaming enablement", async () => {
|
||||
|
||||
@@ -127,7 +127,7 @@ export type WikiImportInsights = {
|
||||
clusters: WikiImportInsightCluster[];
|
||||
};
|
||||
|
||||
type WikiMemoryPalaceItem = {
|
||||
type WikiOverviewItem = {
|
||||
pagePath: string;
|
||||
title: string;
|
||||
kind: "entity" | "concept" | "source" | "synthesis" | "report";
|
||||
@@ -143,27 +143,27 @@ type WikiMemoryPalaceItem = {
|
||||
snippet?: string;
|
||||
};
|
||||
|
||||
type WikiMemoryPalaceCluster = {
|
||||
key: WikiMemoryPalaceItem["kind"];
|
||||
type WikiOverviewCluster = {
|
||||
key: WikiOverviewItem["kind"];
|
||||
label: string;
|
||||
itemCount: number;
|
||||
claimCount: number;
|
||||
questionCount: number;
|
||||
contradictionCount: number;
|
||||
updatedAt?: string;
|
||||
items: WikiMemoryPalaceItem[];
|
||||
items: WikiOverviewItem[];
|
||||
};
|
||||
|
||||
type WikiMemoryPalacePageCounts = Record<WikiMemoryPalaceItem["kind"], number>;
|
||||
type WikiOverviewPageCounts = Record<WikiOverviewItem["kind"], number>;
|
||||
|
||||
export type WikiMemoryPalace = {
|
||||
export type WikiOverview = {
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
pageCounts: WikiMemoryPalacePageCounts;
|
||||
pageCounts: WikiOverviewPageCounts;
|
||||
totalClaims: number;
|
||||
totalQuestions: number;
|
||||
totalContradictions: number;
|
||||
clusters: WikiMemoryPalaceCluster[];
|
||||
clusters: WikiOverviewCluster[];
|
||||
};
|
||||
|
||||
type DoctorMemoryStatusPayload = {
|
||||
@@ -199,7 +199,7 @@ type WikiImportInsightsPayload = {
|
||||
clusters?: unknown;
|
||||
};
|
||||
|
||||
type WikiMemoryPalacePayload = {
|
||||
type WikiOverviewPayload = {
|
||||
totalItems?: unknown;
|
||||
totalPages?: unknown;
|
||||
pageCounts?: unknown;
|
||||
@@ -244,13 +244,13 @@ export type DreamingState = {
|
||||
wikiImportInsightsLoading: boolean;
|
||||
wikiImportInsightsError: string | null;
|
||||
wikiImportInsights: WikiImportInsights | null;
|
||||
wikiMemoryPalaceRequestAgentId?: string | null;
|
||||
wikiMemoryPalaceRequestGeneration?: number;
|
||||
wikiMemoryPalaceActiveRequestGeneration?: number | null;
|
||||
wikiMemoryPalaceAgentId?: string | null;
|
||||
wikiMemoryPalaceLoading: boolean;
|
||||
wikiMemoryPalaceError: string | null;
|
||||
wikiMemoryPalace: WikiMemoryPalace | null;
|
||||
wikiOverviewRequestAgentId?: string | null;
|
||||
wikiOverviewRequestGeneration?: number;
|
||||
wikiOverviewActiveRequestGeneration?: number | null;
|
||||
wikiOverviewAgentId?: string | null;
|
||||
wikiOverviewLoading: boolean;
|
||||
wikiOverviewError: string | null;
|
||||
wikiOverview: WikiOverview | null;
|
||||
lastError: string | null;
|
||||
};
|
||||
|
||||
@@ -283,9 +283,9 @@ export function createDreamingState(
|
||||
wikiImportInsightsLoading: false,
|
||||
wikiImportInsightsError: null,
|
||||
wikiImportInsights: null,
|
||||
wikiMemoryPalaceLoading: false,
|
||||
wikiMemoryPalaceError: null,
|
||||
wikiMemoryPalace: null,
|
||||
wikiOverviewLoading: false,
|
||||
wikiOverviewError: null,
|
||||
wikiOverview: null,
|
||||
lastError: null,
|
||||
};
|
||||
}
|
||||
@@ -636,7 +636,7 @@ function normalizeWikiImportInsights(raw: unknown): WikiImportInsights {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeWikiPageKind(value: unknown): WikiMemoryPalaceItem["kind"] | undefined {
|
||||
function normalizeWikiPageKind(value: unknown): WikiOverviewItem["kind"] | undefined {
|
||||
return value === "entity" ||
|
||||
value === "concept" ||
|
||||
value === "source" ||
|
||||
@@ -646,7 +646,7 @@ function normalizeWikiPageKind(value: unknown): WikiMemoryPalaceItem["kind"] | u
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function createEmptyWikiMemoryPalacePageCounts(): WikiMemoryPalacePageCounts {
|
||||
function createEmptyWikiOverviewPageCounts(): WikiOverviewPageCounts {
|
||||
return {
|
||||
synthesis: 0,
|
||||
entity: 0,
|
||||
@@ -656,10 +656,10 @@ function createEmptyWikiMemoryPalacePageCounts(): WikiMemoryPalacePageCounts {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeWikiMemoryPalacePageCounts(
|
||||
function normalizeWikiOverviewPageCounts(
|
||||
raw: unknown,
|
||||
fallback: WikiMemoryPalacePageCounts,
|
||||
): WikiMemoryPalacePageCounts {
|
||||
fallback: WikiOverviewPageCounts,
|
||||
): WikiOverviewPageCounts {
|
||||
const record = asRecord(raw);
|
||||
return {
|
||||
synthesis: normalizeFiniteInt(record?.synthesis, fallback.synthesis),
|
||||
@@ -670,7 +670,7 @@ function normalizeWikiMemoryPalacePageCounts(
|
||||
};
|
||||
}
|
||||
|
||||
function sumWikiMemoryPalacePageCounts(pageCounts: WikiMemoryPalacePageCounts): number {
|
||||
function sumWikiOverviewPageCounts(pageCounts: WikiOverviewPageCounts): number {
|
||||
return (
|
||||
pageCounts.synthesis +
|
||||
pageCounts.entity +
|
||||
@@ -680,7 +680,7 @@ function sumWikiMemoryPalacePageCounts(pageCounts: WikiMemoryPalacePageCounts):
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeWikiMemoryPalaceItem(raw: unknown): WikiMemoryPalaceItem | null {
|
||||
function normalizeWikiOverviewItem(raw: unknown): WikiOverviewItem | null {
|
||||
const record = asRecord(raw);
|
||||
const pagePath = normalizeTrimmedString(record?.pagePath);
|
||||
const title = normalizeTrimmedString(record?.title);
|
||||
@@ -711,7 +711,7 @@ function normalizeWikiMemoryPalaceItem(raw: unknown): WikiMemoryPalaceItem | nul
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeWikiMemoryPalaceCluster(raw: unknown): WikiMemoryPalaceCluster | null {
|
||||
function normalizeWikiOverviewCluster(raw: unknown): WikiOverviewCluster | null {
|
||||
const record = asRecord(raw);
|
||||
const key = normalizeWikiPageKind(record?.key);
|
||||
const label = normalizeTrimmedString(record?.label);
|
||||
@@ -720,8 +720,8 @@ function normalizeWikiMemoryPalaceCluster(raw: unknown): WikiMemoryPalaceCluster
|
||||
}
|
||||
const items = Array.isArray(record?.items)
|
||||
? record.items
|
||||
.map((entry) => normalizeWikiMemoryPalaceItem(entry))
|
||||
.filter((entry): entry is WikiMemoryPalaceItem => entry !== null)
|
||||
.map((entry) => normalizeWikiOverviewItem(entry))
|
||||
.filter((entry): entry is WikiOverviewItem => entry !== null)
|
||||
: [];
|
||||
return {
|
||||
key,
|
||||
@@ -746,23 +746,23 @@ function normalizeWikiMemoryPalaceCluster(raw: unknown): WikiMemoryPalaceCluster
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeWikiMemoryPalace(raw: unknown): WikiMemoryPalace {
|
||||
function normalizeWikiOverview(raw: unknown): WikiOverview {
|
||||
const record = asRecord(raw);
|
||||
const clusters = Array.isArray(record?.clusters)
|
||||
? record.clusters
|
||||
.map((entry) => normalizeWikiMemoryPalaceCluster(entry))
|
||||
.filter((entry): entry is WikiMemoryPalaceCluster => entry !== null)
|
||||
.map((entry) => normalizeWikiOverviewCluster(entry))
|
||||
.filter((entry): entry is WikiOverviewCluster => entry !== null)
|
||||
: [];
|
||||
const totalItems = normalizeFiniteInt(
|
||||
record?.totalItems,
|
||||
clusters.reduce((sum, cluster) => sum + cluster.itemCount, 0),
|
||||
);
|
||||
const fallbackPageCounts = createEmptyWikiMemoryPalacePageCounts();
|
||||
const fallbackPageCounts = createEmptyWikiOverviewPageCounts();
|
||||
for (const cluster of clusters) {
|
||||
fallbackPageCounts[cluster.key] += cluster.itemCount;
|
||||
}
|
||||
const pageCounts = normalizeWikiMemoryPalacePageCounts(record?.pageCounts, fallbackPageCounts);
|
||||
const fallbackTotalPages = sumWikiMemoryPalacePageCounts(pageCounts) || totalItems;
|
||||
const pageCounts = normalizeWikiOverviewPageCounts(record?.pageCounts, fallbackPageCounts);
|
||||
const fallbackTotalPages = sumWikiOverviewPageCounts(pageCounts) || totalItems;
|
||||
return {
|
||||
totalItems,
|
||||
totalPages: normalizeFiniteInt(record?.totalPages, fallbackTotalPages),
|
||||
@@ -1022,58 +1022,58 @@ export async function loadWikiImportInsights(state: DreamingState): Promise<void
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadWikiMemoryPalace(state: DreamingState): Promise<void> {
|
||||
export async function loadWikiOverview(state: DreamingState): Promise<void> {
|
||||
if (!state.client || !state.connected) {
|
||||
return;
|
||||
}
|
||||
const agentId = resolveSelectedAgentId(state);
|
||||
if (state.wikiMemoryPalaceLoading && state.wikiMemoryPalaceRequestAgentId === agentId) {
|
||||
if (state.wikiOverviewLoading && state.wikiOverviewRequestAgentId === agentId) {
|
||||
return;
|
||||
}
|
||||
if (state.wikiMemoryPalaceAgentId !== agentId) {
|
||||
state.wikiMemoryPalace = null;
|
||||
if (state.wikiOverviewAgentId !== agentId) {
|
||||
state.wikiOverview = null;
|
||||
}
|
||||
if (!canCallMemoryWikiMethod(state, "wiki.palace")) {
|
||||
state.wikiMemoryPalaceActiveRequestGeneration = null;
|
||||
state.wikiMemoryPalaceRequestAgentId = null;
|
||||
state.wikiMemoryPalaceLoading = false;
|
||||
state.wikiMemoryPalace = null;
|
||||
state.wikiMemoryPalaceError = null;
|
||||
if (!canCallMemoryWikiMethod(state, "wiki.overview")) {
|
||||
state.wikiOverviewActiveRequestGeneration = null;
|
||||
state.wikiOverviewRequestAgentId = null;
|
||||
state.wikiOverviewLoading = false;
|
||||
state.wikiOverview = null;
|
||||
state.wikiOverviewError = null;
|
||||
return;
|
||||
}
|
||||
const requestGeneration = (state.wikiMemoryPalaceRequestGeneration ?? 0) + 1;
|
||||
state.wikiMemoryPalaceRequestGeneration = requestGeneration;
|
||||
state.wikiMemoryPalaceActiveRequestGeneration = requestGeneration;
|
||||
state.wikiMemoryPalaceRequestAgentId = agentId;
|
||||
state.wikiMemoryPalaceLoading = true;
|
||||
state.wikiMemoryPalaceError = null;
|
||||
const requestGeneration = (state.wikiOverviewRequestGeneration ?? 0) + 1;
|
||||
state.wikiOverviewRequestGeneration = requestGeneration;
|
||||
state.wikiOverviewActiveRequestGeneration = requestGeneration;
|
||||
state.wikiOverviewRequestAgentId = agentId;
|
||||
state.wikiOverviewLoading = true;
|
||||
state.wikiOverviewError = null;
|
||||
try {
|
||||
const payload = await state.client.request<WikiMemoryPalacePayload>(
|
||||
"wiki.palace",
|
||||
const payload = await state.client.request<WikiOverviewPayload>(
|
||||
"wiki.overview",
|
||||
buildSelectedAgentPayloadForAgentId(agentId),
|
||||
);
|
||||
if (
|
||||
state.wikiMemoryPalaceActiveRequestGeneration !== requestGeneration ||
|
||||
state.wikiMemoryPalaceRequestAgentId !== agentId ||
|
||||
state.wikiOverviewActiveRequestGeneration !== requestGeneration ||
|
||||
state.wikiOverviewRequestAgentId !== agentId ||
|
||||
resolveSelectedAgentId(state) !== agentId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
state.wikiMemoryPalace = normalizeWikiMemoryPalace(payload);
|
||||
state.wikiMemoryPalaceAgentId = agentId;
|
||||
state.wikiOverview = normalizeWikiOverview(payload);
|
||||
state.wikiOverviewAgentId = agentId;
|
||||
} catch (err) {
|
||||
if (
|
||||
state.wikiMemoryPalaceActiveRequestGeneration === requestGeneration &&
|
||||
state.wikiMemoryPalaceRequestAgentId === agentId &&
|
||||
state.wikiOverviewActiveRequestGeneration === requestGeneration &&
|
||||
state.wikiOverviewRequestAgentId === agentId &&
|
||||
resolveSelectedAgentId(state) === agentId
|
||||
) {
|
||||
state.wikiMemoryPalaceError = String(err);
|
||||
state.wikiOverviewError = String(err);
|
||||
}
|
||||
} finally {
|
||||
if (state.wikiMemoryPalaceActiveRequestGeneration === requestGeneration) {
|
||||
state.wikiMemoryPalaceLoading = false;
|
||||
state.wikiMemoryPalaceRequestAgentId = null;
|
||||
state.wikiMemoryPalaceActiveRequestGeneration = null;
|
||||
if (state.wikiOverviewActiveRequestGeneration === requestGeneration) {
|
||||
state.wikiOverviewLoading = false;
|
||||
state.wikiOverviewRequestAgentId = null;
|
||||
state.wikiOverviewActiveRequestGeneration = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
loadDreamDiary,
|
||||
loadDreamingStatus,
|
||||
loadWikiImportInsights,
|
||||
loadWikiMemoryPalace,
|
||||
loadWikiOverview,
|
||||
repairDreamingArtifacts,
|
||||
resetGroundedShortTerm,
|
||||
resetDreamDiary,
|
||||
@@ -298,7 +298,7 @@ class AgentMemoryPanel extends OpenClawLightDomElement {
|
||||
this.runDreamingTask(loadDreamingStatus, scope),
|
||||
this.runDreamingTask(loadDreamDiary, scope),
|
||||
this.runDreamingTask(loadWikiImportInsights, scope),
|
||||
this.runDreamingTask(loadWikiMemoryPalace, scope),
|
||||
this.runDreamingTask(loadWikiOverview, scope),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -470,13 +470,13 @@ class AgentMemoryPanel extends OpenClawLightDomElement {
|
||||
wikiImportInsightsLoading: dreaming.wikiImportInsightsLoading,
|
||||
wikiImportInsightsError: dreaming.wikiImportInsightsError,
|
||||
wikiImportInsights: dreaming.wikiImportInsights,
|
||||
wikiMemoryPalaceLoading: dreaming.wikiMemoryPalaceLoading,
|
||||
wikiMemoryPalaceError: dreaming.wikiMemoryPalaceError,
|
||||
wikiMemoryPalace: dreaming.wikiMemoryPalace,
|
||||
wikiOverviewLoading: dreaming.wikiOverviewLoading,
|
||||
wikiOverviewError: dreaming.wikiOverviewError,
|
||||
wikiOverview: dreaming.wikiOverview,
|
||||
onRefresh: () => void this.loadAll(true),
|
||||
onRefreshDiary: () => void this.runDreamingTask(loadDreamDiary),
|
||||
onRefreshImports: () => void this.refreshWikiData(loadWikiImportInsights),
|
||||
onRefreshMemoryPalace: () => void this.refreshWikiData(loadWikiMemoryPalace),
|
||||
onRefreshWikiOverview: () => void this.refreshWikiData(loadWikiOverview),
|
||||
onOpenConfig: () => void this.context.runtimeConfig.openFile(),
|
||||
onOpenWikiPage: (lookup) => this.openWikiPage(lookup),
|
||||
onBackfillDiary: () => void this.runDreamingTask(backfillDreamDiary),
|
||||
|
||||
@@ -231,9 +231,9 @@ function buildProps(overrides?: Partial<DreamingProps>): DreamingProps {
|
||||
},
|
||||
],
|
||||
},
|
||||
wikiMemoryPalaceLoading: false,
|
||||
wikiMemoryPalaceError: null,
|
||||
wikiMemoryPalace: {
|
||||
wikiOverviewLoading: false,
|
||||
wikiOverviewError: null,
|
||||
wikiOverview: {
|
||||
totalItems: 1,
|
||||
totalPages: 2,
|
||||
pageCounts: {
|
||||
@@ -278,7 +278,7 @@ function buildProps(overrides?: Partial<DreamingProps>): DreamingProps {
|
||||
onRefresh: () => {},
|
||||
onRefreshDiary: () => {},
|
||||
onRefreshImports: () => {},
|
||||
onRefreshMemoryPalace: () => {},
|
||||
onRefreshWikiOverview: () => {},
|
||||
onOpenConfig: () => {},
|
||||
onOpenWikiPage: async () => null,
|
||||
onBackfillDiary: () => {},
|
||||
@@ -430,12 +430,12 @@ describe("dreaming view", () => {
|
||||
expect(subtabs).toEqual([
|
||||
{ label: "Dreams", active: false },
|
||||
{ label: "Imported Insights", active: true },
|
||||
{ label: "Memory Palace", active: false },
|
||||
{ label: "Memory Wiki", active: false },
|
||||
]);
|
||||
container
|
||||
.querySelector("#dream-diary-tab-palace")
|
||||
.querySelector("#dream-diary-tab-wiki")
|
||||
?.dispatchEvent(new MouseEvent("click", { detail: 1, bubbles: true }));
|
||||
expect(viewState.activeDiarySubTab).toBe("palace");
|
||||
expect(viewState.activeDiarySubTab).toBe("wiki");
|
||||
expect(onViewStateChange).toHaveBeenCalledOnce();
|
||||
expect(compactText(container.querySelector(".dreams-diary__date"))).toBe(
|
||||
"Travel · 1 chats · 1 signals",
|
||||
@@ -550,9 +550,9 @@ describe("dreaming view", () => {
|
||||
setDreamSubTab("scene");
|
||||
});
|
||||
|
||||
it("renders the memory palace inside the diary tab", () => {
|
||||
it("renders the wiki overview inside the diary tab", () => {
|
||||
setDreamSubTab("diary");
|
||||
setDreamDiarySubTab("palace");
|
||||
setDreamDiarySubTab("wiki");
|
||||
const container = renderInto(buildProps());
|
||||
expect(compactText(container.querySelector(".dreams-diary__date"))).toBe(
|
||||
"Vault · 2 pages · 2 claim rows · 1 open question · 1 contradiction",
|
||||
@@ -583,15 +583,15 @@ describe("dreaming view", () => {
|
||||
setDreamSubTab("scene");
|
||||
});
|
||||
|
||||
it("keeps non-report memory palace card clicks on details", () => {
|
||||
it("keeps non-report wiki overview card clicks on details", () => {
|
||||
setDreamSubTab("diary");
|
||||
setDreamDiarySubTab("palace");
|
||||
setDreamDiarySubTab("wiki");
|
||||
const container = document.createElement("div");
|
||||
const rerender = () => render(renderDreaming(props), container);
|
||||
const props: DreamingProps = buildProps({ onViewStateChange: rerender });
|
||||
rerender();
|
||||
|
||||
const card = expectElement(container, "[data-palace-page='syntheses/travel-system.md']");
|
||||
const card = expectElement(container, "[data-wiki-page='syntheses/travel-system.md']");
|
||||
card.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
|
||||
expect(textItems(container, ".dreams-diary__insight-list strong")).toContain("Page details");
|
||||
@@ -599,9 +599,9 @@ describe("dreaming view", () => {
|
||||
setDreamSubTab("scene");
|
||||
});
|
||||
|
||||
it("opens report memory palace cards on primary click", async () => {
|
||||
it("opens report wiki overview cards on primary click", async () => {
|
||||
setDreamSubTab("diary");
|
||||
setDreamDiarySubTab("palace");
|
||||
setDreamDiarySubTab("wiki");
|
||||
const onOpenWikiPage = vi.fn().mockResolvedValue({
|
||||
title: "Weekly stock report",
|
||||
path: "reports/weekly-stock.md",
|
||||
@@ -614,7 +614,7 @@ describe("dreaming view", () => {
|
||||
const props: DreamingProps = buildProps({
|
||||
onOpenWikiPage,
|
||||
onViewStateChange: rerender,
|
||||
wikiMemoryPalace: {
|
||||
wikiOverview: {
|
||||
totalItems: 1,
|
||||
totalPages: 1,
|
||||
pageCounts: {
|
||||
@@ -656,7 +656,7 @@ describe("dreaming view", () => {
|
||||
});
|
||||
rerender();
|
||||
|
||||
const card = expectElement(container, "[data-palace-page='reports/weekly-stock.md']");
|
||||
const card = expectElement(container, "[data-wiki-page='reports/weekly-stock.md']");
|
||||
card.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
@@ -683,7 +683,7 @@ describe("dreaming view", () => {
|
||||
|
||||
it("shows a memory-wiki enablement CTA when wiki subtabs are selected but the plugin is disabled", () => {
|
||||
setDreamSubTab("diary");
|
||||
setDreamDiarySubTab("palace");
|
||||
setDreamDiarySubTab("wiki");
|
||||
const onOpenConfig = vi.fn();
|
||||
const container = renderInto(
|
||||
buildProps({
|
||||
@@ -697,7 +697,7 @@ describe("dreaming view", () => {
|
||||
expect(
|
||||
[...container.querySelectorAll(".dreams-diary__empty-hint")].map((node) => compactText(node)),
|
||||
).toEqual([
|
||||
"Imported Insights and Memory Palace are provided by the bundled memory-wiki plugin.",
|
||||
"Imported Insights and Memory Wiki are provided by the bundled memory-wiki plugin.",
|
||||
"Enable plugins.entries.memory-wiki.enabled = true, then reload this tab.",
|
||||
]);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import "../../../components/modal-dialog.ts";
|
||||
import { toSanitizedMarkdownHtml } from "../../../components/markdown.ts";
|
||||
import { t } from "../../../i18n/index.ts";
|
||||
import "../../../styles/dreams.css";
|
||||
import type { DreamingEntry, WikiImportInsights, WikiMemoryPalace } from "./dreaming.ts";
|
||||
import type { DreamingEntry, WikiImportInsights, WikiOverview } from "./dreaming.ts";
|
||||
|
||||
// ── Diary entry parser ─────────────────────────────────────────────────
|
||||
|
||||
@@ -130,13 +130,13 @@ type DreamingProps = {
|
||||
wikiImportInsightsLoading: boolean;
|
||||
wikiImportInsightsError: string | null;
|
||||
wikiImportInsights: WikiImportInsights | null;
|
||||
wikiMemoryPalaceLoading: boolean;
|
||||
wikiMemoryPalaceError: string | null;
|
||||
wikiMemoryPalace: WikiMemoryPalace | null;
|
||||
wikiOverviewLoading: boolean;
|
||||
wikiOverviewError: string | null;
|
||||
wikiOverview: WikiOverview | null;
|
||||
onRefresh: () => void;
|
||||
onRefreshDiary: () => void;
|
||||
onRefreshImports: () => void;
|
||||
onRefreshMemoryPalace: () => void;
|
||||
onRefreshWikiOverview: () => void;
|
||||
onOpenConfig: () => void;
|
||||
onOpenWikiPage: (lookup: string) => Promise<{
|
||||
title: string;
|
||||
@@ -160,7 +160,7 @@ const DREAM_PHRASE_KEYS = [
|
||||
"dreaming.phrases.tidyingKnowledgeGraph",
|
||||
"dreaming.phrases.replayingConversations",
|
||||
"dreaming.phrases.weavingShortTerm",
|
||||
"dreaming.phrases.defragmentingMindPalace",
|
||||
"dreaming.phrases.defragmentingMemoryLane",
|
||||
"dreaming.phrases.filingLooseThoughts",
|
||||
"dreaming.phrases.connectingDots",
|
||||
"dreaming.phrases.compostingContext",
|
||||
@@ -189,10 +189,10 @@ export type DreamingViewState = {
|
||||
dreamIndex: number;
|
||||
dreamLastSwap: number;
|
||||
activeSubTab: "scene" | "diary" | "advanced";
|
||||
activeDiarySubTab: "dreams" | "insights" | "palace";
|
||||
activeDiarySubTab: "dreams" | "insights" | "wiki";
|
||||
advancedWaitingSort: "recent" | "signals";
|
||||
expandedInsightCards: Set<string>;
|
||||
expandedPalaceCards: Set<string>;
|
||||
expandedWikiCards: Set<string>;
|
||||
diaryPage: number;
|
||||
wikiPreviewRequestId: number;
|
||||
wikiPreviewOpen: boolean;
|
||||
@@ -214,7 +214,7 @@ export function createDreamingViewState(): DreamingViewState {
|
||||
activeDiarySubTab: "dreams",
|
||||
advancedWaitingSort: "recent",
|
||||
expandedInsightCards: new Set(),
|
||||
expandedPalaceCards: new Set(),
|
||||
expandedWikiCards: new Set(),
|
||||
diaryPage: 0,
|
||||
wikiPreviewRequestId: 0,
|
||||
wikiPreviewOpen: false,
|
||||
@@ -516,7 +516,7 @@ function formatMessageCount(count: number): string {
|
||||
return t("dreaming.wiki.counts.messages", { count: String(count) });
|
||||
}
|
||||
|
||||
const MEMORY_PALACE_PAGE_COUNT_ORDER: Array<keyof WikiMemoryPalace["pageCounts"]> = [
|
||||
const WIKI_OVERVIEW_PAGE_COUNT_ORDER: Array<keyof WikiOverview["pageCounts"]> = [
|
||||
"source",
|
||||
"synthesis",
|
||||
"report",
|
||||
@@ -524,7 +524,7 @@ const MEMORY_PALACE_PAGE_COUNT_ORDER: Array<keyof WikiMemoryPalace["pageCounts"]
|
||||
"concept",
|
||||
];
|
||||
|
||||
function formatMemoryPalacePageCountLabel(kind: keyof WikiMemoryPalace["pageCounts"]): string {
|
||||
function formatWikiOverviewPageCountLabel(kind: keyof WikiOverview["pageCounts"]): string {
|
||||
switch (kind) {
|
||||
case "source":
|
||||
return t("dreaming.wiki.pageGroups.sources");
|
||||
@@ -540,12 +540,12 @@ function formatMemoryPalacePageCountLabel(kind: keyof WikiMemoryPalace["pageCoun
|
||||
return kind;
|
||||
}
|
||||
|
||||
function formatMemoryPalacePageBreakdown(pageCounts: WikiMemoryPalace["pageCounts"]): string {
|
||||
const parts = MEMORY_PALACE_PAGE_COUNT_ORDER.map((kind) => {
|
||||
function formatWikiOverviewPageBreakdown(pageCounts: WikiOverview["pageCounts"]): string {
|
||||
const parts = WIKI_OVERVIEW_PAGE_COUNT_ORDER.map((kind) => {
|
||||
const count = pageCounts[kind];
|
||||
return count > 0
|
||||
? t("dreaming.wiki.pageGroupSummary", {
|
||||
label: formatMemoryPalacePageCountLabel(kind),
|
||||
label: formatWikiOverviewPageCountLabel(kind),
|
||||
count: formatPageCount(count),
|
||||
})
|
||||
: null;
|
||||
@@ -553,7 +553,7 @@ function formatMemoryPalacePageBreakdown(pageCounts: WikiMemoryPalace["pageCount
|
||||
return parts.length > 0 ? parts.join("; ") : t("dreaming.wiki.noPagesYet");
|
||||
}
|
||||
|
||||
function formatMemoryPalaceClusterSummary(cluster: WikiMemoryPalace["clusters"][number]): string {
|
||||
function formatWikiOverviewClusterSummary(cluster: WikiOverview["clusters"][number]): string {
|
||||
const parts = [
|
||||
t("dreaming.wiki.sectionPageSummary", {
|
||||
label: cluster.label,
|
||||
@@ -730,8 +730,8 @@ function renderDiarySubtabExplainer(activeDiarySubTab: DreamingViewState["active
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.dreamsExplainer")}</p> `;
|
||||
case "insights":
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.insightsExplainer")}</p> `;
|
||||
case "palace":
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.palaceExplainer")}</p> `;
|
||||
case "wiki":
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.wikiExplainer")}</p> `;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
@@ -1242,15 +1242,15 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
function renderWikiOverviewSection(props: DreamingProps) {
|
||||
const state = props.viewState;
|
||||
const palace = props.wikiMemoryPalace;
|
||||
const clusters = palace?.clusters ?? [];
|
||||
const overview = props.wikiOverview;
|
||||
const clusters = overview?.clusters ?? [];
|
||||
|
||||
if (props.wikiMemoryPalaceLoading && clusters.length === 0) {
|
||||
if (props.wikiOverviewLoading && clusters.length === 0) {
|
||||
return html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.loadingPalace")}</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.loadingWiki")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1258,22 +1258,22 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
if (clusters.length === 0) {
|
||||
return html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.emptyPalace")}</div>
|
||||
<div class="dreams-diary__empty-hint">${t("dreaming.wiki.emptyPalaceHint")}</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.emptyWiki")}</div>
|
||||
<div class="dreams-diary__empty-hint">${t("dreaming.wiki.emptyWikiHint")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const clusterIndex = Math.max(0, Math.min(state.diaryPage, clusters.length - 1));
|
||||
const cluster = expectDefined(clusters[clusterIndex], "selected memory palace cluster");
|
||||
const totalPages = palace?.totalPages ?? palace?.totalItems ?? 0;
|
||||
const totalClaims = palace?.totalClaims ?? 0;
|
||||
const totalQuestions = palace?.totalQuestions ?? 0;
|
||||
const totalContradictions = palace?.totalContradictions ?? 0;
|
||||
const pageBreakdown = palace
|
||||
? formatMemoryPalacePageBreakdown(palace.pageCounts)
|
||||
const cluster = expectDefined(clusters[clusterIndex], "selected memory overview cluster");
|
||||
const totalPages = overview?.totalPages ?? overview?.totalItems ?? 0;
|
||||
const totalClaims = overview?.totalClaims ?? 0;
|
||||
const totalQuestions = overview?.totalQuestions ?? 0;
|
||||
const totalContradictions = overview?.totalContradictions ?? 0;
|
||||
const pageBreakdown = overview
|
||||
? formatWikiOverviewPageBreakdown(overview.pageCounts)
|
||||
: t("dreaming.wiki.noPagesYet");
|
||||
const clusterSummary = formatMemoryPalaceClusterSummary(cluster);
|
||||
const clusterSummary = formatWikiOverviewClusterSummary(cluster);
|
||||
const vaultMeta = [
|
||||
formatPageCount(totalPages),
|
||||
...(totalClaims > 0 ? [formatClaimRowCount(totalClaims)] : []),
|
||||
@@ -1300,7 +1300,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<article class="dreams-diary__entry" key="palace-${cluster.key}">
|
||||
<article class="dreams-diary__entry" key="wiki-${cluster.key}">
|
||||
<div class="dreams-diary__accent"></div>
|
||||
<div class="dreams-diary__date">${t("dreaming.wiki.vault")} · ${vaultMeta.join(" · ")}</div>
|
||||
<div class="dreams-diary__prose">
|
||||
@@ -1318,26 +1318,22 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
</div>
|
||||
<div class="dreams-diary__insights">
|
||||
${cluster.items.map((item) => {
|
||||
const expanded = state.expandedPalaceCards.has(item.pagePath);
|
||||
const expanded = state.expandedWikiCards.has(item.pagePath);
|
||||
return html`
|
||||
<article
|
||||
class="dreams-diary__insight-card dreams-diary__insight-card--clickable"
|
||||
data-palace-page=${item.pagePath}
|
||||
data-wiki-page=${item.pagePath}
|
||||
@click=${() => {
|
||||
if (item.kind === "report") {
|
||||
void openWikiPreview(item.pagePath, props);
|
||||
return;
|
||||
}
|
||||
toggleExpandedCard(
|
||||
state.expandedPalaceCards,
|
||||
item.pagePath,
|
||||
props.onViewStateChange,
|
||||
);
|
||||
toggleExpandedCard(state.expandedWikiCards, item.pagePath, props.onViewStateChange);
|
||||
}}
|
||||
>
|
||||
<div class="dreams-diary__insight-topline">
|
||||
<div class="dreams-diary__insight-title">${item.title}</div>
|
||||
<span class="dreams-diary__insight-badge dreams-diary__insight-badge--palace">
|
||||
<span class="dreams-diary__insight-badge dreams-diary__insight-badge--wiki">
|
||||
${formatKindLabel(item.kind)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -1403,7 +1399,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
@click=${(event: Event) => {
|
||||
event.stopPropagation();
|
||||
toggleExpandedCard(
|
||||
state.expandedPalaceCards,
|
||||
state.expandedWikiCards,
|
||||
item.pagePath,
|
||||
props.onViewStateChange,
|
||||
);
|
||||
@@ -1498,14 +1494,14 @@ function renderDreamDiaryEntries(props: DreamingProps) {
|
||||
function renderDiarySection(props: DreamingProps) {
|
||||
const state = props.viewState;
|
||||
const activeDiarySubTab = state.activeDiarySubTab;
|
||||
const wikiTabSelected = activeDiarySubTab === "insights" || activeDiarySubTab === "palace";
|
||||
const wikiTabSelected = activeDiarySubTab === "insights" || activeDiarySubTab === "wiki";
|
||||
const memoryWikiUnavailable = wikiTabSelected && !props.memoryWikiEnabled;
|
||||
const diaryError =
|
||||
activeDiarySubTab === "dreams"
|
||||
? props.dreamDiaryError
|
||||
: activeDiarySubTab === "insights"
|
||||
? props.wikiImportInsightsError
|
||||
: props.wikiMemoryPalaceError;
|
||||
: props.wikiOverviewError;
|
||||
if (diaryError && !memoryWikiUnavailable) {
|
||||
return html`
|
||||
<section class="dreams-diary">
|
||||
@@ -1525,7 +1521,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
tabs: [
|
||||
{ value: "dreams", label: t("dreaming.wiki.dreamsTab") },
|
||||
{ value: "insights", label: t("dreaming.wiki.insightsTab") },
|
||||
{ value: "palace", label: t("dreaming.wiki.palaceTab") },
|
||||
{ value: "wiki", label: t("dreaming.wiki.wikiTab") },
|
||||
],
|
||||
ariaLabel: t("dreaming.diary.title"),
|
||||
panelId: "dream-diary-panel",
|
||||
@@ -1546,7 +1542,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
? props.dreamDiaryLoading
|
||||
: activeDiarySubTab === "insights"
|
||||
? props.wikiImportInsightsLoading
|
||||
: props.wikiMemoryPalaceLoading)}
|
||||
: props.wikiOverviewLoading)}
|
||||
@click=${() => {
|
||||
state.diaryPage = 0;
|
||||
if (memoryWikiUnavailable) {
|
||||
@@ -1556,7 +1552,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
} else if (activeDiarySubTab === "insights") {
|
||||
props.onRefreshImports();
|
||||
} else {
|
||||
props.onRefreshMemoryPalace();
|
||||
props.onRefreshWikiOverview();
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1570,7 +1566,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
? props.wikiImportInsightsLoading
|
||||
? "Reloading…"
|
||||
: "Reload"
|
||||
: props.wikiMemoryPalaceLoading
|
||||
: props.wikiOverviewLoading
|
||||
? "Reloading…"
|
||||
: "Reload"}
|
||||
</button>
|
||||
@@ -1608,7 +1604,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
? renderDreamDiaryEntries(props)
|
||||
: activeDiarySubTab === "insights"
|
||||
? renderDiaryImportsSection(props)
|
||||
: renderMemoryPalaceSection(props)}
|
||||
: renderWikiOverviewSection(props)}
|
||||
</div>
|
||||
${renderWikiPreviewOverlay(props)}
|
||||
</section>
|
||||
|
||||
@@ -930,7 +930,7 @@
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.dreams-diary__insight-badge--palace {
|
||||
.dreams-diary__insight-badge--wiki {
|
||||
color: var(--accent);
|
||||
background: color-mix(in oklab, var(--accent-subtle) 72%, transparent);
|
||||
border-color: color-mix(in oklab, var(--accent) 24%, transparent);
|
||||
|
||||
Reference in New Issue
Block a user