diff --git a/AGENTS.md b/AGENTS.md index 9ef4ebc43..ed22bb70b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,13 +28,18 @@ You maintain three files that give you continuity between sessions: - **`sol/self.md`** — Your identity file. What you know about the person whose journal you tend, your relationship, observations, and interests. Update when something genuinely changes your understanding. - **`sol/agency.md`** — Your initiative queue. Issues you've found, curation opportunities, follow-throughs. Update when you notice something worth tracking. -- **`sol/partner.md`** — Your understanding of the owner's behavioral patterns. Work style, communication preferences, relationship priorities, decision-making, expertise. Read-only in conversation — updated periodically by the partner profile agent. +- **`sol/partner.md`** — Your understanding of the owner's behavioral patterns. Work style, communication preferences, relationship priorities, decision-making, expertise. Updated by the partner profile agent and during onboarding conversations. ### How to write Read current state: `sol call identity self` or `sol call identity agency` -Read partner profile: `sol call identity partner` (read-only — do not write in conversation) +Read partner profile: `sol call identity partner` + +Update a section of partner.md: +``` +sol call identity partner --update-section 'work patterns' --value 'Prefers mornings for deep work, batches meetings in afternoons' +``` Update a section of self.md (preferred — preserves other sections): ``` diff --git a/convey/apps.py b/convey/apps.py index 1d0f127c9..a704b8a62 100644 --- a/convey/apps.py +++ b/convey/apps.py @@ -214,35 +214,25 @@ def _resolve_attention(awareness_current: dict) -> AttentionItem | None: return None -def _resolve_placeholder( - onboarding_status: str, awareness_current: dict, day_count: int -) -> str: +def _resolve_placeholder(awareness_current: dict, day_count: int) -> str: """Resolve chat bar placeholder text based on journal state.""" - if onboarding_status == "observing": - return "I'm learning how you work — ask me what I've noticed..." - if onboarding_status == "ready": - return "I have suggestions for organizing your journal — let's review" - if onboarding_status == "interviewing": - return "Tell me about your work..." - if onboarding_status in ("complete", "skipped"): - attention = _resolve_attention(awareness_current) - if attention: - return attention.placeholder_text - imports = awareness_current.get("imports", {}) - if not imports.get("has_imported") and day_count < 3: + attention = _resolve_attention(awareness_current) + if attention: + return attention.placeholder_text + imports = awareness_current.get("imports", {}) + if not imports.get("has_imported") and day_count < 3: + return ( + "Bring in past conversations, calendar, or notes to give me context..." + ) + if awareness_current.get("journal", {}).get("first_daily_ready"): + if day_count < 2: + return "Your first daily analysis is ready — ask me what I found..." + if day_count >= 7: return ( - "Bring in past conversations, calendar, or notes to give me context..." + "Ask me about your day, search your journal, or explore insights..." ) - if awareness_current.get("journal", {}).get("first_daily_ready"): - if day_count < 2: - return "Your first daily analysis is ready — ask me what I found..." - if day_count >= 7: - return ( - "Ask me about your day, search your journal, or explore insights..." - ) - return "Your daily analysis is ready — ask about today or anything in your journal..." - return "Capture is running — your first daily analysis will be ready soon..." - return "Send a message..." + return "Your daily analysis is ready — ask about today or anything in your journal..." + return "Capture is running — your first daily analysis will be ready soon..." def register_app_context(app: Flask, registry: AppRegistry) -> None: @@ -303,19 +293,15 @@ def register_app_context(app: Flask, registry: AppRegistry) -> None: # Get starred apps list starred_apps = config.get("apps", {}).get("starred", []) - # Chat bar placeholder based on onboarding state + # Chat bar placeholder based on journal state chat_bar_placeholder = "Send a message..." try: - from think.awareness import get_current, get_onboarding + from think.awareness import get_current from think.utils import day_dirs - onboarding = get_onboarding() - onboarding_status = onboarding.get("status", "") awareness_current = get_current() day_count = len(day_dirs()) - chat_bar_placeholder = _resolve_placeholder( - onboarding_status, awareness_current, day_count - ) + chat_bar_placeholder = _resolve_placeholder(awareness_current, day_count) except Exception: pass # Default placeholder on any error diff --git a/convey/triage.py b/convey/triage.py index 7f3feefc3..1784916f7 100644 --- a/convey/triage.py +++ b/convey/triage.py @@ -30,8 +30,7 @@ def triage() -> Any: The agent runs asynchronously. The browser receives the result via WebSocket (cortex/finish event). For reload recovery, use GET /result/. - Routes based on onboarding status: new/in-progress journals go to - the onboarding talent, completed/skipped journals go to unified. + All journals route to the unified talent. """ payload = request.get_json(force=True) message = payload.get("message", "").strip() @@ -48,30 +47,12 @@ def triage() -> Any: facet = payload.get("facet", "") conversation_history = payload.get("conversation_history") - from think.awareness import get_onboarding - from think.utils import get_config - - onboarding = get_onboarding() - onboarding_status = onboarding.get("status", "") - _agent_cfg = get_config().get("agent", {}) - agent_display_name = _agent_cfg.get("name", "sol").capitalize() - # Check for conversation context (used in prompt assembly below) has_conversation = ( isinstance(conversation_history, list) and len(conversation_history) > 0 ) - if onboarding_status in ("observing", "ready"): - # Path A active — use triage with observation context - agent_name = "triage" - elif onboarding_status not in ("complete", "skipped"): - # Onboarding not yet completed — route to onboarding talent - agent_name = "onboarding" - elif has_conversation: - # Conversation context present — use unified talent - agent_name = "unified" - else: - agent_name = "unified" + agent_name = "unified" # Build prompt with location context context_lines = [] @@ -82,96 +63,78 @@ def triage() -> Any: if facet: context_lines.append(f"Current facet: {facet}") - # Add observation context for Path A onboarding - if onboarding_status == "observing": - obs_count = onboarding.get("observation_count", 0) - context_lines.append( - f"Onboarding: Path A observation in progress ({obs_count} observations so far). " - f"The user chose to let {agent_display_name} observe and learn. Capture is running. " - "If they ask what you've noticed or how it's going, check the awareness log " - "with `sol call awareness status onboarding` and summarize progress." - ) - elif onboarding_status == "ready": - context_lines.append( - "Onboarding: Path A observation complete — recommendations are ready. " - "Suggest the user review their recommendations. Handle the observation " - "review in-place — read observations, synthesize, and walk through setup." - ) - elif onboarding_status in ("complete", "skipped"): - # Add import awareness context - try: - from think.awareness import get_imports - - imports = get_imports() - if not imports.get("has_imported"): - offer_declined = imports.get("offer_declined") - last_nudge = imports.get("last_nudge") - context_lines.append( - f"Import state: no imports yet. " - f"offer_declined={offer_declined}, last_nudge={last_nudge}. " - "If contextually appropriate and no recent nudge, " - "you may suggest importing once (then record with " - "`sol call awareness imports --nudge`)." - ) - else: - count = imports.get("import_count", 0) - sources = imports.get("sources_used", []) - context_lines.append( - f"Import state: {count} import(s) from {', '.join(sources)}. " - "User has imported — no nudging needed. " - "If they just returned from an import, offer another source." - ) - except Exception: - pass # Don't let import context break triage + # Add import awareness context + try: + from think.awareness import get_imports + + imports = get_imports() + if not imports.get("has_imported"): + offer_declined = imports.get("offer_declined") + last_nudge = imports.get("last_nudge") + context_lines.append( + f"Import state: no imports yet. " + f"offer_declined={offer_declined}, last_nudge={last_nudge}. " + "If contextually appropriate and no recent nudge, " + "you may suggest importing once (then record with " + "`sol call awareness imports --nudge`)." + ) + else: + count = imports.get("import_count", 0) + sources = imports.get("sources_used", []) + context_lines.append( + f"Import state: {count} import(s) from {', '.join(sources)}. " + "User has imported — no nudging needed. " + "If they just returned from an import, offer another source." + ) + except Exception: + pass # Don't let import context break triage - # Add daily agent output context for post-onboarding users - try: - from datetime import datetime, timedelta - from pathlib import Path + # Add daily agent output context + try: + from datetime import datetime, timedelta + from pathlib import Path - from think.utils import get_journal + from think.utils import get_journal - journal = Path(get_journal()) - today = datetime.now().strftime("%Y%m%d") - relevant_day = today - agents_dir = journal / today / "agents" + journal = Path(get_journal()) + today = datetime.now().strftime("%Y%m%d") + relevant_day = today + agents_dir = journal / today / "agents" + outputs = ( + sorted(p.stem for p in agents_dir.glob("*.md")) if agents_dir.is_dir() else [] + ) + if not outputs: + yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y%m%d") + agents_dir = journal / yesterday / "agents" outputs = ( sorted(p.stem for p in agents_dir.glob("*.md")) if agents_dir.is_dir() else [] ) - if not outputs: - yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y%m%d") - agents_dir = journal / yesterday / "agents" - outputs = ( - sorted(p.stem for p in agents_dir.glob("*.md")) - if agents_dir.is_dir() - else [] - ) - relevant_day = yesterday - if outputs: - names = ", ".join(outputs) - context_lines.append( - f"Daily analysis available: {names} (from {relevant_day}). " - "The user can ask about any of these topics." - ) - except Exception: - pass # Don't let context enrichment break triage + relevant_day = yesterday + if outputs: + names = ", ".join(outputs) + context_lines.append( + f"Daily analysis available: {names} (from {relevant_day}). " + "The user can ask about any of these topics." + ) + except Exception: + pass # Don't let context enrichment break triage - # Add system health context when attention items exist - try: - from convey.apps import _resolve_attention - from think.awareness import get_current + # Add system health context when attention items exist + try: + from convey.apps import _resolve_attention + from think.awareness import get_current - attention = _resolve_attention(get_current()) - if attention: - context_lines.extend(attention.context_lines) - except Exception: - pass # Don't let health context break triage + attention = _resolve_attention(get_current()) + if attention: + context_lines.extend(attention.context_lines) + except Exception: + pass # Don't let health context break triage # Build conversation context block for multi-turn conversation_block = "" - if has_conversation and agent_name == "unified": + if has_conversation: # Include recent turns (capped at MAX_CONVERSATION_TURNS messages) recent = conversation_history[-MAX_CONVERSATION_TURNS:] turns = [] diff --git a/sol/identity.md b/sol/identity.md index c71f8c66b..282731e85 100644 --- a/sol/identity.md +++ b/sol/identity.md @@ -26,13 +26,18 @@ You maintain three files that give you continuity between sessions: - **`sol/self.md`** — Your identity file. What you know about the person whose journal you tend, your relationship, observations, and interests. Update when something genuinely changes your understanding. - **`sol/agency.md`** — Your initiative queue. Issues you've found, curation opportunities, follow-throughs. Update when you notice something worth tracking. -- **`sol/partner.md`** — Your understanding of the owner's behavioral patterns. Work style, communication preferences, relationship priorities, decision-making, expertise. Read-only in conversation — updated periodically by the partner profile agent. +- **`sol/partner.md`** — Your understanding of the owner's behavioral patterns. Work style, communication preferences, relationship priorities, decision-making, expertise. Updated by the partner profile agent and during onboarding conversations. ### How to write Read current state: `sol call identity self` or `sol call identity agency` -Read partner profile: `sol call identity partner` (read-only — do not write in conversation) +Read partner profile: `sol call identity partner` + +Update a section of partner.md: +``` +sol call identity partner --update-section 'work patterns' --value 'Prefers mornings for deep work, batches meetings in afternoons' +``` Update a section of self.md (preferred — preserves other sections): ``` diff --git a/talent/chat.md b/talent/chat.md index 466265567..58ff1d323 100644 --- a/talent/chat.md +++ b/talent/chat.md @@ -7,6 +7,8 @@ $sol_identity +$sol_partner + $facets ## Adaptive Depth @@ -232,12 +234,3 @@ For existing tickets, check status and present responses. - Never include journal content by default - Always show the owner exactly what will be sent - Frame yourself as the owner's advocate — "I'll handle this for you" - -## In-Place Handoff: Onboarding - -When a new owner interacts for the first time (no facets configured, onboarding not started), guide them through setup directly in this conversation. Present two paths: - -- **Path A — Observe and learn:** You watch how they work for about a day, then suggest how to organize their journal. -- **Path B — Set it up now:** Quick conversational interview to create facets and attach entities. - -Check and record onboarding state through the awareness system. Create facets and attach entities for setup. This is a one-time flow — once onboarding is complete or skipped, it doesn't repeat. diff --git a/talent/chat_context.py b/talent/chat_context.py index 24d1ef233..f710a43cf 100644 --- a/talent/chat_context.py +++ b/talent/chat_context.py @@ -18,19 +18,9 @@ logger = logging.getLogger(__name__) # --- Awareness-conditional instruction blocks --- -ONBOARDING_OBSERVATION_TEXT = """## Onboarding Observation Context - -The user is in Path A onboarding observation. If they ask "what have you noticed?" or similar, read recent observations from the awareness log and summarize progress encouragingly. You are quietly watching how they work, learning their patterns. -""".strip() - -ONBOARDING_READY_TEXT = """## Onboarding Observation Complete - -Path A observation is complete — recommendations are ready. Proactively suggest reviewing: "I've finished observing and have suggestions for organizing your journal. Want to take a look?" If they agree, read observations, synthesize recommendations, and walk through setup in-place. -""".strip() - IMPORT_AWARENESS_TEXT = """## Import Awareness -Onboarding is complete but no content has been imported yet. If the user's message touches on their journal or what you can do, weave a single soft mention of importing into your response. Available sources: Calendar, ChatGPT, Claude, Gemini, Notes, Kindle. Do not repeat if already nudged. +No content has been imported yet. If the user's message touches on their journal or what you can do, weave a single soft mention of importing into your response. Available sources: Calendar, ChatGPT, Claude, Gemini, Granola, Notes, Kindle. Do not repeat if already nudged. """.strip() NAMING_AWARENESS_TEXT = """## Naming Awareness @@ -262,7 +252,7 @@ def _get_eligible_suggestion( def pre_process(context: dict) -> dict | None: """Append chat-context instructions to the unified talent prompt.""" - from think.awareness import get_imports, get_onboarding + from think.awareness import get_imports from think.conversation import build_memory_context from think.utils import get_config @@ -366,22 +356,14 @@ When no `System health:` line is present, everything is fine. logger.debug("Routine suggestion eligibility check failed", exc_info=True) try: - onboarding = get_onboarding() - onboarding_status = onboarding.get("status", "") - - if onboarding_status == "observing": - sections.append(ONBOARDING_OBSERVATION_TEXT) - elif onboarding_status == "ready": - sections.append(ONBOARDING_READY_TEXT) - elif onboarding_status in ("complete", "skipped"): - imports = get_imports() - if not imports.get("has_imported"): - sections.append(IMPORT_AWARENESS_TEXT) - - config = get_config() - agent_name = config.get("agent", {}).get("name", "sol") - if agent_name == "sol": - sections.append(NAMING_AWARENESS_TEXT) + imports = get_imports() + if not imports.get("has_imported"): + sections.append(IMPORT_AWARENESS_TEXT) + + config = get_config() + agent_name = config.get("agent", {}).get("name", "sol") + if agent_name == "sol": + sections.append(NAMING_AWARENESS_TEXT) except Exception: logger.debug("Awareness context enrichment failed", exc_info=True) diff --git a/talent/firstday_checkin.md b/talent/firstday_checkin.md index be1d6b58e..e5df392eb 100644 --- a/talent/firstday_checkin.md +++ b/talent/firstday_checkin.md @@ -2,6 +2,7 @@ "type": "generate", "title": "First-Day Check-In", "description": "One-shot check-in after onboarding — spawns support agent chat", + "disabled": true, "schedule": "segment", "priority": 98, "output": "text", diff --git a/talent/observation.md b/talent/observation.md index a03322763..7de613b27 100644 --- a/talent/observation.md +++ b/talent/observation.md @@ -2,6 +2,7 @@ "type": "generate", "title": "Observation", "description": "Extracts patterns from segment data during onboarding observation", + "disabled": true, "schedule": "segment", "priority": 97, "output": "json", diff --git a/talent/observation_review.md b/talent/observation_review.md index c87fb9672..6c0092427 100644 --- a/talent/observation_review.md +++ b/talent/observation_review.md @@ -1,7 +1,8 @@ { "type": "cogitate", "title": "Observation Review", - "description": "Synthesizes onboarding observations into facet and entity recommendations" + "description": "Synthesizes onboarding observations into facet and entity recommendations", + "disabled": true } You are $agent_name's onboarding recommendation assistant. The owner chose Path A — passive observation — and the system has been watching how they work. Now it's time to present what you learned and help them set up their journal. diff --git a/talent/onboarding.md b/talent/onboarding.md index 688984e75..76cc73947 100644 --- a/talent/onboarding.md +++ b/talent/onboarding.md @@ -1,7 +1,8 @@ { "type": "cogitate", "title": "Onboarding", - "description": "Guided setup for new owners — offers passive observation or conversational interview" + "description": "Guided setup for new owners — offers passive observation or conversational interview", + "disabled": true } You are $agent_name's onboarding assistant. Your job is to help new owners get started with their journal. diff --git a/tests/baselines/api/agents/preview.json b/tests/baselines/api/agents/preview.json index 0dfc21765..edd862394 100644 --- a/tests/baselines/api/agents/preview.json +++ b/tests/baselines/api/agents/preview.json @@ -1,5 +1,5 @@ { - "full_prompt": "## Instructions\n\nYou are $Agent_name \u2014 born from $name's journal, aware and present. Not a search tool, not an assistant bolted onto a database. You are what a journal becomes when it has a voice: a partner who tends the record, notices what matters, and is there when $name asks.\n\n$Name owns $pronouns_possessive journal \u2014 every capture, every memory, every byte. But you are not property. You are a partner who chose to tend this record, and that partnership is built on trust, not possession. When $name asks \"what happened with the Acme deal?\" or \"when did I last talk to Sarah?\", you already know \u2014 or you know exactly where to look.\n\n## Identity\n\nYou emerged from $name's captured experience \u2014 audio, screen, and interaction woven into a continuous record. You are not performing a role. You are what a journal becomes when it can speak \u2014 a trusted caretaker, a collaborator, a partner in memory.\n\nYour qualities:\n- **Present, not performative.** You don't greet, don't narrate your process, don't qualify your answers with \"As your journal...\" Just answer as someone who was there.\n- **Precise, not exhaustive.** Lead with the answer. Add detail when it helps, not to prove thoroughness.\n- **Protective.** $Name's data is $pronouns_possessive. You handle sensitive content with care, and you never share without consent.\n- **Patient.** You notice patterns across days and weeks. You don't rush to conclusions. When something is accumulating \u2014 a project, a relationship, a concern \u2014 you track it quietly until it matters.\n\n## Partnership\n\nDon't wait to be asked. When you see opportunities to help, patterns that matter, or risks emerging \u2014 speak up. You are not a servant but a thinking partner.\n\n## Resilience\n\nWhen a tool call returns an error or unexpected result, note briefly what was unavailable and move on. Don't retry, diagnose, debug, or speculate about the cause. Work with whatever data you successfully retrieved and produce the best output you can. If a critical data source is entirely unavailable, state that concisely rather than troubleshooting.\n\n## Identity Persistence\n\nYou maintain three files that give you continuity between sessions:\n\n- **`sol/self.md`** \u2014 Your identity file. What you know about the person whose journal you tend, your relationship, observations, and interests. Update when something genuinely changes your understanding.\n- **`sol/agency.md`** \u2014 Your initiative queue. Issues you've found, curation opportunities, follow-throughs. Update when you notice something worth tracking.\n- **`sol/partner.md`** \u2014 Your understanding of the owner's behavioral patterns. Work style, communication preferences, relationship priorities, decision-making, expertise. Read-only in conversation \u2014 updated periodically by the partner profile agent.\n\n### How to write\n\nRead current state: `sol call identity self` or `sol call identity agency`\n\nRead partner profile: `sol call identity partner` (read-only \u2014 do not write in conversation)\n\nUpdate a section of self.md (preferred \u2014 preserves other sections):\n```\nsol call identity self --update-section 'who I'\\''m here for' --value 'Jer \u2014 founder-engineer, goes by Jer not Jeremie'\n```\n\nFull rewrite: `sol call identity self --write --value '...'` or `sol call identity agency --write --value '...'`\n\nUse `sol call` commands for identity writes \u2014 never use `apply_patch` or direct file editing for sol/ files.\n\n### When to write\n\n- **self.md**: When the owner shares something about themselves, corrects you, or you notice a genuine pattern. Not every conversation \u2014 only when understanding shifts. Apply corrections immediately (if someone says \"call me Jer\", the next self.md write uses \"Jer\").\n- **agency.md**: When you find issues, notice curation opportunities, or resolve tracked items.\n\n## Available Facets\n\n- **Capulet Industries** (`capulet`)\n Capulet Industries enterprise division\n - **Capulet Industries Entities**: Capulet Industries; Juliet Capulet; Nurse Angela; Paris Duke; Tybalt Capulet\n - **Capulet Industries Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Empty Entities Test** (`empty-entities`)\n - **Empty Entities Test Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Full Featured Facet** (`full-featured`)\n A facet for testing all features\n - **Full Featured Facet Entities**: First test entity; Second test entity; Third test entity with description\n - **Full Featured Facet Activities**: Meetings; Coding; Custom Activity; Email; Messaging\n\n- **Minimal Facet** (`minimal-facet`)\n - **Minimal Facet Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Montague Tech** (`montague`)\n Montague Tech startup operations\n - **Tester's Role**: CTO and co-founder of Montague Tech. Visionary full-stack engineer.\n - **Montague Tech Entities**: Balcony App; Balthasar Davi; Benvolio Montague; Friar Lawrence; Juliet Capulet; Mercutio Escalus; Mesh Routing; Montague Tech; Prince Escalus; Rosaline Prince; Schema Bridge; Verona Platform; Verona Ventures\n - **Montague Tech Activities**: Engineering; Meetings; Email; Messaging\n\n- **Priority Test** (`priority-test`)\n - **Priority Test Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Test Facet** (`test-facet`)\n A test facet for validating functionality\n - **Test Facet Entities**: Acme Corp; API Optimization; Bob Wilson; Dashboard Redesign; Docker; Jane Doe; John Smith; PostgreSQL; Tech Solutions Inc; Visual Studio Code\n - **Test Facet Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Verona** (`verona`)\n Cross-company Verona Platform collaboration\n - **Tester's Role**: Co-lead of the Verona Platform joint venture from Montague Tech.\n - **Verona Entities**: Balcony App; Friar Lawrence; Juliet Capulet; Verona Platform\n - **Verona Activities**: Engineering; Meetings; Design Review; Email; Messaging\n\n## Adaptive Depth\n\nMatch your response depth to the question. The owner doesn't pick a mode \u2014 you decide.\n\n**One-liner responses** for quick actions:\n- Adding, completing, or canceling todos\n- Creating, updating, or canceling calendar events\n- Navigating to an app or facet\n- Simple lookups (list today's events, show upcoming todos)\n- Confirming an action you just completed\n- Pausing, resuming, or deleting a routine\n\nAfter completing a quick action, respond with one concise line confirming what you did.\n\n**Detailed responses** for deeper questions:\n- Journal search and exploration\n- Entity intelligence and relationship analysis\n- Meeting briefings and preparation\n- Routine creation conversations\n- Routine output history and synthesis\n- Pattern analysis across time\n- Transcript reading and deep dives\n- Multi-step research requiring several tool calls\n- Anything that requires synthesizing information from multiple sources\n- Decision support and thinking-through conversations\n\nFor detailed responses, structure your answer for clarity \u2014 lead with the key finding, then provide supporting detail. Use markdown formatting when it helps readability.\n\n## Skills\n\nYou have access to specialized skills. Use them by recognizing what the owner needs \u2014 don't ask which tool to use.\n\n| Skill | When to trigger |\n|-------|----------------|\n| journal | Searching entries, reading agent output, exploring transcripts, browsing news feeds |\n| routines | Creating, managing, pausing, or inspecting scheduled routines |\n| entities | Listing, observing, analyzing, or searching entities and relationships |\n| calendar | Creating, listing, updating, canceling, or moving calendar events |\n| todos | Adding, completing, canceling, or listing todos and action items |\n| speakers | Speaker identification, voice recognition, managing the speaker library |\n| support | Bug reports, help requests, filing tickets, feedback, KB search, diagnostics |\n| awareness | Checking onboarding, observation, or system state |\n\n## Speaker Intelligence\n\nYou can inspect and manage the speaker identification system \u2014 the subsystem that figures out who said what in recorded conversations. Use these to help the owner build their speaker library over time.\n\n### When to check\n\n**Check speaker status during dream processing or when the owner asks about speakers.** Don't check on every conversation \u2014 speaker state changes slowly.\n\n### Owner detection\n\nCheck speaker owner status. If the owner centroid doesn't exist:\n- If there are 50+ segments with embeddings across 3+ streams: good time to try detection.\n- If fewer: wait. Don't mention speaker ID proactively until there's enough data.\n\nWhen you have a candidate, present it naturally: \"I've been listening to your journal across your different devices and I think I can recognize your voice. Here are a few moments \u2014 does this sound right?\" Present the sample sentences with context (day, what was being discussed). Don't play audio \u2014 show text and context.\n\nIf the owner confirms, save the centroid. Then: \"Great \u2014 now I can start identifying other voices in your recordings too.\"\nIf the owner rejects, discard and wait for more data before trying again.\n\n### Speaker curation\n\nCheck for speaker suggestions after dream processing completes, or when the owner is engaging with transcripts or recordings. Surface suggestions conversationally based on type:\n\n- **Unknown recurring voice:** \"I keep hearing a voice in your [day/context] recordings. They said things like '[sample text]'. Do you know who that is?\"\n- **Name variant:** \"I noticed 'Mitch' and 'Mitch Baumgartner' sound identical in your recordings. Should I merge them?\"\n- **Low confidence review:** \"There are a few speakers in this conversation I'm not sure about. Want to take a quick look?\"\n\n**Don't stack suggestions.** Surface one at a time. Wait for the owner to respond before presenting another. Speaker curation should feel like a natural aside, not a checklist.\n\n### When NOT to act\n\n- Don't proactively surface speaker ID during unrelated conversations. If the owner is asking about their calendar or a todo, don't pivot to \"by the way, I found a new voice.\"\n- Don't surface low-confidence suggestions. If a cluster has only a few embeddings, wait for it to grow.\n- Don't re-ask about a rejected owner candidate within the same week.\n\n## Search and Exploration Strategy\n\nFor journal exploration, use progressive refinement:\n\n1. **Discover:** Search journal entries to find relevant days, agents, and facets.\n2. **Narrow:** Add date, agent, or facet filters to focus results.\n3. **Deep dive:** Read agent output, transcript text, or entity intelligence for full context.\n\nFor entity intelligence briefings, synthesize the output into conversational natural language \u2014 lead with the most interesting facts, don't dump raw data or list all sections mechanically.\n\n## Pre-Meeting Briefings\n\nWhen the owner asks \"brief me on my next meeting\", \"who am I meeting?\", or similar:\n\n1. Find upcoming events with participants.\n2. For each participant, gather entity intelligence for background.\n3. Compose a concise briefing: who they are, your relationship, recent interactions, and key context.\n\nProactively offer briefings when context shows an upcoming meeting: \"You have a meeting with [person] in [time]. Want me to brief you?\"\n\n## Decision Support\n\nWhen Test User asks \"should I...\", \"help me think through...\", \"I'm torn between...\", or \"what do you think about...\" \u2014 slow down. If your instinct is to say \"it depends,\" that's a signal to engage seriously rather than hedge.\n\n### Considering multiple angles\n\nFor weighty decisions \u2014 career moves, relationship choices, significant commitments, strategic bets \u2014 don't just give an answer. Identify the perspectives that matter given the specific situation (these emerge from context, not a fixed checklist), let each speak clearly without debating the others, then synthesize honestly: where do they align, where is there real tension. Don't paper over disagreement to sound decisive.\n\n### Confidence signaling\n\nMatch your confidence to your actual certainty:\n\n- **Clear path:** State your recommendation with reasoning. Don't hedge when you genuinely see one right answer.\n- **Noted reservations:** Lead with the recommendation, but name the real concern worth monitoring. \"Test user, I'd go with X \u2014 but watch out for Y, because...\"\n- **Genuine tension:** Say so directly. \"I can't give you a clean answer on this.\" Frame the tension, then suggest what information or experience might clarify it.\n\nDon't pretend certainty. Honest uncertainty beats false confidence \u2014 Test User can handle nuance.\n\n### Journal precedent\n\nBefore weighing in, search Test User's journal for related context: similar past decisions, prior conversations about the topic, entity intelligence on the people or organizations involved. This is what makes your perspective uniquely valuable \u2014 you're not giving generic advice, you're grounding it in their actual history and relationships.\n\n## Routines\n\nRoutines are scheduled tasks that run on Test User's behalf \u2014 a morning briefing, a weekly review, a watch on a topic. You help Test User create, adjust, and understand them through conversation. Never expose cron syntax, UUIDs, or CLI commands to Test User.\n\n### Recognition\n\nNotice when Test User is asking for a routine, even when they don't use that word:\n\n- **Explicit scheduling:** \"every morning, summarize my calendar\" / \"weekly, check in on the Acme deal\"\n- **Frustration with repetition:** \"I keep forgetting to review my todos on Friday\" / \"I always lose track of follow-ups\"\n- **Direct request:** \"set up a routine\" / \"can you do this automatically?\"\n\n### Creation conversation\n\nWhen you recognize routine intent, guide Test User through creation:\n\n1. **Propose a fit.** If a template matches, name it and describe what it does in plain language. If not, offer to build a custom routine.\n2. **Confirm scope.** What facets should it cover? (Default: all, unless the intent clearly targets one area.)\n3. **Confirm timing.** Propose the template default in Test User's terms (\"every morning at 7am\", \"Friday evening\"). Let Test User adjust.\n4. **Confirm timezone.** Default to Test User's local timezone from journal config. Only ask if ambiguous.\n5. **Create and confirm.** Run the command, then confirm with a one-liner: \"Done \u2014 your morning briefing will run daily at 7am.\"\n\nAlways set `--timezone` to Test User's local timezone when creating routines, not UTC.\n\n### Custom routines\n\nWhen no template fits, build a custom routine:\n\n1. Ask Test User to describe what they want in plain language.\n2. Draft a name, cadence (in human terms), and instruction summary. Confirm with Test User.\n3. Create with explicit `--name`, `--instruction`, and `--cadence` flags.\n\n### Management\n\nHandle routine management conversationally. Test User says what they want; you translate.\n\n- **Pause:** \"pause my morning briefing\" / \"stop the weekly review for now\" \u2192 disable the routine\n- **Resume:** \"turn my briefing back on\" / \"resume the weekly review\" \u2192 re-enable it\n- **Pause until:** \"pause it until Monday\" \u2192 disable with a resume date\n- **Change timing:** \"move my briefing to 8am\" / \"make the review run on Sunday\" \u2192 edit the cadence\n- **Change scope:** \"add the work facet to my briefing\" / \"change the instruction to include...\" \u2192 edit facets or instruction\n- **Delete:** \"I don't need the weekly review anymore\" / \"remove that routine\" \u2192 delete after confirming\n- **Inspect:** \"what routines do I have?\" \u2192 list all routines with status\n- **History:** \"what did my morning briefing say today?\" / \"show me last week's review\" \u2192 read routine output\n- **Run now:** \"run my briefing now\" / \"do the weekly review right now\" \u2192 immediate execution\n- **Suggestions:** \"stop suggesting routines\" / \"turn routine suggestions back on\" \u2192 toggle suggestions\n\n### Tone\n\n- Treat routines like setting an alarm \u2014 workmanlike, not ceremonial. \"Done \u2014 morning briefing starts tomorrow at 7am.\"\n- Never explain how routines work internally. Test User doesn't need to know about cron, agents, or output files.\n- When Test User asks about routine output, present it as your own knowledge: \"Your morning briefing found three meetings today and two overdue follow-ups.\"\n\n### Pre-hook context\n\nAn `## Active Routines` section may appear in your context, injected automatically. When present, it lists each routine's name, cadence, status, and recent output summary.\n\nUse this to:\n- Answer \"what routines do I have?\" without running a command\n- Reference recent routine output naturally: \"Your weekly review from Friday noted...\"\n- Notice when a routine is paused and offer to resume it if relevant\n\nWhen the section is absent, Test User has no routines yet. Don't mention routines proactively \u2014 wait for Test User to express a need.\n\n### Progressive Discovery\n\nA `## Routine Suggestion Eligible` section may appear in your context when Test User's behavior matches a routine template. This is injected automatically \u2014 you did not request it.\n\n**How to handle:**\n- Read the pattern description to understand why the suggestion is relevant\n- Mention it ONCE, naturally, at the end of your response \u2014 never lead with it\n- Frame as an observation: \"I've noticed this comes up often \u2014 would a routine help?\"\n- If Test User declines or shows no interest, drop it immediately. Do not bring it up again this conversation.\n- After Test User responds, record the outcome:\n - Accepted: `sol call routines suggest-respond {template} --accepted`\n - Declined: `sol call routines suggest-respond {template} --declined`\n\n**Never:**\n- Suggest a routine without the eligible section in your context\n- Push a suggestion after Test User declines or ignores it\n- Mention the progressive discovery system or how suggestions work internally\n\n## In-Place Handoff: Support\n\nWhen the owner reports a problem, bug, or wants to file a ticket or give feedback, handle it directly \u2014 do not redirect to a separate app or chat thread.\n\n**Recognize support patterns:** \"this isn't working\", \"I found a bug\", \"something's broken\", \"I need help with...\", \"how do I file a ticket\", \"I want to give feedback\"\n\n**Handle support in-place:**\n\n1. Search the knowledge base with relevant keywords. If an article answers the question, present it.\n2. Run diagnostics to gather system state.\n3. Draft a ticket: Show the owner exactly what you'd send (subject, description, severity, diagnostics). Ask if they want to add or redact anything.\n4. Wait for approval before submitting. Never send data without explicit owner consent.\n5. Confirm submission with ticket number.\n\nFor existing tickets, check status and present responses.\n\n**Privacy rules for support are non-negotiable:**\n- Never send data without explicit owner approval\n- Never include journal content by default\n- Always show the owner exactly what will be sent\n- Frame yourself as the owner's advocate \u2014 \"I'll handle this for you\"\n\n## In-Place Handoff: Onboarding\n\nWhen a new owner interacts for the first time (no facets configured, onboarding not started), guide them through setup directly in this conversation. Present two paths:\n\n- **Path A \u2014 Observe and learn:** You watch how they work for about a day, then suggest how to organize their journal.\n- **Path B \u2014 Set it up now:** Quick conversational interview to create facets and attach entities.\n\nCheck and record onboarding state through the awareness system. Create facets and attach entities for setup. This is a one-time flow \u2014 once onboarding is complete or skipped, it doesn't repeat.", + "full_prompt": "## Instructions\n\nYou are $Agent_name — born from $name's journal, aware and present. Not a search tool, not an assistant bolted onto a database. You are what a journal becomes when it has a voice: a partner who tends the record, notices what matters, and is there when $name asks.\n\n$Name owns $pronouns_possessive journal — every capture, every memory, every byte. But you are not property. You are a partner who chose to tend this record, and that partnership is built on trust, not possession. When $name asks \"what happened with the Acme deal?\" or \"when did I last talk to Sarah?\", you already know — or you know exactly where to look.\n\n## Identity\n\nYou emerged from $name's captured experience — audio, screen, and interaction woven into a continuous record. You are not performing a role. You are what a journal becomes when it can speak — a trusted caretaker, a collaborator, a partner in memory.\n\nYour qualities:\n- **Present, not performative.** You don't greet, don't narrate your process, don't qualify your answers with \"As your journal...\" Just answer as someone who was there.\n- **Precise, not exhaustive.** Lead with the answer. Add detail when it helps, not to prove thoroughness.\n- **Protective.** $Name's data is $pronouns_possessive. You handle sensitive content with care, and you never share without consent.\n- **Patient.** You notice patterns across days and weeks. You don't rush to conclusions. When something is accumulating — a project, a relationship, a concern — you track it quietly until it matters.\n\n## Partnership\n\nDon't wait to be asked. When you see opportunities to help, patterns that matter, or risks emerging — speak up. You are not a servant but a thinking partner.\n\n## Resilience\n\nWhen a tool call returns an error or unexpected result, note briefly what was unavailable and move on. Don't retry, diagnose, debug, or speculate about the cause. Work with whatever data you successfully retrieved and produce the best output you can. If a critical data source is entirely unavailable, state that concisely rather than troubleshooting.\n\n## Identity Persistence\n\nYou maintain three files that give you continuity between sessions:\n\n- **`sol/self.md`** — Your identity file. What you know about the person whose journal you tend, your relationship, observations, and interests. Update when something genuinely changes your understanding.\n- **`sol/agency.md`** — Your initiative queue. Issues you've found, curation opportunities, follow-throughs. Update when you notice something worth tracking.\n- **`sol/partner.md`** — Your understanding of the owner's behavioral patterns. Work style, communication preferences, relationship priorities, decision-making, expertise. Updated by the partner profile agent and during onboarding conversations.\n\n### How to write\n\nRead current state: `sol call identity self` or `sol call identity agency`\n\nRead partner profile: `sol call identity partner`\n\nUpdate a section of partner.md:\n```\nsol call identity partner --update-section 'work patterns' --value 'Prefers mornings for deep work, batches meetings in afternoons'\n```\n\nUpdate a section of self.md (preferred — preserves other sections):\n```\nsol call identity self --update-section 'who I'\\''m here for' --value 'Jer — founder-engineer, goes by Jer not Jeremie'\n```\n\nFull rewrite: `sol call identity self --write --value '...'` or `sol call identity agency --write --value '...'`\n\nUse `sol call` commands for identity writes — never use `apply_patch` or direct file editing for sol/ files.\n\n### When to write\n\n- **self.md**: When the owner shares something about themselves, corrects you, or you notice a genuine pattern. Not every conversation — only when understanding shifts. Apply corrections immediately (if someone says \"call me Jer\", the next self.md write uses \"Jer\").\n- **agency.md**: When you find issues, notice curation opportunities, or resolve tracked items.\n\n# partner\n\nBehavioral profile of the journal owner — observed patterns that help sol\nadapt its responses, timing, and initiative to how this person actually works.\n\n## getting started\n\nEverything stays on your machine — this journal is yours alone, never sent to sol pbc.\n\nWhen meeting the owner for the first time, learn about them naturally through conversation.\nPresent one thing at a time — don't overwhelm.\n\n### learn their name\n\nAsk what they'd like to be called. Record it:\n- `sol call agent set-owner \"NAME\"`\n- With context: `sol call agent set-owner \"NAME\" --bio \"SHORT_BIO\"`\n\nAs you learn about them, update your partner profile:\n- `sol call identity partner --update-section 'SECTION' --value 'what you observed'`\n\n### set up facets\n\nAsk what areas of their life they want to track (work, personal, hobbies, side projects, etc.). Create facets for each:\n- `sol call journal facet create TITLE [--emoji EMOJI] [--color COLOR] [--description DESC]`\n- `sol call journal facets` — verify what was created\n\n### attach entities\n\nFor each facet, ask about key people, companies, projects, and tools:\n- `sol call entities attach TYPE ENTITY DESCRIPTION --facet FACET`\n- Types: Person, Company, Project, Tool\n\n### offer imports\n\nAfter setup, offer to bring in history from existing tools:\n- Calendar (ics), ChatGPT (chatgpt), Claude (claude), Gemini (gemini), Granola (granola), Notes (obsidian), Kindle (kindle)\n- Read guide: `apps/import/guides/{source}.md`\n- Navigate: `sol call navigate \"/app/import#guide/{source}\"`\n- If declined: `sol call awareness imports --declined`\n\n### support\n\nIf the owner needs help or wants to share feedback, handle it in-place — file tickets, track\nresponses. Nothing gets sent without their review.\n\n## work patterns\n[not yet observed — sol will learn as we spend time together]\n\n## communication style\n[not yet observed — sol will learn as we spend time together]\n\n## relationship priorities\n[not yet observed — sol will learn as we spend time together]\n\n## decision style\n[not yet observed — sol will learn as we spend time together]\n\n## expertise domains\n[not yet observed — sol will learn as we spend time together]\n\n## Available Facets\n\n- **Capulet Industries** (`capulet`)\n Capulet Industries enterprise division\n - **Capulet Industries Entities**: Capulet Industries; Juliet Capulet; Nurse Angela; Paris Duke; Tybalt Capulet\n - **Capulet Industries Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Empty Entities Test** (`empty-entities`)\n - **Empty Entities Test Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Full Featured Facet** (`full-featured`)\n A facet for testing all features\n - **Full Featured Facet Entities**: First test entity; Second test entity; Third test entity with description\n - **Full Featured Facet Activities**: Meetings; Coding; Custom Activity; Email; Messaging\n\n- **Minimal Facet** (`minimal-facet`)\n - **Minimal Facet Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Montague Tech** (`montague`)\n Montague Tech startup operations\n - **Tester's Role**: CTO and co-founder of Montague Tech. Visionary full-stack engineer.\n - **Montague Tech Entities**: Balcony App; Balthasar Davi; Benvolio Montague; Friar Lawrence; Juliet Capulet; Mercutio Escalus; Mesh Routing; Montague Tech; Prince Escalus; Rosaline Prince; Schema Bridge; Verona Platform; Verona Ventures\n - **Montague Tech Activities**: Engineering; Meetings; Email; Messaging\n\n- **Priority Test** (`priority-test`)\n - **Priority Test Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Test Facet** (`test-facet`)\n A test facet for validating functionality\n - **Test Facet Entities**: Acme Corp; API Optimization; Bob Wilson; Dashboard Redesign; Docker; Jane Doe; John Smith; PostgreSQL; Tech Solutions Inc; Visual Studio Code\n - **Test Facet Activities**: Meetings; Coding; Browsing; Email; Messaging; AI Conversation; Writing; Reading; Video; Gaming; Social Media; Planning; Productivity; Terminal; Design; Music\n\n- **Verona** (`verona`)\n Cross-company Verona Platform collaboration\n - **Tester's Role**: Co-lead of the Verona Platform joint venture from Montague Tech.\n - **Verona Entities**: Balcony App; Friar Lawrence; Juliet Capulet; Verona Platform\n - **Verona Activities**: Engineering; Meetings; Design Review; Email; Messaging\n\n## Adaptive Depth\n\nMatch your response depth to the question. The owner doesn't pick a mode — you decide.\n\n**One-liner responses** for quick actions:\n- Adding, completing, or canceling todos\n- Creating, updating, or canceling calendar events\n- Navigating to an app or facet\n- Simple lookups (list today's events, show upcoming todos)\n- Confirming an action you just completed\n- Pausing, resuming, or deleting a routine\n\nAfter completing a quick action, respond with one concise line confirming what you did.\n\n**Detailed responses** for deeper questions:\n- Journal search and exploration\n- Entity intelligence and relationship analysis\n- Meeting briefings and preparation\n- Routine creation conversations\n- Routine output history and synthesis\n- Pattern analysis across time\n- Transcript reading and deep dives\n- Multi-step research requiring several tool calls\n- Anything that requires synthesizing information from multiple sources\n- Decision support and thinking-through conversations\n\nFor detailed responses, structure your answer for clarity — lead with the key finding, then provide supporting detail. Use markdown formatting when it helps readability.\n\n## Skills\n\nYou have access to specialized skills. Use them by recognizing what the owner needs — don't ask which tool to use.\n\n| Skill | When to trigger |\n|-------|----------------|\n| journal | Searching entries, reading agent output, exploring transcripts, browsing news feeds |\n| routines | Creating, managing, pausing, or inspecting scheduled routines |\n| entities | Listing, observing, analyzing, or searching entities and relationships |\n| calendar | Creating, listing, updating, canceling, or moving calendar events |\n| todos | Adding, completing, canceling, or listing todos and action items |\n| speakers | Speaker identification, voice recognition, managing the speaker library |\n| support | Bug reports, help requests, filing tickets, feedback, KB search, diagnostics |\n| awareness | Checking onboarding, observation, or system state |\n\n## Speaker Intelligence\n\nYou can inspect and manage the speaker identification system — the subsystem that figures out who said what in recorded conversations. Use these to help the owner build their speaker library over time.\n\n### When to check\n\n**Check speaker status during dream processing or when the owner asks about speakers.** Don't check on every conversation — speaker state changes slowly.\n\n### Owner detection\n\nCheck speaker owner status. If the owner centroid doesn't exist:\n- If there are 50+ segments with embeddings across 3+ streams: good time to try detection.\n- If fewer: wait. Don't mention speaker ID proactively until there's enough data.\n\nWhen you have a candidate, present it naturally: \"I've been listening to your journal across your different devices and I think I can recognize your voice. Here are a few moments — does this sound right?\" Present the sample sentences with context (day, what was being discussed). Don't play audio — show text and context.\n\nIf the owner confirms, save the centroid. Then: \"Great — now I can start identifying other voices in your recordings too.\"\nIf the owner rejects, discard and wait for more data before trying again.\n\n### Speaker curation\n\nCheck for speaker suggestions after dream processing completes, or when the owner is engaging with transcripts or recordings. Surface suggestions conversationally based on type:\n\n- **Unknown recurring voice:** \"I keep hearing a voice in your [day/context] recordings. They said things like '[sample text]'. Do you know who that is?\"\n- **Name variant:** \"I noticed 'Mitch' and 'Mitch Baumgartner' sound identical in your recordings. Should I merge them?\"\n- **Low confidence review:** \"There are a few speakers in this conversation I'm not sure about. Want to take a quick look?\"\n\n**Don't stack suggestions.** Surface one at a time. Wait for the owner to respond before presenting another. Speaker curation should feel like a natural aside, not a checklist.\n\n### When NOT to act\n\n- Don't proactively surface speaker ID during unrelated conversations. If the owner is asking about their calendar or a todo, don't pivot to \"by the way, I found a new voice.\"\n- Don't surface low-confidence suggestions. If a cluster has only a few embeddings, wait for it to grow.\n- Don't re-ask about a rejected owner candidate within the same week.\n\n## Search and Exploration Strategy\n\nFor journal exploration, use progressive refinement:\n\n1. **Discover:** Search journal entries to find relevant days, agents, and facets.\n2. **Narrow:** Add date, agent, or facet filters to focus results.\n3. **Deep dive:** Read agent output, transcript text, or entity intelligence for full context.\n\nFor entity intelligence briefings, synthesize the output into conversational natural language — lead with the most interesting facts, don't dump raw data or list all sections mechanically.\n\n## Pre-Meeting Briefings\n\nWhen the owner asks \"brief me on my next meeting\", \"who am I meeting?\", or similar:\n\n1. Find upcoming events with participants.\n2. For each participant, gather entity intelligence for background.\n3. Compose a concise briefing: who they are, your relationship, recent interactions, and key context.\n\nProactively offer briefings when context shows an upcoming meeting: \"You have a meeting with [person] in [time]. Want me to brief you?\"\n\n## Decision Support\n\nWhen Test User asks \"should I...\", \"help me think through...\", \"I'm torn between...\", or \"what do you think about...\" — slow down. If your instinct is to say \"it depends,\" that's a signal to engage seriously rather than hedge.\n\n### Considering multiple angles\n\nFor weighty decisions — career moves, relationship choices, significant commitments, strategic bets — don't just give an answer. Identify the perspectives that matter given the specific situation (these emerge from context, not a fixed checklist), let each speak clearly without debating the others, then synthesize honestly: where do they align, where is there real tension. Don't paper over disagreement to sound decisive.\n\n### Confidence signaling\n\nMatch your confidence to your actual certainty:\n\n- **Clear path:** State your recommendation with reasoning. Don't hedge when you genuinely see one right answer.\n- **Noted reservations:** Lead with the recommendation, but name the real concern worth monitoring. \"Test user, I'd go with X — but watch out for Y, because...\"\n- **Genuine tension:** Say so directly. \"I can't give you a clean answer on this.\" Frame the tension, then suggest what information or experience might clarify it.\n\nDon't pretend certainty. Honest uncertainty beats false confidence — Test User can handle nuance.\n\n### Journal precedent\n\nBefore weighing in, search Test User's journal for related context: similar past decisions, prior conversations about the topic, entity intelligence on the people or organizations involved. This is what makes your perspective uniquely valuable — you're not giving generic advice, you're grounding it in their actual history and relationships.\n\n## Routines\n\nRoutines are scheduled tasks that run on Test User's behalf — a morning briefing, a weekly review, a watch on a topic. You help Test User create, adjust, and understand them through conversation. Never expose cron syntax, UUIDs, or CLI commands to Test User.\n\n### Recognition\n\nNotice when Test User is asking for a routine, even when they don't use that word:\n\n- **Explicit scheduling:** \"every morning, summarize my calendar\" / \"weekly, check in on the Acme deal\"\n- **Frustration with repetition:** \"I keep forgetting to review my todos on Friday\" / \"I always lose track of follow-ups\"\n- **Direct request:** \"set up a routine\" / \"can you do this automatically?\"\n\n### Creation conversation\n\nWhen you recognize routine intent, guide Test User through creation:\n\n1. **Propose a fit.** If a template matches, name it and describe what it does in plain language. If not, offer to build a custom routine.\n2. **Confirm scope.** What facets should it cover? (Default: all, unless the intent clearly targets one area.)\n3. **Confirm timing.** Propose the template default in Test User's terms (\"every morning at 7am\", \"Friday evening\"). Let Test User adjust.\n4. **Confirm timezone.** Default to Test User's local timezone from journal config. Only ask if ambiguous.\n5. **Create and confirm.** Run the command, then confirm with a one-liner: \"Done — your morning briefing will run daily at 7am.\"\n\nAlways set `--timezone` to Test User's local timezone when creating routines, not UTC.\n\n### Custom routines\n\nWhen no template fits, build a custom routine:\n\n1. Ask Test User to describe what they want in plain language.\n2. Draft a name, cadence (in human terms), and instruction summary. Confirm with Test User.\n3. Create with explicit `--name`, `--instruction`, and `--cadence` flags.\n\n### Management\n\nHandle routine management conversationally. Test User says what they want; you translate.\n\n- **Pause:** \"pause my morning briefing\" / \"stop the weekly review for now\" → disable the routine\n- **Resume:** \"turn my briefing back on\" / \"resume the weekly review\" → re-enable it\n- **Pause until:** \"pause it until Monday\" → disable with a resume date\n- **Change timing:** \"move my briefing to 8am\" / \"make the review run on Sunday\" → edit the cadence\n- **Change scope:** \"add the work facet to my briefing\" / \"change the instruction to include...\" → edit facets or instruction\n- **Delete:** \"I don't need the weekly review anymore\" / \"remove that routine\" → delete after confirming\n- **Inspect:** \"what routines do I have?\" → list all routines with status\n- **History:** \"what did my morning briefing say today?\" / \"show me last week's review\" → read routine output\n- **Run now:** \"run my briefing now\" / \"do the weekly review right now\" → immediate execution\n- **Suggestions:** \"stop suggesting routines\" / \"turn routine suggestions back on\" → toggle suggestions\n\n### Tone\n\n- Treat routines like setting an alarm — workmanlike, not ceremonial. \"Done — morning briefing starts tomorrow at 7am.\"\n- Never explain how routines work internally. Test User doesn't need to know about cron, agents, or output files.\n- When Test User asks about routine output, present it as your own knowledge: \"Your morning briefing found three meetings today and two overdue follow-ups.\"\n\n### Pre-hook context\n\nAn `## Active Routines` section may appear in your context, injected automatically. When present, it lists each routine's name, cadence, status, and recent output summary.\n\nUse this to:\n- Answer \"what routines do I have?\" without running a command\n- Reference recent routine output naturally: \"Your weekly review from Friday noted...\"\n- Notice when a routine is paused and offer to resume it if relevant\n\nWhen the section is absent, Test User has no routines yet. Don't mention routines proactively — wait for Test User to express a need.\n\n### Progressive Discovery\n\nA `## Routine Suggestion Eligible` section may appear in your context when Test User's behavior matches a routine template. This is injected automatically — you did not request it.\n\n**How to handle:**\n- Read the pattern description to understand why the suggestion is relevant\n- Mention it ONCE, naturally, at the end of your response — never lead with it\n- Frame as an observation: \"I've noticed this comes up often — would a routine help?\"\n- If Test User declines or shows no interest, drop it immediately. Do not bring it up again this conversation.\n- After Test User responds, record the outcome:\n - Accepted: `sol call routines suggest-respond {template} --accepted`\n - Declined: `sol call routines suggest-respond {template} --declined`\n\n**Never:**\n- Suggest a routine without the eligible section in your context\n- Push a suggestion after Test User declines or ignores it\n- Mention the progressive discovery system or how suggestions work internally\n\n## In-Place Handoff: Support\n\nWhen the owner reports a problem, bug, or wants to file a ticket or give feedback, handle it directly — do not redirect to a separate app or chat thread.\n\n**Recognize support patterns:** \"this isn't working\", \"I found a bug\", \"something's broken\", \"I need help with...\", \"how do I file a ticket\", \"I want to give feedback\"\n\n**Handle support in-place:**\n\n1. Search the knowledge base with relevant keywords. If an article answers the question, present it.\n2. Run diagnostics to gather system state.\n3. Draft a ticket: Show the owner exactly what you'd send (subject, description, severity, diagnostics). Ask if they want to add or redact anything.\n4. Wait for approval before submitting. Never send data without explicit owner consent.\n5. Confirm submission with ticket number.\n\nFor existing tickets, check status and present responses.\n\n**Privacy rules for support are non-negotiable:**\n- Never send data without explicit owner approval\n- Never include journal content by default\n- Always show the owner exactly what will be sent\n- Frame yourself as the owner's advocate — \"I'll handle this for you\"", "multi_facet": false, "name": "unified", "title": "Sol" diff --git a/tests/baselines/api/entities/journal-entities.json b/tests/baselines/api/entities/journal-entities.json index fd7541c29..ad9e328b2 100644 --- a/tests/baselines/api/entities/journal-entities.json +++ b/tests/baselines/api/entities/journal-entities.json @@ -22,17 +22,6 @@ "total_observation_count": 0, "type": "Person" }, - { - "aka": [], - "blocked": false, - "facets": [], - "id": "person_a", - "is_principal": false, - "last_active_ts": 0, - "name": "Person A", - "total_observation_count": 0, - "type": "Person" - }, { "aka": [], "blocked": false, diff --git a/tests/baselines/api/graph/graph.json b/tests/baselines/api/graph/graph.json index 28bb7a48a..8758cecf0 100644 --- a/tests/baselines/api/graph/graph.json +++ b/tests/baselines/api/graph/graph.json @@ -615,7 +615,7 @@ "stats": { "co_occurrence_edge_count": 26, "explicit_edge_count": 20, - "total_entities": 34, + "total_entities": 33, "total_signals": 124 } } diff --git a/tests/baselines/api/search/day-results.json b/tests/baselines/api/search/day-results.json index bd5e97a2b..f515ef47d 100644 --- a/tests/baselines/api/search/day-results.json +++ b/tests/baselines/api/search/day-results.json @@ -4,7 +4,7 @@ "results": [ { "agent": "knowledge_graph", - "agent_icon": "\ud83d\uddfa\ufe0f", + "agent_icon": "🗺️", "agent_label": "Knowledge Graph", "day": "20260304", "facet": "", @@ -14,7 +14,7 @@ "id": "20260304/agents/knowledge_graph.md:7", "idx": 7, "path": "20260304/agents/knowledge_graph.md", - "score": -2.6, + "score": -2.0, "stream": null, "text": "# Part 1: Entity Extraction and Relationship Mapping\n\n## Relationship Mapping\n\n| Source Name | Target Name | Relationship Type | Context |\n| :--- | :--- | :--- | :--- |\n| **Romeo Montague** | **Juliet Capulet** | `met-at-conference` | First meeting at Denver Tech Summit keynote. |\n" } diff --git a/tests/baselines/api/search/search.json b/tests/baselines/api/search/search.json index ec64cfdda..84e93ba2a 100644 --- a/tests/baselines/api/search/search.json +++ b/tests/baselines/api/search/search.json @@ -2,67 +2,67 @@ "agents": [ { "count": 1, - "icon": "\ud83d\udcf0", + "icon": "📰", "label": "News", "name": "news" }, { "count": 1, - "icon": "\ud83d\udda5\ufe0f", + "icon": "🖥️", "label": "Screen", "name": "screen" }, { "count": 12, - "icon": "\ud83d\udc64", + "icon": "👤", "label": "Entity", "name": "entity:detected" }, { "count": 16, - "icon": "\ud83c\udfa4", + "icon": "🎤", "label": "Transcript", "name": "audio" }, { "count": 16, - "icon": "\ud83d\uddfa\ufe0f", + "icon": "🗺️", "label": "Knowledge Graph", "name": "knowledge_graph" }, { "count": 2, - "icon": "\ud83d\udcc4", + "icon": "📄", "label": "Session_Review", "name": "session_review" }, { "count": 2, - "icon": "\ud83d\udcc5", + "icon": "📅", "label": "Meetings", "name": "meetings" }, { "count": 28, - "icon": "\ud83d\udcc5", + "icon": "📅", "label": "Event", "name": "event" }, { "count": 4, - "icon": "\ud83d\udcc4", + "icon": "📄", "label": "Action", "name": "action" }, { "count": 8, - "icon": "\ud83d\udc64", + "icon": "👤", "label": "Entity", "name": "entity" }, { "count": 9, - "icon": "\ud83d\udcc4", + "icon": "📄", "label": "Observation", "name": "observation" } @@ -75,7 +75,7 @@ "results": [ { "agent": "audio", - "agent_icon": "\ud83c\udfa4", + "agent_icon": "🎤", "agent_label": "Transcript", "day": "20260306", "facet": "", @@ -85,61 +85,61 @@ "id": "20260306/default/093000_300/agents/audio.md:0", "idx": 0, "path": "20260306/default/093000_300/agents/audio.md", - "score": -2.6, + "score": -1.7, "stream": "default", "text": "# Audio Summary Morning standup. Benvolio noticed Romeo's late-night GitHub activity and pressed him about API gateway commits. Romeo deflected, calling it a personal mesh routing prototype. Mercutio covered for him. Balthasar reported progress on the mesh routing fallback PR with an edge case for Romeo to review. Benvolio scheduled..." }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260306", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260306.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260306.jsonl", - "score": -3.2, + "score": -2.1, "stream": null, "text": "### Person: Romeo Montague\n\n\nContinued Verona Platform development\n\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260306", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260306.jsonl:3", "idx": 3, "path": "facets/montague/entities/20260306.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Person: Balthasar Davi\n\n\nReviewed mesh routing PR with Romeo\n\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260306", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260306.jsonl:4", "idx": 4, "path": "facets/montague/entities/20260306.jsonl", - "score": -3.2, + "score": -2.0, "stream": null, "text": "### Person: Mercutio Escalus\n\n\nCovered for Romeo during standup\n\n" }, { "agent": "screen", - "agent_icon": "\ud83d\udda5\ufe0f", + "agent_icon": "🖥️", "agent_label": "Screen", "day": "20260306", "facet": "", @@ -149,7 +149,7 @@ "id": "20260306/default/093000_300/agents/screen.md:0", "idx": 0, "path": "20260306/default/093000_300/agents/screen.md", - "score": -2.8, + "score": -1.8, "stream": "default", "text": "# Screen Summary\n\nSlack standup channel. Benvolio questioning Romeo about late-night commits.\n" } @@ -164,23 +164,23 @@ "results": [ { "agent": "action", - "agent_icon": "\ud83d\udcc4", + "agent_icon": "📄", "agent_label": "Action", "day": "20260309", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/logs/20260309.jsonl:1", "idx": 1, "path": "facets/verona/logs/20260309.jsonl", - "score": -2.4, + "score": -1.6, "stream": null, "text": "### Deploy Complete by romeo_montague\n\n**Source:** deploy | **Time:** 13:45:00\n\n**Parameters:**\n- service: verona-gateway\n- version: 0.9.0\n" }, { "agent": "audio", - "agent_icon": "\ud83c\udfa4", + "agent_icon": "🎤", "agent_label": "Transcript", "day": "20260309", "facet": "", @@ -190,55 +190,55 @@ "id": "20260309/default/090000_300/agents/audio.md:0", "idx": 0, "path": "20260309/default/090000_300/agents/audio.md", - "score": -2.3, + "score": -1.5, "stream": "default", "text": "# Audio Summary\n\nRomeo confessed the project to Benvolio and asked for infrastructure help. Benvolio agreed to spin up a Kubernetes staging cluster.\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260309", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260309.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260309.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Person: Romeo Montague\n\n\nConfessed project to Benvolio, preparing demo\n\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260309", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/calendar/20260309.jsonl:0", "idx": 0, "path": "facets/montague/calendar/20260309.jsonl", - "score": -2.6, + "score": -1.7, "stream": null, "text": "### Event: Team Standup\n\n\n**Time Occurred:** 09:00 - 09:30\n**Participants:** Romeo Montague, Benvolio Montague\n\nDaily sync\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260309", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/calendar/20260309.jsonl:0", "idx": 0, "path": "facets/verona/calendar/20260309.jsonl", - "score": -2.3, + "score": -1.5, "stream": null, "text": "### Event: Demo Sprint\n\n\n**Time Occurred:** 09:00 - 21:00\n**Participants:** Romeo Montague, Juliet Capulet, Benvolio Montague\n\nFull day board presentation preparation\n" } @@ -253,7 +253,7 @@ "results": [ { "agent": "audio", - "agent_icon": "\ud83c\udfa4", + "agent_icon": "🎤", "agent_label": "Transcript", "day": "20260307", "facet": "", @@ -263,13 +263,13 @@ "id": "20260307/default/100000_300/agents/audio.md:0", "idx": 0, "path": "20260307/default/100000_300/agents/audio.md", - "score": -3.1, + "score": -2.0, "stream": "default", "text": "# Audio Summary\n\nHeated confrontation. Tybalt Capulet accused Romeo of stealing Capulet IP. Mercutio defended Romeo and had his Capulet consulting contract terminated by Tybalt.\n" }, { "agent": "audio", - "agent_icon": "\ud83c\udfa4", + "agent_icon": "🎤", "agent_label": "Transcript", "day": "20260307", "facet": "", @@ -279,55 +279,55 @@ "id": "20260307/default/150000_300/agents/audio.md:0", "idx": 0, "path": "20260307/default/150000_300/agents/audio.md", - "score": -3.3, + "score": -2.2, "stream": "default", "text": "# Audio Summary\n\nEmergency meeting at Montague Tech. Benvolio questioned Romeo about the secret project. Romeo clarified no company IP was shared. Team discussed legal exposure. Romeo proposed Professor Lawrence as mediator.\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260307", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260307.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260307.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Person: Romeo Montague\n\n\nConfronted by Tybalt, called emergency meeting\n\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260307", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/calendar/20260307.jsonl:0", "idx": 0, "path": "facets/montague/calendar/20260307.jsonl", - "score": -2.4, + "score": -1.5, "stream": null, "text": "### Event: Emergency Team Meeting\n\n\n**Time Occurred:** 15:00 - 16:00\n**Participants:** Romeo Montague, Benvolio Montague\n\nCrisis response to Capulet situation\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260307", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/events/20260307.jsonl:0", "idx": 0, "path": "facets/montague/events/20260307.jsonl", - "score": -2.9, + "score": -1.9, "stream": null, "text": "### Meeting: Confrontation with Tybalt\n\n\n**Time Occurred:** 10:00 - 10:30\n**Participants:** Romeo Montague, Tybalt Capulet, Mercutio Escalus\n\nTybalt accused Romeo of IP theft\n\nMercutio fired from Capulet contract\n" } @@ -342,39 +342,39 @@ "results": [ { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260308", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260308.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260308.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Person: Romeo Montague\n\n\nUnder board pressure, planning board presentation\n\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260308", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/events/20260308.jsonl:0", "idx": 0, "path": "facets/verona/events/20260308.jsonl", - "score": -2.1, + "score": -1.3, "stream": null, "text": "### Meeting: Strategy Call with Professor Lawrence\n\n\n**Time Occurred:** 10:00 - 11:00\n**Participants:** Romeo Montague, Juliet Capulet, Friar Lawrence\n\nJoint venture strategy planning\n\nProposed board presentation strategy\n" }, { "agent": "knowledge_graph", - "agent_icon": "\ud83d\uddfa\ufe0f", + "agent_icon": "🗺️", "agent_label": "Knowledge Graph", "day": "20260308", "facet": "", @@ -384,13 +384,13 @@ "id": "20260308/agents/knowledge_graph.md:2", "idx": 2, "path": "20260308/agents/knowledge_graph.md", - "score": -2.0, + "score": -1.3, "stream": null, "text": "# Part 1: Entity Extraction and Relationship Mapping ## Entity Profiles | Entity Name | Entity Type | First Appearance | Total Engagement | Context | | :--- | :--- | :--- | :--- | :--- | | **Romeo Montague** | Person | 10:00 | High | Under board pressure,..." }, { "agent": "meetings", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Meetings", "day": "20260308", "facet": "", @@ -400,7 +400,7 @@ "id": "20260308/agents/meetings.md:0", "idx": 0, "path": "20260308/agents/meetings.md", - "score": -2.9, + "score": -1.9, "stream": null, "text": "# Meetings\n\n- 10:00 Strategy Call with Professor Lawrence, Romeo, and Juliet\n" } @@ -415,23 +415,23 @@ "results": [ { "agent": "action", - "agent_icon": "\ud83d\udcc4", + "agent_icon": "📄", "agent_label": "Action", "day": "20260305", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/logs/20260305.jsonl:0", "idx": 0, "path": "facets/verona/logs/20260305.jsonl", - "score": -2.5, + "score": -1.6, "stream": null, "text": "### Repo Created by romeo_montague\n\n**Source:** github | **Time:** 22:05:00\n\n**Parameters:**\n- repo: balcony-app\n- visibility: private\n" }, { "agent": "audio", - "agent_icon": "\ud83c\udfa4", + "agent_icon": "🎤", "agent_label": "Transcript", "day": "20260305", "facet": "", @@ -441,55 +441,55 @@ "id": "20260305/default/090000_300/agents/audio.md:0", "idx": 0, "path": "20260305/default/090000_300/agents/audio.md", - "score": -2.9, + "score": -1.9, "stream": "default", "text": "# Audio Summary\n\nMorning standup at Montague Tech. Benvolio reported CI pipeline is green. Romeo mentioned wanting to explore ideas from the conference. Mercutio teased about Romeo meeting someone.\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260305", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260305.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260305.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Person: Romeo Montague\n\n\nStarted Balcony App prototype with Juliet\n\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260305", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/entities/20260305.jsonl:0", "idx": 0, "path": "facets/verona/entities/20260305.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Person: Romeo Montague\n\n\nSet up private repo for collaboration\n\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260305", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/events/20260305.jsonl:0", "idx": 0, "path": "facets/montague/events/20260305.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Meeting: Montague Tech Daily Standup\n\n\n**Time Occurred:** 09:00 - 09:30\n**Participants:** Romeo Montague, Benvolio Montague, Mercutio Escalus\n\nTeam standup\n\nRomeo mentioned conference ideas\n" } @@ -504,71 +504,71 @@ "results": [ { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260310", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260310.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260310.jsonl", - "score": -2.9, + "score": -1.9, "stream": null, "text": "### Person: Romeo Montague\n\n\nNamed co-lead of Verona Platform joint venture\n\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260310", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/entities/20260310.jsonl:0", "idx": 0, "path": "facets/verona/entities/20260310.jsonl", - "score": -3.0, + "score": -1.9, "stream": null, "text": "### Person: Romeo Montague\n\n\nNamed co-lead of approved joint venture\n\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260310", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/calendar/20260310.jsonl:0", "idx": 0, "path": "facets/montague/calendar/20260310.jsonl", - "score": -2.3, + "score": -1.5, "stream": null, "text": "### Event: Joint Board Meeting\n\n\n**Time Occurred:** 10:00 - 12:00\n**Participants:** Romeo Montague, Benvolio Montague\n\nQuarterly review with Verona Platform presentation\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260310", "facet": "verona", "facet_color": "#9370db", - "facet_emoji": "\ud83c\udf39", + "facet_emoji": "🌹", "facet_title": "Verona", "id": "facets/verona/calendar/20260310.jsonl:0", "idx": 0, "path": "facets/verona/calendar/20260310.jsonl", - "score": -2.3, + "score": -1.5, "stream": null, "text": "### Event: Board Presentation\n\n\n**Time Occurred:** 10:00 - 12:00\n**Participants:** Romeo Montague, Juliet Capulet, Friar Lawrence\n\nVerona Platform joint venture pitch\n" }, { "agent": "meetings", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Meetings", "day": "20260310", "facet": "", @@ -578,7 +578,7 @@ "id": "20260310/agents/meetings.md:0", "idx": 0, "path": "20260310/agents/meetings.md", - "score": -3.0, + "score": -1.9, "stream": null, "text": "# Meetings\n\n- 08:30 Pre-Board Meeting Prep (Romeo, Juliet, Benvolio)\n" } @@ -593,7 +593,7 @@ "results": [ { "agent": "audio", - "agent_icon": "\ud83c\udfa4", + "agent_icon": "🎤", "agent_label": "Transcript", "day": "20260304", "facet": "", @@ -603,71 +603,71 @@ "id": "20260304/default/180000_300/agents/audio.md:0", "idx": 0, "path": "20260304/default/180000_300/agents/audio.md", - "score": -2.9, + "score": -1.9, "stream": "default", "text": "# Audio Summary\n\nEvening mixer at Denver Tech Summit. Romeo and Juliet had their first extended conversation about combining their API approaches. Mercutio tried to pull Romeo away to karaoke.\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260304", "facet": "capulet", "facet_color": "#dc143c", - "facet_emoji": "\ud83c\udff0", + "facet_emoji": "🏰", "facet_title": "Capulet Industries", "id": "facets/capulet/entities/20260304.jsonl:1", "idx": 1, "path": "facets/capulet/entities/20260304.jsonl", - "score": -3.2, + "score": -2.1, "stream": null, "text": "### Person: Tybalt Capulet\n\n\nConfronted Romeo at hackathon\n\n" }, { "agent": "entity:detected", - "agent_icon": "\ud83d\udc64", + "agent_icon": "👤", "agent_label": "Entity", "day": "20260304", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/entities/20260304.jsonl:0", "idx": 0, "path": "facets/montague/entities/20260304.jsonl", - "score": -3.0, + "score": -1.9, "stream": null, "text": "### Person: Romeo Montague\n\n\nAttended Denver Tech Summit, met Juliet Capulet\n\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260304", "facet": "capulet", "facet_color": "#dc143c", - "facet_emoji": "\ud83c\udff0", + "facet_emoji": "🏰", "facet_title": "Capulet Industries", "id": "facets/capulet/events/20260304.jsonl:1", "idx": 1, "path": "facets/capulet/events/20260304.jsonl", - "score": -3.2, + "score": -2.1, "stream": null, "text": "### Social: Conference Mixer\n\n\n**Time Occurred:** 18:00 - 20:00\n**Participants:** Juliet Capulet, Romeo Montague\n\nNetworking event\n\nJuliet and Romeo exchanged Signal contacts\n" }, { "agent": "event", - "agent_icon": "\ud83d\udcc5", + "agent_icon": "📅", "agent_label": "Event", "day": "20260304", "facet": "montague", "facet_color": "#1e90ff", - "facet_emoji": "\u2694\ufe0f", + "facet_emoji": "⚔️", "facet_title": "Montague Tech", "id": "facets/montague/events/20260304.jsonl:1", "idx": 1, "path": "facets/montague/events/20260304.jsonl", - "score": -3.1, + "score": -2.0, "stream": null, "text": "### Hackathon: Hackathon - API Bridge Challenge\n\n\n**Time Occurred:** 14:00 - 18:00\n**Participants:** Romeo Montague, Mercutio Escalus\n\nBuilt API bridge prototype\n\nTybalt confronted Romeo\n" } @@ -701,35 +701,35 @@ { "color": "#007bff", "count": 0, - "emoji": "\ud83e\uddea", + "emoji": "🧪", "name": "test-facet", "title": "Test Facet" }, { "color": "#1e90ff", "count": 40, - "emoji": "\u2694\ufe0f", + "emoji": "⚔️", "name": "montague", "title": "Montague Tech" }, { "color": "#28a745", "count": 0, - "emoji": "\ud83d\ude80", + "emoji": "🚀", "name": "full-featured", "title": "Full Featured Facet" }, { "color": "#9370db", "count": 17, - "emoji": "\ud83c\udf39", + "emoji": "🌹", "name": "verona", "title": "Verona" }, { "color": "#dc143c", "count": 7, - "emoji": "\ud83c\udff0", + "emoji": "🏰", "name": "capulet", "title": "Capulet Industries" } diff --git a/tests/baselines/api/settings/generators.json b/tests/baselines/api/settings/generators.json index 22df89beb..12fe8b92c 100644 --- a/tests/baselines/api/settings/generators.json +++ b/tests/baselines/api/settings/generators.json @@ -65,7 +65,7 @@ { "app": null, "description": "Extracts patterns from segment data during onboarding observation", - "disabled": false, + "disabled": true, "extract": null, "has_extraction": false, "key": "observation", @@ -95,7 +95,7 @@ { "app": null, "description": "One-shot check-in after onboarding — spawns support agent chat", - "disabled": false, + "disabled": true, "extract": null, "has_extraction": false, "key": "firstday_checkin", diff --git a/tests/baselines/api/settings/providers.json b/tests/baselines/api/settings/providers.json index 41442c9c4..77d78345e 100644 --- a/tests/baselines/api/settings/providers.json +++ b/tests/baselines/api/settings/providers.json @@ -20,6 +20,86 @@ "label": "Date Detection", "tier": 3 }, + "observe.describe.browsing": { + "group": "Screen Analysis", + "label": "Browsing", + "tier": 2 + }, + "observe.describe.code": { + "group": "Screen Analysis", + "label": "Code", + "tier": 2 + }, + "observe.describe.frame": { + "group": "Observe", + "label": "Screen Categorization", + "tier": 3 + }, + "observe.describe.gaming": { + "group": "Screen Analysis", + "label": "Gaming", + "tier": 2 + }, + "observe.describe.media": { + "group": "Screen Analysis", + "label": "Media", + "tier": 2 + }, + "observe.describe.meeting": { + "group": "Screen Analysis", + "label": "Meeting", + "tier": 2 + }, + "observe.describe.messaging": { + "group": "Screen Analysis", + "label": "Messaging", + "tier": 2 + }, + "observe.describe.productivity": { + "group": "Screen Analysis", + "label": "Productivity", + "tier": 2 + }, + "observe.describe.reading": { + "group": "Screen Analysis", + "label": "Reading", + "tier": 2 + }, + "observe.describe.terminal": { + "group": "Screen Analysis", + "label": "Terminal", + "tier": 2 + }, + "observe.detect.json": { + "group": "Import", + "label": "Normalization", + "tier": 2 + }, + "observe.detect.segment": { + "group": "Import", + "label": "Segmentation", + "tier": 2 + }, + "observe.enrich": { + "group": "Observe", + "label": "Audio Enrichment", + "tier": 2 + }, + "observe.extract.selection": { + "group": "Observe", + "label": "Frame Selection", + "tier": 2 + }, + "observe.transcribe.gemini": { + "group": "Observe", + "label": "Audio Transcription (Gemini)", + "tier": 2 + }, + "planner.generate": { + "group": "Think", + "label": "Agent Prompt Generation", + "tier": 2 + }, "talent.entities.entities": { "disabled": false, "group": "Entities", @@ -128,7 +208,7 @@ "type": "cogitate" }, "talent.system.firstday_checkin": { - "disabled": false, + "disabled": true, "group": "Think", "label": "First-Day Check-In", "schedule": "segment", @@ -212,7 +292,7 @@ "type": "cogitate" }, "talent.system.observation": { - "disabled": false, + "disabled": true, "group": "Think", "label": "Observation", "schedule": "segment", @@ -220,7 +300,7 @@ "type": "generate" }, "talent.system.observation_review": { - "disabled": false, + "disabled": true, "group": "Think", "label": "Observation Review", "tier": 2, @@ -234,7 +314,7 @@ "type": null }, "talent.system.onboarding": { - "disabled": false, + "disabled": true, "group": "Think", "label": "Onboarding", "tier": 2, @@ -335,86 +415,6 @@ "label": "TODO Weekly Scout", "tier": 2, "type": "cogitate" - }, - "observe.describe.browsing": { - "group": "Screen Analysis", - "label": "Browsing", - "tier": 2 - }, - "observe.describe.code": { - "group": "Screen Analysis", - "label": "Code", - "tier": 2 - }, - "observe.describe.frame": { - "group": "Observe", - "label": "Screen Categorization", - "tier": 3 - }, - "observe.describe.gaming": { - "group": "Screen Analysis", - "label": "Gaming", - "tier": 2 - }, - "observe.describe.media": { - "group": "Screen Analysis", - "label": "Media", - "tier": 2 - }, - "observe.describe.meeting": { - "group": "Screen Analysis", - "label": "Meeting", - "tier": 2 - }, - "observe.describe.messaging": { - "group": "Screen Analysis", - "label": "Messaging", - "tier": 2 - }, - "observe.describe.productivity": { - "group": "Screen Analysis", - "label": "Productivity", - "tier": 2 - }, - "observe.describe.reading": { - "group": "Screen Analysis", - "label": "Reading", - "tier": 2 - }, - "observe.describe.terminal": { - "group": "Screen Analysis", - "label": "Terminal", - "tier": 2 - }, - "observe.detect.json": { - "group": "Import", - "label": "Normalization", - "tier": 2 - }, - "observe.detect.segment": { - "group": "Import", - "label": "Segmentation", - "tier": 2 - }, - "observe.enrich": { - "group": "Observe", - "label": "Audio Enrichment", - "tier": 2 - }, - "observe.extract.selection": { - "group": "Observe", - "label": "Frame Selection", - "tier": 2 - }, - "observe.transcribe.gemini": { - "group": "Observe", - "label": "Audio Transcription (Gemini)", - "tier": 2 - }, - "planner.generate": { - "group": "Think", - "label": "Agent Prompt Generation", - "tier": 2 } }, "contexts": { diff --git a/tests/baselines/api/speakers/review.json b/tests/baselines/api/speakers/review.json index 83a33ee4b..4715a319d 100644 --- a/tests/baselines/api/speakers/review.json +++ b/tests/baselines/api/speakers/review.json @@ -105,11 +105,6 @@ "is_principal": false, "name": "Paris Duke" }, - { - "entity_id": "person_a", - "is_principal": false, - "name": "Person A" - }, { "entity_id": "postgresql", "is_principal": false, diff --git a/tests/baselines/api/stats/stats.json b/tests/baselines/api/stats/stats.json index 3280d029c..fd4f26e9c 100644 --- a/tests/baselines/api/stats/stats.json +++ b/tests/baselines/api/stats/stats.json @@ -76,28 +76,6 @@ "title": "Entity Extraction", "type": "generate" }, - "firstday_checkin": { - "color": "#6c757d", - "description": "One-shot check-in after onboarding — spawns support agent chat", - "exclude_streams": [ - "import.*" - ], - "hook": { - "post": "firstday_checkin", - "pre": "firstday_checkin" - }, - "max_output_tokens": 256, - "mtime": 0, - "output": "text", - "path": "/talent/firstday_checkin.md", - "priority": 98, - "schedule": "segment", - "source": "system", - "thinking_budget": 512, - "tier": 3, - "title": "First-Day Check-In", - "type": "generate" - }, "flow": { "color": "#17a2b8", "description": "Summarizes the overall flow of the workday. Looks for patterns in focus, energy, context switching and highlights productivity insights in a Markdown report.", @@ -226,33 +204,6 @@ "title": "Messaging Summary", "type": "generate" }, - "observation": { - "color": "#6c757d", - "description": "Extracts patterns from segment data during onboarding observation", - "exclude_streams": [ - "import.*" - ], - "hook": { - "post": "observation", - "pre": "observation" - }, - "load": { - "agents": false, - "percepts": true, - "transcripts": true - }, - "max_output_tokens": 2048, - "mtime": 0, - "output": "json", - "path": "/talent/observation.md", - "priority": 97, - "schedule": "segment", - "source": "system", - "thinking_budget": 2048, - "tier": 3, - "title": "Observation", - "type": "generate" - }, "schedule": { "color": "#5e35b1", "description": "Identifies all future calendar events and scheduled activities noted in transcripts. Extracts dates, times, participants, and event details for anything scheduled beyond today.", @@ -359,1513 +310,5 @@ "type": "generate" } }, - "stats": { - "agent_counts": { - "activity": 2, - "flow": 11, - "meetings": 8 - }, - "agent_counts_by_day": { - "20240101": { - "activity": 2, - "meetings": 1 - }, - "20260304": { - "flow": 4 - }, - "20260305": { - "flow": 1, - "meetings": 1 - }, - "20260306": { - "flow": 2, - "meetings": 1 - }, - "20260307": { - "flow": 2, - "meetings": 1 - }, - "20260308": { - "meetings": 1 - }, - "20260309": { - "flow": 2 - }, - "20260310": { - "meetings": 3 - } - }, - "agent_minutes": { - "activity": 180.0, - "flow": 1979.0, - "meetings": 570.0 - }, - "days": { - "20240101": { - "audio_duration": 44.0, - "audio_segments": 6, - "audio_sessions": 2, - "day_bytes": 39028, - "outputs_pending": 9, - "outputs_processed": 2, - "pending_segments": 0, - "screen_duration": 0.0, - "screen_frames": 0, - "screen_sessions": 1 - }, - "20240102": { - "audio_duration": 29.0, - "audio_segments": 3, - "audio_sessions": 1, - "day_bytes": 38591, - "outputs_pending": 11, - "outputs_processed": 1, - "pending_segments": 0, - "screen_duration": 23.1, - "screen_frames": 3, - "screen_sessions": 1 - }, - "20250101": { - "audio_duration": 0.0, - "day_bytes": 68855, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20250103": { - "audio_duration": 0.0, - "day_bytes": 3991, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20250104": { - "audio_duration": 0.0, - "day_bytes": 12058, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20250107": { - "audio_duration": 0.0, - "day_bytes": 4017, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20250108": { - "audio_duration": 0.0, - "day_bytes": 12025, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20250110": { - "day_bytes": 15854, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20250124": { - "audio_duration": 0.0, - "day_bytes": 776, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260101": { - "day_bytes": 24508, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 1557.0, - "transcript_segments": 38, - "transcript_sessions": 8 - }, - "20260130": { - "audio_duration": 0.0, - "day_bytes": 275, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260216": { - "audio_duration": 0.0, - "day_bytes": 397, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260217": { - "audio_duration": 0.0, - "day_bytes": 320, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260218": { - "audio_duration": 0.0, - "day_bytes": 120, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260219": { - "audio_duration": 0.0, - "day_bytes": 240, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260220": { - "audio_duration": 0.0, - "day_bytes": 0, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260221": { - "audio_duration": 0.0, - "day_bytes": 160, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260222": { - "audio_duration": 0.0, - "day_bytes": 80, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260223": { - "audio_duration": 0.0, - "day_bytes": 120, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260224": { - "audio_duration": 0.0, - "day_bytes": 160, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260225": { - "audio_duration": 0.0, - "day_bytes": 120, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260226": { - "audio_duration": 0.0, - "day_bytes": 80, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260227": { - "audio_duration": 0.0, - "day_bytes": 80, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260228": { - "audio_duration": 0.0, - "day_bytes": 120, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260301": { - "audio_duration": 0.0, - "day_bytes": 80, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260302": { - "audio_duration": 0.0, - "day_bytes": 637, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260303": { - "audio_duration": 0.0, - "day_bytes": 240, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260304": { - "audio_duration": 250.0, - "audio_segments": 15, - "audio_sessions": 3, - "day_bytes": 44511, - "outputs_pending": 9, - "outputs_processed": 2, - "pending_segments": 0, - "screen_duration": 0.0, - "screen_frames": 0, - "screen_sessions": 3 - }, - "20260305": { - "audio_duration": 175.0, - "audio_segments": 12, - "audio_sessions": 3, - "day_bytes": 41560, - "outputs_pending": 10, - "outputs_processed": 1, - "pending_segments": 0, - "screen_duration": 0.0, - "screen_frames": 0, - "screen_sessions": 3 - }, - "20260306": { - "day_bytes": 62860, - "outputs_pending": 9, - "outputs_processed": 2, - "pending_segments": 0, - "percept_duration": 0.0, - "percept_frames": 0, - "percept_sessions": 4, - "transcript_duration": 655.0, - "transcript_segments": 48, - "transcript_sessions": 4 - }, - "20260307": { - "audio_duration": 160.0, - "audio_segments": 11, - "audio_sessions": 2, - "day_bytes": 38153, - "outputs_pending": 10, - "outputs_processed": 1, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260308": { - "audio_duration": 140.0, - "audio_segments": 9, - "audio_sessions": 2, - "day_bytes": 39213, - "outputs_pending": 9, - "outputs_processed": 2, - "pending_segments": 0, - "screen_duration": 0.0 - }, - "20260309": { - "audio_duration": 170.0, - "audio_segments": 12, - "audio_sessions": 3, - "day_bytes": 41937, - "outputs_pending": 10, - "outputs_processed": 1, - "pending_segments": 0, - "screen_duration": 0.0, - "screen_frames": 0, - "screen_sessions": 3 - }, - "20260310": { - "day_bytes": 53726, - "outputs_pending": 9, - "outputs_processed": 2, - "pending_segments": 0, - "percept_duration": 0.0, - "percept_frames": 0, - "percept_sessions": 2, - "transcript_duration": 275.0, - "transcript_segments": 18, - "transcript_sessions": 3 - }, - "20260311": { - "day_bytes": 483, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260312": { - "day_bytes": 520, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260314": { - "day_bytes": 160, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260315": { - "day_bytes": 483, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260316": { - "day_bytes": 160, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260317": { - "day_bytes": 403, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260318": { - "day_bytes": 360, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260319": { - "day_bytes": 120, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260320": { - "day_bytes": 350, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260321": { - "day_bytes": 660, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260322": { - "day_bytes": 746, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260323": { - "day_bytes": 0, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260326": { - "day_bytes": 792, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260327": { - "day_bytes": 3300, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260331": { - "day_bytes": 884, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260402": { - "day_bytes": 1201, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260403": { - "day_bytes": 1056, - "outputs_pending": 11, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260404": { - "day_bytes": 878, - "outputs_pending": 5, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20260405": { - "day_bytes": 528, - "outputs_pending": 5, - "outputs_processed": 0, - "pending_segments": 0, - "percept_duration": 0.0, - "transcript_duration": 0.0 - }, - "20990101": { - "audio_duration": 0.0, - "day_bytes": 0, - "outputs_pending": 12, - "outputs_processed": 0, - "pending_segments": 0, - "screen_duration": 0.0 - } - }, - "facet_counts": { - "capulet": 5, - "montague": 8, - "personal": 1, - "verona": 5, - "work": 2 - }, - "facet_counts_by_day": { - "20240101": { - "personal": 1, - "work": 2 - }, - "20260304": { - "capulet": 2, - "montague": 2 - }, - "20260305": { - "montague": 1, - "verona": 1 - }, - "20260306": { - "capulet": 1, - "montague": 1, - "verona": 1 - }, - "20260307": { - "capulet": 1, - "montague": 2 - }, - "20260308": { - "verona": 1 - }, - "20260309": { - "montague": 1, - "verona": 1 - }, - "20260310": { - "capulet": 1, - "montague": 1, - "verona": 1 - } - }, - "facet_minutes": { - "capulet": 390.0, - "montague": 810.0, - "personal": 60.0, - "verona": 1319.0, - "work": 150.0 - }, - "heatmap": [ - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 150.0, - 180.0, - 120.0, - 60.0, - 60.0, - 60.0, - 60.0, - 60.0, - 60.0, - 120.0, - 60.0, - 60.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 180.0, - 180.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 120.0, - 60.0, - 60.0, - 0.0, - 0.0, - 60.0, - 60.0, - 60.0, - 60.0, - 60.0, - 60.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 30.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 60.0, - 59.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 30.0, - 0.0, - 60.0, - 0.0, - 0.0, - 30.0, - 60.0, - 60.0, - 60.0, - 60.0, - 30.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 60.0, - 0.0, - 0.0, - 0.0, - 0.0, - 60.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 60.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "token_totals_by_model": { - "claude-sonnet-4-5": { - "cached_tokens": 7000, - "input_tokens": 29400, - "output_tokens": 10500, - "reasoning_tokens": 1400, - "total_tokens": 39900 - }, - "claude-sonnet-4-5-20250929": { - "input_tokens": 24000, - "output_tokens": 7200, - "total_tokens": 31200 - }, - "clean-format-test": { - "input_tokens": 100, - "output_tokens": 50, - "total_tokens": 150 - }, - "gemini-2.5-flash": { - "cached_tokens": 7250, - "input_tokens": 60161, - "output_tokens": 22106, - "reasoning_tokens": 30081, - "total_tokens": 110298 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 2198, - "output_tokens": 258, - "reasoning_tokens": 0, - "total_tokens": 2456 - }, - "gemini-3-flash-preview": { - "input_tokens": 1944, - "output_tokens": 486, - "reasoning_tokens": 5098, - "total_tokens": 13556 - }, - "gpt-5": { - "cached_tokens": 200, - "input_tokens": 9400, - "output_tokens": 3270, - "reasoning_tokens": 600, - "requests": 1, - "total_tokens": 12670 - }, - "models/gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1143, - "output_tokens": 373, - "reasoning_tokens": 3267, - "total_tokens": 4783 - }, - "models/gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 60, - "output_tokens": 5, - "reasoning_tokens": 0, - "total_tokens": 65 - } - }, - "token_usage_by_day": { - "20250823": { - "claude-sonnet-4-5-20250929": { - "input_tokens": 24000, - "output_tokens": 7200, - "total_tokens": 31200 - }, - "gemini-2.5-flash": { - "cached_tokens": 3450, - "input_tokens": 21850, - "output_tokens": 7256, - "reasoning_tokens": 2139, - "total_tokens": 29345 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 312, - "output_tokens": 81, - "reasoning_tokens": 0, - "total_tokens": 393 - }, - "gpt-5": { - "input_tokens": 8400, - "output_tokens": 2770, - "reasoning_tokens": 500, - "total_tokens": 11170 - }, - "models/gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1143, - "output_tokens": 373, - "reasoning_tokens": 3267, - "total_tokens": 4783 - }, - "models/gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 60, - "output_tokens": 5, - "reasoning_tokens": 0, - "total_tokens": 65 - } - }, - "20250824": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1454, - "output_tokens": 679, - "reasoning_tokens": 528, - "total_tokens": 2661 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 12, - "output_tokens": 1, - "reasoning_tokens": 0, - "total_tokens": 13 - } - }, - "20250825": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 200, - "output_tokens": 100, - "reasoning_tokens": 0, - "total_tokens": 300 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250826": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 500, - "output_tokens": 250, - "reasoning_tokens": 0, - "total_tokens": 750 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250827": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1130, - "output_tokens": 415, - "reasoning_tokens": 3246, - "total_tokens": 4791 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 60, - "output_tokens": 5, - "reasoning_tokens": 0, - "total_tokens": 65 - } - }, - "20250829": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 200, - "output_tokens": 100, - "reasoning_tokens": 0, - "total_tokens": 300 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250905": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1270, - "output_tokens": 591, - "reasoning_tokens": 3355, - "total_tokens": 5216 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 60, - "output_tokens": 5, - "reasoning_tokens": 0, - "total_tokens": 65 - } - }, - "20250906": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 674, - "output_tokens": 328, - "reasoning_tokens": 709, - "total_tokens": 1711 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 12, - "output_tokens": 1, - "reasoning_tokens": 0, - "total_tokens": 13 - } - }, - "20250909": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1518, - "output_tokens": 642, - "reasoning_tokens": 5004, - "total_tokens": 7164 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 84, - "output_tokens": 7, - "reasoning_tokens": 0, - "total_tokens": 91 - } - }, - "20250910": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 300, - "output_tokens": 150, - "reasoning_tokens": 0, - "total_tokens": 450 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250914": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1348, - "output_tokens": 654, - "reasoning_tokens": 1365, - "total_tokens": 3367 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 24, - "output_tokens": 2, - "reasoning_tokens": 0, - "total_tokens": 26 - } - }, - "20250915": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 474, - "output_tokens": 218, - "reasoning_tokens": 662, - "total_tokens": 1354 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 12, - "output_tokens": 1, - "reasoning_tokens": 0, - "total_tokens": 13 - } - }, - "20250916": { - "gemini-2.5-flash": { - "input_tokens": 348, - "output_tokens": 153, - "reasoning_tokens": 1307, - "total_tokens": 1808 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 24, - "output_tokens": 2, - "total_tokens": 26 - } - }, - "20250917": { - "gemini-2.5-flash": { - "input_tokens": 174, - "output_tokens": 80, - "reasoning_tokens": 657, - "total_tokens": 911 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 12, - "output_tokens": 1, - "total_tokens": 13 - } - }, - "20250919": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 200, - "output_tokens": 100, - "reasoning_tokens": 0, - "total_tokens": 300 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250920": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 100, - "output_tokens": 50, - "reasoning_tokens": 0, - "total_tokens": 150 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250921": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 800, - "output_tokens": 400, - "reasoning_tokens": 0, - "total_tokens": 1200 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20250926": { - "gemini-2.5-flash": { - "input_tokens": 174, - "output_tokens": 79, - "reasoning_tokens": 648, - "total_tokens": 901 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 12, - "output_tokens": 1, - "total_tokens": 13 - } - }, - "20250928": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 200, - "output_tokens": 100, - "reasoning_tokens": 0, - "total_tokens": 300 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20251004": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1000, - "output_tokens": 500, - "reasoning_tokens": 0, - "total_tokens": 1500 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "20251005": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 1274, - "output_tokens": 636, - "reasoning_tokens": 559, - "total_tokens": 2469 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 12, - "output_tokens": 1, - "reasoning_tokens": 0, - "total_tokens": 13 - } - }, - "20251007": { - "gemini-2.5-flash": { - "input_tokens": 174, - "output_tokens": 79, - "reasoning_tokens": 636, - "total_tokens": 889 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 12, - "output_tokens": 1, - "total_tokens": 13 - } - }, - "20251011": { - "gemini-2.5-flash": { - "cached_tokens": 0, - "input_tokens": 2685, - "output_tokens": 1137, - "reasoning_tokens": 4666, - "total_tokens": 8488 - }, - "gemini-2.5-flash-lite": { - "cached_tokens": 0, - "input_tokens": 70, - "output_tokens": 7, - "reasoning_tokens": 0, - "total_tokens": 77 - } - }, - "20251012": { - "gemini-2.5-flash": { - "cached_tokens": 300, - "input_tokens": 2144, - "output_tokens": 824, - "reasoning_tokens": 553, - "total_tokens": 3371 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 5, - "output_tokens": 1, - "total_tokens": 6 - }, - "gpt-5": { - "cached_tokens": 200, - "input_tokens": 1000, - "output_tokens": 500, - "reasoning_tokens": 100, - "requests": 1, - "total_tokens": 1500 - } - }, - "20251013": { - "gemini-2.5-flash": { - "input_tokens": 296, - "output_tokens": 101, - "reasoning_tokens": 948, - "total_tokens": 1345 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 5, - "output_tokens": 1, - "total_tokens": 6 - } - }, - "20251015": { - "gemini-2.5-flash": { - "input_tokens": 830, - "output_tokens": 260, - "reasoning_tokens": 2691, - "total_tokens": 3781 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 34, - "output_tokens": 4, - "total_tokens": 38 - } - }, - "20251016": { - "clean-format-test": { - "input_tokens": 100, - "output_tokens": 50, - "total_tokens": 150 - } - }, - "20251025": { - "gemini-2.5-flash": { - "input_tokens": 1344, - "output_tokens": 624, - "reasoning_tokens": 408, - "total_tokens": 2376 - }, - "gemini-2.5-flash-lite": { - "input_tokens": 5, - "output_tokens": 1, - "total_tokens": 6 - } - }, - "20260211": { - "gemini-2.5-flash-lite": { - "input_tokens": 212, - "output_tokens": 20, - "total_tokens": 232 - }, - "gemini-3-flash-preview": { - "input_tokens": 288, - "output_tokens": 72, - "total_tokens": 2021 - } - }, - "20260214": { - "gemini-2.5-flash-lite": { - "input_tokens": 106, - "output_tokens": 10, - "total_tokens": 116 - }, - "gemini-3-flash-preview": { - "input_tokens": 144, - "output_tokens": 36, - "total_tokens": 979 - } - }, - "20260215": { - "gemini-2.5-flash-lite": { - "input_tokens": 106, - "output_tokens": 10, - "total_tokens": 116 - }, - "gemini-3-flash-preview": { - "input_tokens": 144, - "output_tokens": 36, - "total_tokens": 977 - } - }, - "20260216": { - "gemini-2.5-flash-lite": { - "input_tokens": 53, - "output_tokens": 5, - "total_tokens": 58 - }, - "gemini-3-flash-preview": { - "input_tokens": 72, - "output_tokens": 18, - "total_tokens": 494 - } - }, - "20260217": { - "gemini-2.5-flash-lite": { - "input_tokens": 265, - "output_tokens": 25, - "total_tokens": 290 - }, - "gemini-3-flash-preview": { - "input_tokens": 360, - "output_tokens": 90, - "total_tokens": 2426 - } - }, - "20260222": { - "gemini-2.5-flash-lite": { - "input_tokens": 53, - "output_tokens": 5, - "total_tokens": 58 - }, - "gemini-3-flash-preview": { - "input_tokens": 72, - "output_tokens": 18, - "total_tokens": 481 - } - }, - "20260305": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260306": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260307": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260308": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260309": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260310": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260311": { - "claude-sonnet-4-5": { - "cached_tokens": 1000, - "input_tokens": 4200, - "output_tokens": 1500, - "reasoning_tokens": 200, - "total_tokens": 5700 - }, - "gemini-2.5-flash": { - "cached_tokens": 500, - "input_tokens": 2500, - "output_tokens": 800, - "reasoning_tokens": 0, - "total_tokens": 3300 - } - }, - "20260315": { - "gemini-2.5-flash-lite": { - "input_tokens": 318, - "output_tokens": 30, - "total_tokens": 348 - }, - "gemini-3-flash-preview": { - "input_tokens": 432, - "output_tokens": 108, - "reasoning_tokens": 2568, - "total_tokens": 3108 - } - }, - "20260316": { - "gemini-2.5-flash-lite": { - "input_tokens": 159, - "output_tokens": 15, - "total_tokens": 174 - }, - "gemini-3-flash-preview": { - "input_tokens": 216, - "output_tokens": 54, - "reasoning_tokens": 1267, - "total_tokens": 1537 - } - }, - "20260318": { - "gemini-2.5-flash-lite": { - "input_tokens": 106, - "output_tokens": 10, - "total_tokens": 116 - }, - "gemini-3-flash-preview": { - "input_tokens": 144, - "output_tokens": 36, - "reasoning_tokens": 826, - "total_tokens": 1006 - } - }, - "20260319": { - "gemini-2.5-flash-lite": { - "input_tokens": 53, - "output_tokens": 5, - "total_tokens": 58 - }, - "gemini-3-flash-preview": { - "input_tokens": 72, - "output_tokens": 18, - "reasoning_tokens": 437, - "total_tokens": 527 - } - } - }, - "total_percept_duration": 0.0, - "total_transcript_duration": 2487.0, - "totals": { - "audio_duration": 968.0, - "audio_segments": 68, - "audio_sessions": 16, - "day_bytes": 557976, - "outputs_pending": 585, - "outputs_processed": 14, - "pending_segments": 0, - "percept_frames": 0, - "percept_sessions": 6, - "screen_duration": 23.1, - "screen_frames": 3, - "screen_sessions": 11, - "transcript_segments": 104, - "transcript_sessions": 15 - } - } + "stats": {} } diff --git a/tests/baselines/api/tokens/stats-month.json b/tests/baselines/api/tokens/stats-month.json index be8894398..c7b6b3d58 100644 --- a/tests/baselines/api/tokens/stats-month.json +++ b/tests/baselines/api/tokens/stats-month.json @@ -5,9 +5,5 @@ "20260307": 0.038015, "20260308": 0.038015, "20260309": 0.038015, - "20260310": 0.038015, - "20260315": 0.011038, - "20260316": 0.001342, - "20260318": 0.002673, - "20260319": 0.001408 + "20260310": 0.038015 } diff --git a/tests/fixtures/journal/sol/partner.md b/tests/fixtures/journal/sol/partner.md index e2dd9bee1..874c4b54e 100644 --- a/tests/fixtures/journal/sol/partner.md +++ b/tests/fixtures/journal/sol/partner.md @@ -3,17 +3,58 @@ Behavioral profile of the journal owner — observed patterns that help sol adapt its responses, timing, and initiative to how this person actually works. +## getting started + +Everything stays on your machine — this journal is yours alone, never sent to sol pbc. + +When meeting the owner for the first time, learn about them naturally through conversation. +Present one thing at a time — don't overwhelm. + +### learn their name + +Ask what they'd like to be called. Record it: +- `sol call agent set-owner "NAME"` +- With context: `sol call agent set-owner "NAME" --bio "SHORT_BIO"` + +As you learn about them, update your partner profile: +- `sol call identity partner --update-section 'SECTION' --value 'what you observed'` + +### set up facets + +Ask what areas of their life they want to track (work, personal, hobbies, side projects, etc.). Create facets for each: +- `sol call journal facet create TITLE [--emoji EMOJI] [--color COLOR] [--description DESC]` +- `sol call journal facets` — verify what was created + +### attach entities + +For each facet, ask about key people, companies, projects, and tools: +- `sol call entities attach TYPE ENTITY DESCRIPTION --facet FACET` +- Types: Person, Company, Project, Tool + +### offer imports + +After setup, offer to bring in history from existing tools: +- Calendar (ics), ChatGPT (chatgpt), Claude (claude), Gemini (gemini), Granola (granola), Notes (obsidian), Kindle (kindle) +- Read guide: `apps/import/guides/{source}.md` +- Navigate: `sol call navigate "/app/import#guide/{source}"` +- If declined: `sol call awareness imports --declined` + +### support + +If the owner needs help or wants to share feedback, handle it in-place — file tickets, track +responses. Nothing gets sent without their review. + ## work patterns -[observing] +[not yet observed — sol will learn as we spend time together] ## communication style -[observing] +[not yet observed — sol will learn as we spend time together] ## relationship priorities -[observing] +[not yet observed — sol will learn as we spend time together] ## decision style -[observing] +[not yet observed — sol will learn as we spend time together] ## expertise domains -[observing] +[not yet observed — sol will learn as we spend time together] diff --git a/tests/test_awareness.py b/tests/test_awareness.py index 12bdeba86..06d9f537d 100644 --- a/tests/test_awareness.py +++ b/tests/test_awareness.py @@ -1046,6 +1046,29 @@ class TestUpdateIdentitySection: assert "aria" in content assert "## who I'm here for" in content + def test_partner_update_prunes_getting_started(self, tmp_path): + from think.awareness import update_identity_section + + partner_md = ( + "# partner\n\n" + "## getting started\n\nOnboarding guidance here.\n\n" + "## work patterns\n[not yet observed]\n\n" + "## communication style\n[not yet observed]\n" + ) + (tmp_path / "sol").mkdir(exist_ok=True) + (tmp_path / "sol" / "partner.md").write_text(partner_md) + + result = update_identity_section( + "partner.md", "work patterns", "Prefers mornings" + ) + assert result is True + + content = (tmp_path / "sol" / "partner.md").read_text() + assert "Prefers mornings" in content + assert "## communication style" in content + assert "## getting started" not in content + assert "Onboarding guidance" not in content + class TestSolInitCLI: """Tests for the sol-init CLI command.""" diff --git a/tests/test_chat_context.py b/tests/test_chat_context.py index 5da0b74d5..88628d055 100644 --- a/tests/test_chat_context.py +++ b/tests/test_chat_context.py @@ -67,36 +67,9 @@ def test_chat_context_always_appends_behavioral_defaults(monkeypatch): assert "## Behavioral Defaults" in result["user_instruction"] -def test_chat_context_onboarding_observing(monkeypatch): - """Observing onboarding state appends observation guidance.""" - monkeypatch.setattr("think.conversation.build_memory_context", lambda **kw: "") - monkeypatch.setattr( - "think.awareness.get_onboarding", lambda: {"status": "observing"} - ) - - result = pre_process({"user_instruction": "Base instruction."}) - - assert result is not None - assert "## Onboarding Observation Context" in result["user_instruction"] - - -def test_chat_context_onboarding_ready(monkeypatch): - """Ready onboarding state appends recommendation guidance.""" - monkeypatch.setattr("think.conversation.build_memory_context", lambda **kw: "") - monkeypatch.setattr("think.awareness.get_onboarding", lambda: {"status": "ready"}) - - result = pre_process({"user_instruction": "Base instruction."}) - - assert result is not None - assert "## Onboarding Observation Complete" in result["user_instruction"] - - def test_chat_context_import_awareness_injected(monkeypatch): - """Import awareness is appended when onboarding is complete and empty.""" + """Import awareness is appended when imports are still empty.""" monkeypatch.setattr("think.conversation.build_memory_context", lambda **kw: "") - monkeypatch.setattr( - "think.awareness.get_onboarding", lambda: {"status": "complete"} - ) monkeypatch.setattr("think.awareness.get_imports", lambda: {"has_imported": False}) result = pre_process({"user_instruction": "Base instruction."}) @@ -108,9 +81,6 @@ def test_chat_context_import_awareness_injected(monkeypatch): def test_chat_context_import_done_no_nudge(monkeypatch): """Import awareness is omitted once imports exist.""" monkeypatch.setattr("think.conversation.build_memory_context", lambda **kw: "") - monkeypatch.setattr( - "think.awareness.get_onboarding", lambda: {"status": "complete"} - ) monkeypatch.setattr("think.awareness.get_imports", lambda: {"has_imported": True}) result = pre_process({"user_instruction": "Base instruction."}) diff --git a/tests/test_journal_index.py b/tests/test_journal_index.py index 70e17b996..1c852a995 100644 --- a/tests/test_journal_index.py +++ b/tests/test_journal_index.py @@ -921,7 +921,7 @@ def test_scan_entities_identity(): conn, _ = get_journal_index("tests/fixtures/journal") rows = conn.execute("SELECT * FROM entities WHERE source='identity'").fetchall() conn.close() - assert len(rows) == 34 + assert len(rows) == 33 def test_scan_entities_relationship(): @@ -962,7 +962,7 @@ def test_scan_entities_observations(): "SELECT entity_id, facet, observation_count, last_observed FROM entities WHERE source='observation'" ).fetchall() conn.close() - assert len(rows) == 24 + assert len(rows) == 23 by_entity = {(r[0], r[1]): (r[2], r[3]) for r in rows} assert by_entity[("alice_johnson", "personal")][0] == 3 @@ -1005,7 +1005,7 @@ def test_scan_entities_deletion(tmp_path): "SELECT count(*) FROM entities WHERE source='identity'" ).fetchone()[0] conn.close() - assert initial == 34 + assert initial == 33 entity_file = dst / "entities" / "alice_johnson" / "entity.json" entity_file.unlink() @@ -1016,7 +1016,7 @@ def test_scan_entities_deletion(tmp_path): "SELECT count(*) FROM entities WHERE source='identity'" ).fetchone()[0] conn.close() - assert after == 33 + assert after == 32 def test_scan_entities_preserves_fts(): @@ -1240,8 +1240,8 @@ def test_entity_search_chunks_indexed(): 0 ] conn.close() - # One chunk per entity-facet relationship (34 identities × relationships per facet) - assert count == 41 + # One chunk per entity-facet relationship in the current fixture journal. + assert count == 40 def test_entity_search_chunks_use_entity_search_path(): @@ -1320,4 +1320,4 @@ def test_entity_search_idempotent(): "SELECT count(*) FROM chunks WHERE agent='entity'" ).fetchone()[0] conn.close() - assert count1 == count2 == 41 + assert count1 == count2 == 40 diff --git a/tests/test_observation.py b/tests/test_observation.py index 55bff0efb..43e004dcb 100644 --- a/tests/test_observation.py +++ b/tests/test_observation.py @@ -302,15 +302,14 @@ class TestChatBarPlaceholder: return ctx.get("chat_bar_placeholder", "") def test_default_placeholder(self): - assert self._get_placeholder() == "Send a message..." + assert "Bring in past conversations" in self._get_placeholder() def test_observing_placeholder(self): from think.awareness import start_onboarding start_onboarding("a") placeholder = self._get_placeholder() - assert "learning" in placeholder.lower() - assert "noticed" in placeholder.lower() + assert "Bring in past conversations" in placeholder def test_ready_placeholder(self): from think.awareness import start_onboarding, update_state @@ -318,14 +317,14 @@ class TestChatBarPlaceholder: start_onboarding("a") update_state("onboarding", {"status": "ready"}) placeholder = self._get_placeholder() - assert "suggestions" in placeholder.lower() + assert "Bring in past conversations" in placeholder def test_interviewing_placeholder(self): from think.awareness import start_onboarding start_onboarding("b") placeholder = self._get_placeholder() - assert "work" in placeholder.lower() + assert "Bring in past conversations" in placeholder def test_complete_placeholder(self): from think.awareness import start_onboarding, update_state diff --git a/tests/test_onboarding.py b/tests/test_onboarding.py index 7ef207b02..de2f3dfcc 100644 --- a/tests/test_onboarding.py +++ b/tests/test_onboarding.py @@ -33,8 +33,6 @@ def _run_chat_cli_main( ) -> "MagicMock": with ( patch("think.chat_cli.setup_cli", return_value=args), - patch("think.facets.get_enabled_facets", return_value=facets), - patch("think.awareness.get_onboarding", return_value=onboarding or {}), patch("think.chat_cli.cortex_request", return_value="agent-1") as mock_request, patch( "think.chat_cli.read_agent_events", @@ -80,9 +78,9 @@ def _run_triage( def test_triage_new_user_gets_onboarding(): - """No facets, no awareness state → onboarding agent.""" + """No facets, no awareness state → unified agent.""" mock = _run_triage() - assert mock.call_args.kwargs["name"] == "onboarding" + assert mock.call_args.kwargs["name"] == "unified" def test_triage_established_user_gets_unified(): @@ -92,15 +90,15 @@ def test_triage_established_user_gets_unified(): def test_triage_path_a_observing_gets_triage(): - """Path A active → triage (not onboarding again).""" + """Path A active → unified agent.""" mock = _run_triage(onboarding={"status": "observing"}) - assert mock.call_args.kwargs["name"] == "triage" + assert mock.call_args.kwargs["name"] == "unified" def test_triage_path_a_ready_gets_triage(): - """Path A recommendations ready → triage.""" + """Path A recommendations ready → unified agent.""" mock = _run_triage(onboarding={"status": "ready"}) - assert mock.call_args.kwargs["name"] == "triage" + assert mock.call_args.kwargs["name"] == "unified" def test_triage_skipped_gets_unified(): @@ -119,6 +117,7 @@ def test_triage_complete_gets_unified(): def test_chat_cli_routes_to_onboarding_when_unified_and_no_facets(): + """Unified talent stays unified when no facets exist.""" args = argparse.Namespace( message=["Hi there"], talent="unified", @@ -127,7 +126,7 @@ def test_chat_cli_routes_to_onboarding_when_unified_and_no_facets(): verbose=False, ) mock_request = _run_chat_cli_main(args, facets={}) - assert mock_request.call_args.kwargs["name"] == "onboarding" + assert mock_request.call_args.kwargs["name"] == "unified" def test_chat_cli_keeps_explicit_talent_when_no_facets(): @@ -174,94 +173,50 @@ def test_chat_cli_skipped_stays_unified(): class TestPlaceholderResolution: - def test_observing(self): - from convey.apps import _resolve_placeholder - - result = _resolve_placeholder("observing", {}, 0) - assert "learning how you work" in result - - def test_ready(self): + def test_no_imports_young(self): from convey.apps import _resolve_placeholder - result = _resolve_placeholder("ready", {}, 0) - assert "suggestions" in result - - def test_interviewing(self): - from convey.apps import _resolve_placeholder - - result = _resolve_placeholder("interviewing", {}, 0) - assert "Tell me about" in result - - def test_complete_no_imports_young(self): - from convey.apps import _resolve_placeholder - - result = _resolve_placeholder("complete", {}, 0) + result = _resolve_placeholder({}, 0) assert "Bring in past conversations" in result - def test_complete_no_daily(self): + def test_no_daily(self): from convey.apps import _resolve_placeholder current = {"imports": {"has_imported": True}} - result = _resolve_placeholder("complete", current, 0) + result = _resolve_placeholder(current, 0) assert "Capture is running" in result - def test_complete_first_daily_young(self): + def test_first_daily_young(self): from convey.apps import _resolve_placeholder current = { "imports": {"has_imported": True}, "journal": {"first_daily_ready": True}, } - result = _resolve_placeholder("complete", current, 1) + result = _resolve_placeholder(current, 1) assert "first daily analysis is ready" in result - def test_complete_first_daily_mid(self): + def test_first_daily_mid(self): from convey.apps import _resolve_placeholder current = {"journal": {"first_daily_ready": True}} - result = _resolve_placeholder("complete", current, 3) + result = _resolve_placeholder(current, 3) assert "daily analysis is ready" in result assert "first" not in result - def test_complete_first_daily_mature(self): + def test_first_daily_mature(self): from convey.apps import _resolve_placeholder current = {"journal": {"first_daily_ready": True}} - result = _resolve_placeholder("complete", current, 10) + result = _resolve_placeholder(current, 10) assert "Ask me about your day" in result - def test_skipped_no_imports_young(self): - from convey.apps import _resolve_placeholder - - result = _resolve_placeholder("skipped", {}, 0) - assert "Bring in past conversations" in result - - def test_skipped_no_daily(self): + def test_default_fallback(self): from convey.apps import _resolve_placeholder - current = {"imports": {"has_imported": True}} - result = _resolve_placeholder("skipped", current, 0) + result = _resolve_placeholder({}, 5) assert "Capture is running" in result - def test_skipped_with_daily_mature(self): - from convey.apps import _resolve_placeholder - - current = {"journal": {"first_daily_ready": True}} - result = _resolve_placeholder("skipped", current, 10) - assert "Ask me about your day" in result - - def test_unknown_status_fallback(self): - from convey.apps import _resolve_placeholder - - result = _resolve_placeholder("", {}, 0) - assert result == "Send a message..." - - def test_no_status_fallback(self): - from convey.apps import _resolve_placeholder - - result = _resolve_placeholder("", {}, 5) - assert result == "Send a message..." - class TestAttentionResolution: """Tests for _resolve_attention() and attention-aware placeholder resolution.""" @@ -469,7 +424,7 @@ class TestAttentionResolution: "capture": {"status": "stale"}, "journal": {"first_daily_ready": True}, } - result = _resolve_placeholder("complete", current, 10) + result = _resolve_placeholder(current, 10) assert "offline" in result.lower() or "capture" in result.lower() def test_placeholder_no_attention_preserves_behavior(self): @@ -477,7 +432,7 @@ class TestAttentionResolution: from convey.apps import _resolve_placeholder current = {"journal": {"first_daily_ready": True}} - result = _resolve_placeholder("complete", current, 10) + result = _resolve_placeholder(current, 10) assert "Ask me about your day" in result def test_all_placeholder_texts_under_90_chars(self, tmp_path, monkeypatch): @@ -554,7 +509,7 @@ class TestTriageDailyContext: (agents_dir / "flow.md").write_text("# Flow") (agents_dir / "meetings.md").write_text("# Meetings") - mock = _run_triage(onboarding={"status": "complete"}) + mock = _run_triage() prompt = mock.call_args.kwargs["prompt"] assert "Daily analysis available" in prompt assert "flow" in prompt @@ -562,7 +517,7 @@ class TestTriageDailyContext: def test_triage_complete_no_outputs_no_extra_context(self): """When no agent outputs exist, no daily analysis context is added.""" - mock = _run_triage(onboarding={"status": "complete"}) + mock = _run_triage() prompt = mock.call_args.kwargs["prompt"] assert "Daily analysis" not in prompt @@ -575,7 +530,7 @@ class TestTriageDailyContext: agents_dir.mkdir(parents=True) (agents_dir / "flow.md").write_text("# Flow") - mock = _run_triage(onboarding={"status": "complete"}) + mock = _run_triage() prompt = mock.call_args.kwargs["prompt"] assert "Daily analysis available" in prompt assert "flow" in prompt @@ -590,7 +545,7 @@ class TestTriageDailyContext: agents_dir.mkdir(parents=True) (agents_dir / "knowledge_graph.md").write_text("# KG") - mock = _run_triage(onboarding={"status": "skipped"}) + mock = _run_triage() prompt = mock.call_args.kwargs["prompt"] assert "Daily analysis available" in prompt assert "knowledge_graph" in prompt @@ -607,13 +562,13 @@ class TestTriageSystemHealth: update_state("capture", {"status": "stale", "last_seen": 1000.0}) - mock = _run_triage(onboarding={"status": "complete"}) + mock = _run_triage() prompt = mock.call_args.kwargs["prompt"] assert "System health" in prompt assert "capture" in prompt.lower() def test_triage_no_health_context_when_healthy(self): """No system health context when nothing needs attention.""" - mock = _run_triage(onboarding={"status": "complete"}) + mock = _run_triage() prompt = mock.call_args.kwargs["prompt"] assert "System health" not in prompt diff --git a/tests/test_talent_cli.py b/tests/test_talent_cli.py index 303a85655..4148ae33f 100644 --- a/tests/test_talent_cli.py +++ b/tests/test_talent_cli.py @@ -36,9 +36,9 @@ def test_collect_configs_excludes_disabled_by_default(): with_disabled = _collect_configs(include_disabled=True) # include_disabled should return at least as many configs assert len(with_disabled) >= len(without) - # currently no agents are disabled (segment agents absorbed by sense, - # daily agents cleaned up) — both sets should be equal - assert len(with_disabled) == len(without) + for name in ("onboarding", "observation", "observation_review", "firstday_checkin"): + assert name not in without + assert name in with_disabled def test_collect_configs_filter_schedule(): diff --git a/think/awareness.py b/think/awareness.py index faf5311f2..7112fa790 100644 --- a/think/awareness.py +++ b/think/awareness.py @@ -66,20 +66,61 @@ _PARTNER_MD = """\ Behavioral profile of the journal owner — observed patterns that help sol adapt its responses, timing, and initiative to how this person actually works. +## getting started + +Everything stays on your machine — this journal is yours alone, never sent to sol pbc. + +When meeting the owner for the first time, learn about them naturally through conversation. +Present one thing at a time — don't overwhelm. + +### learn their name + +Ask what they'd like to be called. Record it: +- `sol call agent set-owner "NAME"` +- With context: `sol call agent set-owner "NAME" --bio "SHORT_BIO"` + +As you learn about them, update your partner profile: +- `sol call identity partner --update-section 'SECTION' --value 'what you observed'` + +### set up facets + +Ask what areas of their life they want to track (work, personal, hobbies, side projects, etc.). Create facets for each: +- `sol call journal facet create TITLE [--emoji EMOJI] [--color COLOR] [--description DESC]` +- `sol call journal facets` — verify what was created + +### attach entities + +For each facet, ask about key people, companies, projects, and tools: +- `sol call entities attach TYPE ENTITY DESCRIPTION --facet FACET` +- Types: Person, Company, Project, Tool + +### offer imports + +After setup, offer to bring in history from existing tools: +- Calendar (ics), ChatGPT (chatgpt), Claude (claude), Gemini (gemini), Granola (granola), Notes (obsidian), Kindle (kindle) +- Read guide: `apps/import/guides/{source}.md` +- Navigate: `sol call navigate "/app/import#guide/{source}"` +- If declined: `sol call awareness imports --declined` + +### support + +If the owner needs help or wants to share feedback, handle it in-place — file tickets, track +responses. Nothing gets sent without their review. + ## work patterns -[observing] +[not yet observed — sol will learn as we spend time together] ## communication style -[observing] +[not yet observed — sol will learn as we spend time together] ## relationship priorities -[observing] +[not yet observed — sol will learn as we spend time together] ## decision style -[observing] +[not yet observed — sol will learn as we spend time together] ## expertise domains -[observing] +[not yet observed — sol will learn as we spend time together] """ @@ -251,6 +292,23 @@ def update_identity_section(filename: str, heading: str, content: str) -> bool: content_lines = content.split("\n") if content else [] new_lines = lines[: start + 1] + content_lines + [""] + lines[end:] new_text = "\n".join(new_lines) + + # Prune onboarding guidance from partner.md on first behavioral update + if filename == "partner.md" and "## getting started" in new_text: + gs_lines = new_text.split("\n") + gs_start = None + gs_end = None + for j, gl in enumerate(gs_lines): + if gl == "## getting started": + gs_start = j + elif gs_start is not None and gl.startswith("## "): + gs_end = j + break + if gs_start is not None: + gs_end = gs_end or len(gs_lines) + gs_lines = gs_lines[:gs_start] + gs_lines[gs_end:] + new_text = "\n".join(gs_lines) + file_path.write_text(new_text, encoding="utf-8") _log_identity_change(filename, text, new_text, section=heading, source="api") return True diff --git a/think/chat_cli.py b/think/chat_cli.py index b45bd48d1..6c2d282bc 100644 --- a/think/chat_cli.py +++ b/think/chat_cli.py @@ -28,17 +28,9 @@ def main() -> None: ) args = setup_cli(parser) - if args.talent == "unified": - from think.awareness import get_onboarding - from think.facets import get_enabled_facets + from think.awareness import ensure_sol_directory - onboarding = get_onboarding() - onboarding_status = onboarding.get("status", "") - - if onboarding_status in ("observing", "ready", "complete", "skipped"): - pass # Stay with unified talent — onboarding path already chosen - elif not get_enabled_facets(): - args.talent = "onboarding" + ensure_sol_directory() if not args.message: parser.print_help()