aifirst: lift context/agents/tools runtime to lib-aifirst-ui

The mechanism (context surface registry, persona storage + hooks, tool
parser/dispatcher) is now generic and lives in @crema/aifirst-ui/{context,
agents,tools}. This template keeps only the arcadia-shaped configuration:

- agents.ts — owns DEFAULT_AGENTS + legacy/retired migration sets, calls
  configureAgents() at module load, re-exports the runtime
- admin-tools.ts — keeps the 19 arcadia tool definitions, binds the
  runtime via createToolRuntime(TOOLS), re-exports the bound functions
- admin-context.ts — deleted; 18 routes now import directly from
  @crema/aifirst-ui/context

Routes that import from ~/lib/agents and ~/lib/admin-tools are unchanged
(wrapper modules preserve the existing import surface).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jules
2026-05-05 15:18:48 +10:00
parent c968ac0735
commit a286b9cdce
21 changed files with 73 additions and 370 deletions

View File

@@ -47,7 +47,7 @@ import {
} from "~/lib/arcadia/audit-logs"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Audit log")
@@ -187,7 +187,7 @@ export default function ActivityRoute() {
}),
[logs],
)
useRegisterAdminContext("audit_log", summary)
useRegisterContext("audit_log", summary)
const table = useTable<AuditLog>({
data: logs,

View File

@@ -101,7 +101,7 @@ import {
subscribeActiveReasoning,
type ReasoningEffort,
} from "~/lib/arcadia/llm-configs"
import { formatAdminContextForPrompt } from "~/lib/admin-context"
import { formatContextForPrompt } from "@crema/aifirst-ui/context"
import { ConfirmCard } from "~/components/assistant/confirm-card"
import { renderToolResult } from "~/components/assistant/tool-result-renderers"
@@ -722,7 +722,7 @@ function ChatSurface({
ARCADIA_KNOWLEDGE,
persona,
handoffNote,
formatAdminContextForPrompt(),
formatContextForPrompt(),
]
.filter(Boolean)
.join("\n\n")

View File

@@ -63,7 +63,7 @@ import {
import { listTenants, type Tenant } from "~/lib/arcadia/tenants"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Announcements")
@@ -218,7 +218,7 @@ export default function AnnouncementsRoute() {
}),
[items],
)
useRegisterAdminContext("announcements", summary)
useRegisterContext("announcements", summary)
const table = useTable<Announcement>({
data: items,

View File

@@ -90,7 +90,7 @@ wait_for assistant-ui-control
\`\`\`"`
import { formatAdminContextForPrompt } from "~/lib/admin-context"
import { formatContextForPrompt } from "@crema/aifirst-ui/context"
import {
buildDenialMessages,
classifyCalls,
@@ -111,7 +111,7 @@ function buildAdminPreface(activeAgent: Agent | undefined, uiControl: boolean):
const persona = activeAgent
? `Active persona: ${activeAgent.name}${activeAgent.role}\n${activeAgent.prompt}`
: ""
const ctx = formatAdminContextForPrompt()
const ctx = formatContextForPrompt()
const parts = [
"You are the operator's assistant inside Arcadia Admin. Be precise and direct. You have native function tools attached to this conversation — call them whenever the user asks about live platform state (counts, statuses, listings, lookups). Never invent tenant slugs, user counts, or statuses; if you need data, call a tool.",
"Two retrieval surfaces exist for documentation/knowledge: `search_docs` (browser-side, BM25 over the bundled arcadia docs — fast, always available, small corpus) and `search_kb` (server-side, BM25 over arcadia-search — `docs` (arcadia parity), `operator-tools` (arcadia-search + arcadia-admin admin docs), `files` (uploaded files), plus any custom corpora the operator adds via /search). For questions about the bundled arcadia docs either is fine; prefer `search_kb` for richer hits or for content outside the bundled docs (uploaded files, the admin tooling itself, tenant-specific knowledge). If unsure what corpora exist, call `list_search_corpora`. When `search_kb` returns a chunk_id you want to expand, call `read_chunk(chunk_id, corpus)`. When the operator says results look stale or after they've uploaded new files, call `rebuild_search_corpus(tenant, corpus)`.",

View File

@@ -84,7 +84,7 @@ import {
} from "~/lib/arcadia/storage-configs"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Buckets")
@@ -194,7 +194,7 @@ export default function BucketsRoute() {
}),
[activeConfig, buckets],
)
useRegisterAdminContext("buckets", summary)
useRegisterContext("buckets", summary)
return (
<AppShell>

View File

@@ -35,7 +35,7 @@ import {
} from "~/lib/arcadia/health"
import { listTenants, type Tenant } from "~/lib/arcadia/tenants"
import { listUsers, type User } from "~/lib/arcadia/users"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
@@ -96,7 +96,7 @@ export default function HomeRoute() {
}
}, [data])
useRegisterAdminContext("overview", stats)
useRegisterContext("overview", stats)
return (
<AppShell>

View File

@@ -65,7 +65,7 @@ import { listUsers, type User } from "~/lib/arcadia/users"
import { listRoles, type Role } from "~/lib/arcadia/roles"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Memberships")
@@ -272,7 +272,7 @@ export default function MembershipsRoute() {
}),
[memberships],
)
useRegisterAdminContext("memberships", summary)
useRegisterContext("memberships", summary)
const table = useTable<Membership>({
data: filtered,

View File

@@ -81,7 +81,7 @@ import {
} from "~/lib/arcadia/health"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Monitoring")
@@ -193,7 +193,7 @@ export default function MonitoringRoute() {
}),
[data],
)
useRegisterAdminContext("monitoring", summary)
useRegisterContext("monitoring", summary)
return (
<AppShell>

View File

@@ -61,7 +61,7 @@ import {
import { listDroplets, type Droplet } from "~/lib/arcadia/monitoring"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Networking")
@@ -107,7 +107,7 @@ export default function NetworkingRoute() {
if (session) refresh()
}, [session, refresh])
useRegisterAdminContext("networking", {
useRegisterContext("networking", {
firewalls: firewalls.length,
vpcs: vpcs.length,
domains: domains.length,

View File

@@ -72,7 +72,7 @@ import {
} from "~/lib/arcadia/scheduled-tasks"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Scheduled tasks")
@@ -227,7 +227,7 @@ export default function ScheduledTasksRoute() {
}),
[tasks],
)
useRegisterAdminContext("scheduled_tasks", summary)
useRegisterContext("scheduled_tasks", summary)
const table = useTable<ScheduledTask>({
data: tasks,

View File

@@ -60,7 +60,7 @@ import {
} from "~/lib/search-admin"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Search")
@@ -157,7 +157,7 @@ export default function SearchRoute() {
}),
[tenants, corpora],
)
useRegisterAdminContext("search", adminSurface)
useRegisterContext("search", adminSurface)
const rebuild = useCallback(
async (tenant: string, corpus: string) => {

View File

@@ -77,7 +77,7 @@ import {
} from "~/lib/arcadia/secrets"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Secrets")
@@ -234,7 +234,7 @@ export default function SecretsRoute() {
}),
[secrets],
)
useRegisterAdminContext("secrets", summary)
useRegisterContext("secrets", summary)
const table = useTable<Secret>({
data: filtered,

View File

@@ -59,7 +59,7 @@ import {
} from "~/lib/arcadia/sso"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("SSO")
@@ -102,7 +102,7 @@ export default function SsoRoute() {
if (session) refresh()
}, [session, refresh])
useRegisterAdminContext("sso", {
useRegisterContext("sso", {
identity_providers: idps.length,
enabled_idps: idps.filter((i) => i.enabled).length,
active_sessions: sessions.length,

View File

@@ -70,7 +70,7 @@ import {
} from "~/lib/arcadia/status-page"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Status page")
@@ -129,7 +129,7 @@ export default function StatusPageRoute() {
if (session) refresh()
}, [session, refresh])
useRegisterAdminContext("status_page", {
useRegisterContext("status_page", {
components: components.length,
open_incidents: incidents.filter((i) => i.status !== "resolved").length,
subscribers: subscribers.length,

View File

@@ -77,7 +77,7 @@ import {
} from "~/lib/arcadia/storage-configs"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Storage")
@@ -250,7 +250,7 @@ export default function StorageRoute() {
}),
[configs],
)
useRegisterAdminContext("storage", summary)
useRegisterContext("storage", summary)
const table = useTable<StorageConfig>({
data: configs,

View File

@@ -36,7 +36,7 @@ import {
} from "~/lib/arcadia/tenants"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Tenants")
@@ -160,7 +160,7 @@ export default function TenantsRoute() {
}),
[tenants],
)
useRegisterAdminContext("tenants", tenantSummary)
useRegisterContext("tenants", tenantSummary)
const table = useTable<Tenant>({
data: tenants,

View File

@@ -87,7 +87,7 @@ import {
} from "~/lib/arcadia/users"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
import { UserDetailSheet } from "~/components/users/user-detail-sheet"
export const meta = () => pageTitle("Users")
@@ -167,7 +167,7 @@ export default function UsersRoute() {
}),
[users, invitations, roles],
)
useRegisterAdminContext("users", summary)
useRegisterContext("users", summary)
return (
<AppShell>

View File

@@ -77,7 +77,7 @@ import {
} from "~/lib/arcadia/webhooks"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"
import { useRegisterContext } from "@crema/aifirst-ui/context"
export const meta = () => pageTitle("Webhooks")
@@ -228,7 +228,7 @@ export default function WebhooksRoute() {
}),
[webhooks],
)
useRegisterAdminContext("webhooks", summary)
useRegisterContext("webhooks", summary)
const table = useTable<Webhook>({
data: webhooks,