77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
import { useEffect, useState, useSyncExternalStore } from "react";
|
|
import { useHarnessRuntime, type HarnessRuntimeStore } from "./assistant/use-harness-runtime.js";
|
|
import { Thread } from "./components/chat/Thread.js";
|
|
import { AppShell } from "./components/layout/AppShell.js";
|
|
import { SkillsPanel } from "./components/skills/SkillsPanel.js";
|
|
import { EventTimeline } from "./components/timeline/EventTimeline.js";
|
|
import { HarnessClientStore } from "./store/harness-client-store.js";
|
|
|
|
type AppStore = HarnessRuntimeStore & {
|
|
start(): Promise<void>;
|
|
getModelConfig(): Promise<{ modelConfig: { configured: boolean; modelId?: string; supportsImages?: boolean } }>;
|
|
};
|
|
|
|
export type AppProps = {
|
|
store?: AppStore;
|
|
autoStart?: boolean;
|
|
modelConfigured?: boolean;
|
|
};
|
|
|
|
export function App({ store: suppliedStore, autoStart = true, modelConfigured: suppliedModelConfigured }: AppProps) {
|
|
const [ownedStore] = useState(() => new HarnessClientStore());
|
|
const store = suppliedStore ?? ownedStore;
|
|
const snapshot = useSyncExternalStore(
|
|
(subscriber) => store.subscribe(subscriber),
|
|
() => store.getSnapshot(),
|
|
() => store.getSnapshot(),
|
|
);
|
|
const [model, setModel] = useState({
|
|
configured: suppliedModelConfigured ?? false,
|
|
modelId: suppliedModelConfigured === true ? "Configured model" : "No model",
|
|
supportsImages: false,
|
|
});
|
|
const runtime = useHarnessRuntime(store, { isSendDisabled: !model.configured });
|
|
const busy = Object.values(snapshot.runs).some((run) => run.status === "running");
|
|
const approvalPending = Object.values(snapshot.approvals).some((approval) => approval.status === "pending");
|
|
|
|
useEffect(() => {
|
|
if (!autoStart) return undefined;
|
|
let active = true;
|
|
void store.start().then(async () => {
|
|
const response = await store.getModelConfig();
|
|
if (active) setModel({
|
|
configured: response.modelConfig.configured,
|
|
modelId: response.modelConfig.modelId ?? "No model",
|
|
supportsImages: response.modelConfig.supportsImages ?? false,
|
|
});
|
|
}).catch(() => undefined);
|
|
return () => { active = false; };
|
|
}, [autoStart, store]);
|
|
|
|
return (
|
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
<AppShell
|
|
topBar={{
|
|
...(snapshot.sessionId === undefined ? {} : { sessionId: snapshot.sessionId }),
|
|
connection: snapshot.connection,
|
|
modelLabel: model.modelId,
|
|
supportsImages: model.supportsImages,
|
|
onOpenSettings: () => { window.dispatchEvent(new CustomEvent("agent-studio:open-settings")); },
|
|
onReset: () => window.location.reload(),
|
|
}}
|
|
skills={<SkillsPanel skills={snapshot.skills} />}
|
|
chat={<Thread
|
|
runtime={runtime}
|
|
busy={busy}
|
|
modelConfigured={model.configured}
|
|
connection={snapshot.connection}
|
|
error={snapshot.error}
|
|
approvalPending={approvalPending}
|
|
/>}
|
|
timeline={<EventTimeline events={snapshot.timeline} />}
|
|
/>
|
|
</AssistantRuntimeProvider>
|
|
);
|
|
}
|