fix: restore baseline validation (#3142)
This commit is contained in:
committed by
GitHub
parent
27377efaf3
commit
8ea94a119f
@@ -15,7 +15,7 @@ import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore';
|
|||||||
import { useTerminalStore } from '@/stores/useTerminalStore';
|
import { useTerminalStore } from '@/stores/useTerminalStore';
|
||||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||||
import { resetStreamingState } from '@/sync/streaming';
|
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 { resetSessionOrdering } from '@/sync/session-ordering';
|
||||||
import { resetSessionActivityTiming } from '@/sync/session-activity-timing';
|
import { resetSessionActivityTiming } from '@/sync/session-activity-timing';
|
||||||
import { syncDesktopSettings } from '@/lib/persistence';
|
import { syncDesktopSettings } from '@/lib/persistence';
|
||||||
|
|||||||
@@ -457,13 +457,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
}, [chatRenderMode, isMessageCompleted, isUser, visibleParts]);
|
}, [chatRenderMode, isMessageCompleted, isUser, visibleParts]);
|
||||||
|
|
||||||
|
|
||||||
const assistantTextParts = React.useMemo(() => {
|
|
||||||
if (isUser) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return visibleParts.filter((part) => part.type === 'text');
|
|
||||||
}, [isUser, visibleParts]);
|
|
||||||
|
|
||||||
const toolParts = React.useMemo(() => {
|
const toolParts = React.useMemo(() => {
|
||||||
if (isUser) {
|
if (isUser) {
|
||||||
return [];
|
return [];
|
||||||
@@ -545,19 +538,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
|
|
||||||
const shouldHideUserMessage = isUser && displayParts.length === 0;
|
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 themeVariant = currentTheme?.metadata.variant;
|
||||||
const isDarkTheme = React.useMemo(() => {
|
const isDarkTheme = React.useMemo(() => {
|
||||||
if (themeVariant) {
|
if (themeVariant) {
|
||||||
|
|||||||
@@ -1343,16 +1343,6 @@ const AssistantMessageBody = React.memo(({
|
|||||||
return resolved ? { id: resolved.id, path: resolved.path } : null;
|
return resolved ? { id: resolved.id, path: resolved.path } : null;
|
||||||
}, [availableWorktreesByProject, canUseProjectPlanActions, currentSessionId, effectiveDirectory, getDirectoryForSession, projects]);
|
}, [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 isActiveTool = React.useCallback((toolPart: ToolPartType): boolean => {
|
||||||
const state = (toolPart as Record<string, unknown>).state as Record<string, unknown> | undefined ?? {};
|
const state = (toolPart as Record<string, unknown>).state as Record<string, unknown> | undefined ?? {};
|
||||||
const status = state?.status;
|
const status = state?.status;
|
||||||
@@ -1381,42 +1371,6 @@ const AssistantMessageBody = React.memo(({
|
|||||||
return isActiveTool(toolPart) || isToolFinalized(toolPart);
|
return isActiveTool(toolPart) || isToolFinalized(toolPart);
|
||||||
}, [isActiveTool, isToolFinalized]);
|
}, [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 hasCopyableText = Boolean(hasTextContent) && !awaitingMessageCompletion;
|
||||||
|
|
||||||
const handleForkClick = React.useCallback(
|
const handleForkClick = React.useCallback(
|
||||||
|
|||||||
@@ -486,8 +486,6 @@ export const Header: React.FC = () => {
|
|||||||
const pathSegments = activeProject.path.split(/[\\/]/).filter(Boolean);
|
const pathSegments = activeProject.path.split(/[\\/]/).filter(Boolean);
|
||||||
return pathSegments[pathSegments.length - 1] ?? null;
|
return pathSegments[pathSegments.length - 1] ?? null;
|
||||||
}, [activeProject]);
|
}, [activeProject]);
|
||||||
const quotaResults = useQuotaStore((state) => state.results);
|
|
||||||
const fetchAllQuotas = useQuotaStore((state) => state.fetchAllQuotas);
|
|
||||||
const loadQuotaSettings = useQuotaStore((state) => state.loadSettings);
|
const loadQuotaSettings = useQuotaStore((state) => state.loadSettings);
|
||||||
|
|
||||||
const { isMobile } = useDeviceInfo();
|
const { isMobile } = useDeviceInfo();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { Event } from '@opencode-ai/sdk/v2/client';
|
|||||||
import React, { act } from 'react';
|
import React, { act } from 'react';
|
||||||
import { createRoot, type Root } from 'react-dom/client';
|
import { createRoot, type Root } from 'react-dom/client';
|
||||||
import { deriveRecentSessions } from '../recent/activitySections';
|
import { deriveRecentSessions } from '../recent/activitySections';
|
||||||
import { applyGlobalSessionStatusEvent, useGlobalSessionStatusStore , replaceGlobalSessionStatusById} from '@/sync/global-session-status';
|
import { applyGlobalSessionStatusEvent, replaceGlobalSessionStatusById } from '@/sync/global-session-status';
|
||||||
import {
|
import {
|
||||||
buildSidebarSessionProjection,
|
buildSidebarSessionProjection,
|
||||||
getDescendantIds,
|
getDescendantIds,
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ import { describe, expect, test } from 'bun:test';
|
|||||||
import React, { act } from 'react';
|
import React, { act } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import type { Session } from '@opencode-ai/sdk/v2';
|
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 { useNotificationStore } from '@/sync/notification-store';
|
||||||
import { useCollapsedSessionActivityState } from './collapsedActivityState';
|
import { useCollapsedSessionActivityState } from './collapsedActivityState';
|
||||||
import type { SessionNode } from '../types';
|
import type { SessionNode } from '../types';
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ export const createSessionGoalRuntime = ({
|
|||||||
getOpenCodeAuthHeaders,
|
getOpenCodeAuthHeaders,
|
||||||
getSmallModelService,
|
getSmallModelService,
|
||||||
emitGoalNotification,
|
emitGoalNotification,
|
||||||
|
isEnabled = isSessionGoalEnabled,
|
||||||
idleQuietMs = IDLE_QUIET_MS,
|
idleQuietMs = IDLE_QUIET_MS,
|
||||||
kickoffQuietMs = KICKOFF_QUIET_MS,
|
kickoffQuietMs = KICKOFF_QUIET_MS,
|
||||||
maxAutoTurns = MAX_AUTO_TURNS,
|
maxAutoTurns = MAX_AUTO_TURNS,
|
||||||
@@ -444,7 +445,7 @@ export const createSessionGoalRuntime = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const tick = async (sessionId, directory) => {
|
const tick = async (sessionId, directory) => {
|
||||||
if (!isSessionGoalEnabled()) return;
|
if (!isEnabled()) return;
|
||||||
|
|
||||||
const session = await openCodeFetch(`/session/${encodeURIComponent(sessionId)}`, { directory })
|
const session = await openCodeFetch(`/session/${encodeURIComponent(sessionId)}`, { directory })
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ const startIdleTick = async (fetchImpl) => {
|
|||||||
buildOpenCodeUrl: (pathname) => `http://opencode.test${pathname}`,
|
buildOpenCodeUrl: (pathname) => `http://opencode.test${pathname}`,
|
||||||
getOpenCodeAuthHeaders: () => ({}),
|
getOpenCodeAuthHeaders: () => ({}),
|
||||||
getSmallModelService,
|
getSmallModelService,
|
||||||
|
isEnabled: () => true,
|
||||||
idleQuietMs: 10,
|
idleQuietMs: 10,
|
||||||
});
|
});
|
||||||
runtime.processPayload({
|
runtime.processPayload({
|
||||||
type: 'session.status',
|
type: 'session.status',
|
||||||
properties: { sessionID: SESSION_ID, status: { type: 'idle' }, directory: DIRECTORY },
|
properties: { sessionID: SESSION_ID, status: { type: 'idle' }, directory: DIRECTORY },
|
||||||
});
|
});
|
||||||
await vi.advanceTimersByTimeAsync(10);
|
await vi.runOnlyPendingTimersAsync();
|
||||||
return { runtime, getSmallModelService };
|
return { runtime, getSmallModelService };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -156,6 +157,7 @@ describe('session goal live activity gate', () => {
|
|||||||
buildOpenCodeUrl: (pathname) => `http://opencode.test${pathname}`,
|
buildOpenCodeUrl: (pathname) => `http://opencode.test${pathname}`,
|
||||||
getOpenCodeAuthHeaders: () => ({}),
|
getOpenCodeAuthHeaders: () => ({}),
|
||||||
getSmallModelService: async () => service,
|
getSmallModelService: async () => service,
|
||||||
|
isEnabled: () => true,
|
||||||
idleQuietMs: 10,
|
idleQuietMs: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -163,7 +165,7 @@ describe('session goal live activity gate', () => {
|
|||||||
type: 'session.status',
|
type: 'session.status',
|
||||||
properties: { sessionID: SESSION_ID, status: { type: 'idle' }, directory: DIRECTORY },
|
properties: { sessionID: SESSION_ID, status: { type: 'idle' }, directory: DIRECTORY },
|
||||||
});
|
});
|
||||||
await vi.advanceTimersByTimeAsync(10);
|
await vi.runOnlyPendingTimersAsync();
|
||||||
|
|
||||||
expect(service.generateSmallModelText).toHaveBeenCalledOnce();
|
expect(service.generateSmallModelText).toHaveBeenCalledOnce();
|
||||||
const patch = requests.find((request) => request.pathname === `/session/${SESSION_ID}` && request.method === 'PATCH');
|
const patch = requests.find((request) => request.pathname === `/session/${SESSION_ID}` && request.method === 'PATCH');
|
||||||
|
|||||||
Reference in New Issue
Block a user