personal memory agent
0
fork

Configure Feed

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

Add facet classification segment generator with priority support

- Add priority-based ordering to run_generators_via_cortex() so generators
can depend on outputs from earlier generators (lower priority runs first)
- Create muse/facets.md segment generator (priority 90) that classifies
segment activity into facets based on other segment outputs
- Uses tier 3 (LITE) model with agents-only sources for fast/cheap inference
- Update muse CLI to display priority field prominently
- Document generator priority support in CORTEX.md with recommended ranges

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

+93 -11
+55
muse/facets.md
··· 1 + { 2 + 3 + "title": "Facet Classification", 4 + "description": "Classifies segment activity into relevant facets based on other segment outputs", 5 + "color": "#7c4dff", 6 + "schedule": "segment", 7 + "priority": 90, 8 + "tier": 3, 9 + "thinking_budget": 1024, 10 + "max_output_tokens": 512, 11 + "output": "json", 12 + "instructions": { 13 + "system": "journal", 14 + "facets": "short", 15 + "sources": {"audio": false, "screen": false, "agents": true} 16 + } 17 + 18 + } 19 + 20 + Classify the activity in this recording segment into relevant facets. 21 + 22 + ## Input 23 + 24 + You receive segment output summaries (activity synthesis, screen record, entities) from the current recording window. 25 + 26 + ## Output Format 27 + 28 + Return a JSON array. Each object has: 29 + - `facet`: facet ID (the slug in parentheses, like "work" or "personal") 30 + - `activity`: 1-sentence description of what was observed for this facet 31 + - `level`: engagement level - "high" (primary focus), "medium" (significant), or "low" (brief/peripheral) 32 + 33 + Only include facets with clear evidence of activity. Omit facets with no connection to observed content. 34 + 35 + ## Examples 36 + 37 + Single facet, focused work: 38 + ```json 39 + [{"facet": "work", "activity": "Code review and authentication module development", "level": "high"}] 40 + ``` 41 + 42 + Multiple facets: 43 + ```json 44 + [ 45 + {"facet": "work", "activity": "Team standup meeting discussing sprint progress", "level": "high"}, 46 + {"facet": "personal", "activity": "Brief personal email check", "level": "low"} 47 + ] 48 + ``` 49 + 50 + No clear facet match: 51 + ```json 52 + [] 53 + ``` 54 + 55 + Return ONLY the JSON array, no other text.
+14 -4
think/dream.py
··· 178 178 def run_generators_via_cortex( 179 179 day: str, force: bool, segment: str | None = None 180 180 ) -> tuple[int, int]: 181 - """Run generators via cortex requests sequentially. 181 + """Run generators via cortex requests sequentially in priority order. 182 + 183 + Generators are sorted by their `priority` field (default: 50), with lower 184 + numbers running first. This allows generators that depend on other outputs 185 + to run after those outputs are created. 182 186 183 187 Args: 184 188 day: YYYYMMDD format ··· 198 202 logging.info("No generators found for schedule: %s", target_schedule) 199 203 return (0, 0) 200 204 205 + # Sort generators by priority (lower numbers first, default 50) 206 + sorted_generators = sorted( 207 + generators.items(), 208 + key=lambda x: (x[1].get("priority", 50), x[0]), 209 + ) 210 + 201 211 logging.info( 202 212 "Running %d generators for %s via cortex: %s", 203 213 len(generators), 204 214 day, 205 - list(generators.keys()), 215 + [name for name, _ in sorted_generators], 206 216 ) 207 217 208 218 success_count = 0 209 219 fail_count = 0 210 220 211 - # Run generators sequentially 212 - for generator_name, generator_data in generators.items(): 221 + # Run generators sequentially in priority order 222 + for generator_name, generator_data in sorted_generators: 213 223 logging.info("Starting generator: %s", generator_name) 214 224 215 225 # Build config for cortex request
+24 -7
think/muse_cli.py
··· 276 276 print(f"\n{rel_path}\n") 277 277 278 278 # Display frontmatter fields 279 - # Order: title, description, then alphabetical for the rest 279 + # Order: title, description, key config fields, then alphabetical for the rest 280 280 priority_keys = [ 281 281 "title", 282 282 "description", 283 283 "schedule", 284 + "priority", 284 285 "output", 285 286 "tools", 286 287 "hook", ··· 350 351 return text, 0 351 352 # Show first half and last half 352 353 half = max_lines // 2 353 - truncated = lines[:half] + ["", f"... ({len(lines) - max_lines} lines omitted)"] + lines[-half:] 354 + truncated = ( 355 + lines[:half] 356 + + ["", f"... ({len(lines) - max_lines} lines omitted)"] 357 + + lines[-half:] 358 + ) 354 359 return "\n".join(truncated), len(lines) - max_lines 355 360 356 361 ··· 530 535 # Pre-hook info 531 536 if dry_run_event.get("pre_hook"): 532 537 mods = dry_run_event.get("pre_hook_modifications", []) 533 - print(f" Pre-hook: {dry_run_event.get('pre_hook')} (modified: {', '.join(mods) or 'none'})") 538 + print( 539 + f" Pre-hook: {dry_run_event.get('pre_hook')} (modified: {', '.join(mods) or 'none'})" 540 + ) 534 541 535 542 # System instruction (show before first if pre-hook modified it) 536 543 if dry_run_event.get("system_instruction_before"): ··· 553 560 dry_run_event.get("user_instruction_before", ""), 554 561 full=full, 555 562 ) 556 - _format_section("USER INSTRUCTION", dry_run_event.get("user_instruction", ""), full=full) 563 + _format_section( 564 + "USER INSTRUCTION", dry_run_event.get("user_instruction", ""), full=full 565 + ) 557 566 558 567 # Extra context (agents only) 559 568 if dry_run_event.get("extra_context"): 560 - _format_section("EXTRA CONTEXT", dry_run_event.get("extra_context", ""), full=full) 569 + _format_section( 570 + "EXTRA CONTEXT", dry_run_event.get("extra_context", ""), full=full 571 + ) 561 572 562 573 # Prompt (show before first if pre-hook modified it) 563 574 prompt_source = dry_run_event.get("prompt_source", "") 564 575 if prompt_source: 565 576 prompt_source = f" (source: {_relative_path(prompt_source)})" 566 577 if dry_run_event.get("prompt_before"): 567 - _format_section("PROMPT (before pre-hook)", dry_run_event.get("prompt_before", ""), full=full) 568 - _format_section(f"PROMPT{prompt_source}", dry_run_event.get("prompt", ""), full=full) 578 + _format_section( 579 + "PROMPT (before pre-hook)", 580 + dry_run_event.get("prompt_before", ""), 581 + full=full, 582 + ) 583 + _format_section( 584 + f"PROMPT{prompt_source}", dry_run_event.get("prompt", ""), full=full 585 + ) 569 586 570 587 # Transcript (generators only, show before first if pre-hook modified it) 571 588 if "transcript" in dry_run_event: