feat: add hermes_local session management and show provider/model in run details

This commit is contained in:
HenkDz 2026-04-03 09:50:34 +01:00
parent 388650afc7
commit acfd7c260a
2 changed files with 37 additions and 3 deletions

View file

@ -41,6 +41,7 @@ export const LEGACY_SESSIONED_ADAPTER_TYPES = new Set([
"codex_local", "codex_local",
"cursor", "cursor",
"gemini_local", "gemini_local",
"hermes_local",
"opencode_local", "opencode_local",
"pi_local", "pi_local",
]); ]);
@ -76,6 +77,11 @@ export const ADAPTER_SESSION_MANAGEMENT: Record<string, AdapterSessionManagement
nativeContextManagement: "unknown", nativeContextManagement: "unknown",
defaultSessionCompaction: DEFAULT_SESSION_COMPACTION_POLICY, defaultSessionCompaction: DEFAULT_SESSION_COMPACTION_POLICY,
}, },
hermes_local: {
supportsSessionResume: true,
nativeContextManagement: "confirmed",
defaultSessionCompaction: ADAPTER_MANAGED_SESSION_POLICY,
},
}; };
function isRecord(value: unknown): value is Record<string, unknown> { function isRecord(value: unknown): value is Record<string, unknown> {

View file

@ -263,12 +263,16 @@ function runMetrics(run: HeartbeatRun) {
); );
const cost = const cost =
visibleRunCostUsd(usage, result); visibleRunCostUsd(usage, result);
const provider = asNonEmptyString(usage?.provider) ?? null;
const model = asNonEmptyString(usage?.model) ?? null;
return { return {
input, input,
output, output,
cached, cached,
cost, cost,
totalTokens: input + output, totalTokens: input + output,
provider,
model,
}; };
} }
@ -1127,6 +1131,7 @@ export function AgentDetail() {
agentRouteId={canonicalAgentRef} agentRouteId={canonicalAgentRef}
selectedRunId={urlRunId ?? null} selectedRunId={urlRunId ?? null}
adapterType={agent.adapterType} adapterType={agent.adapterType}
adapterConfig={agent.adapterConfig}
/> />
)} )}
@ -2905,6 +2910,7 @@ function RunsTab({
agentRouteId, agentRouteId,
selectedRunId, selectedRunId,
adapterType, adapterType,
adapterConfig,
}: { }: {
runs: HeartbeatRun[]; runs: HeartbeatRun[];
companyId: string; companyId: string;
@ -2912,6 +2918,7 @@ function RunsTab({
agentRouteId: string; agentRouteId: string;
selectedRunId: string | null; selectedRunId: string | null;
adapterType: string; adapterType: string;
adapterConfig: Record<string, unknown>;
}) { }) {
const { isMobile } = useSidebar(); const { isMobile } = useSidebar();
@ -2940,7 +2947,7 @@ function RunsTab({
<ArrowLeft className="h-3.5 w-3.5" /> <ArrowLeft className="h-3.5 w-3.5" />
Back to runs Back to runs
</Link> </Link>
<RunDetail key={selectedRun.id} run={selectedRun} agentRouteId={agentRouteId} adapterType={adapterType} /> <RunDetail key={selectedRun.id} run={selectedRun} agentRouteId={agentRouteId} adapterType={adapterType} adapterConfig={adapterConfig} />
</div> </div>
); );
} }
@ -2971,7 +2978,7 @@ function RunsTab({
{/* Right: run detail — natural height, page scrolls */} {/* Right: run detail — natural height, page scrolls */}
{selectedRun && ( {selectedRun && (
<div className="flex-1 min-w-0 pl-4"> <div className="flex-1 min-w-0 pl-4">
<RunDetail key={selectedRun.id} run={selectedRun} agentRouteId={agentRouteId} adapterType={adapterType} /> <RunDetail key={selectedRun.id} run={selectedRun} agentRouteId={agentRouteId} adapterType={adapterType} adapterConfig={adapterConfig} />
</div> </div>
)} )}
</div> </div>
@ -2980,7 +2987,7 @@ function RunsTab({
/* ---- Run Detail (expanded) ---- */ /* ---- Run Detail (expanded) ---- */
function RunDetail({ run: initialRun, agentRouteId, adapterType }: { run: HeartbeatRun; agentRouteId: string; adapterType: string }) { function RunDetail({ run: initialRun, agentRouteId, adapterType, adapterConfig }: { run: HeartbeatRun; agentRouteId: string; adapterType: string; adapterConfig: Record<string, unknown> }) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate(); const navigate = useNavigate();
const { data: hydratedRun } = useQuery({ const { data: hydratedRun } = useQuery({
@ -3174,6 +3181,27 @@ function RunDetail({ run: initialRun, agentRouteId, adapterType }: { run: Heartb
</Button> </Button>
)} )}
</div> </div>
{/* Adapter type · provider · model */}
{(() => {
const displayProvider = metrics.provider
?? asNonEmptyString(adapterConfig?.provider);
const displayModel = metrics.model
?? asNonEmptyString(adapterConfig?.model);
if (!adapterType && !displayProvider && !displayModel) return null;
return (
<div className="text-[11px] text-muted-foreground font-mono flex items-center gap-1.5 flex-wrap">
{adapterType && (
<span className="bg-muted rounded px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide">{adapterType.replace(/_/g, " ")}</span>
)}
{displayProvider && displayModel && (
<span>{displayProvider}/{displayModel}</span>
)}
{!displayProvider && displayModel && (
<span>{displayModel}</span>
)}
</div>
);
})()}
{resumeRun.isError && ( {resumeRun.isError && (
<div className="text-xs text-destructive"> <div className="text-xs text-destructive">
{resumeRun.error instanceof Error ? resumeRun.error.message : "Failed to resume run"} {resumeRun.error instanceof Error ? resumeRun.error.message : "Failed to resume run"}