Files
PaperClipAI/ui/src/lib/muse-invitation-draft.ts
T
DottaandPaperclip 896f3c7f3b Add personal Muse operator setup and composer controls (#16450)
Add Muse invitation, connection readiness and scoped operator controls.

Keep connection state tied to the current company, person, agent and binding. Surface failed reads and invalidate stale cleanup observations; require current authority and exact readback. Preserve Dot/Grok setup, source-trust limits and existing design tokens.

Carry actual merged backend e6dcc6b095 through a normal forward merge. Preserve its project-write authority fix, existing migration bytes and upstream idle-serving behavior; no new UI behavior is authored by this source carry. Current-head CI and substantive review remain merge gates; final canonical and deployed-provider qualification remain separate.

Refs #16450.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-17 23:01:42 -05:00

34 lines
1.7 KiB
TypeScript

import { AGENT_ROLES, type AgentRole } from "@paperclipai/shared";
export interface MuseInvitationDraft { name: string; role: AgentRole }
export const emptyMuseInvitationDraft: MuseInvitationDraft = { name: "", role: "general" };
function key(companyId: string, operatorId: string) {
return `paperclip.muse-invitation.${encodeURIComponent(companyId)}.${encodeURIComponent(operatorId)}`;
}
/** Only the name and role survive navigation. Pairing capabilities never enter storage. */
export function readMuseInvitationDraft(companyId: string, operatorId: string | null): MuseInvitationDraft {
if (!operatorId) return { ...emptyMuseInvitationDraft };
try {
const value: unknown = JSON.parse(localStorage.getItem(key(companyId, operatorId)) ?? "null");
if (typeof value === "object" && value !== null && "name" in value && "role" in value
&& typeof value.name === "string" && value.name.length <= 100
&& AGENT_ROLES.some(role => role === value.role)) {
return { name: value.name, role: AGENT_ROLES.find(role => role === value.role)! };
}
} catch { /* Storage is optional; the mounted draft remains usable. */ }
return { ...emptyMuseInvitationDraft };
}
export function saveMuseInvitationDraft(companyId: string, operatorId: string | null, draft: MuseInvitationDraft) {
if (!operatorId) return;
try { localStorage.setItem(key(companyId, operatorId), JSON.stringify({ name: draft.name, role: draft.role })); }
catch { /* Storage is optional. */ }
}
export function clearMuseInvitationDraft(companyId: string, operatorId: string | null) {
if (!operatorId) return;
try { localStorage.removeItem(key(companyId, operatorId)); } catch { /* Storage is optional. */ }
}