mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 00:02:19 +00:00
Renames the pane, splits the list into this computer and paired Orca
servers, and puts a count of what is on above it with a Turn on all that
skips offline and too-old hosts and keeps going past a host that refuses.
Consenting once persists a standing consent so a server that later becomes
reachable turns on without another dialog; turning one off by hand drops it.
Rows past the sixth fold away, ordered by what the user can act on. Status
sentences now say how much is searchable instead of Ready, and an off
computer says so with its switch alone.
(cherry picked from commit f4cfac3ee2)
214 lines
6.6 KiB
TypeScript
214 lines
6.6 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import type { PublicKnownRuntimeEnvironment } from '../../../../shared/runtime-environments'
|
|
import { toRuntimeExecutionHostId } from '../../../../shared/execution-host'
|
|
import { useConfirmationDialog } from '@/components/confirmation-dialog-context'
|
|
import { useMountedRef } from '@/hooks/useMountedRef'
|
|
import { translate } from '@/i18n/i18n'
|
|
import { useAppStore } from '@/store'
|
|
import {
|
|
getRuntimeServerConnectionState,
|
|
isRuntimeServerTransportConnected,
|
|
type RuntimeHostDetails
|
|
} from './runtime-environment-host-details'
|
|
import { SessionHistoryComputerRow } from './SessionHistoryComputerRow'
|
|
import type { SessionSearchComputerState } from './session-search-computer-rollup'
|
|
import {
|
|
isHostTooOldError,
|
|
sessionSearchCheckingMessage,
|
|
sessionSearchReadErrorMessage,
|
|
sessionSearchStatusDetails,
|
|
sessionSearchStatusMessage
|
|
} from './session-history-status-copy'
|
|
import { useSessionSearchStatus } from './use-session-search-status'
|
|
|
|
export function SessionHistoryServerRow({
|
|
environment,
|
|
details,
|
|
refresh = 0,
|
|
onError,
|
|
onStateChange,
|
|
onUserToggle
|
|
}: {
|
|
environment: PublicKnownRuntimeEnvironment
|
|
details: RuntimeHostDetails | undefined
|
|
/** Bumped by the pane after it changes this host from outside the row. */
|
|
refresh?: number
|
|
onError: (message: string | null) => void
|
|
/** Lets the pane count and order computers it does not itself poll. */
|
|
onStateChange?: (environmentId: string, state: SessionSearchComputerState) => void
|
|
onUserToggle?: (environmentId: string, enabled: boolean) => void
|
|
}): React.JSX.Element {
|
|
const hostId = toRuntimeExecutionHostId(environment.id)
|
|
const confirm = useConfirmationDialog()
|
|
const mounted = useMountedRef()
|
|
const openSettingsPage = useAppStore((state) => state.openSettingsPage)
|
|
const openSettingsTarget = useAppStore((state) => state.openSettingsTarget)
|
|
const [tooOldOnSet, setTooOldOnSet] = useState(false)
|
|
const [busy, setBusy] = useState(false)
|
|
const connectionState = getRuntimeServerConnectionState(details)
|
|
const connected = isRuntimeServerTransportConnected(connectionState)
|
|
const { status, failed, hostTooOld, adopt } = useSessionSearchStatus({
|
|
executionHostId: hostId,
|
|
active: connected && !tooOldOnSet,
|
|
refresh
|
|
})
|
|
// A status read or a set call can each prove the server predates session search.
|
|
const tooOld = tooOldOnSet || hostTooOld
|
|
const enabled = status?.enabled === true
|
|
const state = resolveServerState({
|
|
tooOld,
|
|
connected,
|
|
checking: connectionState === 'checking',
|
|
enabled,
|
|
answered: Boolean(status)
|
|
})
|
|
|
|
useEffect(() => {
|
|
onStateChange?.(environment.id, state)
|
|
}, [environment.id, onStateChange, state])
|
|
|
|
async function setEnabled(next: boolean): Promise<void> {
|
|
setBusy(true)
|
|
onError(null)
|
|
try {
|
|
adopt(await window.api.aiVault.setSearchEnabled(hostId, next))
|
|
} catch (error) {
|
|
if (!mounted.current) {
|
|
return
|
|
}
|
|
if (isHostTooOldError(error)) {
|
|
setTooOldOnSet(true)
|
|
return
|
|
}
|
|
onError(
|
|
translate(
|
|
'sessionHistory.settings.serverToggleError',
|
|
'Could not change session search on {{host}}. Try again.',
|
|
{ host: environment.name }
|
|
)
|
|
)
|
|
} finally {
|
|
if (mounted.current) {
|
|
setBusy(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
async function toggle(): Promise<void> {
|
|
onUserToggle?.(environment.id, !enabled)
|
|
if (enabled) {
|
|
await setEnabled(false)
|
|
return
|
|
}
|
|
setBusy(true)
|
|
let accepted = false
|
|
try {
|
|
accepted = await confirm({
|
|
title: translate(
|
|
'sessionHistory.settings.serverEnableTitle',
|
|
'Turn on session search on {{host}}?',
|
|
{ host: environment.name }
|
|
),
|
|
description: translate(
|
|
'sessionHistory.settings.serverEnableConsent',
|
|
'Orca will make the agent conversations and tool output on {{host}} searchable from Agent Session History. The searchable copy stays on {{host}}; results are sent to this computer when you search. The first pass runs in the background and can take a few minutes.',
|
|
{ host: environment.name }
|
|
),
|
|
confirmLabel: translate('sessionHistory.settings.enableConfirm', 'Turn on')
|
|
})
|
|
} finally {
|
|
if (mounted.current) {
|
|
setBusy(false)
|
|
}
|
|
}
|
|
if (!accepted || !mounted.current) {
|
|
return
|
|
}
|
|
await setEnabled(true)
|
|
}
|
|
|
|
function openServerSettings(): void {
|
|
openSettingsPage()
|
|
openSettingsTarget({ pane: 'servers', repoId: null, sectionId: environment.id })
|
|
}
|
|
|
|
const row = {
|
|
kind: 'server' as const,
|
|
name: environment.name,
|
|
version: details?.runtimeStatus?.appVersion ?? null,
|
|
onToggle: () => void toggle()
|
|
}
|
|
if (tooOld) {
|
|
return (
|
|
<SessionHistoryComputerRow
|
|
{...row}
|
|
dimmed
|
|
checked={false}
|
|
disabled
|
|
status={translate('sessionHistory.settings.serverTooOld', 'Needs a newer version of Orca.')}
|
|
action={{
|
|
label: translate('sessionHistory.settings.updateServer', 'Update server'),
|
|
onClick: openServerSettings
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
if (!connected) {
|
|
// Checking is not yet evidence of an unreachable host, so it does not claim the index was left behind.
|
|
const checking = connectionState === 'checking'
|
|
return (
|
|
<SessionHistoryComputerRow
|
|
{...row}
|
|
dimmed={!checking}
|
|
checked={enabled}
|
|
disabled
|
|
status={
|
|
checking
|
|
? sessionSearchCheckingMessage()
|
|
: translate('sessionHistory.settings.serverOffline', 'Offline')
|
|
}
|
|
/>
|
|
)
|
|
}
|
|
// An off computer says so with its switch; a sentence repeating it is noise.
|
|
let statusText: string | undefined = sessionSearchCheckingMessage()
|
|
if (failed) {
|
|
statusText = sessionSearchReadErrorMessage()
|
|
} else if (status) {
|
|
statusText = enabled ? sessionSearchStatusMessage(status) : undefined
|
|
}
|
|
return (
|
|
<SessionHistoryComputerRow
|
|
{...row}
|
|
checked={enabled}
|
|
disabled={busy}
|
|
{...(statusText === undefined ? {} : { status: statusText })}
|
|
details={sessionSearchStatusDetails(status)}
|
|
/>
|
|
)
|
|
}
|
|
|
|
/** What the pane needs to count and order this row, from what the row already knows. */
|
|
function resolveServerState(args: {
|
|
tooOld: boolean
|
|
connected: boolean
|
|
checking: boolean
|
|
enabled: boolean
|
|
answered: boolean
|
|
}): SessionSearchComputerState {
|
|
if (args.tooOld) {
|
|
return 'needs-update'
|
|
}
|
|
// A probe still in flight is not evidence of an unreachable host.
|
|
if (args.checking) {
|
|
return 'checking'
|
|
}
|
|
if (!args.connected) {
|
|
return 'offline'
|
|
}
|
|
if (!args.answered) {
|
|
return 'checking'
|
|
}
|
|
return args.enabled ? 'on' : 'off'
|
|
}
|