feat(tools): persona selector across standalone tools + dashboard chat

Wire the legal-domain persona picker into corpus, deep-research, korrespond and
the dashboard chat. Each endpoint reads the chosen profile, resolves its packages
against client 57, and scopes retrieval via package_ids (falling back to family
when omitted). New dashboard tenants now subscribe to all DBN domain packages so
persona switching survives the subscription intersection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 23:03:31 +02:00
parent 662fbf7d6d
commit d156f8cf6b
13 changed files with 251 additions and 20 deletions
+29
View File
@@ -12,6 +12,12 @@ require_once __DIR__ . '/../includes/layout_dashboard.php';
<section class="dash-card" style="display:flex; flex-direction:column; min-height:60vh;">
<div class="dms-filters" style="margin-bottom:8px;">
<label style="font-size:13px;display:inline-flex;gap:6px;align-items:center;">
⚖️ <span>Domain:</span>
<select id="chatPersona" aria-label="Legal domain persona">
<option value="">Default (family law)</option>
</select>
</label>
<label style="font-size:13px;display:inline-flex;gap:6px;align-items:center;">
📁 <span>Scope:</span>
<select id="chatFolderScope">
@@ -86,6 +92,27 @@ require_once __DIR__ . '/../includes/layout_dashboard.php';
function safe(s) { return String(s ?? '').replace(/[&<>"]/g, c => ({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;' }[c])); }
// Populate the legal-domain persona picker (personas.php lives at the site root).
(async function loadPersonas() {
const sel = document.getElementById('chatPersona');
if (!sel) return;
try {
const resp = await fetch('/api/personas.php', { credentials: 'same-origin' });
if (!resp.ok) return;
const data = await resp.json();
const personas = (data && data.personas) || [];
if (!personas.length) return;
sel.innerHTML = '';
personas.forEach(p => {
const opt = document.createElement('option');
opt.value = p.slug;
opt.textContent = p.name;
sel.appendChild(opt);
});
if (data.default_persona) sel.value = data.default_persona;
} catch (_) { /* keep the default option */ }
})();
function clearEmpty() {
const empty = $log.querySelector('.chat-empty');
if (empty) empty.remove();
@@ -132,7 +159,9 @@ require_once __DIR__ . '/../includes/layout_dashboard.php';
const folderScope = document.getElementById('chatFolderScope');
const includeSub = document.getElementById('chatIncludeSub');
const includeRel = document.getElementById('chatIncludeRelated');
const persona = document.getElementById('chatPersona');
const body = { question, history: history.slice(0, -1) };
if (persona && persona.value) body.profile = persona.value;
if (folderScope && folderScope.value && folderScope.value !== 'all') {
body.folder_id = folderScope.value;
body.include_subfolders = includeSub && includeSub.checked;