import { useEffect, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { CheckCircle2, CircleAlert, FlaskConical, LoaderCircle, ScrollText } from "lucide-react";
import {
asPiManagementApiError,
getPiManagementLogs,
getPiManagementStatus,
runPiManagementTest,
} from "../api/pi-management";
import { listModels } from "../api/models";
import { Button } from "../components/ui/button";
import { nextTabIndex } from "../components/ui/tab-navigation";
import { WorkAreaPanel } from "./WorkAreaPanel";
type Feedback = { tone: "success" | "error"; message: string } | undefined;
type SmokeState = "passed" | "failed" | undefined;
function errorMessage(error: unknown, fallback: string): string {
return asPiManagementApiError(error)?.message ?? fallback;
}
function ReadinessRail({ ready, configured, credentials, smokeState }: {
ready: boolean;
configured: boolean;
credentials: "present" | "missing";
smokeState: SmokeState;
}) {
return (
{label}
{state === "ready" ?
{children};
}
function PiInstructionSteps({ details }: { details: PiPlatformDetails }) {
return Using {details.terminal}, before editing files or running any lifecycle command, enter the exact checkout or worktree root. Run the platform command below, then run every command in this list from that directory: tht resolves the installation descriptor and configuration from the selected project. A worktree is an independent checkout, so this prevents changing a different copy by mistake.
The relevant layout is:
The deploy/ directory contains the selected installation descriptor and profile files. You do not need to create or manage a bin/ directory: tht is the installed host CLI. If discovery finds multiple descriptors, pass the intended one explicitly with --installation <absolute-path>/thothii-installation.yaml. If tht is not on your PATH, install it using the installation guide.
Edit {details.catalogPath} when adding or correcting a provider or model. Its modelCatalog block is the only authored model source for sessions, metadata generation, and embedding. It declares endpoint references and authentication modes, but never secret values. Provider integrations remain declarative; do not add model-provider code under harness/.pi/extensions/ or edit files under generated/.
Use modelCatalog.defaults.interaction as the single installation-wide LLM default for Core and Administration. When Admin AI is configured, a selectable model needs both session and metadataGeneration blocks. The user's last model choice takes precedence and does not depend on workspace. Runtime files such as Pi models.json and settings.json are generated projections and must not be edited.
Before making each model available, verify an actual Core tool-call/review cycle through Pi and a small test description-generation run through LiteLLM. Configuration validation and Pi smoke tests alone do not certify both paths. Use approved test data; generation may incur provider charges.
During the initial installation, run tht setup and complete the prompt named “Pi credentials file location”; it can create the empty protected template. On an existing installation with a missing credential, correct the protected credential file selected during setup by following the installation guide. Keep {details.credentialProtection}, then restart and run tht pi doctor and tht pi test. Never paste credentials into the installation YAML, this page, a command, or a log: YAML may contain only approved environment-variable names or authentication modes.
After changing the Installation Model Catalog or a provider credential, reload the running core service. The command validates the YAML and regenerates every runtime projection before recreation. This is a configuration reload, not a Pi version update; it keeps the current image.
Use tht pi update for the routine build: it resolves the latest stable Pi release from the registry, builds it locally, drains active sessions, and recreates only core. Add --version <VERSION> when an explicitly reviewed version is required. The digest-pinned --source pull form is for an already-built, reviewed registry image; its declared version must match the digest-pinned image. Configuration changes use Reload above, not update.
Advanced: pull an immutable, digest-pinned image.
Start with these diagnostics, in order:
modelCatalog field, then reload. Do not repair generated JSON directly.baseUrl, network, or provider error: correct the catalog endpoint or network, then retry.If maintenance is inactive, no recovery is needed: correct the cause and retry the original command.
If maintenance is active after an update, rollback the previous image:
If maintenance is active after a restart, fix the cause first, then recover the captured lifecycle state:
After the selected remedy, verify the installation:
Using the host terminal:
Pi management is not permitted
Ask an installation administrator to manage the model catalog and diagnostics.
{errorMessage(statusQuery.error, "Pi management is unavailable")}
Loading Pi management…
: statusQuery.data && (Bundled runtime
Pi {statusQuery.data.version ?? "version unavailable"}
{statusQuery.data.ready ? "Ready" : "Needs attention"}
{statusQuery.data.message}
} {feedback &&
{feedback.tone === "error" ?
This value is read from modelCatalog.defaults.interaction. Change the installation YAML and reload Pi; this page never creates a second model default.
Provider
{statusQuery.data.config.provider}
Model
{statusQuery.data.config.model}
Reasoning
{statusQuery.data.config.reasoning}
The catalog default is unavailable.
Fix modelCatalog.defaults.interaction in the installation YAML, reload Pi, then retry this panel.
Models currently available for interactive sessions in this installation.
Loading session models…
) : modelsQuery.isError ? (Could not load the session model catalog.
{model.name}
{model.provider}/{model.id}
No session models are configured for this installation.
)}Shows at most 200 recent lines with declared secret values removed.
Could not load sanitized Pi diagnostics.
} {logsQuery.data &&{logsQuery.data.lines.join("\n") || "No diagnostic lines are available."}}