diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3f598da38..35f84f8d87f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai - QA/lab: add character-vibes evaluation reports with model selection and parallel runs so live QA can compare candidate behavior faster. - Plugins/provider-auth: let provider manifests declare `providerAuthAliases` so provider variants can share env vars, auth profiles, config-backed auth, and API-key onboarding choices without core-specific wiring. - iOS: pin release versioning to an explicit CalVer in `apps/ios/version.json`, keep TestFlight iteration on the same short version until maintainers intentionally promote the next gateway version, and add the documented `pnpm ios:version:pin -- --from-gateway` workflow for release trains. (#63001) Thanks @ngutman. +- Control UI/dreaming: surface grounded historical replay as its own Scene lane with grounded-led promotion hints and a safe clear-grounded action for staged backfill-only signals. (#63395) Thanks @mbelinky. ### Fixes diff --git a/docs/concepts/dreaming.md b/docs/concepts/dreaming.md index 3a733a86784..0a3795f4bb1 100644 --- a/docs/concepts/dreaming.md +++ b/docs/concepts/dreaming.md @@ -88,7 +88,12 @@ There is also a grounded historical backfill lane for review and recovery work: - `memory rem-backfill --path ... --stage-short-term` stages grounded durable candidates into the same short-term evidence store the normal deep phase already uses. - `memory rem-backfill --rollback` and `--rollback-short-term` remove those staged backfill artifacts without touching ordinary diary entries or live short-term recall. -The Control UI exposes the same diary backfill/reset flow so you can inspect results in the Dreams scene before deciding whether the grounded candidates deserve promotion. +The Control UI exposes the same diary backfill/reset flow so you can inspect +results in the Dreams scene before deciding whether the grounded candidates +deserve promotion. The Scene also shows a distinct grounded lane so you can see +which staged short-term entries came from historical replay, which promoted +items were grounded-led, and clear only grounded-only staged entries without +touching ordinary live short-term state. ## Deep ranking signals @@ -216,8 +221,9 @@ When enabled, the Gateway **Dreams** tab shows: - current dreaming enabled state - phase-level status and managed-sweep presence -- short-term, long-term, and promoted-today counts +- short-term, grounded, signal, and promoted-today counts - next scheduled run timing +- a distinct grounded Scene lane for staged historical replay entries - an expandable Dream Diary reader backed by `doctor.memory.dreamDiary` ## Related diff --git a/docs/gateway/doctor.md b/docs/gateway/doctor.md index 9f6db957e6f..b904cd83bbb 100644 --- a/docs/gateway/doctor.md +++ b/docs/gateway/doctor.md @@ -95,9 +95,10 @@ cat ~/.openclaw/openclaw.json ## Dreams UI backfill and reset -The Control UI Dreams scene includes **Backfill** and **Reset** actions for the -grounded diary workflow. These actions use gateway doctor-style RPC methods, but -they are **not** part of `openclaw doctor` CLI repair/migration. +The Control UI Dreams scene includes **Backfill**, **Reset**, and **Clear Grounded** +actions for the grounded dreaming workflow. These actions use gateway +doctor-style RPC methods, but they are **not** part of `openclaw doctor` CLI +repair/migration. What they do: @@ -105,13 +106,16 @@ What they do: workspace, runs the grounded REM diary pass, and writes reversible backfill entries into `DREAMS.md`. - **Reset** removes only those marked backfill diary entries from `DREAMS.md`. +- **Clear Grounded** removes only staged grounded-only short-term entries that + came from historical replay and have not accumulated live recall or daily + support yet. What they do **not** do by themselves: - they do not edit `MEMORY.md` - they do not run full doctor migrations - they do not automatically stage grounded candidates into the live short-term - promotion store + promotion store unless you explicitly run the staged CLI path first If you want grounded historical replay to influence the normal deep promotion lane, use the CLI flow instead: diff --git a/extensions/memory-core/runtime-api.ts b/extensions/memory-core/runtime-api.ts index 480dd4d28f4..59fb9f76895 100644 --- a/extensions/memory-core/runtime-api.ts +++ b/extensions/memory-core/runtime-api.ts @@ -17,6 +17,7 @@ export { checkQmdBinaryAvailability } from "openclaw/plugin-sdk/memory-core-host export { hasConfiguredMemorySecretInput } from "openclaw/plugin-sdk/memory-core-host-secret"; export { auditShortTermPromotionArtifacts, + removeGroundedShortTermCandidates, repairShortTermPromotionArtifacts, } from "./src/short-term-promotion.js"; export type { BuiltinMemoryEmbeddingProviderDoctorMetadata } from "./src/memory/provider-adapters.js"; diff --git a/src/gateway/method-scopes.ts b/src/gateway/method-scopes.ts index c385147408e..55a4b90408c 100644 --- a/src/gateway/method-scopes.ts +++ b/src/gateway/method-scopes.ts @@ -138,6 +138,7 @@ const METHOD_SCOPE_GROUPS: Record = { "node.pending.enqueue", "doctor.memory.backfillDreamDiary", "doctor.memory.resetDreamDiary", + "doctor.memory.resetGroundedShortTerm", ], [ADMIN_SCOPE]: [ "channels.logout", diff --git a/src/gateway/server-methods-list.ts b/src/gateway/server-methods-list.ts index 91eca496a54..e8bf4309882 100644 --- a/src/gateway/server-methods-list.ts +++ b/src/gateway/server-methods-list.ts @@ -7,6 +7,7 @@ const BASE_METHODS = [ "doctor.memory.dreamDiary", "doctor.memory.backfillDreamDiary", "doctor.memory.resetDreamDiary", + "doctor.memory.resetGroundedShortTerm", "logs.tail", "channels.status", "channels.logout", diff --git a/src/gateway/server-methods/doctor.memory-core-runtime.ts b/src/gateway/server-methods/doctor.memory-core-runtime.ts index 2836b9bbb0b..8cf62c3a5b6 100644 --- a/src/gateway/server-methods/doctor.memory-core-runtime.ts +++ b/src/gateway/server-methods/doctor.memory-core-runtime.ts @@ -3,3 +3,4 @@ export { previewGroundedRemMarkdown, writeBackfillDiaryEntries, } from "../../../extensions/memory-core/api.js"; +export { removeGroundedShortTermCandidates } from "../../../extensions/memory-core/runtime-api.js"; diff --git a/src/gateway/server-methods/doctor.test.ts b/src/gateway/server-methods/doctor.test.ts index 65eaa535d79..92bd501b623 100644 --- a/src/gateway/server-methods/doctor.test.ts +++ b/src/gateway/server-methods/doctor.test.ts @@ -18,6 +18,7 @@ const getMemorySearchManager = vi.hoisted(() => vi.fn()); const previewGroundedRemMarkdown = vi.hoisted(() => vi.fn()); const writeBackfillDiaryEntries = vi.hoisted(() => vi.fn()); const removeBackfillDiaryEntries = vi.hoisted(() => vi.fn()); +const removeGroundedShortTermCandidates = vi.hoisted(() => vi.fn()); vi.mock("../../config/config.js", () => ({ loadConfig, @@ -40,6 +41,7 @@ vi.mock("./doctor.memory-core-runtime.js", () => ({ previewGroundedRemMarkdown, writeBackfillDiaryEntries, removeBackfillDiaryEntries, + removeGroundedShortTermCandidates, })); import { doctorHandlers } from "./doctor.js"; @@ -100,6 +102,17 @@ const invokeDoctorMemoryResetDreamDiary = async (respond: ReturnType) => { + await doctorHandlers["doctor.memory.resetGroundedShortTerm"]({ + req: {} as never, + params: {} as never, + respond: respond as never, + context: {} as never, + client: null, + isWebchatConnect: () => false, + }); +}; + const expectEmbeddingErrorResponse = (respond: ReturnType, error: string) => { expect(respond).toHaveBeenCalledWith( true, @@ -121,6 +134,10 @@ describe("doctor.memory.status", () => { resolveAgentWorkspaceDir.mockReset().mockReturnValue("/tmp/openclaw"); resolveMemorySearchConfig.mockReset().mockReturnValue({ enabled: true }); getMemorySearchManager.mockReset(); + previewGroundedRemMarkdown.mockReset(); + writeBackfillDiaryEntries.mockReset(); + removeBackfillDiaryEntries.mockReset(); + removeGroundedShortTermCandidates.mockReset(); }); it("returns gateway embedding probe status for the default agent", async () => { @@ -701,6 +718,32 @@ describe("doctor.memory.status", () => { }); }); +describe("doctor.memory dream actions", () => { + it("clears grounded-only staged short-term entries without touching the diary", async () => { + resolveAgentWorkspaceDir.mockReturnValue("/tmp/openclaw"); + removeGroundedShortTermCandidates.mockResolvedValue({ + removed: 3, + storePath: "/tmp/openclaw/memory/.dreams/short-term-recall.json", + }); + const respond = vi.fn(); + + await invokeDoctorMemoryResetGroundedShortTerm(respond); + + expect(removeGroundedShortTermCandidates).toHaveBeenCalledWith({ + workspaceDir: "/tmp/openclaw", + }); + expect(respond).toHaveBeenCalledWith( + true, + { + agentId: "main", + action: "resetGroundedShortTerm", + removedShortTermEntries: 3, + }, + undefined, + ); + }); +}); + describe("doctor.memory.dreamDiary", () => { beforeEach(() => { loadConfig.mockClear(); diff --git a/src/gateway/server-methods/doctor.ts b/src/gateway/server-methods/doctor.ts index e98e78802c7..fd993b8c9a1 100644 --- a/src/gateway/server-methods/doctor.ts +++ b/src/gateway/server-methods/doctor.ts @@ -16,6 +16,7 @@ import { getActiveMemorySearchManager } from "../../plugins/memory-runtime.js"; import { formatError } from "../server-utils.js"; import { removeBackfillDiaryEntries, + removeGroundedShortTermCandidates, previewGroundedRemMarkdown, writeBackfillDiaryEntries, } from "./doctor.memory-core-runtime.js"; @@ -122,15 +123,16 @@ export type DoctorMemoryDreamDiaryPayload = { updatedAtMs?: number; }; -export type DoctorMemoryDreamDiaryActionPayload = { +export type DoctorMemoryDreamActionPayload = { agentId: string; - path: string; - action: "backfill" | "reset"; - found: boolean; + action: "backfill" | "reset" | "resetGroundedShortTerm"; + path?: string; + found?: boolean; scannedFiles?: number; written?: number; replaced?: number; removedEntries?: number; + removedShortTermEntries?: number; }; function extractIsoDayFromPath(filePath: string): string | null { @@ -880,7 +882,7 @@ export const doctorHandlers: GatewayRequestHandlers = { const sourceFiles = await listWorkspaceDailyFiles(memoryDir); if (sourceFiles.length === 0) { const dreamDiary = await readDreamDiary(workspaceDir); - const payload: DoctorMemoryDreamDiaryActionPayload = { + const payload: DoctorMemoryDreamActionPayload = { agentId, path: dreamDiary.path, action: "backfill", @@ -919,7 +921,7 @@ export const doctorHandlers: GatewayRequestHandlers = { timezone: remConfig.timezone, }); const dreamDiary = await readDreamDiary(workspaceDir); - const payload: DoctorMemoryDreamDiaryActionPayload = { + const payload: DoctorMemoryDreamActionPayload = { agentId, path: dreamDiary.path, action: "backfill", @@ -936,7 +938,7 @@ export const doctorHandlers: GatewayRequestHandlers = { const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId); const removed = await removeBackfillDiaryEntries({ workspaceDir }); const dreamDiary = await readDreamDiary(workspaceDir); - const payload: DoctorMemoryDreamDiaryActionPayload = { + const payload: DoctorMemoryDreamActionPayload = { agentId, path: dreamDiary.path, action: "reset", @@ -945,4 +947,16 @@ export const doctorHandlers: GatewayRequestHandlers = { }; respond(true, payload, undefined); }, + "doctor.memory.resetGroundedShortTerm": async ({ respond }) => { + const cfg = loadConfig(); + const agentId = resolveDefaultAgentId(cfg); + const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId); + const removed = await removeGroundedShortTermCandidates({ workspaceDir }); + const payload: DoctorMemoryDreamActionPayload = { + agentId, + action: "resetGroundedShortTerm", + removedShortTermEntries: removed.removed, + }; + respond(true, payload, undefined); + }, }; diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts index 90b08db6094..4bc8b3f68b0 100644 --- a/ui/src/i18n/locales/en.ts +++ b/ui/src/i18n/locales/en.ts @@ -293,19 +293,24 @@ export const en: TranslationMap = { scene: { backfill: "Backfill", reset: "Reset", + clearGrounded: "Clear Grounded", working: "Working…", }, stats: { shortTerm: "Short-term", + grounded: "Grounded", signals: "Signals", promoted: "Promoted", phaseHits: "Phase Hits", }, trace: { shortTerm: "Short-term", + grounded: "Grounded", signals: "Signals", promoted: "Promoted", + groundedLed: "grounded-led", emptyShortTerm: "No active short-term items.", + emptyGrounded: "No staged grounded items.", emptySignals: "No active signals.", emptyPromoted: "Nothing promoted yet today.", }, diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index 8a74330aebf..5ccf58787f3 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -69,6 +69,7 @@ import { backfillDreamDiary, loadDreamDiary, loadDreamingStatus, + resetGroundedShortTerm, resetDreamDiary, resolveConfiguredDreaming, updateDreamingEnabled, @@ -2118,6 +2119,7 @@ export function renderApp(state: AppViewState) { ? renderDreaming({ active: dreamingOn, shortTermCount: state.dreamingStatus?.shortTermCount ?? 0, + groundedSignalCount: state.dreamingStatus?.groundedSignalCount ?? 0, totalSignalCount: state.dreamingStatus?.totalSignalCount ?? 0, promotedCount: state.dreamingStatus?.promotedToday ?? 0, phaseSignalCount: state.dreamingStatus?.phaseSignalCount ?? 0, @@ -2139,6 +2141,7 @@ export function renderApp(state: AppViewState) { onRefreshDiary: () => loadDreamDiary(state), onBackfillDiary: () => backfillDreamDiary(state), onResetDiary: () => resetDreamDiary(state), + onResetGroundedShortTerm: () => resetGroundedShortTerm(state), onToggleEnabled: applyDreamingEnabled, onRequestUpdate: requestHostUpdate, }) diff --git a/ui/src/ui/controllers/dreaming.test.ts b/ui/src/ui/controllers/dreaming.test.ts index fc59648e05f..dfdb3541470 100644 --- a/ui/src/ui/controllers/dreaming.test.ts +++ b/ui/src/ui/controllers/dreaming.test.ts @@ -3,6 +3,7 @@ import { backfillDreamDiary, loadDreamDiary, loadDreamingStatus, + resetGroundedShortTerm, resetDreamDiary, resolveConfiguredDreaming, updateDreamingEnabled, @@ -501,4 +502,27 @@ describe("dreaming controller", () => { expect(state.dreamDiaryContent).toBeNull(); expect(state.dreamDiaryActionLoading).toBe(false); }); + + it("clears grounded staged entries and reloads only dreaming status", async () => { + const { state, request } = createState(); + state.dreamDiaryContent = "keep existing diary"; + request.mockImplementation(async (method: string) => { + if (method === "doctor.memory.resetGroundedShortTerm") { + return { action: "resetGroundedShortTerm", removedShortTermEntries: 2 }; + } + if (method === "doctor.memory.status") { + return { dreaming: null }; + } + return {}; + }); + + const ok = await resetGroundedShortTerm(state); + + expect(ok).toBe(true); + expect(request).toHaveBeenCalledWith("doctor.memory.resetGroundedShortTerm", {}); + expect(request).toHaveBeenCalledWith("doctor.memory.status", {}); + expect(request).not.toHaveBeenCalledWith("doctor.memory.dreamDiary", {}); + expect(state.dreamDiaryContent).toBe("keep existing diary"); + expect(state.dreamDiaryActionLoading).toBe(false); + }); }); diff --git a/ui/src/ui/controllers/dreaming.ts b/ui/src/ui/controllers/dreaming.ts index f80aa3f48e8..c1cf0d6fb12 100644 --- a/ui/src/ui/controllers/dreaming.ts +++ b/ui/src/ui/controllers/dreaming.ts @@ -89,11 +89,12 @@ type DoctorMemoryDreamDiaryPayload = { content?: unknown; }; -type DoctorMemoryDreamDiaryActionPayload = { +type DoctorMemoryDreamActionPayload = { action?: unknown; removedEntries?: unknown; written?: unknown; replaced?: unknown; + removedShortTermEntries?: unknown; }; export type DreamingState = { @@ -344,7 +345,13 @@ export async function loadDreamDiary(state: DreamingState): Promise { async function runDreamDiaryAction( state: DreamingState, - method: "doctor.memory.backfillDreamDiary" | "doctor.memory.resetDreamDiary", + method: + | "doctor.memory.backfillDreamDiary" + | "doctor.memory.resetDreamDiary" + | "doctor.memory.resetGroundedShortTerm", + options?: { + reloadDiary?: boolean; + }, ): Promise { if (!state.client || !state.connected || state.dreamDiaryActionLoading) { return false; @@ -353,8 +360,10 @@ async function runDreamDiaryAction( state.dreamingStatusError = null; state.dreamDiaryError = null; try { - await state.client.request(method, {}); - await loadDreamDiary(state); + await state.client.request(method, {}); + if (options?.reloadDiary !== false) { + await loadDreamDiary(state); + } await loadDreamingStatus(state); return true; } catch (err) { @@ -375,6 +384,12 @@ export async function resetDreamDiary(state: DreamingState): Promise { return runDreamDiaryAction(state, "doctor.memory.resetDreamDiary"); } +export async function resetGroundedShortTerm(state: DreamingState): Promise { + return runDreamDiaryAction(state, "doctor.memory.resetGroundedShortTerm", { + reloadDiary: false, + }); +} + async function writeDreamingPatch( state: DreamingState, patch: Record, diff --git a/ui/src/ui/views/dreaming.test.ts b/ui/src/ui/views/dreaming.test.ts index 1afa5d179c2..c74b6dd64e3 100644 --- a/ui/src/ui/views/dreaming.test.ts +++ b/ui/src/ui/views/dreaming.test.ts @@ -8,6 +8,7 @@ function buildProps(overrides?: Partial): DreamingProps { return { active: true, shortTermCount: 47, + groundedSignalCount: 9, totalSignalCount: 182, promotedCount: 12, phaseSignalCount: 29, @@ -52,8 +53,8 @@ function buildProps(overrides?: Partial): DreamingProps { snippet: "Use the Happy Together calendar for flights.", recallCount: 3, dailyCount: 2, - groundedCount: 0, - totalSignalCount: 5, + groundedCount: 4, + totalSignalCount: 9, lightHits: 0, remHits: 0, phaseHitCount: 0, @@ -76,6 +77,7 @@ function buildProps(overrides?: Partial): DreamingProps { onRefreshDiary: () => {}, onBackfillDiary: () => {}, onResetDiary: () => {}, + onResetGroundedShortTerm: () => {}, onToggleEnabled: () => {}, ...overrides, }; @@ -114,36 +116,44 @@ describe("dreaming view", () => { it("displays memory stats", () => { const container = renderInto(buildProps()); const values = container.querySelectorAll(".dreams__stat-value"); - expect(values.length).toBe(3); + expect(values.length).toBe(4); expect(values[0]?.textContent).toBe("47"); - expect(values[1]?.textContent).toBe("182"); - expect(values[2]?.textContent).toBe("12"); + expect(values[1]?.textContent).toBe("9"); + expect(values[2]?.textContent).toBe("182"); + expect(values[3]?.textContent).toBe("12"); }); - it("renders short-term, signals, and promoted detail sections", () => { + it("renders short-term, grounded, signals, and promoted detail sections", () => { const container = renderInto(buildProps()); const titles = [...container.querySelectorAll(".dreams__trace-title")].map((node) => node.textContent?.trim(), ); - expect(titles).toEqual(["Short-term", "Signals", "Promoted"]); + expect(titles).toEqual(["Short-term", "Grounded", "Signals", "Promoted"]); expect( container.querySelector('[data-kind="shortTerm"] .dreams__trace-snippet')?.textContent, ).toContain("Emma prefers shorter"); + expect( + container.querySelector('[data-kind="grounded"] .dreams__trace-meta')?.textContent, + ).toContain("1 grounded"); expect( container.querySelector('[data-kind="signals"] .dreams__trace-meta')?.textContent, ).toContain("3 signals"); expect( container.querySelector('[data-kind="promoted"] .dreams__trace-source')?.textContent, ).toContain("memory/2026-04-04.md:4-5"); + expect( + container.querySelector('[data-kind="promoted"] .dreams__trace-meta')?.textContent, + ).toContain("grounded-led"); }); - it("renders scene backfill and reset controls", () => { + it("renders scene backfill, reset, and clear grounded controls", () => { const container = renderInto(buildProps()); const buttons = [...container.querySelectorAll("button")].map((node) => node.textContent?.trim(), ); expect(buttons).toContain("Backfill"); expect(buttons).toContain("Reset"); + expect(buttons).toContain("Clear Grounded"); }); it("shows dream bubble when active", () => { diff --git a/ui/src/ui/views/dreaming.ts b/ui/src/ui/views/dreaming.ts index 7bfadf4c1f5..2b01990a152 100644 --- a/ui/src/ui/views/dreaming.ts +++ b/ui/src/ui/views/dreaming.ts @@ -258,6 +258,7 @@ function renderDiaryNavigator( export type DreamingProps = { active: boolean; shortTermCount: number; + groundedSignalCount: number; totalSignalCount: number; promotedCount: number; phaseSignalCount: number; @@ -324,6 +325,7 @@ export type DreamingProps = { onRefreshDiary: () => void; onBackfillDiary: () => void; onResetDiary: () => void; + onResetGroundedShortTerm: () => void; onToggleEnabled: (enabled: boolean) => void; onRequestUpdate?: () => void; }; @@ -473,6 +475,7 @@ export function renderDreaming(props: DreamingProps) { // ── Scene renderer ──────────────────────────────────────────────────── function renderScene(props: DreamingProps, idle: boolean, dreamText: string) { + const groundedEntries = props.shortTermEntries.filter((entry) => entry.groundedCount > 0); return html`
${STARS.map( @@ -548,6 +551,13 @@ function renderScene(props: DreamingProps, idle: boolean, dreamText: string) { > ${t("dreaming.scene.reset")} +
@@ -558,6 +568,13 @@ function renderScene(props: DreamingProps, idle: boolean, dreamText: string) { ${t("dreaming.stats.shortTerm")}
+
+ ${props.groundedSignalCount} + ${t("dreaming.stats.grounded")} +
+
${props.totalSignalCount} + [ + `${entry.groundedCount} grounded`, + entry.recallCount > 0 + ? `${entry.recallCount} recall${entry.recallCount === 1 ? "" : "s"}` + : null, + entry.dailyCount > 0 ? `${entry.dailyCount} daily` : null, + isGroundedLed(entry) ? t("dreaming.trace.groundedLed") : null, + ] + .filter(Boolean) + .join(" · "), + })} ${renderTraceSection("signals", props.signalEntries, { count: props.totalSignalCount, emptyKey: "dreaming.trace.emptySignals", @@ -610,6 +642,8 @@ function renderScene(props: DreamingProps, idle: boolean, dreamText: string) { meta: (entry) => [ entry.promotedAt ? formatCompactDateTime(entry.promotedAt) : null, + entry.groundedCount > 0 ? `${entry.groundedCount} grounded` : null, + isGroundedLed(entry) ? t("dreaming.trace.groundedLed") : null, entry.totalSignalCount > 0 ? `${entry.totalSignalCount} signal${entry.totalSignalCount === 1 ? "" : "s"} before promote` : null, @@ -643,8 +677,21 @@ function formatCompactDateTime(value: string): string { }); } +function isGroundedLed( + entry: Pick< + DreamingProps["shortTermEntries"][number], + "groundedCount" | "recallCount" | "dailyCount" + >, +): boolean { + return ( + entry.groundedCount > 0 && + entry.groundedCount >= entry.recallCount && + entry.groundedCount >= entry.dailyCount + ); +} + function renderTraceSection( - kind: "shortTerm" | "signals" | "promoted", + kind: "shortTerm" | "grounded" | "signals" | "promoted", entries: DreamingProps["shortTermEntries"], options: { count: number;