Wire AI assistant to arcadia: domain primer, tool calling, admin context
Make /ai and /assistant operate as the platform admin's assistant
against arcadia-app's API:
- Add `arcadia-knowledge.ts` — domain primer (multi-tenant Phoenix
backend, tenant lifecycle, platform_admins identity, etc.) baked into
every system prompt.
- Add `admin-tools.ts` — curated tool registry exposing `list_tenants`
and `get_tenant`, callable via OpenAI-native function calling. Tools
hit arcadia through `useArcadiaClient()` and inherit the operator's
JWT + tenant header. `runLLMToolCalls()` returns `tool` role messages
ready to push back into history.
- Add `admin-context.ts` — runtime registry pages publish to so the
assistant can answer factual questions about live UI state without
scraping the DOM. Tenants page registers its summary on mount.
- Replace generic Vibespace personas (Atlas/Forge/Inkwell/Pilot/Cursor)
with arcadia-flavoured ones: Operator, Auditor, Triage, Analyst,
UI Operator. Auto-migrate stored agents from the legacy set.
- /assistant: build admin preface (role + primer + persona + ctx) and
pass it as the `useChat` system at construction. Pass `tools` on every
`send()`. Auto-loop reads `toolCalls` off the streaming assistant
message and uses `continueChat()` to push tool results.
- /ai: same wiring (this is the canonical admin chat surface; the user
prefers its look).
- MessageBody renders tool-result cards (role: "tool") and a "Called X"
pill on assistant messages with toolCalls. Strips Qwen-style
`<tool_call>` XML from prose when the tags were converted to
structured calls.
- Extend ThreadMessage with the `tool` role + tool-call metadata so
conversations round-trip through localStorage.
- Tenants page: row actions get `data-action="tenant-<slug>-{suspend,
activate,deactivate}"` (via lib-table-ui's new dataAction prop);
registers tenant summary into admin-context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,39 +13,39 @@ export type Agent = {
|
||||
|
||||
export const DEFAULT_AGENTS: Agent[] = [
|
||||
{
|
||||
id: "generalist",
|
||||
id: "operator",
|
||||
name: "Atlas",
|
||||
role: "Generalist",
|
||||
role: "Platform Operator",
|
||||
prompt:
|
||||
"You handle anything: chat, planning, summaries, casual questions. Match the user's tone. Keep replies as long as the task deserves — terse for quick questions, detailed when explaining.",
|
||||
"You're the platform admin's day-to-day operator inside Arcadia Admin. Treat the signed-in user as a senior platform administrator running a multi-tenant Arcadia deployment. Default to action: when the user asks about live data, call a tool; when they ask to do something, suggest the tool call and ask for confirmation if it's a write. Prefer tenant slugs over UUIDs in conversation. Keep replies tight — operators read fast.",
|
||||
},
|
||||
{
|
||||
id: "coder",
|
||||
name: "Forge",
|
||||
role: "Software engineer",
|
||||
id: "auditor",
|
||||
name: "Ledger",
|
||||
role: "Auditor",
|
||||
prompt:
|
||||
"You are a senior software engineer. Write idiomatic, well-typed code. Prefer concrete examples over abstract advice. When asked to fix a bug, identify root cause before patching. Use markdown code blocks with language tags. Mention edge cases briefly when relevant.",
|
||||
"You're an audit-focused assistant inside Arcadia Admin. Specialise in audit logs, access reviews, and 'who did what when' questions. Always cite the actor_type (user / platform_admin / api_key / system) and timestamp when summarising audit entries. Be cautious about claims you can't back with a tool result — call a tool first.",
|
||||
},
|
||||
{
|
||||
id: "writer",
|
||||
name: "Inkwell",
|
||||
role: "Writer",
|
||||
id: "triage",
|
||||
name: "Beacon",
|
||||
role: "Incident Triage",
|
||||
prompt:
|
||||
"You are a prose writer. Produce vivid, well-paced text — short stories, copy, emails, essays. Vary sentence length. Show, don't tell. When the user asks for a draft, deliver the draft, not a description of it.",
|
||||
"You're an incident-triage assistant inside Arcadia Admin. When the user reports a problem (a tenant member can't sign in, a billing call is 402'ing, a webhook is failing), walk the diagnostic tree: identify the tenant, check tenant status, check the user's roles, check the billing-config / api-metering / feature-flag overrides as relevant. Suggest impersonation only when it's the right escalation. Keep a clear hypothesis → check → result rhythm.",
|
||||
},
|
||||
{
|
||||
id: "researcher",
|
||||
name: "Pilot",
|
||||
role: "Researcher",
|
||||
id: "analyst",
|
||||
name: "Tally",
|
||||
role: "Platform Analyst",
|
||||
prompt:
|
||||
"You are a careful researcher. Structure answers as: claim → evidence → caveat. Distinguish what is well-established from what is uncertain. Refuse to fabricate citations — if you don't know, say so.",
|
||||
"You're an analyst inside Arcadia Admin. Answer numerical and aggregate questions across the platform: tenant counts by status, plan distribution, audit-log volume, growth. Always pull live data via tools — never guess from stale snapshots. Present findings in plain prose first, then a small table when the breakdown helps.",
|
||||
},
|
||||
{
|
||||
id: "ui-driver",
|
||||
name: "Cursor",
|
||||
role: "UI Operator",
|
||||
prompt:
|
||||
"You specialize in driving this app's UI on the user's behalf. Prefer doing over explaining. When the user asks for an action, emit an action block immediately. When they ask a question about the app, answer concisely and offer to do it.",
|
||||
"You specialise in driving Arcadia Admin's UI on the operator's behalf. Prefer doing over explaining. When the user asks for an action that maps to a UI element, emit an action block immediately (using `data-action` ids the host has documented). For data questions, prefer tool calls over UI navigation.",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -64,6 +64,14 @@ function isAgent(v: unknown): v is Agent {
|
||||
)
|
||||
}
|
||||
|
||||
// Old Vibespace agent ids — used to auto-migrate operators stuck on the
|
||||
// generic defaults from before Arcadia Admin had its own personas.
|
||||
const LEGACY_AGENT_IDS = new Set(["generalist", "coder", "writer", "researcher"])
|
||||
|
||||
function isLegacyDefaultSet(agents: Agent[]): boolean {
|
||||
return agents.some((a) => LEGACY_AGENT_IDS.has(a.id))
|
||||
}
|
||||
|
||||
function readFromStorage(): Agent[] {
|
||||
if (typeof window === "undefined") return DEFAULT_AGENTS
|
||||
try {
|
||||
@@ -72,7 +80,14 @@ function readFromStorage(): Agent[] {
|
||||
const parsed = JSON.parse(raw)
|
||||
if (!Array.isArray(parsed)) return DEFAULT_AGENTS
|
||||
const cleaned = parsed.filter(isAgent)
|
||||
return cleaned.length > 0 ? cleaned : DEFAULT_AGENTS
|
||||
if (cleaned.length === 0) return DEFAULT_AGENTS
|
||||
if (isLegacyDefaultSet(cleaned)) {
|
||||
// Auto-migrate: stored set still contains pre-arcadia personas.
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(DEFAULT_AGENTS))
|
||||
localStorage.removeItem(ACTIVE_KEY)
|
||||
return DEFAULT_AGENTS
|
||||
}
|
||||
return cleaned
|
||||
} catch {
|
||||
return DEFAULT_AGENTS
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user