qa296 commited on
Commit
705e71e
·
1 Parent(s): f21c50d

feat(memory): add daily journal support

Browse files

Add journal write support across memory storage and manager layers,
including a tool wrapper for agent use.

Create or append to date-based journal files with timestamped entries
and frontmatter metadata for new files.

Also add helpers to load CORE memory and recent journal entries, and
expose the journal tool in the agent loop imports.

agent/agent_loop.py CHANGED
@@ -12,7 +12,7 @@ from tools.bash_tool import run_bash
12
  from tools.file_tools import run_read, run_write, run_edit
13
  from tools.code_executor import run_create_plugin
14
  from tools.browser_tool import run_browser
15
- from tools.memory_tools import run_remember, run_recall
16
 
17
 
18
  # Tool definitions sent to the API
 
12
  from tools.file_tools import run_read, run_write, run_edit
13
  from tools.code_executor import run_create_plugin
14
  from tools.browser_tool import run_browser
15
+ from tools.memory_tools import run_remember, run_recall, run_journal
16
 
17
 
18
  # Tool definitions sent to the API
memory/manager.py CHANGED
@@ -59,3 +59,30 @@ class MemoryManager:
59
  async def list_memories(self, category: str | None = None) -> list[dict]:
60
  """List all stored memories with their metadata."""
61
  return await self.storage.list_memories(category)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  async def list_memories(self, category: str | None = None) -> list[dict]:
60
  """List all stored memories with their metadata."""
61
  return await self.storage.list_memories(category)
62
+
63
+ async def journal(self, content: str) -> str:
64
+ """Write a journal entry to today's daily journal file.
65
+
66
+ Args:
67
+ content: The journal entry content.
68
+
69
+ Returns:
70
+ Confirmation message with the file path.
71
+ """
72
+ path = await self.storage.journal(content)
73
+ return f"Journal entry added to {path}"
74
+
75
+ async def load_critical(self) -> str:
76
+ """Load critical memories (CORE.md)."""
77
+ return await self.storage.load_core()
78
+
79
+ async def load_recent_journal(self, days: int = 3) -> str:
80
+ """Load recent journal entries.
81
+
82
+ Args:
83
+ days: Number of days to look back.
84
+
85
+ Returns:
86
+ Combined journal entries.
87
+ """
88
+ return await self.storage.load_recent_journal(days)
memory/storage.py CHANGED
@@ -155,3 +155,61 @@ class MemoryStorage:
155
  fm = {}
156
  body = parts[2].strip()
157
  return fm, body
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  fm = {}
156
  body = parts[2].strip()
157
  return fm, body
158
+
159
+ async def journal(self, content: str) -> Path:
160
+ """Write a journal entry to today's daily journal file.
161
+
162
+ Args:
163
+ content: The journal entry content.
164
+
165
+ Returns:
166
+ The path to the journal file.
167
+ """
168
+ journal_dir = self.base_path / "journal"
169
+ journal_dir.mkdir(parents=True, exist_ok=True)
170
+
171
+ today = datetime.now().strftime("%Y-%m-%d")
172
+ journal_path = journal_dir / f"{today}.md"
173
+
174
+ # Append to existing journal or create new one
175
+ timestamp = datetime.now().strftime("%H:%M:%S")
176
+ entry = f"\n\n## {timestamp}\n\n{content}\n"
177
+
178
+ if journal_path.exists():
179
+ async with aiofiles.open(journal_path, "a", encoding="utf-8") as f:
180
+ await f.write(entry)
181
+ else:
182
+ frontmatter = {
183
+ "date": today,
184
+ "type": "journal",
185
+ }
186
+ text = self._format_with_frontmatter(frontmatter, f"# Journal - {today}\n{entry}")
187
+ async with aiofiles.open(journal_path, "w", encoding="utf-8") as f:
188
+ await f.write(text)
189
+
190
+ return journal_path
191
+
192
+ async def load_recent_journal(self, days: int = 3) -> str:
193
+ """Load recent journal entries.
194
+
195
+ Args:
196
+ days: Number of days to look back.
197
+
198
+ Returns:
199
+ Combined journal entries.
200
+ """
201
+ journal_dir = self.base_path / "journal"
202
+ if not journal_dir.exists():
203
+ return ""
204
+
205
+ from datetime import timedelta
206
+
207
+ parts = []
208
+ for i in range(days):
209
+ date = datetime.now() - timedelta(days=i)
210
+ journal_path = journal_dir / f"{date.strftime('%Y-%m-%d')}.md"
211
+ if journal_path.exists():
212
+ async with aiofiles.open(journal_path, "r", encoding="utf-8") as f:
213
+ parts.append(await f.read())
214
+
215
+ return "\n\n---\n\n".join(parts)
tools/memory_tools.py CHANGED
@@ -44,3 +44,16 @@ async def run_recall(query: str) -> str:
44
  """
45
  manager = _get_manager()
46
  return await manager.recall(query)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  """
45
  manager = _get_manager()
46
  return await manager.recall(query)
47
+
48
+
49
+ async def run_journal(content: str) -> str:
50
+ """Write a journal entry to today's daily journal.
51
+
52
+ Args:
53
+ content: The journal entry content.
54
+
55
+ Returns:
56
+ Confirmation message.
57
+ """
58
+ manager = _get_manager()
59
+ return await manager.journal(content)