personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Rename hook functions to post_process for pre/post hook architecture

Update generator frontmatter to use {"post": "hookname"} object syntax
instead of plain string, enabling future pre-hook support. Rename
process() to post_process() in hook modules to match the new convention.

- Update 14 generator .md files with new hook object syntax
- Rename process() to post_process() in anticipation.py, entities.py, occurrence.py
- Simplify hook docstrings to reference HookContext
- Update THINK.md to reflect agents.py/generate.py consolidation
- Minor formatting cleanup in cluster.py and google.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+31 -55
+2 -3
docs/THINK.md
··· 230 230 231 231 ## Key Components 232 232 233 - - **cortex.py** - Central agent manager, file watcher, event distribution, routes to agents.py or generate.py 233 + - **cortex.py** - Central agent manager, file watcher, event distribution, spawns agents.py 234 234 - **cortex_client.py** - Client functions: `cortex_request()`, `cortex_agents()`, `wait_for_agents()` 235 235 - **mcp.py** - FastMCP server with journal search tools 236 - - **agents.py** - CLI entry point for tool-using agents (NDJSON protocol) 237 - - **generate.py** - CLI entry point for generators (NDJSON protocol), spawned by Cortex 236 + - **agents.py** - Unified CLI entry point for both tool-using agents and generators (NDJSON protocol) 238 237 - **models.py** - Unified `generate()`/`agenerate()` API, provider routing, token logging 239 238 - **batch.py** - `Batch` class for concurrent LLM requests with dynamic queuing 240 239
+4 -10
muse/anticipation.py
··· 3 3 4 4 """Hook for extracting anticipation events from generator output results. 5 5 6 - This hook is invoked via "hook": "anticipation" in generator frontmatter. 6 + This hook is invoked via "hook": {"post": "anticipation"} in generator frontmatter. 7 7 It extracts structured JSON events for future scheduled items and writes 8 8 them to facet-based JSONL files. 9 9 """ ··· 22 22 from think.utils import get_output_topic, load_prompt 23 23 24 24 25 - def process(result: str, context: dict) -> str | None: 25 + def post_process(result: str, context: dict) -> str | None: 26 26 """Extract anticipation events from generator output result. 27 27 28 28 This hook extracts structured JSON events for future scheduled items ··· 30 30 31 31 Args: 32 32 result: The generated output markdown content. 33 - context: Hook context with keys: 34 - - day: YYYYMMDD string 35 - - segment: segment key or None 36 - - name: generator name, e.g., "schedule" 37 - - output_path: absolute path to output file 38 - - meta: dict with frontmatter 39 - - transcript: the clustered transcript markdown 40 - - span: True if processing a span of segments 33 + context: HookContext with keys including day, segment, name, 34 + output_path, meta, transcript, span. 41 35 42 36 Returns: 43 37 None - this hook does not modify the output result.
+1 -1
muse/decisions.md
··· 3 3 "title": "Decision Actions", 4 4 "description": "Tracks consequential decision-actions that change state, plans, resources, responsibilities, or timing in ways that affect other people.", 5 5 "occurrences": "Create an occurrence for every decision-action observed. Include the time span, decision type, actors involved, entities affected, and impact assessment. Each occurrence should capture both the intent and enactment of the decision.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#dc3545", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/documentation.md
··· 3 3 "title": "Documentation Moments", 4 4 "description": "Finds when important knowledge is shared in the transcript and suggests what should be written down. Output is a Markdown list of documentation opportunities with time ranges and destinations.", 5 5 "occurrences": "Record an occurrence whenever a new procedure, decision or troubleshooting step is described. Capture the related file or tool and where the documentation should live such as wiki or README.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#007bff", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/entities.md
··· 4 4 "description": "Extracts people, companies, projects, and tools from segment content", 5 5 "color": "#2e7d32", 6 6 "schedule": "segment", 7 - "hook": "entities", 7 + "hook": {"post": "entities"}, 8 8 "thinking_budget": 4096, 9 9 "max_output_tokens": 1024, 10 10 "output": "md",
+4 -9
muse/entities.py
··· 3 3 4 4 """Hook for extracting entities from insight results and writing to JSONL. 5 5 6 - This hook is invoked via "hook": "entities" in insight frontmatter. 6 + This hook is invoked via "hook": {"post": "entities"} in generator frontmatter. 7 7 It parses the markdown entity list and writes deduplicated entities 8 8 to a JSONL file in the segment directory. 9 9 """ ··· 64 64 return None 65 65 66 66 67 - def process(result: str, context: dict) -> str | None: 67 + def post_process(result: str, context: dict) -> str | None: 68 68 """Parse entity list and write to segment JSONL file. 69 69 70 70 Args: 71 71 result: The generated output content (markdown entity list). 72 - context: Hook context with keys: 73 - - day: YYYYMMDD string 74 - - segment: segment key (HHMMSS_LEN) 75 - - name: e.g., "entities" 76 - - output_path: absolute path to output file 77 - - meta: dict with frontmatter 78 - - transcript: the clustered transcript markdown 72 + context: HookContext with keys including day, segment, name, 73 + output_path, meta, transcript. 79 74 80 75 Returns: 81 76 None - this hook does not modify the output result.
+1 -1
muse/files.md
··· 3 3 "title": "File Interactions", 4 4 "description": "Reviews the day's transcript to capture each significant file or attachment that was opened, saved or shared. Generates a Markdown timeline with context about how the file was used.", 5 5 "occurrences": "Create an occurrence for every notable file referenced, including the path when known and the related project. Ignore terminal output and focus on visible file explorers or sharing actions.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#28a745", 8 8 "schedule": "daily", 9 9 "disabled": true,
+1 -1
muse/flow.md
··· 3 3 "title": "Day Overview", 4 4 "description": "Summarizes the overall flow of the workday. Looks for patterns in focus, energy, context switching and highlights productivity insights in a Markdown report.", 5 5 "occurrences": "Create an occurrence for noteworthy shifts in work rhythms or focus. Include timestamps when deep work starts or ends, or when energy levels noticeably change. Classify each as work or personal based on the surrounding context.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#17a2b8", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/followups.md
··· 3 3 "title": "Follow-Up Items", 4 4 "description": "Scans the day chronologically to capture promised tasks or reminders for future action. The prompt outputs a concise Markdown list of the most important follow-ups.", 5 5 "occurrences": "Whenever a future task or commitment is mentioned, create an occurrence with the expected action and deadline if known. Note who requested it and whether it is work or personal.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#ffc107", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/knowledge_graph.md
··· 3 3 "title": "Knowledge Graph", 4 4 "description": "Extracts people, projects, tools and other entities from the transcript and maps how they relate. Produces a Markdown report plus narrative describing network hubs and bridges discovered during the day.", 5 5 "occurrences": "For each entity interaction or relationship mentioned, create an occurrence describing the connection. Include start and end times when the relationship is visible, and capture the type of link such as works-on or discusses-with.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#6f42c1", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/media.md
··· 3 3 "title": "Media Consumption", 4 4 "description": "Identifies when videos, articles, music or social content are consumed. Classifies each instance as work or personal and reports the source in chronological sections.", 5 5 "occurrences": "Create an occurrence whenever media consumption is noted, noting the application or site and whether it was for work or leisure. Include a short summary of the topic if visible.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#fd7e14", 8 8 "schedule": "daily", 9 9 "disabled": true,
+1 -1
muse/meetings.md
··· 3 3 "title": "Meeting Summary", 4 4 "description": "Detects all meetings throughout the day by analyzing audio and screen cues. For each meeting it notes time range, participants, topics discussed and whether slides were shown.", 5 5 "occurrences": "Each meeting should generate an occurrence with start and end times, list of participants and a concise summary. If slides are present, mention them in the details field.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#e83e8c", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/messages.md
··· 3 3 "title": "Messaging Activity", 4 4 "description": "Tracks use of email and chat applications across the day. Each interaction is summarized with participants, app used and visible message content.", 5 5 "occurrences": "Create an occurrence for every message read or sent. Include the time block, app name, contacts involved and whether $preferred was reading or replying. Summaries should capture any visible text.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#78909c", 8 8 "schedule": "daily", 9 9 "output": "md"
+4 -10
muse/occurrence.py
··· 3 3 4 4 """Hook for extracting occurrence events from generator output results. 5 5 6 - This hook is invoked via "hook": "occurrence" in generator frontmatter. 6 + This hook is invoked via "hook": {"post": "occurrence"} in generator frontmatter. 7 7 It extracts structured JSON events from markdown summaries and writes 8 8 them to facet-based JSONL files. 9 9 """ ··· 22 22 from think.utils import get_output_topic, load_prompt 23 23 24 24 25 - def process(result: str, context: dict) -> str | None: 25 + def post_process(result: str, context: dict) -> str | None: 26 26 """Extract occurrence events from generator output result. 27 27 28 28 This hook extracts structured JSON events from markdown output summaries ··· 30 30 31 31 Args: 32 32 result: The generated output markdown content. 33 - context: Hook context with keys: 34 - - day: YYYYMMDD string 35 - - segment: segment key or None 36 - - name: generator name, e.g., "meetings", "flow" 37 - - output_path: absolute path to output file 38 - - meta: dict with frontmatter including "occurrences" 39 - - transcript: the clustered transcript markdown 40 - - span: True if processing a span of segments 33 + context: HookContext with keys including day, segment, name, 34 + output_path, meta, transcript, span. 41 35 42 36 Returns: 43 37 None - this hook does not modify the output result.
+1 -1
muse/opportunities.md
··· 3 3 "title": "Innovation Opportunities", 4 4 "description": "Scans conversations and tasks for sparks of new ideas, problem statements and potential ventures. Outputs a list of the most promising opportunities with context and suggested next steps.", 5 5 "occurrences": "Whenever a novel idea or pain point is raised, record an occurrence describing the opportunity and any proposed solution. Include who mentioned it and classify the potential impact.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#20c997", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/research.md
··· 3 3 "title": "Research Needs", 4 4 "description": "Highlights moments where additional information would help progress work. Produces a list of targeted research tasks with time ranges and context.", 5 5 "occurrences": "Log an occurrence each time a knowledge gap or open question appears. Mention the problem area and any suggested resources to investigate so the task can be assigned.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#ff5722", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/schedule.md
··· 2 2 3 3 "title": "Upcoming Schedule", 4 4 "description": "Identifies all future calendar events and scheduled activities noted in transcripts. Extracts dates, times, participants, and event details for anything scheduled beyond today.", 5 - "hook": "anticipation", 5 + "hook": {"post": "anticipation"}, 6 6 "color": "#5e35b1", 7 7 "schedule": "daily", 8 8 "output": "md"
+1 -1
muse/timeline.md
··· 3 3 "title": "Day Timeline", 4 4 "description": "Constructs a detailed chronological timeline documenting every activity, task shift, and event throughout the workday. Creates a comprehensive historical record with rich descriptions of what happened when.", 5 5 "occurrences": "Create an occurrence for each hour segment, don't break down hours into any smaller segments the goal for timeline occurrences is for them to capture whatever happened within each hour of the day where there was activity.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#7b1fa2", 8 8 "schedule": "daily", 9 9 "output": "md"
+1 -1
muse/tools.md
··· 3 3 "title": "Tool Usage", 4 4 "description": "Catalogues every application or service used throughout the day and how long it was active. The report details which tools are critical, supporting or distracting.", 5 5 "occurrences": "Whenever a tool is launched or actively used, create an occurrence noting the time span, purpose and intensity of use. Distinguish between core work tools and occasional utilities.", 6 - "hook": "occurrence", 6 + "hook": {"post": "occurrence"}, 7 7 "color": "#795548", 8 8 "schedule": "daily", 9 9 "disabled": true,
-2
think/cluster.py
··· 511 511 return markdown, len(entries) 512 512 513 513 514 - 515 - 516 514 def _segments_overlap( 517 515 seg_start: datetime, seg_end: datetime, range_start: datetime, range_end: datetime 518 516 ) -> bool:
+2 -6
think/providers/google.py
··· 77 77 raise ValueError("GOOGLE_API_KEY not found in environment") 78 78 client = genai.Client( 79 79 api_key=api_key, 80 - http_options=types.HttpOptions( 81 - retry_options=types.HttpRetryOptions() 82 - ), 80 + http_options=types.HttpOptions(retry_options=types.HttpRetryOptions()), 83 81 ) 84 82 return client 85 83 ··· 638 636 # Create client with retry enabled 639 637 client = genai.Client( 640 638 api_key=api_key, 641 - http_options=types.HttpOptions( 642 - retry_options=types.HttpRetryOptions() 643 - ), 639 + http_options=types.HttpOptions(retry_options=types.HttpRetryOptions()), 644 640 ) 645 641 646 642 # Create fresh chat session