fix: restore baseline validation (#3142)

This commit is contained in:
Alexandre Reyes Martins
2026-08-26 17:15:49 +03:00
committed by GitHub
parent 27377efaf3
commit 8ea94a119f
8 changed files with 9 additions and 74 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore';
import { useTerminalStore } from '@/stores/useTerminalStore';
import { useSessionUIStore } from '@/sync/session-ui-store';
import { resetStreamingState } from '@/sync/streaming';
import { useGlobalSessionStatusStore, replaceGlobalSessionStatusById } from '@/sync/global-session-status';
import { replaceGlobalSessionStatusById } from '@/sync/global-session-status';
import { resetSessionOrdering } from '@/sync/session-ordering';
import { resetSessionActivityTiming } from '@/sync/session-activity-timing';
import { syncDesktopSettings } from '@/lib/persistence';
@@ -457,13 +457,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
}, [chatRenderMode, isMessageCompleted, isUser, visibleParts]);
const assistantTextParts = React.useMemo(() => {
if (isUser) {
return [];
}
return visibleParts.filter((part) => part.type === 'text');
}, [isUser, visibleParts]);
const toolParts = React.useMemo(() => {
if (isUser) {
return [];
@@ -545,19 +538,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
const shouldHideUserMessage = isUser && displayParts.length === 0;
// Message is considered to have an "open step" if info.finish is not yet present
const hasOpenStep = typeof messageFinish !== 'string';
const shouldCoordinateRendering = React.useMemo(() => {
if (isUser) {
return false;
}
if (assistantTextParts.length === 0 || toolParts.length === 0) {
return hasOpenStep;
}
return true;
}, [assistantTextParts.length, toolParts.length, hasOpenStep, isUser]);
const themeVariant = currentTheme?.metadata.variant;
const isDarkTheme = React.useMemo(() => {
if (themeVariant) {
@@ -1343,16 +1343,6 @@ const AssistantMessageBody = React.memo(({
return resolved ? { id: resolved.id, path: resolved.path } : null;
}, [availableWorktreesByProject, canUseProjectPlanActions, currentSessionId, effectiveDirectory, getDirectoryForSession, projects]);
const hasTools = toolParts.length > 0;
const hasPendingTools = React.useMemo(() => {
return toolParts.some((toolPart) => {
const state = (toolPart as Record<string, unknown>).state as Record<string, unknown> | undefined ?? {};
const status = state?.status;
return status === 'pending' || status === 'running' || status === 'started';
});
}, [toolParts]);
const isActiveTool = React.useCallback((toolPart: ToolPartType): boolean => {
const state = (toolPart as Record<string, unknown>).state as Record<string, unknown> | undefined ?? {};
const status = state?.status;
@@ -1381,42 +1371,6 @@ const AssistantMessageBody = React.memo(({
return isActiveTool(toolPart) || isToolFinalized(toolPart);
}, [isActiveTool, isToolFinalized]);
const allToolsFinalized = React.useMemo(() => {
if (toolParts.length === 0) {
return true;
}
if (hasPendingTools) {
return false;
}
return toolParts.every((toolPart) => isToolFinalized(toolPart));
}, [toolParts, hasPendingTools, isToolFinalized]);
const reasoningParts = React.useMemo(() => {
return visibleParts.filter((part) => part.type === 'reasoning');
}, [visibleParts]);
const reasoningComplete = React.useMemo(() => {
if (reasoningParts.length === 0) {
return true;
}
return reasoningParts.every((part) => {
const time = (part as Record<string, unknown>).time as { end?: number } | undefined;
return typeof time?.end === 'number';
});
}, [reasoningParts]);
// Message is considered to have an "open step" if info.finish is not yet present
const hasOpenStep = typeof messageFinish !== 'string';
const shouldHoldForReasoning =
reasoningParts.length > 0 &&
hasTools &&
(hasPendingTools || hasOpenStep || !allToolsFinalized);
const shouldHoldTools = awaitingMessageCompletion
|| (hasTools && (hasPendingTools || hasOpenStep || !allToolsFinalized));
const shouldHoldReasoning = awaitingMessageCompletion || shouldHoldForReasoning;
const hasCopyableText = Boolean(hasTextContent) && !awaitingMessageCompletion;
const handleForkClick = React.useCallback(
@@ -486,8 +486,6 @@ export const Header: React.FC = () => {
const pathSegments = activeProject.path.split(/[\\/]/).filter(Boolean);
return pathSegments[pathSegments.length - 1] ?? null;
}, [activeProject]);
const quotaResults = useQuotaStore((state) => state.results);
const fetchAllQuotas = useQuotaStore((state) => state.fetchAllQuotas);
const loadQuotaSettings = useQuotaStore((state) => state.loadSettings);
const { isMobile } = useDeviceInfo();
@@ -4,7 +4,7 @@ import type { Event } from '@opencode-ai/sdk/v2/client';
import React, { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { deriveRecentSessions } from '../recent/activitySections';
import { applyGlobalSessionStatusEvent, useGlobalSessionStatusStore , replaceGlobalSessionStatusById} from '@/sync/global-session-status';
import { applyGlobalSessionStatusEvent, replaceGlobalSessionStatusById } from '@/sync/global-session-status';
import {
buildSidebarSessionProjection,
getDescendantIds,
@@ -2,7 +2,7 @@ import { describe, expect, test } from 'bun:test';
import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import type { Session } from '@opencode-ai/sdk/v2';
import { useGlobalSessionStatusStore , replaceGlobalSessionStatusById} from '@/sync/global-session-status';
import { replaceGlobalSessionStatusById } from '@/sync/global-session-status';
import { useNotificationStore } from '@/sync/notification-store';
import { useCollapsedSessionActivityState } from './collapsedActivityState';
import type { SessionNode } from '../types';
@@ -249,6 +249,7 @@ export const createSessionGoalRuntime = ({
getOpenCodeAuthHeaders,
getSmallModelService,
emitGoalNotification,
isEnabled = isSessionGoalEnabled,
idleQuietMs = IDLE_QUIET_MS,
kickoffQuietMs = KICKOFF_QUIET_MS,
maxAutoTurns = MAX_AUTO_TURNS,
@@ -444,7 +445,7 @@ export const createSessionGoalRuntime = ({
};
const tick = async (sessionId, directory) => {
if (!isSessionGoalEnabled()) return;
if (!isEnabled()) return;
const session = await openCodeFetch(`/session/${encodeURIComponent(sessionId)}`, { directory })
.catch((error) => {
@@ -35,13 +35,14 @@ const startIdleTick = async (fetchImpl) => {
buildOpenCodeUrl: (pathname) => `http://opencode.test${pathname}`,
getOpenCodeAuthHeaders: () => ({}),
getSmallModelService,
isEnabled: () => true,
idleQuietMs: 10,
});
runtime.processPayload({
type: 'session.status',
properties: { sessionID: SESSION_ID, status: { type: 'idle' }, directory: DIRECTORY },
});
await vi.advanceTimersByTimeAsync(10);
await vi.runOnlyPendingTimersAsync();
return { runtime, getSmallModelService };
};
@@ -156,6 +157,7 @@ describe('session goal live activity gate', () => {
buildOpenCodeUrl: (pathname) => `http://opencode.test${pathname}`,
getOpenCodeAuthHeaders: () => ({}),
getSmallModelService: async () => service,
isEnabled: () => true,
idleQuietMs: 10,
});
@@ -163,7 +165,7 @@ describe('session goal live activity gate', () => {
type: 'session.status',
properties: { sessionID: SESSION_ID, status: { type: 'idle' }, directory: DIRECTORY },
});
await vi.advanceTimersByTimeAsync(10);
await vi.runOnlyPendingTimersAsync();
expect(service.generateSmallModelText).toHaveBeenCalledOnce();
const patch = requests.find((request) => request.pathname === `/session/${SESSION_ID}` && request.method === 'PATCH');