personal memory agent
0

Configure Feed

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

refactor(chat): update sol identity copy

Chat copy: owner-facing voice canon, retiring surveillance register and incorrect acting subjects in favor of sol taking in and keeping experience in the owner's journal.

+84 -6
+1 -1
solstone/apps/chat/copy.py
··· 67 67 # but not yet analyzed"). Backend-only — no chat_copy.js twin. Substance locked 68 68 # (wording VPX-refinable). Copy-canon compliant: no surveillance verbs. 69 69 CHAT_DEFERRED_NOT_ANALYZED = "Today's segments aren't analyzed yet — they'll process during your deferred window." 70 - CHAT_THINKING_ENGINE_NOT_CHOSEN = "No thinking engine is chosen yet. Choose one in Thinking so I can answer from your observations." 70 + CHAT_THINKING_ENGINE_NOT_CHOSEN = "no thinking engine is chosen yet. choose one in thinking so i can answer from your journal." 71 71 # fmt: on 72 72 73 73 from typing import Literal
+1 -1
solstone/apps/chat/routes.py
··· 147 147 identity = config.get("identity", {}) 148 148 owner_name = str(identity.get("preferred") or identity.get("name") or "").strip() 149 149 agent_name = str(config.get("agent", {}).get("name") or "").strip() 150 - return owner_name or "Owner", agent_name or "Sol" 150 + return owner_name or "Owner", agent_name or "sol" 151 151 152 152 153 153 def _build_sol_message_origins(
+32 -1
solstone/apps/chat/tests/test_routes.py
··· 417 417 418 418 assert response.status_code == 200 419 419 assert state["owner_name"] == "Owner" 420 - assert state["agent_name"] == "Sol" 420 + assert state["agent_name"] == "sol" 421 + 422 + 423 + def test_state_configured_agent_name_overrides_fallback(tmp_path, monkeypatch): 424 + journal = tmp_path / "journal" 425 + config_dir = journal / "config" 426 + config_dir.mkdir(parents=True) 427 + (config_dir / "journal.json").write_text( 428 + json.dumps( 429 + { 430 + "setup": {"completed_at": 1700000000000}, 431 + "agent": {"name": "Astra"}, 432 + } 433 + ) 434 + + "\n", 435 + encoding="utf-8", 436 + ) 437 + _set_today(monkeypatch, "20990110") 438 + env = _make_env(journal, monkeypatch) 439 + 440 + response, state = _state_json(env, "20990109") 441 + 442 + assert response.status_code == 200 443 + assert state["owner_name"] == "Owner" 444 + assert state["agent_name"] == "Astra" 445 + 446 + 447 + def test_workspace_agent_fallback_copy_is_lowercase(): 448 + source = Path("solstone/apps/chat/workspace.html").read_text(encoding="utf-8") 449 + 450 + assert "let agentName = 'sol';" in source 451 + assert "agentName = String(state.agent_name || 'sol');" in source 421 452 422 453 423 454 def test_state_today_unresolved_request_sets_open_id(journal_copy, monkeypatch):
+2 -2
solstone/apps/chat/workspace.html
··· 45 45 let isToday = false; 46 46 let solOpenRequestId = ''; 47 47 let ownerName = 'Owner'; 48 - let agentName = 'Sol'; 48 + let agentName = 'sol'; 49 49 let thinkingSurfaces = 'on_tap'; 50 50 let renderCtx = null; 51 51 let searchTimer = null; ··· 113 113 const state = await window.apiJson('/app/chat/api/state?day=' + encodeURIComponent(day)); 114 114 115 115 ownerName = String(state.owner_name || 'Owner'); 116 - agentName = String(state.agent_name || 'Sol'); 116 + agentName = String(state.agent_name || 'sol'); 117 117 thinkingSurfaces = String(state.thinking_surfaces || 'on_tap'); 118 118 todayDay = String(state.today_day || ''); 119 119 solOpenRequestId = String(state.sol_open_request_id || '');
+1 -1
solstone/convey/chat.py
··· 1950 1950 if kind == "owner_message": 1951 1951 history_lines.append(f"**Owner**: {event['text']}") 1952 1952 elif kind == "sol_message": 1953 - history_lines.append(f"**Sol**: {event['text']}") 1953 + history_lines.append(f"**sol**: {event['text']}") 1954 1954 if history_lines: 1955 1955 parts.append("Recent chat:\n" + "\n".join(history_lines[-6:])) 1956 1956
+39
tests/test_convey_chat.py
··· 165 165 return [event for event in read_chat_events(day) if event["kind"] == kind] 166 166 167 167 168 + def test_build_talent_prompt_uses_lowercase_sol_transcript_role(tmp_path, monkeypatch): 169 + import solstone.convey.chat as chat 170 + 171 + _setup_journal(tmp_path, monkeypatch) 172 + _reset_chat_state(chat) 173 + day = "20990102" 174 + monkeypatch.setattr(chat, "_today_day", lambda: day) 175 + monkeypatch.setattr( 176 + "solstone.convey.chat_stream.time.time", 177 + lambda: _ms(2099, 1, 2, 9, 0, 0) / 1000, 178 + ) 179 + append_chat_event( 180 + "owner_message", 181 + text="owner hello", 182 + app="sol", 183 + path="/app/sol", 184 + facet="work", 185 + ) 186 + append_chat_event( 187 + "sol_message", 188 + use_id="use-sol", 189 + text="sol hello", 190 + notes="", 191 + requested_target=None, 192 + requested_task=None, 193 + ) 194 + 195 + prompt = chat._build_talent_prompt( 196 + "support", 197 + "file a ticket", 198 + {}, 199 + {"app": "sol", "path": "/app/sol", "facet": "work"}, 200 + ) 201 + 202 + assert "**Owner**: owner hello" in prompt 203 + assert "**sol**: sol hello" in prompt 204 + assert "**Sol**:" not in prompt 205 + 206 + 168 207 @pytest.fixture 169 208 def chat_client(tmp_path, monkeypatch): 170 209 import solstone.convey.chat as chat
+8
tests/test_convey_chat_deferred.py
··· 171 171 return [event for event in read_chat_events(day) if event["kind"] == kind] 172 172 173 173 174 + def test_thinking_engine_not_chosen_copy_is_pinned_byte_for_byte(): 175 + assert ( 176 + CHAT_THINKING_ENGINE_NOT_CHOSEN 177 + == "no thinking engine is chosen yet. choose one in thinking so i can " 178 + "answer from your journal." 179 + ) 180 + 181 + 174 182 def test_compose_honest_degradation_fires_for_deferred_today_pending(): 175 183 import solstone.convey.chat as chat 176 184