Three changes paired with SmartAssistant 2.10.0:
1. prompt_builder.py — every prompt section (tool_rules, writing_style,
legal_assistance, personality_case, personality_office, other_rules_case,
other_rules_office) now reads from context['assistantPrompts'][key]
with the existing string as a fallback default. Admins editing
AssistantPrompt rows in the EspoCRM UI immediately affect the next
conversation. A missing/inactive row falls back to the in-code default
so deleting everything in the UI never bricks Shira.
Also: the user-profile section now prefers context['userProfile']
(from the new UserProfile entity) over the explicit `user_profile`
parameter, completing the move from /opt/data/profiles/*.md to the CRM.
2. crm_tools.py:save_memory — stops POSTing directly to /Note with a
'🧠 category:' prefix. Routes through SmartAssistant/action/executeTool
so the PHP saveMemory handler actually creates a CaseMemory row.
Cause of yesterday's prod issue: CaseMemory table was empty even
though Shira was "saving" to it.
3. scripts/migrate_legacy_memory_notes.py — one-shot migration that
sweeps all existing '🧠 category: ...' Notes into CaseMemory rows
and soft-deletes the originals. Idempotent; safe to re-run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>