personal memory agent
0
fork

Configure Feed

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

Remove unused dream --run single-prompt mode

The --run flag was only used manually from the CLI and never called
programmatically by supervisor or any other component. Drop the
run_single_prompt() function (~270 lines), its argparse flag, dispatch
block, validation references, test, and docs mention.

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

+11 -325
+1 -1
docs/THINK.md
··· 24 24 25 25 ```bash 26 26 sol call transcripts read YYYYMMDD [--start HHMMSS --length MINUTES] 27 - sol dream [--day YYYYMMDD] [--segment HHMMSS_LEN] [--stream NAME] [--refresh] [--run NAME] [--flush] 27 + sol dream [--day YYYYMMDD] [--segment HHMMSS_LEN] [--stream NAME] [--refresh] [--flush] 28 28 sol supervisor [--no-observers] 29 29 sol cortex [--host HOST] [--port PORT] [--path PATH] 30 30 sol muse list [--schedule daily|segment] [--json]
-22
tests/test_dream_full.py
··· 111 111 configs = get_muse_configs(schedule="daily") 112 112 for name, config in configs.items(): 113 113 assert "priority" in config, f"Scheduled prompt '{name}' missing priority" 114 - 115 - 116 - def test_run_single_prompt_validates_schedule(tmp_path, monkeypatch): 117 - """Test that --run validates schedule compatibility.""" 118 - mod = importlib.import_module("think.dream") 119 - journal = copy_journal(tmp_path) 120 - monkeypatch.setenv("JOURNAL_PATH", str(journal)) 121 - 122 - # Mock to avoid actual execution 123 - def mock_cortex_request(*args, **kwargs): 124 - return "mock-id" 125 - 126 - def mock_wait_for_agents(*args, **kwargs): 127 - return ({"mock-id": "finish"}, []) 128 - 129 - monkeypatch.setattr(mod, "cortex_request", mock_cortex_request) 130 - monkeypatch.setattr(mod, "wait_for_agents", mock_wait_for_agents) 131 - 132 - # Running a daily prompt with --segment should fail 133 - # Note: This requires a real daily prompt in the fixtures 134 - # For now, just verify the function exists and is callable 135 - assert callable(mod.run_single_prompt)
+10 -302
think/dream.py
··· 605 605 return (total_success, total_failed, all_failed_names) 606 606 607 607 608 - def run_single_prompt( 609 - day: str, 610 - name: str, 611 - segment: str | None = None, 612 - refresh: bool = False, 613 - facet: str | None = None, 614 - stream: str | None = None, 615 - ) -> bool: 616 - """Run a single prompt (generator or agent) by name. 617 - 618 - Args: 619 - day: Day in YYYYMMDD format 620 - name: Prompt name from muse/*.md (e.g., 'activity', 'timeline') 621 - segment: Optional segment key in HHMMSS_LEN format 622 - refresh: Whether to regenerate existing output 623 - facet: Optional facet name for multi-facet agents 624 - 625 - Returns: 626 - True if successful, False if failed 627 - """ 628 - # Load all configs to find the prompt 629 - all_configs = get_muse_configs(include_disabled=True) 630 - 631 - if name not in all_configs: 632 - logging.error(f"Prompt not found: {name}") 633 - logging.info(f"Available prompts: {', '.join(sorted(all_configs.keys()))}") 634 - return False 635 - 636 - config = all_configs[name] 637 - 638 - if config.get("disabled"): 639 - logging.warning(f"Prompt '{name}' is disabled") 640 - return False 641 - 642 - is_generate = config["type"] == "generate" 643 - 644 - # Validate segment compatibility with schedule 645 - prompt_schedule = config.get("schedule") 646 - if segment and prompt_schedule == "daily": 647 - logging.error( 648 - f"'{name}' is a daily prompt (schedule='daily'), " 649 - "but --segment was specified. Remove --segment to run this prompt." 650 - ) 651 - return False 652 - if not segment and prompt_schedule == "segment": 653 - logging.error( 654 - f"'{name}' is a segment prompt (schedule='segment'), " 655 - "but no --segment was specified. Add --segment HHMMSS_LEN to run this prompt." 656 - ) 657 - return False 658 - 659 - if facet and not config.get("multi_facet"): 660 - logging.warning(f"'{name}' is not a multi-facet agent, --facet will be ignored") 661 - facet = None 662 - 663 - day_formatted = iso_date(day) 664 - 665 - if is_generate: 666 - logging.info(f"Running generator: {name}") 667 - 668 - request_config = { 669 - "day": day, 670 - "output": config.get("output", "md"), 671 - } 672 - if segment: 673 - request_config["segment"] = segment 674 - if refresh: 675 - request_config["refresh"] = True 676 - 677 - try: 678 - agent_id = _cortex_request_with_retry( 679 - prompt="", 680 - name=name, 681 - config=request_config, 682 - ) 683 - if agent_id is None: 684 - logging.error(f"Failed to send cortex request for generator '{name}'") 685 - return False 686 - logging.info(f"Spawned generator {name} (ID: {agent_id})") 687 - emit( 688 - "agent_started", 689 - day=day, 690 - segment=segment, 691 - name=name, 692 - agent_id=agent_id, 693 - ) 694 - 695 - completed, timed_out = wait_for_agents([agent_id], timeout=610) 696 - 697 - if timed_out: 698 - logging.error(f"Generator {name} timed out (ID: {agent_id})") 699 - emit( 700 - "agent_completed", 701 - day=day, 702 - segment=segment, 703 - name=name, 704 - agent_id=agent_id, 705 - state="timeout", 706 - ) 707 - return False 708 - 709 - end_state = completed.get(agent_id, "unknown") 710 - if end_state == "finish": 711 - logging.info(f"Generator {name} completed successfully") 712 - emit( 713 - "agent_completed", 714 - day=day, 715 - segment=segment, 716 - name=name, 717 - agent_id=agent_id, 718 - state="finish", 719 - ) 720 - day_log(day, f"dream --run {name} ok") 721 - return True 722 - else: 723 - logging.error(f"Generator {name} ended with state: {end_state}") 724 - emit( 725 - "agent_completed", 726 - day=day, 727 - segment=segment, 728 - name=name, 729 - agent_id=agent_id, 730 - state=end_state, 731 - ) 732 - return False 733 - 734 - except Exception as e: 735 - logging.error(f"Failed to run generator {name}: {e}") 736 - return False 737 - 738 - else: 739 - logging.info(f"Running agent: {name}") 740 - 741 - input_summary = day_input_summary(day) 742 - spawned_ids: list[tuple[str, str | None]] = [] 743 - 744 - if config.get("multi_facet"): 745 - facets_data = get_facets() 746 - enabled_facets = { 747 - k: v for k, v in facets_data.items() if not v.get("muted", False) 748 - } 749 - active_facets = get_active_facets(day) 750 - always_run = config.get("always", False) 751 - 752 - if facet: 753 - if facet not in enabled_facets: 754 - logging.error(f"Facet '{facet}' not found or is muted") 755 - return False 756 - target_facets = [facet] 757 - else: 758 - target_facets = [ 759 - f for f in enabled_facets.keys() if always_run or f in active_facets 760 - ] 761 - 762 - if not target_facets: 763 - logging.warning(f"No active facets for {name} on {day_formatted}") 764 - return True 765 - 766 - for facet_name in target_facets: 767 - try: 768 - logging.info(f"Spawning {name} for facet: {facet_name}") 769 - request_config = {"facet": facet_name, "day": day} 770 - env: dict[str, str] = { 771 - "SOL_DAY": day, 772 - "SOL_FACET": facet_name, 773 - } 774 - if segment: 775 - request_config["segment"] = segment 776 - env["SOL_SEGMENT"] = segment 777 - if stream: 778 - env["SOL_STREAM"] = stream 779 - request_config["env"] = env 780 - agent_id = _cortex_request_with_retry( 781 - prompt=f"Processing facet '{facet_name}' for {day_formatted}: {input_summary}. Use get_facet('{facet_name}') to load context.", 782 - name=name, 783 - config=request_config, 784 - ) 785 - if agent_id is None: 786 - logging.error( 787 - f"Failed to send cortex request for {name}/{facet_name}" 788 - ) 789 - continue 790 - spawned_ids.append((agent_id, facet_name)) 791 - emit( 792 - "agent_started", 793 - day=day, 794 - segment=segment, 795 - name=name, 796 - agent_id=agent_id, 797 - facet=facet_name, 798 - ) 799 - logging.info(f"Started {name} for {facet_name} (ID: {agent_id})") 800 - except Exception as e: 801 - logging.error(f"Failed to spawn {name} for {facet_name}: {e}") 802 - 803 - else: 804 - try: 805 - request_config = {"day": day} 806 - env: dict[str, str] = {"SOL_DAY": day} 807 - if segment: 808 - request_config["segment"] = segment 809 - env["SOL_SEGMENT"] = segment 810 - if stream: 811 - env["SOL_STREAM"] = stream 812 - request_config["env"] = env 813 - 814 - agent_id = _cortex_request_with_retry( 815 - prompt=f"Running task for {day_formatted}: {input_summary}.", 816 - name=name, 817 - config=request_config, 818 - ) 819 - if agent_id is None: 820 - logging.error(f"Failed to send cortex request for '{name}'") 821 - return False 822 - spawned_ids.append((agent_id, None)) 823 - emit( 824 - "agent_started", 825 - day=day, 826 - segment=segment, 827 - name=name, 828 - agent_id=agent_id, 829 - ) 830 - logging.info(f"Started {name} agent (ID: {agent_id})") 831 - except Exception as e: 832 - logging.error(f"Failed to spawn {name}: {e}") 833 - return False 834 - 835 - if not spawned_ids: 836 - return False 837 - 838 - logging.info(f"Waiting for {len(spawned_ids)} agent(s)...") 839 - completed, timed_out = wait_for_agents( 840 - [agent_id for agent_id, _ in spawned_ids], timeout=610 841 - ) 842 - 843 - if timed_out: 844 - logging.warning(f"{len(timed_out)} agent(s) timed out: {timed_out}") 845 - 846 - error_count = 0 847 - for agent_id, agent_facet in spawned_ids: 848 - if agent_id in timed_out: 849 - emit( 850 - "agent_completed", 851 - day=day, 852 - segment=segment, 853 - name=name, 854 - agent_id=agent_id, 855 - state="timeout", 856 - **({"facet": agent_facet} if agent_facet else {}), 857 - ) 858 - continue 859 - end_state = completed.get(agent_id, "unknown") 860 - emit( 861 - "agent_completed", 862 - day=day, 863 - segment=segment, 864 - name=name, 865 - agent_id=agent_id, 866 - state=end_state, 867 - **({"facet": agent_facet} if agent_facet else {}), 868 - ) 869 - if end_state == "error": 870 - logging.error(f"Agent {agent_id} ended with error") 871 - error_count += 1 872 - 873 - success = len(completed) > 0 and len(timed_out) == 0 and error_count == 0 874 - if success: 875 - day_log(day, f"dream --run {name} ok") 876 - elif error_count > 0: 877 - logging.error(f"{error_count} agent(s) ended with errors") 878 - return success 879 - 880 - 881 608 def run_activity_prompts( 882 609 day: str, 883 610 activity_id: str, ··· 1385 1112 parser.add_argument( 1386 1113 "--segments", 1387 1114 action="store_true", 1388 - help="Re-process all segments for the day (incompatible with --segment, --run, --facet)", 1389 - ) 1390 - parser.add_argument( 1391 - "--run", 1392 - metavar="NAME", 1393 - help="Run a single prompt by name (e.g., 'activity', 'timeline')", 1115 + help="Re-process all segments for the day (incompatible with --segment, --facet)", 1394 1116 ) 1395 1117 parser.add_argument( 1396 1118 "--facet", 1397 1119 metavar="NAME", 1398 - help="Target a specific facet (only used with --run or --activity)", 1120 + help="Target a specific facet (only used with --activity)", 1399 1121 ) 1400 1122 parser.add_argument( 1401 1123 "--activity", ··· 1439 1161 incompatible.append("--day") 1440 1162 if args.segment: 1441 1163 incompatible.append("--segment") 1442 - if args.run: 1443 - incompatible.append("--run") 1444 1164 if args.facet: 1445 1165 incompatible.append("--facet") 1446 1166 if args.activity: ··· 1464 1184 if not day_dir.is_dir(): 1465 1185 parser.error(f"Day folder not found: {day_dir}") 1466 1186 1467 - if args.facet and not args.run and not args.activity: 1468 - parser.error("--facet requires --run or --activity") 1187 + if args.facet and not args.activity: 1188 + parser.error("--facet requires --activity") 1469 1189 1470 1190 if args.activity and not args.facet: 1471 1191 parser.error("--activity requires --facet") ··· 1485 1205 if args.activity and not args.day: 1486 1206 parser.error("--activity requires --day") 1487 1207 1488 - if args.activity and (args.segment or args.run or args.segments or args.flush): 1208 + if args.activity and (args.segment or args.segments or args.flush): 1489 1209 parser.error( 1490 - "--activity is incompatible with --segment, --run, --segments, and --flush" 1210 + "--activity is incompatible with --segment, --segments, and --flush" 1491 1211 ) 1492 1212 1493 1213 if args.flush and not args.segment: 1494 1214 parser.error("--flush requires --segment") 1495 1215 1496 - if args.flush and (args.run or args.segments or args.refresh): 1497 - parser.error("--flush is incompatible with --run, --segments, and --refresh") 1216 + if args.flush and (args.segments or args.refresh): 1217 + parser.error("--flush is incompatible with --segments and --refresh") 1498 1218 1499 - if args.segments and (args.segment or args.run or args.facet): 1500 - parser.error("--segments is incompatible with --segment, --run, and --facet") 1219 + if args.segments and (args.segment or args.facet): 1220 + parser.error("--segments is incompatible with --segment and --facet") 1501 1221 1502 1222 # Start callosum connection 1503 1223 _callosum = CallosumConnection(defaults={"rev": get_rev()}) ··· 1527 1247 day=day, 1528 1248 segment=args.segment, 1529 1249 verbose=args.verbose, 1530 - stream=args.stream, 1531 - ) 1532 - sys.exit(0 if success else 1) 1533 - 1534 - # Handle single prompt execution mode 1535 - if args.run: 1536 - success = run_single_prompt( 1537 - day=day, 1538 - name=args.run, 1539 - segment=args.segment, 1540 - refresh=args.refresh, 1541 - facet=args.facet, 1542 1250 stream=args.stream, 1543 1251 ) 1544 1252 sys.exit(0 if success else 1)