rename agents/main→assistant, role-based default entry agent
Nightly Build / build (push) Successful in 6m31s
Nightly Build / build (push) Successful in 6m31s
This commit is contained in:
@@ -313,7 +313,7 @@ export function renderAgent(msg) {
|
||||
<div class="copilot-agent-header">
|
||||
<i class="bi bi-${icon}"></i>
|
||||
<span>
|
||||
<strong>${msg.parent_agent_id ?? 'main'}</strong>
|
||||
<strong>${msg.parent_agent_id ?? 'assistant'}</strong>
|
||||
<i class="bi bi-arrow-right mx-1" style="font-size:0.7rem"></i>
|
||||
<strong>${msg.agent_id}</strong>
|
||||
</span>
|
||||
@@ -334,7 +334,7 @@ export function renderAgentEnd(msg) {
|
||||
<span>
|
||||
<strong>${msg.agent_id}</strong>
|
||||
<i class="bi bi-arrow-right mx-1" style="font-size:0.7rem"></i>
|
||||
<strong>${msg.parent_agent_id ?? 'main'}</strong>
|
||||
<strong>${msg.parent_agent_id ?? 'assistant'}</strong>
|
||||
</span>
|
||||
<span class="copilot-agent-badge done">${t('copilot.agent_finished')}</span>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,9 @@ import { LightElement } from '../lib/base.js';
|
||||
import { t } from '../lib/i18n.js';
|
||||
|
||||
const ADMIN_ID = 'admin';
|
||||
// Source-driven chat agent (needs a project run-context): never a personal default,
|
||||
// so it is excluded from the role assistant picker (mirrors the server-side guard).
|
||||
const PROJECT_COORDINATOR_ID = 'project-coordinator';
|
||||
|
||||
export class RolesPage extends LightElement {
|
||||
|
||||
@@ -12,6 +15,7 @@ export class RolesPage extends LightElement {
|
||||
_open: { state: true },
|
||||
_roles: { state: true },
|
||||
_groups: { state: true },
|
||||
_agents: { state: true },
|
||||
_error: { state: true },
|
||||
_modal: { state: true }, // null | { mode: 'create'|'edit', role?, form }
|
||||
};
|
||||
@@ -22,6 +26,7 @@ export class RolesPage extends LightElement {
|
||||
this._open = false;
|
||||
this._roles = null;
|
||||
this._groups = null;
|
||||
this._agents = null;
|
||||
this._error = null;
|
||||
this._modal = null;
|
||||
}
|
||||
@@ -45,14 +50,19 @@ export class RolesPage extends LightElement {
|
||||
async _load() {
|
||||
this._error = null;
|
||||
try {
|
||||
const [rRes, gRes] = await Promise.all([
|
||||
const [rRes, gRes, aRes] = await Promise.all([
|
||||
fetch('/api/roles'),
|
||||
fetch('/api/tool-permission-groups'),
|
||||
fetch('/api/agents'),
|
||||
]);
|
||||
if (!rRes.ok) throw new Error(`HTTP ${rRes.status}`);
|
||||
if (!gRes.ok) throw new Error(`HTTP ${gRes.status}`);
|
||||
if (!aRes.ok) throw new Error(`HTTP ${aRes.status}`);
|
||||
this._roles = await rRes.json();
|
||||
this._groups = await gRes.json();
|
||||
// Only `type:chat` agents are entry agents; project-coordinator is source-driven.
|
||||
this._agents = (await aRes.json())
|
||||
.filter(a => a.type === 'chat' && a.id !== PROJECT_COORDINATOR_ID);
|
||||
} catch (e) {
|
||||
this._error = e.message;
|
||||
}
|
||||
@@ -76,12 +86,26 @@ export class RolesPage extends LightElement {
|
||||
} catch { return []; }
|
||||
}
|
||||
|
||||
_mergeAttrs(attrs, uiMode, allowedGroups) {
|
||||
// The role's default entry agent (data-driven, §0.1). Empty means "fall back to the
|
||||
// instance default assistant" — the server resolver handles it, so we store nothing.
|
||||
_attrsChatAgent(attrs) {
|
||||
try { const a = JSON.parse(attrs || '{}').chat_agent; return typeof a === 'string' ? a : ''; }
|
||||
catch { return ''; }
|
||||
}
|
||||
|
||||
// Display name for a chat-agent id (falls back to the id, or the default label when unset).
|
||||
_agentName(id) {
|
||||
if (!id) return t('roles.form.assistant_default');
|
||||
return this._agents?.find(a => a.id === id)?.name ?? id;
|
||||
}
|
||||
|
||||
_mergeAttrs(attrs, uiMode, allowedGroups, chatAgent) {
|
||||
let o = {};
|
||||
try { o = JSON.parse(attrs || '{}') ?? {}; } catch { o = {}; }
|
||||
if (uiMode === 'simple') o.ui_mode = 'simple'; else delete o.ui_mode;
|
||||
const extras = Array.isArray(allowedGroups) ? allowedGroups.filter(Boolean) : [];
|
||||
if (extras.length) o.permission_groups = extras; else delete o.permission_groups;
|
||||
if (chatAgent) o.chat_agent = chatAgent; else delete o.chat_agent;
|
||||
const keys = Object.keys(o);
|
||||
return keys.length ? JSON.stringify(o) : null;
|
||||
}
|
||||
@@ -89,7 +113,7 @@ export class RolesPage extends LightElement {
|
||||
_openCreate() {
|
||||
this._modal = {
|
||||
mode: 'create',
|
||||
form: { id: '', label: '', permission_group: this._groups?.[0]?.id ?? 'default', attrs: '', ui_mode: 'full', allowed_groups: [] },
|
||||
form: { id: '', label: '', permission_group: this._groups?.[0]?.id ?? 'default', attrs: '', ui_mode: 'full', allowed_groups: [], chat_agent: '' },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,7 +121,7 @@ export class RolesPage extends LightElement {
|
||||
this._modal = {
|
||||
mode: 'edit',
|
||||
role,
|
||||
form: { label: role.label, permission_group: role.permission_group, attrs: role.attrs ?? '', ui_mode: this._attrsUiMode(role.attrs), allowed_groups: this._attrsAllowedGroups(role.attrs) },
|
||||
form: { label: role.label, permission_group: role.permission_group, attrs: role.attrs ?? '', ui_mode: this._attrsUiMode(role.attrs), allowed_groups: this._attrsAllowedGroups(role.attrs), chat_agent: this._attrsChatAgent(role.attrs) },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -129,7 +153,7 @@ export class RolesPage extends LightElement {
|
||||
id: form.id.trim(),
|
||||
label: form.label.trim(),
|
||||
permission_group: form.permission_group,
|
||||
attrs: this._mergeAttrs(form.attrs, form.ui_mode, form.allowed_groups),
|
||||
attrs: this._mergeAttrs(form.attrs, form.ui_mode, form.allowed_groups, form.chat_agent),
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
@@ -146,7 +170,7 @@ export class RolesPage extends LightElement {
|
||||
body: JSON.stringify({
|
||||
label: form.label.trim(),
|
||||
permission_group: form.permission_group,
|
||||
attrs: this._mergeAttrs(form.attrs, form.ui_mode, form.allowed_groups),
|
||||
attrs: this._mergeAttrs(form.attrs, form.ui_mode, form.allowed_groups, form.chat_agent),
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
@@ -226,6 +250,14 @@ export class RolesPage extends LightElement {
|
||||
</select>
|
||||
<div class="form-text" style="font-size:.75rem">${unsafeHTML(t('roles.form.interface_hint'))}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">${t('roles.form.assistant')}</label>
|
||||
<select class="form-select" @change=${e => this._patch('chat_agent', e.target.value)}>
|
||||
<option value="" ?selected=${!form.chat_agent}>${t('roles.form.assistant_default')}</option>
|
||||
${(this._agents ?? []).map(a => html`<option value=${a.id} ?selected=${form.chat_agent === a.id}>${a.name}</option>`)}
|
||||
</select>
|
||||
<div class="form-text" style="font-size:.75rem">${t('roles.form.assistant_hint')}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">${t('roles.form.attrs')} <span class="text-muted">${t('roles.form.attrs_hint')}</span></label>
|
||||
<input class="form-control font-monospace" placeholder=${t('roles.form.attrs_ph')} .value=${form.attrs}
|
||||
@@ -275,6 +307,7 @@ export class RolesPage extends LightElement {
|
||||
<th>${t('roles.col.label')}</th>
|
||||
<th>${t('roles.col.group')}</th>
|
||||
<th>${t('roles.col.interface')}</th>
|
||||
<th>${t('roles.col.assistant')}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -289,6 +322,7 @@ export class RolesPage extends LightElement {
|
||||
<td>${this._attrsUiMode(r.attrs) === 'simple'
|
||||
? html`<span class="badge" style="background:var(--accent-soft);color:var(--accent)">${t('roles.badge.simple')}</span>`
|
||||
: html`<span class="badge bg-secondary">${t('roles.badge.full')}</span>`}</td>
|
||||
<td>${this._agentName(this._attrsChatAgent(r.attrs))}</td>
|
||||
<td>
|
||||
<div class="um-actions">
|
||||
<button class="um-btn-icon" title=${isAdmin ? t('roles.tooltip.locked') : t('roles.tooltip.edit')}
|
||||
|
||||
+6
-2
@@ -118,7 +118,7 @@ export default {
|
||||
'llmr.empty': 'No requests found.',
|
||||
'llmr.total': '{n} rows',
|
||||
'llmr.filter.agent_id': 'Agent ID',
|
||||
'llmr.filter.agent_ph': 'e.g. main',
|
||||
'llmr.filter.agent_ph': 'e.g. assistant',
|
||||
'llmr.filter.source': 'Source',
|
||||
'llmr.filter.source_ph': 'e.g. web, tic, cron',
|
||||
'llmr.filter.from': 'From',
|
||||
@@ -556,7 +556,7 @@ export default {
|
||||
'approval.form.source': 'Source',
|
||||
'approval.form.source_any': 'Any',
|
||||
'approval.form.agent_id': 'Agent ID',
|
||||
'approval.form.agent_id_ph': 'main (empty = any)',
|
||||
'approval.form.agent_id_ph': 'assistant (empty = any)',
|
||||
'approval.form.note': 'Note',
|
||||
'approval.form.note_ph': 'Short description…',
|
||||
'approval.form.cancel': 'Cancel',
|
||||
@@ -656,6 +656,7 @@ export default {
|
||||
'roles.col.label': 'Label',
|
||||
'roles.col.group': 'Permission group',
|
||||
'roles.col.interface': 'Interface',
|
||||
'roles.col.assistant': 'Assistant',
|
||||
|
||||
'roles.badge.simple': 'Simple',
|
||||
'roles.badge.full': 'Full',
|
||||
@@ -674,6 +675,9 @@ export default {
|
||||
'roles.form.interface_full': 'Full — all pages',
|
||||
'roles.form.interface_simple': 'Simple — chat only',
|
||||
'roles.form.interface_hint': 'Members with the simple interface see only chat and inbox. Stored as <code>ui_mode</code> in the attrs JSON below.',
|
||||
'roles.form.assistant': 'Default assistant',
|
||||
'roles.form.assistant_default': 'Default (Assistant)',
|
||||
'roles.form.assistant_hint': 'The agent members of this role chat with by default. Overridable per person later.',
|
||||
'roles.form.attrs': 'Attrs',
|
||||
'roles.form.attrs_hint': '(JSON, optional)',
|
||||
'roles.form.attrs_ph': '{}',
|
||||
|
||||
+6
-2
@@ -118,7 +118,7 @@ export default {
|
||||
'llmr.empty': 'Aucune requête trouvée.',
|
||||
'llmr.total': '{n} lignes',
|
||||
'llmr.filter.agent_id': 'ID de l\'agent',
|
||||
'llmr.filter.agent_ph': 'ex. main',
|
||||
'llmr.filter.agent_ph': 'ex. assistant',
|
||||
'llmr.filter.source': 'Source',
|
||||
'llmr.filter.source_ph': 'ex. web, tic, cron',
|
||||
'llmr.filter.from': 'De',
|
||||
@@ -556,7 +556,7 @@ export default {
|
||||
'approval.form.source': 'Source',
|
||||
'approval.form.source_any': 'Toute',
|
||||
'approval.form.agent_id': 'ID de l\'agent',
|
||||
'approval.form.agent_id_ph': 'main (vide = toute)',
|
||||
'approval.form.agent_id_ph': 'assistant (vide = toute)',
|
||||
'approval.form.note': 'Note',
|
||||
'approval.form.note_ph': 'Brève description…',
|
||||
'approval.form.cancel': 'Annuler',
|
||||
@@ -656,6 +656,7 @@ export default {
|
||||
'roles.col.label': 'Libellé',
|
||||
'roles.col.group': 'Groupe de permissions',
|
||||
'roles.col.interface': 'Interface',
|
||||
'roles.col.assistant': 'Assistant',
|
||||
|
||||
'roles.badge.simple': 'Simple',
|
||||
'roles.badge.full': 'Complet',
|
||||
@@ -674,6 +675,9 @@ export default {
|
||||
'roles.form.interface_full': 'Complet — toutes les pages',
|
||||
'roles.form.interface_simple': 'Simple — discussion uniquement',
|
||||
'roles.form.interface_hint': 'Les membres avec l\'interface simple voient seulement la discussion et la boîte de réception. Stocké comme <code>ui_mode</code> dans le JSON d\'attributs ci-dessous.',
|
||||
'roles.form.assistant': 'Assistant par défaut',
|
||||
'roles.form.assistant_default': 'Par défaut (Assistant)',
|
||||
'roles.form.assistant_hint': 'L\'agent avec lequel les membres de ce rôle discutent par défaut. Remplaçable par personne plus tard.',
|
||||
'roles.form.attrs': 'Attributs',
|
||||
'roles.form.attrs_hint': '(JSON, facultatif)',
|
||||
'roles.form.attrs_ph': '{}',
|
||||
|
||||
+6
-2
@@ -118,7 +118,7 @@ export default {
|
||||
'llmr.empty': 'Nessuna richiesta trovata.',
|
||||
'llmr.total': '{n} righe',
|
||||
'llmr.filter.agent_id': 'ID Agente',
|
||||
'llmr.filter.agent_ph': 'es. main',
|
||||
'llmr.filter.agent_ph': 'es. assistant',
|
||||
'llmr.filter.source': 'Sorgente',
|
||||
'llmr.filter.source_ph': 'es. web, tic, cron',
|
||||
'llmr.filter.from': 'Da',
|
||||
@@ -556,7 +556,7 @@ export default {
|
||||
'approval.form.source': 'Origine',
|
||||
'approval.form.source_any': 'Qualsiasi',
|
||||
'approval.form.agent_id': 'ID agente',
|
||||
'approval.form.agent_id_ph': 'main (vuoto = qualsiasi)',
|
||||
'approval.form.agent_id_ph': 'assistant (vuoto = qualsiasi)',
|
||||
'approval.form.note': 'Nota',
|
||||
'approval.form.note_ph': 'Breve descrizione…',
|
||||
'approval.form.cancel': 'Annulla',
|
||||
@@ -656,6 +656,7 @@ export default {
|
||||
'roles.col.label': 'Etichetta',
|
||||
'roles.col.group': 'Gruppo di permessi',
|
||||
'roles.col.interface': 'Interfaccia',
|
||||
'roles.col.assistant': 'Assistente',
|
||||
|
||||
'roles.badge.simple': 'Semplice',
|
||||
'roles.badge.full': 'Completa',
|
||||
@@ -674,6 +675,9 @@ export default {
|
||||
'roles.form.interface_full': 'Completa — tutte le pagine',
|
||||
'roles.form.interface_simple': 'Semplice — solo chat',
|
||||
'roles.form.interface_hint': 'I membri con interfaccia semplice vedono solo chat e richieste. Memorizzato come <code>ui_mode</code> nell\'attrs JSON qui sotto.',
|
||||
'roles.form.assistant': 'Assistente predefinito',
|
||||
'roles.form.assistant_default': 'Predefinito (Assistente)',
|
||||
'roles.form.assistant_hint': 'L\'agente con cui i membri di questo ruolo parlano di default. In futuro sovrascrivibile per persona.',
|
||||
'roles.form.attrs': 'Attrs',
|
||||
'roles.form.attrs_hint': '(JSON, opzionale)',
|
||||
'roles.form.attrs_ph': '{}',
|
||||
|
||||
Reference in New Issue
Block a user