feat(auth): add generic OIDC login with mandatory groups
This commit is contained in:
@@ -80,6 +80,8 @@ export function AppShell() {
|
||||
} as CSSProperties;
|
||||
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
||||
const activeSessionIdRef = useRef<string | null>(null);
|
||||
const activeSessionEpochRef = useRef(0);
|
||||
const newSessionOperationRef = useRef<{ target: string | null; epoch: number } | null>(null);
|
||||
const resumeInvocationRef = useRef(0);
|
||||
const latestResumeIntentRef = useRef<{ token: number; id: string } | null>(null);
|
||||
const resumeInFlightRef = useRef(new Map<string, {
|
||||
@@ -165,6 +167,7 @@ export function AppShell() {
|
||||
|
||||
function selectActiveSession(id: string | null) {
|
||||
// Keep async Resume completions synchronized before React commits the state update.
|
||||
if (activeSessionIdRef.current !== id) activeSessionEpochRef.current += 1;
|
||||
activeSessionIdRef.current = id;
|
||||
setActiveSessionId(id);
|
||||
}
|
||||
@@ -505,6 +508,7 @@ export function AppShell() {
|
||||
|
||||
function startNewSession() {
|
||||
invalidateResumeIntent();
|
||||
newSessionOperationRef.current = null;
|
||||
resetSession();
|
||||
// Starting a new question closes any open session detail panel: the reader is
|
||||
// moving away from that session, so its left-hand box must not linger.
|
||||
@@ -519,11 +523,18 @@ export function AppShell() {
|
||||
}
|
||||
|
||||
function beginSessionCreation() {
|
||||
newSessionOperationRef.current = {
|
||||
target: activeSessionIdRef.current,
|
||||
epoch: activeSessionEpochRef.current,
|
||||
};
|
||||
setAwaitingQuestion(false);
|
||||
setCreatingSession(true);
|
||||
}
|
||||
|
||||
function finishSessionCreation(id: string) {
|
||||
const operation = newSessionOperationRef.current;
|
||||
newSessionOperationRef.current = null;
|
||||
if (!operation || operation.target !== activeSessionIdRef.current || operation.epoch !== activeSessionEpochRef.current) return;
|
||||
// React batches these updates, preserving the provisional session view
|
||||
// while useSessionStream opens the durable session's SSE channel.
|
||||
selectActiveSession(id);
|
||||
@@ -533,21 +544,25 @@ export function AppShell() {
|
||||
}
|
||||
|
||||
function failSessionCreation(message?: string) {
|
||||
const operation = newSessionOperationRef.current;
|
||||
newSessionOperationRef.current = null;
|
||||
if (!operation || operation.target !== activeSessionIdRef.current || operation.epoch !== activeSessionEpochRef.current) return;
|
||||
setCreatingSession(false);
|
||||
resetSession();
|
||||
toast.error(message ?? "Failed to create session. Your question is ready to retry.");
|
||||
}
|
||||
|
||||
async function stopSession() {
|
||||
if (!activeSessionId) return;
|
||||
const id = activeSessionId;
|
||||
const id = activeSessionIdRef.current;
|
||||
if (!id) return;
|
||||
const guard = captureAuthOperation({ sessionId: id, disposalEpoch: operationEpochRef.current });
|
||||
if (!guard) return;
|
||||
invalidateResumeIntent();
|
||||
try {
|
||||
await closeSession(id);
|
||||
} finally {
|
||||
if (!isAuthOperationCurrent(guard, { sessionId: id, disposalEpoch: operationEpochRef.current })) return;
|
||||
if (!isAuthOperationCurrent(guard, { sessionId: id, disposalEpoch: operationEpochRef.current })
|
||||
|| activeSessionIdRef.current !== id) return;
|
||||
resetSession();
|
||||
selectActiveSession(null);
|
||||
setAwaitingQuestion(false);
|
||||
@@ -643,7 +658,7 @@ export function AppShell() {
|
||||
</header>
|
||||
)}
|
||||
<CentralStatus working={working} />
|
||||
{activeSessionId && <WidgetHost sessionId={activeSessionId} />}
|
||||
{activeSessionId && <WidgetHost key={`widget:${activeSessionId}:${activeSessionEpochRef.current}`} sessionId={activeSessionId} />}
|
||||
{finalized && !agentActive && (
|
||||
<div className="rounded-2xl border border-border/80 bg-card p-5 text-center shadow-md">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -668,6 +683,7 @@ export function AppShell() {
|
||||
<div className="rounded-2xl border border-border/80 bg-card shadow-md transition-colors focus-within:border-primary/50 focus-within:ring-3 focus-within:ring-ring/15">
|
||||
<div className="px-2.5 py-2">
|
||||
<SteerInput
|
||||
key={`steer:${activeSessionId ?? "new"}:${activeSessionEpochRef.current}`}
|
||||
sessionId={activeSessionId}
|
||||
onSessionCreating={beginSessionCreation}
|
||||
onSessionCreated={finishSessionCreation}
|
||||
|
||||
Reference in New Issue
Block a user