diff mrjunejune/inference/copilot_sidecar.py @ 280:49e9e591c9bb

Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
author MrJuneJune <me@mrjunejune.com>
date Tue, 18 Aug 2026 19:14:53 -0700
parents 056790c4fb0d
children c57149ad216e
line wrap: on
line diff
--- a/mrjunejune/inference/copilot_sidecar.py	Mon Aug 17 22:22:36 2026 -0700
+++ b/mrjunejune/inference/copilot_sidecar.py	Tue Aug 18 19:14:53 2026 -0700
@@ -2,6 +2,7 @@
 
 import argparse
 import asyncio
+import hashlib
 import json
 import os
 import sys
@@ -20,8 +21,27 @@
 JsonObject = dict[str, Any]
 Emit = Callable[[JsonObject], Awaitable[None]]
 
-_KNOWN_PROFILES: frozenset = frozenset({"public_visitor", "invited_friend", "june_admin"})
+_KNOWN_PROFILES: frozenset = frozenset({
+    "public_visitor",
+    "invited_friend",
+    "june_admin",
+    "canvas_orchestrator",
+})
 _PROMPT_VERSION: int = 1
+_CANVAS_ORCHESTRATOR_PROMPT = """\
+You orchestrate spatial agent sessions on an infinite canvas.
+There is one user input scratchpad and any number of conversation entities.
+The user message includes only the visible canvas context plus one new thought.
+Treat visible conversation entities as the only append candidates. Append when
+the new thought clearly continues one of them; otherwise create a new
+conversation. Never append to a conversation ID absent from visible context.
+Return only one compact JSON object with these fields:
+- action: "create" or "append"
+- conversation_id: an existing numeric conversation ID for append, otherwise 0
+- title: a short session title
+- response: the direct response that should appear on the canvas
+Do not use Markdown fences, tools, or text outside the JSON object.
+"""
 
 # Fixed namespace for uuid5 SDK session ID derivation.  Must never change.
 _SDK_SESSION_NAMESPACE = uuid.UUID("3f7e8a1d-9b52-4c6f-a0d3-82e1f5c94b7a")
@@ -254,7 +274,17 @@
     def _compile_all_profiles(self) -> None:
         compiled: dict[str, CompiledProfile] = {}
         for profile in sorted(_KNOWN_PROFILES):
-            result = self._compile_fn(profile)
+            if profile == "canvas_orchestrator":
+                content = _CANVAS_ORCHESTRATOR_PROMPT.strip()
+                result = {
+                    "content": content,
+                    "version": 1,
+                    "hash": hashlib.sha256(
+                        content.encode("utf-8")
+                    ).hexdigest(),
+                }
+            else:
+                result = self._compile_fn(profile)
             compiled[profile] = CompiledProfile(
                 profile=profile,
                 content=result["content"],
@@ -327,7 +357,9 @@
 
             await self.evict_idle_sessions(
                 exclude=conversation_id,
-                reserve=1 if command_name == "turn.start" else 0,
+                reserve=1
+                if command_name in {"turn.start", "conversation.warm"}
+                else 0,
             )
             async with self._conversation_command_lock(conversation_id):
                 if command_name == "turn.start":
@@ -365,6 +397,45 @@
                         )
                         return
                     await self._start_turn(request_id, conversation_id, prompt, compiled, history)
+                elif command_name == "conversation.warm":
+                    compiled = self._validate_profile_fields(
+                        command.get("prompt_profile"),
+                        command.get("prompt_version"),
+                        command.get("knowledge_version"),
+                    )
+                    if compiled is None:
+                        await self._fail(
+                            request_id,
+                            conversation_id,
+                            "invalid_prompt_profile",
+                            "a known prompt profile and current versions are required",
+                        )
+                        return
+                    resume_existing = command.get("resume_existing", True)
+                    if not isinstance(resume_existing, bool):
+                        await self._fail(
+                            request_id,
+                            conversation_id,
+                            "invalid_request",
+                            "resume_existing must be a boolean",
+                        )
+                        return
+                    await self._get_conversation(
+                        conversation_id,
+                        compiled,
+                        [],
+                        resume_existing=resume_existing,
+                    )
+                    await self._send(
+                        "session.warmed",
+                        request_id,
+                        conversation_id,
+                    )
+                    await self._send(
+                        "turn.done",
+                        request_id,
+                        conversation_id,
+                    )
                 elif command_name == "turn.abort":
                     await self._abort_turn(request_id, conversation_id)
                 elif command_name == "conversation.delete":
@@ -486,6 +557,8 @@
         conversation_id: str,
         compiled: CompiledProfile,
         history: list[dict[str, str]],
+        *,
+        resume_existing: bool = True,
     ) -> Conversation:
         derived_id = _derive_sdk_session_id(conversation_id, compiled)
         async with self._conversations_lock:
@@ -508,12 +581,13 @@
                 raise RuntimeError("Copilot session capacity exhausted")
 
             base_options = self._session_options(compiled)
-            # Try to resume a persisted derived session (no history injection).
             session: Any = None
-            try:
-                session = await self._client.resume_session(derived_id, **base_options)
-            except Exception:
-                pass
+            if resume_existing:
+                # Resume persisted user-facing sessions, then create if absent.
+                try:
+                    session = await self._client.resume_session(derived_id, **base_options)
+                except Exception:
+                    pass
 
             if session is None:
                 # Fresh create — inject bounded transcript context into system message.