diff --git a/docs/chat-commands.md b/docs/chat-commands.md index ef02870e..3ad5c116 100644 --- a/docs/chat-commands.md +++ b/docs/chat-commands.md @@ -15,6 +15,8 @@ These commands work inside chat channels and interactive agent sessions: | `/dream-log ` | Show a specific Dream memory change | | `/dream-restore` | List recent Dream memory versions | | `/dream-restore ` | Restore memory to the state before a specific change | +| `/dream-prompt` | Show how Dream is being guided for memory | +| `/dream-prompt init` | Create an editable Dream memory guide at `prompts/dream.md` | | `/skill` | List enabled skills and their descriptions | | `/trigger` | Show local trigger usage | | `/trigger ` | Create a named local trigger for the current chat/session | diff --git a/docs/memory.md b/docs/memory.md index 38da6cc7..2b847497 100644 --- a/docs/memory.md +++ b/docs/memory.md @@ -64,6 +64,9 @@ This is why nanobot's memory is not just archival. It is interpretive. workspace/ ├── SOUL.md # The bot's long-term voice and communication style ├── USER.md # Stable knowledge about the user +├── prompts/ +│ ├── README.md # Notes for memory guidance files +│ └── dream.md # Optional instructions for how Dream organizes memory └── memory/ ├── MEMORY.md # Project facts, decisions, and durable context ├── history.jsonl # Append-only history summaries @@ -120,6 +123,8 @@ Memory is not hidden behind the curtain. Users can inspect and guide it. | `/dream-log ` | Show a specific Dream change | | `/dream-restore` | List recent Dream memory versions | | `/dream-restore ` | Restore memory to the state before a specific change | +| `/dream-prompt` | Show how Dream is being guided for memory | +| `/dream-prompt init` | Create an editable Dream memory guide at `prompts/dream.md` | These commands exist for a reason: automatic memory is powerful, but users should always retain the right to inspect, understand, and restore it. @@ -135,6 +140,28 @@ This gives memory a history of its own: That turns memory from a silent mutation into an auditable process. +## Guiding Dream + +Dream decides what to keep, update, or forget using nanobot's built-in memory instructions. Most users can leave this alone. + +If one workspace needs a different memory style, create an editable guide: + +```text +/dream-prompt init +``` + +This creates: + +```text +workspace/prompts/dream.md +``` + +Edit that file in plain Markdown. When it has content, Dream follows it for this workspace before reading the latest conversation history. You do not need to paste history into the file; Dream adds the current `## Conversation History` block automatically. + +To return to nanobot's default behavior, delete `prompts/dream.md` or leave it empty. + +Each workspace has its own guide. Changing this file does not affect other nanobot workspaces. + ## Configuration Dream is configured under `agents.defaults.dream`: diff --git a/nanobot/agent/memory.py b/nanobot/agent/memory.py index 5c9f5bf3..ecefaec4 100644 --- a/nanobot/agent/memory.py +++ b/nanobot/agent/memory.py @@ -481,13 +481,39 @@ class MemoryStore: def get_latest_cursor(self) -> int: return max(self._next_cursor() - 1, 0) + @property + def dream_prompt_file(self) -> Path: + return self.workspace / "prompts" / "dream.md" + + def has_dream_prompt_override(self) -> bool: + with suppress(OSError): + return self.dream_prompt_file.is_file() and bool( + self.dream_prompt_file.read_text(encoding="utf-8").strip() + ) + return False + + @staticmethod + def default_dream_prompt() -> str: + from nanobot.agent.skills import BUILTIN_SKILLS_DIR + + return render_template( + "agent/dream.md", + strip=True, + skill_creator_path=str(BUILTIN_SKILLS_DIR / "skill-creator" / "SKILL.md"), + ) + + def _dream_template(self) -> str: + with suppress(OSError): + text = self.dream_prompt_file.read_text(encoding="utf-8") + if text.strip(): + return text.rstrip() + return self.default_dream_prompt() + def build_dream_prompt(self, *, max_entries: int = 20) -> tuple[str, int] | None: """Build the Dream prompt with unprocessed history context. Returns ``(prompt, last_cursor)`` or ``None`` if nothing to process. """ - from nanobot.agent.skills import BUILTIN_SKILLS_DIR - last_cursor = self.get_last_dream_cursor() entries = self.read_unprocessed_history(since_cursor=last_cursor) if not entries: @@ -498,10 +524,7 @@ class MemoryStore: f"[{e['timestamp']}] {truncate_text(e['content'], 500)}" for e in batch ) - skill_creator_path = str(BUILTIN_SKILLS_DIR / "skill-creator" / "SKILL.md") - template = render_template( - "agent/dream.md", strip=True, skill_creator_path=skill_creator_path, - ) + template = self._dream_template() prompt = f"{template}\n\n## Conversation History\n{history_text}" return (prompt, batch[-1]["cursor"]) diff --git a/nanobot/channels/telegram.py b/nanobot/channels/telegram.py index a2a4022e..b6d8e6b0 100644 --- a/nanobot/channels/telegram.py +++ b/nanobot/channels/telegram.py @@ -418,6 +418,7 @@ class TelegramChannel(BaseChannel): BotCommand("dream", "Run Dream memory consolidation now"), BotCommand("dream_log", "Show the latest Dream memory change"), BotCommand("dream_restore", "Restore Dream memory to an earlier version"), + BotCommand("dream_prompt", "Show or initialize the Dream prompt override"), BotCommand("help", "Show available commands"), ] @@ -477,6 +478,8 @@ class TelegramChannel(BaseChannel): return content.replace("/dream_log", "/dream-log", 1) if content == "/dream_restore" or content.startswith("/dream_restore "): return content.replace("/dream_restore", "/dream-restore", 1) + if content == "/dream_prompt" or content.startswith("/dream_prompt "): + return content.replace("/dream_prompt", "/dream-prompt", 1) return content async def start(self) -> None: @@ -523,7 +526,9 @@ class TelegramChannel(BaseChannel): ) self._app.add_handler( MessageHandler( - filters.Regex(r"^/(dream-log|dream_log|dream-restore|dream_restore)(?:@\w+)?(?:\s+.*)?$"), + filters.Regex( + r"^/(dream-log|dream_log|dream-restore|dream_restore|dream-prompt|dream_prompt)(?:@\w+)?(?:\s+.*)?$" + ), self._forward_command, ) ) diff --git a/nanobot/command/builtin.py b/nanobot/command/builtin.py index 719c5e56..1258bc80 100644 --- a/nanobot/command/builtin.py +++ b/nanobot/command/builtin.py @@ -106,6 +106,13 @@ BUILTIN_COMMAND_SPECS: tuple[BuiltinCommandSpec, ...] = ( "Revert memory to a previous Dream snapshot.", "undo-2", ), + BuiltinCommandSpec( + "/dream-prompt", + "Dream memory", + "Tell Dream how to organize this workspace's memory.", + "file-text", + "[init]", + ), BuiltinCommandSpec( "/skill", "List skills", @@ -408,6 +415,49 @@ async def cmd_dream(ctx: CommandContext) -> OutboundMessage: ) +async def cmd_dream_prompt(ctx: CommandContext) -> OutboundMessage: + """Show or set up the workspace Dream memory instructions.""" + store = ctx.loop.context.memory + path = store.dream_prompt_file + args = ctx.args.strip().lower() + + if args == "init": + if path.exists(): + content = ( + f"Dream memory instructions already exist at `{path}`.\n\n" + "Edit that file, or delete/empty it to return to nanobot's default." + ) + else: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(store.default_dream_prompt() + "\n", encoding="utf-8") + content = ( + f"Created Dream memory instructions at `{path}`.\n\n" + "Edit that file to teach Dream how to organize memory. " + "Delete or empty it to return to nanobot's default." + ) + elif args: + content = "Usage: /dream-prompt [init]" + elif store.has_dream_prompt_override(): + content = ( + "Dream memory instructions: custom for this workspace\n\n" + f"- Path: `{path}`\n" + "- Delete or empty this file to return to nanobot's default." + ) + else: + content = ( + "Dream memory instructions: nanobot default\n\n" + f"- Editable file: `{path}`\n" + "- Run `/dream-prompt init` to create an editable copy." + ) + + return OutboundMessage( + channel=ctx.msg.channel, + chat_id=ctx.msg.chat_id, + content=content, + metadata={**dict(ctx.msg.metadata or {}), "render_as": "text"}, + ) + + def _format_dream_no_input_message() -> str: return "\n".join([ "Dream has no conversation history to process yet.", @@ -422,6 +472,7 @@ def _format_dream_no_input_message() -> str: "- Enable `agents.defaults.idleCompactAfterMinutes` so completed chats become Dream input automatically.", "- Compact the current chat into memory once that manual action is available.", "- If you expected history to exist, check whether `memory/history.jsonl` has new entries after the Dream cursor.", + "- Use `/dream-prompt` to see or change how Dream organizes memory.", ]) @@ -508,7 +559,10 @@ async def cmd_dream_log(ctx: CommandContext) -> OutboundMessage: if not git.is_initialized(): if store.get_last_dream_cursor() == 0: - msg = "Dream has not run yet. Run `/dream`, or wait for the next scheduled Dream cycle." + msg = ( + "Dream has not run yet. Run `/dream`, or wait for the next scheduled Dream cycle.\n\n" + "Use `/dream-prompt` to see or change how Dream organizes memory." + ) else: msg = "Dream history is not available because memory versioning is not initialized." return OutboundMessage( @@ -539,7 +593,10 @@ async def cmd_dream_log(ctx: CommandContext) -> OutboundMessage: commit, diff = result content = _format_dream_log_content(commit, diff) else: - content = "Dream memory has no saved versions yet." + content = ( + "Dream memory has no saved versions yet.\n\n" + "Use `/dream-prompt` to see or change how Dream organizes memory." + ) return OutboundMessage( channel=ctx.msg.channel, chat_id=ctx.msg.chat_id, @@ -821,6 +878,8 @@ def register_builtin_commands(router: CommandRouter) -> None: router.prefix("/dream-log ", cmd_dream_log) router.exact("/dream-restore", cmd_dream_restore) router.prefix("/dream-restore ", cmd_dream_restore) + router.exact("/dream-prompt", cmd_dream_prompt) + router.prefix("/dream-prompt ", cmd_dream_prompt) router.exact("/skill", cmd_skill) router.exact("/help", cmd_help) router.exact("/pairing", cmd_pairing) diff --git a/nanobot/templates/prompts/README.md b/nanobot/templates/prompts/README.md new file mode 100644 index 00000000..0084d1f0 --- /dev/null +++ b/nanobot/templates/prompts/README.md @@ -0,0 +1,11 @@ +# Dream Memory Instructions + +This folder is for plain-language instructions that tell Dream how to organize memory in this workspace. + +Most users do not need to edit anything here. To guide Dream differently for this workspace, run: + +```text +/dream-prompt init +``` + +That creates `prompts/dream.md`. Edit it in plain Markdown. Delete or empty it to return to nanobot's default memory behavior. diff --git a/nanobot/utils/helpers.py b/nanobot/utils/helpers.py index a0577dca..36270fa2 100644 --- a/nanobot/utils/helpers.py +++ b/nanobot/utils/helpers.py @@ -741,6 +741,7 @@ def sync_workspace_templates(workspace: Path, silent: bool = False) -> list[str] if item.name.endswith(".md") and not item.name.startswith("."): _write(item, workspace / item.name) _write(tpl / "memory" / "MEMORY.md", workspace / "memory" / "MEMORY.md") + _write(tpl / "prompts" / "README.md", workspace / "prompts" / "README.md") _write(None, workspace / "memory" / "history.jsonl") (workspace / "skills").mkdir(exist_ok=True) diff --git a/tests/agent/test_dream.py b/tests/agent/test_dream.py index c105eec1..6427ba1f 100644 --- a/tests/agent/test_dream.py +++ b/tests/agent/test_dream.py @@ -61,6 +61,34 @@ class TestBuildDreamPrompt: prompt, _ = result assert "skill-creator" in prompt + def test_workspace_dream_prompt_overrides_default(self, store): + store.dream_prompt_file.parent.mkdir(parents=True) + store.dream_prompt_file.write_text( + "Custom Dream prompt.", + encoding="utf-8", + ) + store.append_history("keep this fact") + + result = store.build_dream_prompt() + + assert result is not None + prompt, _ = result + assert prompt.startswith("Custom Dream prompt.") + assert "memory consolidation engine" not in prompt + assert "## Conversation History" in prompt + assert "keep this fact" in prompt + + def test_empty_workspace_dream_prompt_uses_default(self, store): + store.dream_prompt_file.parent.mkdir(parents=True) + store.dream_prompt_file.write_text(" \n", encoding="utf-8") + store.append_history("test") + + result = store.build_dream_prompt() + + assert result is not None + prompt, _ = result + assert "memory consolidation engine" in prompt + def test_truncates_long_entries(self, store): long_content = "x" * 2000 store.append_history(long_content) diff --git a/tests/agent/test_onboard_logic.py b/tests/agent/test_onboard_logic.py index fadc20aa..10deb476 100644 --- a/tests/agent/test_onboard_logic.py +++ b/tests/agent/test_onboard_logic.py @@ -375,6 +375,15 @@ class TestSyncWorkspaceTemplates: assert (workspace / "memory").exists() or (workspace / "skills").exists() + def test_creates_prompt_readme_without_dream_override(self, tmp_path): + workspace = tmp_path / "workspace" + + added = sync_workspace_templates(workspace, silent=True) + + assert "prompts/README.md" in {path.replace("\\", "/") for path in added} + assert (workspace / "prompts" / "README.md").exists() + assert not (workspace / "prompts" / "dream.md").exists() + def test_returns_list_of_added_files(self, tmp_path): """Should return list of relative paths for added files.""" workspace = tmp_path / "workspace" diff --git a/tests/channels/test_telegram_channel.py b/tests/channels/test_telegram_channel.py index bbf9e2eb..a6ce5464 100644 --- a/tests/channels/test_telegram_channel.py +++ b/tests/channels/test_telegram_channel.py @@ -280,6 +280,7 @@ async def test_start_creates_separate_pools_with_proxy(monkeypatch) -> None: assert any(cmd.command == "dream" for cmd in app.bot.commands) assert any(cmd.command == "dream_log" for cmd in app.bot.commands) assert any(cmd.command == "dream_restore" for cmd in app.bot.commands) + assert any(cmd.command == "dream_prompt" for cmd in app.bot.commands) @pytest.mark.asyncio @@ -1570,6 +1571,14 @@ async def test_forward_command_normalizes_telegram_safe_dream_aliases() -> None: assert len(handled) == 1 assert handled[0]["content"] == "/dream-restore deadbeef" + handled.clear() + update = _make_telegram_update(text="/dream_prompt@nanobot_test init", reply_to_message=None) + + await channel._forward_command(update, None) + + assert len(handled) == 1 + assert handled[0]["content"] == "/dream-prompt init" + def test_telegram_bus_slash_command_regex_matches_agent_loop_commands() -> None: """Bus-routed slash commands must match the Telegram handler regex (see builtin router).""" @@ -1588,6 +1597,7 @@ def test_telegram_bus_slash_command_regex_matches_agent_loop_commands() -> None: assert pat.fullmatch("/trigger@nanobot_bot CI summary") assert pat.fullmatch("/dream-log deadbeef") is None assert pat.fullmatch("/dream-restore deadbeef") is None + assert pat.fullmatch("/dream-prompt init") is None @pytest.mark.asyncio @@ -1608,6 +1618,7 @@ async def test_on_help_includes_restart_command() -> None: assert "/skill" in help_text assert "/dream" in help_text assert "/dream-log" in help_text + assert "/dream-prompt" in help_text assert "/goal" in help_text assert "/trigger" in help_text assert "/pairing" in help_text diff --git a/tests/command/test_builtin_dream.py b/tests/command/test_builtin_dream.py index 660969c4..b634b303 100644 --- a/tests/command/test_builtin_dream.py +++ b/tests/command/test_builtin_dream.py @@ -5,8 +5,16 @@ from types import SimpleNamespace import pytest +from nanobot.agent.memory import MemoryStore from nanobot.bus.events import InboundMessage, OutboundMessage -from nanobot.command.builtin import cmd_dream, cmd_dream_log, cmd_dream_restore +from nanobot.command.builtin import ( + build_help_text, + builtin_command_palette, + cmd_dream, + cmd_dream_log, + cmd_dream_prompt, + cmd_dream_restore, +) from nanobot.command.router import CommandContext from nanobot.utils.gitstore import CommitInfo @@ -94,6 +102,12 @@ def _make_dream_ctx(tmp_path) -> tuple[CommandContext, _FakeBus]: return ctx, bus +def _make_dream_prompt_ctx(tmp_path, raw: str = "/dream-prompt", args: str = "") -> CommandContext: + msg = InboundMessage(channel="cli", sender_id="u1", chat_id="direct", content=raw) + loop = SimpleNamespace(context=SimpleNamespace(memory=MemoryStore(tmp_path))) + return CommandContext(msg=msg, session=None, key=msg.session_key, raw=raw, args=args, loop=loop) + + @pytest.mark.asyncio async def test_dream_no_history_explains_how_to_create_input(tmp_path) -> None: ctx, bus = _make_dream_ctx(tmp_path) @@ -109,6 +123,7 @@ async def test_dream_no_history_explains_how_to_create_input(tmp_path) -> None: assert "idle auto-compact" in content assert "Dream cursor" in content assert "agents.defaults.idleCompactAfterMinutes" in content + assert "/dream-prompt" in content @pytest.mark.asyncio @@ -185,6 +200,61 @@ async def test_dream_log_before_first_run_is_clear() -> None: assert "Dream has not run yet." in out.content assert "Run `/dream`" in out.content + assert "/dream-prompt" in out.content + + +@pytest.mark.asyncio +async def test_dream_log_without_saved_versions_mentions_prompt_command() -> None: + git = _FakeGit(initialized=True, commits=[]) + + out = await cmd_dream_log(_make_ctx("/dream-log", git)) + + assert "Dream memory has no saved versions yet." in out.content + assert "/dream-prompt" in out.content + + +@pytest.mark.asyncio +async def test_dream_prompt_reports_default_prompt(tmp_path) -> None: + out = await cmd_dream_prompt(_make_dream_prompt_ctx(tmp_path)) + + assert "Dream memory instructions: nanobot default" in out.content + assert "prompts" in out.content + assert "dream.md" in out.content + assert "/dream-prompt init" in out.content + + +@pytest.mark.asyncio +async def test_dream_prompt_init_copies_default_prompt(tmp_path) -> None: + ctx = _make_dream_prompt_ctx(tmp_path, "/dream-prompt init", "init") + + out = await cmd_dream_prompt(ctx) + + prompt_file = tmp_path / "prompts" / "dream.md" + assert "Created Dream memory instructions" in out.content + assert prompt_file.read_text(encoding="utf-8") == MemoryStore.default_dream_prompt() + "\n" + + +@pytest.mark.asyncio +async def test_dream_prompt_init_does_not_overwrite_existing_prompt(tmp_path) -> None: + prompt_file = tmp_path / "prompts" / "dream.md" + prompt_file.parent.mkdir() + prompt_file.write_text("custom", encoding="utf-8") + ctx = _make_dream_prompt_ctx(tmp_path, "/dream-prompt init", "init") + + out = await cmd_dream_prompt(ctx) + + assert "already exist" in out.content + assert prompt_file.read_text(encoding="utf-8") == "custom" + + +def test_dream_prompt_command_in_help_and_palette() -> None: + palette = builtin_command_palette() + + assert any( + item["command"] == "/dream-prompt" and item["arg_hint"] == "[init]" + for item in palette + ) + assert "/dream-prompt [init]" in build_help_text() @pytest.mark.asyncio diff --git a/tests/command/test_router_dispatchable.py b/tests/command/test_router_dispatchable.py index ec6b3c6e..e03ca008 100644 --- a/tests/command/test_router_dispatchable.py +++ b/tests/command/test_router_dispatchable.py @@ -26,12 +26,14 @@ class TestIsDispatchableCommand: assert router.is_dispatchable_command("/dream") assert router.is_dispatchable_command("/dream-log") assert router.is_dispatchable_command("/dream-restore") + assert router.is_dispatchable_command("/dream-prompt") assert router.is_dispatchable_command("/goal") assert router.is_dispatchable_command("/pairing") def test_prefix_commands_match(self, router: CommandRouter) -> None: assert router.is_dispatchable_command("/dream-log abc123") assert router.is_dispatchable_command("/dream-restore def456") + assert router.is_dispatchable_command("/dream-prompt init") assert router.is_dispatchable_command("/model fast") assert router.is_dispatchable_command("/goal migrate the database") assert router.is_dispatchable_command("/pairing list") diff --git a/webui/src/i18n/locales/en/common.json b/webui/src/i18n/locales/en/common.json index 97f152ba..2c1d5c27 100644 --- a/webui/src/i18n/locales/en/common.json +++ b/webui/src/i18n/locales/en/common.json @@ -921,6 +921,10 @@ "title": "Restore memory", "description": "Revert memory to a previous Dream snapshot." }, + "dream_prompt": { + "title": "Dream memory", + "description": "Tell Dream how to organize this workspace's memory." + }, "goal": { "title": "Long-running goal", "description": "Tell the agent to treat this as a sustained multi-step goal." diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json index 59ede1bb..3f28191f 100644 --- a/webui/src/i18n/locales/es/common.json +++ b/webui/src/i18n/locales/es/common.json @@ -911,6 +911,10 @@ "title": "Restaurar memoria", "description": "Revierte la memoria a una instantánea Dream anterior." }, + "dream_prompt": { + "title": "Memoria de Dream", + "description": "Indica a Dream cómo organizar la memoria de este espacio de trabajo." + }, "goal": { "title": "Objetivo a largo plazo", "description": "Indica al agente que trate esto como un objetivo sostenido en varios pasos." diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json index 33a71746..1c6d4462 100644 --- a/webui/src/i18n/locales/fr/common.json +++ b/webui/src/i18n/locales/fr/common.json @@ -911,6 +911,10 @@ "title": "Restaurer la mémoire", "description": "Revenir à un instantané Dream précédent." }, + "dream_prompt": { + "title": "Mémoire Dream", + "description": "Indiquez à Dream comment organiser la mémoire de cet espace de travail." + }, "goal": { "title": "Objectif long terme", "description": "Demandez à l’agent de traiter ceci comme un objectif multi‑étapes durable." diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json index fcc998d7..453c054d 100644 --- a/webui/src/i18n/locales/id/common.json +++ b/webui/src/i18n/locales/id/common.json @@ -911,6 +911,10 @@ "title": "Pulihkan memori", "description": "Kembalikan memori ke snapshot Dream sebelumnya." }, + "dream_prompt": { + "title": "Memori Dream", + "description": "Atur cara Dream menyusun memori workspace ini." + }, "goal": { "title": "Tujuan jangka panjang", "description": "Instruksikan agen memperlakukan ini sebagai tujuan multi-langkah yang berkelanjutan." diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json index 5a242c7b..a65f59ca 100644 --- a/webui/src/i18n/locales/ja/common.json +++ b/webui/src/i18n/locales/ja/common.json @@ -911,6 +911,10 @@ "title": "メモリを復元", "description": "以前の Dream スナップショットへメモリを戻します。" }, + "dream_prompt": { + "title": "Dream の記憶", + "description": "このワークスペースの記憶を Dream がどう整理するかを設定します。" + }, "goal": { "title": "長期目標", "description": "持続的な複数ステップの目標として扱うようエージェントに伝えます。" diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json index 0ddedafe..d60b0735 100644 --- a/webui/src/i18n/locales/ko/common.json +++ b/webui/src/i18n/locales/ko/common.json @@ -911,6 +911,10 @@ "title": "메모리 복원", "description": "이전 Dream 스냅샷으로 메모리를 되돌립니다." }, + "dream_prompt": { + "title": "Dream 메모리", + "description": "Dream이 이 워크스페이스의 메모리를 정리하는 방식을 설정합니다." + }, "goal": { "title": "장기 목표", "description": "에이전트에게 지속적인 다단계 목표로 처리하도록 지시합니다." diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json index 184dee06..3e884634 100644 --- a/webui/src/i18n/locales/vi/common.json +++ b/webui/src/i18n/locales/vi/common.json @@ -911,6 +911,10 @@ "title": "Khôi phục bộ nhớ", "description": "Đưa bộ nhớ về một snapshot Dream trước đó." }, + "dream_prompt": { + "title": "Bộ nhớ Dream", + "description": "Cho Dream biết cách sắp xếp bộ nhớ của workspace này." + }, "goal": { "title": "Mục tiêu dài hạn", "description": "Yêu cầu agent xử lý đây là mục tiêu nhiều bước kéo dài." diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json index d07b197f..02b46e58 100644 --- a/webui/src/i18n/locales/zh-CN/common.json +++ b/webui/src/i18n/locales/zh-CN/common.json @@ -920,6 +920,10 @@ "title": "恢复记忆", "description": "将记忆恢复到之前的 Dream 快照。" }, + "dream_prompt": { + "title": "Dream 记忆", + "description": "设置 Dream 如何整理当前工作区的记忆。" + }, "goal": { "title": "长期目标", "description": "让助手把当前请求当作需要多步骤持续推进的目标。" diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json index e01fc6ab..127857f1 100644 --- a/webui/src/i18n/locales/zh-TW/common.json +++ b/webui/src/i18n/locales/zh-TW/common.json @@ -911,6 +911,10 @@ "title": "恢復記憶", "description": "將記憶恢復到之前的 Dream 快照。" }, + "dream_prompt": { + "title": "Dream 記憶", + "description": "設定 Dream 如何整理目前工作區的記憶。" + }, "goal": { "title": "長期目標", "description": "請助理把這則請求當成需要多步驟持續推進的目標。" diff --git a/webui/src/tests/i18n.test.tsx b/webui/src/tests/i18n.test.tsx index 2a47d2f8..0ee70200 100644 --- a/webui/src/tests/i18n.test.tsx +++ b/webui/src/tests/i18n.test.tsx @@ -20,6 +20,7 @@ const SLASH_COMMAND_KEYS = [ "dream", "dream_log", "dream_restore", + "dream_prompt", "goal", "trigger", "help",