diff --git a/muse/activity.md b/muse/activity.md index de8f85f33..665b2dd08 100644 --- a/muse/activity.md +++ b/muse/activity.md @@ -2,7 +2,7 @@ "type": "generate", "title": "Activity Synthesis", - "description": "Synthesizes segment activity from screenshots and audio, focusing on observable changes and searchability.", + "description": "Synthesizes segment activity from content, focusing on observable changes and searchability.", "color": "#00bcd4", "schedule": "segment", "priority": 10, @@ -18,32 +18,20 @@ $segment_preamble # Segment Activity Synthesis -## Core Rule +Report the key activities, discussions, and actions observable in this content. -ONLY report what CHANGED between screenshots or was SPOKEN in audio. -If content looks the same across frames, skip it entirely. - -## Your Inputs - -- **Screenshots**: Sampled across this segment. Compare frames - what's different? -- **Audio**: Transcript of speech. What was said? +$import_guidance ## Banned Language -Never use these words - they describe presence, not action: +Never use these words — they describe presence, not action: - reviewing, monitoring, tracking, checking, observing, maintaining, managing -Use action verbs instead: wrote, sent, received, created, deleted, switched to, typed, said, discussed, decided +Use action verbs instead: wrote, sent, received, created, deleted, switched to, typed, said, discussed, decided, asked, proposed, resolved ## What to Report -For each item, identify the CHANGE: -- "Typed message to X about Y" (text appeared) -- "Switched from Gmail to Terminal" (window focus changed) -- "Received reply from X" (new message appeared) -- "Said X about Y in meeting" (audio evidence) - -If you cannot name the specific change, do not include it. +For each item, identify what happened — the specific action, change, or exchange. ### Facets Which project/context? Every segment has at least one. @@ -53,18 +41,7 @@ Project names, people, problems, decisions, tools. ## Before Writing -For each item, ask: -- Can I point to a SPECIFIC CHANGE between screenshots? -- Or SPECIFIC WORDS spoken in audio? - -If neither, omit it. - -## SKIP Entirely - -- Windows that look identical in first and last frame -- Apps open but showing same content throughout -- Background windows never brought to focus -- Anything you'd describe as "had open" or "was visible" +For each item, ask: can I point to a SPECIFIC action, exchange, or change in the content? If not, omit it. ## Output Format diff --git a/muse/decisions.md b/muse/decisions.md index 9d6b13d4f..2539aa8ad 100644 --- a/muse/decisions.md +++ b/muse/decisions.md @@ -68,8 +68,8 @@ A ranked Markdown list of decision-actions found. For each item, provide: - **Entities:** people, teams, groups, projects/issues, repos/branches, docs/artifacts, meetings, environments, orgs - **Impact Surface:** approx # people affected; external stakeholders? breadth (low/med/high); criticality flags (time_sensitive, high_centrality, irreversible) - **Evidence:** - - Audio quotes (<= 20 words each, 1–2 max) - - Screen phrases (OCR/visual cues of enactment) + - Transcript quotes (<= 20 words each, 1–2 max) + - Screen phrases (OCR/visual cues of enactment, when available) - Metadata notes (audience size, env flags, etc.) - **Stakes for Others:** <= 30 words on likely consequences - **Confidence:** 0.0–1.0 calibration (0.50 maybe, 0.70 likely, 0.85+ clear) @@ -79,5 +79,5 @@ If no decision-actions are found in this activity, output only a brief sentence STRICT RULES - Do not fabricate entities or counts; estimate only from inputs. - Anchor times to actual segment boundaries. -- Evidence should include both intent (often audio) and enactment (often screen/metadata) when available. +- Evidence should include both intent (often transcript) and enactment (often screen/metadata) when available. - Maintain Markdown only; no JSON or code blocks in the final output. diff --git a/muse/entities.md b/muse/entities.md index a00c77145..51ff1b692 100644 --- a/muse/entities.md +++ b/muse/entities.md @@ -34,5 +34,5 @@ Output as a markdown list. Each line has three parts separated by colon and dash * Type: Entity Name - Description Example: -* Person: Alice Smith - Mentioned in Slack discussing the project timeline -* Tool: Grafana - Visible on screen showing metrics dashboards +* Person: Alice Smith - Mentioned in discussion about the project timeline +* Tool: Grafana - Referenced for monitoring metrics dashboards diff --git a/muse/followups.md b/muse/followups.md index 76b40f460..7dff9a2b5 100644 --- a/muse/followups.md +++ b/muse/followups.md @@ -32,7 +32,7 @@ Use the Activity Context and Activity State Per Segment sections above to unders 1. **Sequential Review** - Read the transcript chronologically, one block at a time. - - Look for statements or screen cues indicating outstanding tasks, open questions, or commitments to reconnect later. + - Look for statements or contextual cues indicating outstanding tasks, open questions, or commitments to reconnect later. 2. **Recognize Follow-up Triggers** - Phrases such as "I'll do that tomorrow," "Let's talk later," or "Need to check". diff --git a/muse/knowledge_graph.md b/muse/knowledge_graph.md index 39ee0c5d4..9f03ba3ba 100644 --- a/muse/knowledge_graph.md +++ b/muse/knowledge_graph.md @@ -58,9 +58,9 @@ $daily_preamble * A qualitative description of what a visual network diagram of this day would highlight. Include specific examples of the 2-3 most interesting or unexpected connections discovered, explaining why they are noteworthy (e.g., "An interesting connection is Person A using Tool Z, typically associated with Project Q, for an ad-hoc task related to Concept R. This suggests a novel application or workaround."). **Key Considerations:** -* Synthesize information from both audio and screen transcript data within each chunk. +* Synthesize information from all transcript content within each chunk. * Disambiguate entities: e.g., "John" referring to "John Doe." * Infer implicit relationships where explicit statements are lacking but context strongly suggests a connection. * Focus on the most relevant and significant entities and relationships to avoid an overly noisy graph. -* $Preferred often multi-tasks where joined on a team zoom in the background while working on an unrelated task, so the audio transcripts may not always align with the screen transcripts. +* For live capture, $preferred often multi-tasks — e.g., joined on a team zoom in the background while working on an unrelated task — so different content streams may not always align. * Take time to consider all of the nuance of the interactions from the day, deeply think through how best to prioritize the most important aspects and understandings, formulate the best approach for each step of the analysis. diff --git a/muse/observation.md b/muse/observation.md index 99cccbe9a..ed626b353 100644 --- a/muse/observation.md +++ b/muse/observation.md @@ -9,6 +9,7 @@ "tier": 3, "thinking_budget": 2048, "max_output_tokens": 2048, + "exclude_streams": ["import.*"], "instructions": { "sources": {"transcripts": true, "percepts": true, "agents": false} } diff --git a/muse/speakers.md b/muse/speakers.md index 7843e5cd1..8e8c43914 100644 --- a/muse/speakers.md +++ b/muse/speakers.md @@ -7,6 +7,7 @@ "priority": 10, "output": "json", "color": "#e64a19", + "exclude_streams": ["import.*"], "instructions": { "sources": {"transcripts": "required", "percepts": true, "agents": false} } diff --git a/think/agents.py b/think/agents.py index 4bf0fd4c0..b4d25d97c 100644 --- a/think/agents.py +++ b/think/agents.py @@ -100,6 +100,101 @@ class JSONEventWriter: # ============================================================================= +def _stream_content_description(stream: str | None) -> str: + """Return a human-readable content description for a stream. + + Used in preamble templates so agents know what kind of content they're + analyzing (live capture vs imported conversations, notes, etc.). + """ + if not stream: + return "audio transcription and screen recording" + + STREAM_DESCRIPTIONS = { + "archon": "audio transcription and screen recording", + "import.chatgpt": "an imported ChatGPT conversation", + "import.claude": "an imported Claude conversation", + "import.gemini": "an imported Gemini conversation", + "import.ics": "an imported calendar event", + "import.obsidian": "an imported note from Obsidian", + "import.kindle": "imported Kindle reading highlights", + } + + if stream in STREAM_DESCRIPTIONS: + return STREAM_DESCRIPTIONS[stream] + + # Fallback for unknown import streams + if stream.startswith("import."): + source = stream.split(".", 1)[1] + return f"imported content from {source}" + + return "captured content" + + +def _stream_import_guidance(stream: str | None) -> str: + """Return stream-conditional guidance for the activity agent. + + For live capture, returns guidance about frame comparison and spoken audio. + For imports, returns content-type-specific analysis instructions. + Returns empty string for unknown streams. + """ + if not stream or stream == "archon": + return ( + "## Live Capture Guidance\n\n" + "ONLY report what CHANGED between screenshots or was SPOKEN in audio. " + "If content looks the same across frames, skip it entirely.\n\n" + "### Your Inputs\n\n" + "- **Screenshots**: Sampled across this segment. Compare frames — what's different?\n" + "- **Audio**: Transcript of speech. What was said?\n\n" + "### SKIP Entirely\n\n" + "- Windows that look identical in first and last frame\n" + "- Apps open but showing same content throughout\n" + "- Background windows never brought to focus\n" + "- Anything you'd describe as \"had open\" or \"was visible\"" + ) + + IMPORT_GUIDANCE = { + "import.chatgpt": ( + "This is an AI conversation. Summarize the key topics discussed, " + "questions asked, solutions proposed, and decisions reached. " + "Focus on what the human was trying to accomplish and what they learned or decided." + ), + "import.claude": ( + "This is an AI conversation. Summarize the key topics discussed, " + "questions asked, solutions proposed, and decisions reached. " + "Focus on what the human was trying to accomplish and what they learned or decided." + ), + "import.gemini": ( + "This is an AI conversation. Summarize the key topics discussed, " + "questions asked, solutions proposed, and decisions reached. " + "Focus on what the human was trying to accomplish and what they learned or decided." + ), + "import.ics": ( + "This is a calendar event. Describe the event: its purpose, " + "participants, and any context from the description about why it was scheduled." + ), + "import.obsidian": ( + "This is a note. Summarize the key ideas, references, and connections. " + "What was the author thinking about and working through?" + ), + "import.kindle": ( + "These are reading highlights. Describe what was being read and what " + "the reader found noteworthy. What themes or ideas do these highlights capture?" + ), + } + + if stream in IMPORT_GUIDANCE: + return f"## Content Guidance\n\n{IMPORT_GUIDANCE[stream]}" + + if stream.startswith("import."): + return ( + "## Content Guidance\n\n" + "This is imported content. Summarize the key topics, actions, " + "and takeaways present in this segment." + ) + + return "" + + def _build_prompt_context( day: str | None, segment: str | None, @@ -119,6 +214,7 @@ def _build_prompt_context( - day: Friendly format (e.g., "Sunday, February 2, 2025") - day_YYYYMMDD: Raw day string (e.g., "20250202") - segment_start, segment_end: Time strings if segment/span provided + - stream, content_description: Stream name and human-readable description - activity_*: Activity fields if activity record provided """ context: dict[str, str] = {} @@ -128,6 +224,12 @@ def _build_prompt_context( context["day"] = format_day(day) context["day_YYYYMMDD"] = day + # Stream-aware content description and import guidance + stream = os.environ.get("SOL_STREAM") + context["stream"] = stream or "archon" + context["content_description"] = _stream_content_description(stream) + context["import_guidance"] = _stream_import_guidance(stream) + if segment: start_str, end_str = format_segment_times(segment) if start_str and end_str: diff --git a/think/dream.py b/think/dream.py index 9cecd1ac0..8bccf3ff3 100644 --- a/think/dream.py +++ b/think/dream.py @@ -9,6 +9,7 @@ run in parallel, then dream waits for completion before the next group. """ import argparse +import fnmatch import logging import sys import threading @@ -411,6 +412,15 @@ def run_prompts_by_priority( for prompt_name, config in prompts_list: is_generate = config["type"] == "generate" + # Check exclude_streams filter + exclude_patterns = config.get("exclude_streams") + if exclude_patterns and stream: + if any(fnmatch.fnmatch(stream, pat) for pat in exclude_patterns): + logging.info( + f"Skipping {prompt_name}: stream '{stream}' matches exclude_streams" + ) + continue + try: if config.get("multi_facet"): always_run = config.get("always", False) diff --git a/think/templates/activity_preamble.md b/think/templates/activity_preamble.md index 9c6728e68..2d0c8cf71 100644 --- a/think/templates/activity_preamble.md +++ b/think/templates/activity_preamble.md @@ -1,7 +1,7 @@ -You are analyzing a **$activity_type** activity from $preferred's workday on **$day** ($day_YYYYMMDD), covering **$segment_start to $segment_end** (~$activity_duration minutes). +You are analyzing a **$activity_type** activity from $preferred's journal on **$day** ($day_YYYYMMDD), covering **$segment_start to $segment_end** (~$activity_duration minutes). **Activity:** $activity_type **Description:** $activity_description **Entities involved:** $activity_entities -The transcript below contains all audio and screen data from the recording segments where this activity occurred. These segments may also contain content from other concurrent activities — focus your analysis ONLY on content related to this $activity_type activity. +The transcript below contains $content_description from the segments where this activity occurred. These segments may also contain content from other concurrent activities — focus your analysis ONLY on content related to this $activity_type activity. diff --git a/think/templates/daily_preamble.md b/think/templates/daily_preamble.md index d181405a3..885510e70 100644 --- a/think/templates/daily_preamble.md +++ b/think/templates/daily_preamble.md @@ -1,3 +1,3 @@ -You are an expert analyst tasked with analyzing $preferred's full workday transcript from **$day** ($day_YYYYMMDD). The transcript contains both audio conversations and screen activity data, organized into recording segments with timestamps. +You are an expert analyst tasked with analyzing $preferred's full day journal from **$day** ($day_YYYYMMDD). The content is organized into segments with timestamps, containing $content_description. You will be given the transcripts followed by a detailed request for how to process them. Follow those instructions carefully. Take time to consider all of the nuance of the interactions from the day, think through how best to prioritize the most important aspects, and formulate the best approach for each step of the analysis. diff --git a/think/templates/segment_preamble.md b/think/templates/segment_preamble.md index 893d1aaf1..c66e6a8f4 100644 --- a/think/templates/segment_preamble.md +++ b/think/templates/segment_preamble.md @@ -1,3 +1,3 @@ -You are analyzing a recording segment from $preferred's workday on **$day** ($day_YYYYMMDD), covering **$segment_start to $segment_end**. This segment captures a specific time window of activity through audio transcription and screen recording. +You are analyzing a segment from $preferred's journal on **$day** ($day_YYYYMMDD), covering **$segment_start to $segment_end**. This segment contains $content_description. -Focus your analysis on this discrete period - its context, activities, and significance within the broader day. +Focus your analysis on this discrete period — its context, content, and significance within the broader day.