Full management surfaces for the platform-admin tenant, mirroring the
existing Tenants pattern (DataTable + row actions + create/edit dialogs +
ConfirmDialog for destructive ops, all data-action tagged for the
command bus, useRegisterAdminContext publishing for the assistant).
- Storage (/storage): backends + credentials. Write-only secret fields,
Validate/Activate/Deactivate/Set-default/Mark-degraded/Maintenance.
- Users (/users): tabs for Users, Invitations, Roles. Per-user View
drawer with profile, role add/remove, API keys (one-time reveal on
create), usage + quota.
- Secrets (/secrets): /api/v1/admin/secrets — create/rotate/rollback,
versions dialog, enable/disable, generate-value helper.
- Webhooks (/webhooks): CRUD, pause/resume, regenerate-secret with
one-time reveal, send test event, deliveries dialog.
- Scheduled tasks (/scheduled-tasks): cron CRUD, run-now trigger,
enable/disable, expandable run history.
- Audit log (/activity): replaces the empty stub. Filter by severity,
resource type, date range; click for full JSON detail.
All endpoints are hand-rolled HTTP because most aren't covered by the
generated OpenAPI typed paths yet — switch to arcadia.typed.* when the
backend wires them into OpenApiSpex.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three layers:
1. GFM markdown — add remark-gfm so tables, task lists, strikethrough,
autolinks render properly. Style table elements (overflow-aware
container, muted header, divider rows). Render `[ ]` task list items
as visible checkboxes.
2. Structured tool-result rendering — new `tool-result-renderers.tsx`
dispatches by tool name to render a small UI block beneath each
ToolCallCard:
- list_tenants → table with status pills + plan column
- get_tenant → tenant detail card
- get_platform_stats → KPI tiles (total + per-status)
- list_audit_log → timeline rows with actor_type + action
- list_users → user list with role chips
- suspend_tenant / activate_tenant → tenant card with action confirm
ToolCallCard collapses by default — operators expand for raw JSON.
3. Custom ```card``` blocks the LLM can emit inline:
- {"kind":"pill","status":"…"} — status pill
- {"kind":"stat","label":"…","value":…} — stat tile
- {"kind":"callout","tone":"info|warning|danger|success",…} — callout
Malformed blocks fall through to the prose unchanged. Client strips
well-formed blocks from prose and renders them as components.
Domain primer updated to teach the model the card schemas and remind it
NOT to re-render tool-result data as markdown tables (that's done
automatically — it should add commentary only).
Layers are independent: 1 + 2 always work; 3 is purely additive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New tools in admin-tools.ts:
- list_audit_log({limit?}) — recent audit entries (terse: actor, action,
target, timestamp). Hits /api/v1/admin/audit-log.
- get_platform_stats() — aggregate counts (tenants by status + by plan),
composed locally from list_tenants until arcadia exposes a real stats
endpoint.
- list_users({limit?}) — users in the currently-selected tenant via
/api/v1/users.
- suspend_tenant({slug}) — write tool, suspends a tenant by slug.
- activate_tenant({slug}) — write tool, restores a suspended/deactivated
tenant.
Inline write confirmation:
- New ConfirmCard component renders below the assistant message that
proposed a write. Shows tool(args) and Confirm/Deny buttons.
- classifyCalls() splits LLM tool calls into reads/writes. Auto-loop
runs reads immediately; for any writes, holds them in pendingConfirm
state instead of dispatching.
- On Confirm: runs writes with allowWrites:true, prepends prior read
results, continueChat to produce the final answer.
- On Deny: synthesises tool-result messages telling the model the user
declined; continueChat so it can acknowledge.
- Arcadia-knowledge primer updated to tell the model the user sees an
inline confirm card automatically — it shouldn't ask in prose first.
Wired into both /ai and /assistant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Adds the first admin screen — /tenants — listing tenants from
GET /api/v1/admin/tenants with search, status badges, plan, created
date, and a per-row menu with suspend / activate / deactivate actions.
Hand-typed shapes in app/lib/arcadia/tenants.ts because arcadia's admin
endpoints aren't yet covered by /api/openapi (same 'ok'-placeholder
issue documented in lib-arcadia-client/scripts/sync-spec.mjs). When
the spec gains coverage, switch to arcadia.typed.GET(...) and drop the
manual types.
Trims the inherited consumer-app sidenav (Resources / Assistant / AI /
Library) down to admin-shaped items: Overview, Tenants, Audit log,
Settings. The unused route files stay in place; they just aren't linked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial commit. Spun up via the docs/STARTER.md recipe: cp from vibespace,
reset git, rename package, set brand to "Arcadia Admin" with Shield icon
in app/lib/identity.ts.
Inherits the full Crema sibling-lib wiring including @crema/arcadia-client
(typed HTTP + Phoenix Channels realtime against arcadia-core) and
@crema/arcadia-auth-ui (login/signup/password-reset/2FA forms). The /login
route already renders <LoginForm>; <ArcadiaProvider> in app/root.tsx reads
VITE_ARCADIA_URL (default localhost:4000) and VITE_ARCADIA_TENANT (default
"default").
CLAUDE.md and README rewritten to frame this as the admin app for
arcadia-core. docs/STARTER.md removed — arcadia-admin is a leaf consumer,
not a downstream starter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>