chore: rebuild CentOS 7 release package
This commit is contained in:
@@ -541,7 +541,13 @@ function assertFrontendSidebarCollapseContract() {
|
||||
});
|
||||
assert(source.includes("const SIDEBAR_COLLAPSE_STORAGE_KEY = 'cc-web-sidebar-collapsed'"), 'Desktop sidebar preference should use a stable storage key');
|
||||
assert(source.includes("const SIDEBAR_DRAWER_MEDIA_QUERY = '(max-width: 768px)'"), 'Sidebar drawer mode should match the mobile CSS breakpoint');
|
||||
assert(source.includes("const SIDEBAR_DESKTOP_COLLAPSE_MEDIA_QUERY = '(width > 768px) and (hover: hover) and (pointer: fine)'"), 'Desktop collapse mode should match the hover rail CSS capabilities');
|
||||
assert(source.includes("const SIDEBAR_DESKTOP_COLLAPSE_MEDIA_QUERY = '(width > 768px)'"), 'Wide sidebar collapse mode should be available by viewport width');
|
||||
assert(source.includes("const SIDEBAR_HOVER_PREVIEW_MEDIA_QUERY = '(width > 768px) and (hover: hover) and (pointer: fine)'"), 'Hover preview should retain the fine-pointer capability gate');
|
||||
assert(
|
||||
source.includes('const sidebarDesktopCollapseMedia = window.matchMedia(SIDEBAR_DESKTOP_COLLAPSE_MEDIA_QUERY)')
|
||||
&& source.includes('const sidebarHoverPreviewMedia = window.matchMedia(SIDEBAR_HOVER_PREVIEW_MEDIA_QUERY)'),
|
||||
'Wide fixed collapse and hover preview should use separate media queries'
|
||||
);
|
||||
assert(source.includes("app.classList.toggle('sidebar-collapsed', nextCollapsed)"), 'Desktop sidebar should keep its fixed state on the app root');
|
||||
assert(
|
||||
indexSource.includes('id="user-outline-panel" class="user-outline-panel"')
|
||||
@@ -566,19 +572,35 @@ function assertFrontendSidebarCollapseContract() {
|
||||
'Sidebar toggle should preserve the mobile drawer path before using desktop fixed collapse'
|
||||
);
|
||||
assert(
|
||||
source.includes('[sidebarDrawerMedia, sidebarDesktopCollapseMedia].forEach((media) =>')
|
||||
source.includes('[sidebarDrawerMedia, sidebarDesktopCollapseMedia, sidebarHoverPreviewMedia].forEach((media) =>')
|
||||
&& source.includes("media.addEventListener('change', handleSidebarInputModeChange)"),
|
||||
'Sidebar should reset transient state when either drawer or desktop collapse mode changes'
|
||||
);
|
||||
|
||||
assert(styleSource.includes('--sidebar-rail-width: 14px'), 'Collapsed desktop sidebar should retain a narrow hover rail');
|
||||
const desktopCollapseStyleStart = styleSource.indexOf('@media (width > 768px) and (hover: hover) and (pointer: fine)');
|
||||
const desktopCollapseStyleEnd = styleSource.indexOf('@media (prefers-reduced-motion: reduce)', desktopCollapseStyleStart);
|
||||
const desktopCollapseStyleStart = styleSource.indexOf('@media (width > 768px)');
|
||||
const hoverPreviewStyleStart = styleSource.indexOf('@media (width > 768px) and (hover: hover) and (pointer: fine)');
|
||||
const desktopCollapseStyleEnd = hoverPreviewStyleStart;
|
||||
assert(
|
||||
desktopCollapseStyleStart >= 0 && desktopCollapseStyleEnd > desktopCollapseStyleStart,
|
||||
'Desktop hover behavior should stay isolated from touch and narrow-screen drawers'
|
||||
desktopCollapseStyleStart >= 0
|
||||
&& hoverPreviewStyleStart > desktopCollapseStyleStart
|
||||
&& desktopCollapseStyleEnd > desktopCollapseStyleStart,
|
||||
'Wide fixed collapse styles should be separate from hover preview styles'
|
||||
);
|
||||
const desktopCollapseStyle = styleSource.slice(desktopCollapseStyleStart, desktopCollapseStyleEnd);
|
||||
assert(
|
||||
/\.menu-btn\s*\{[^}]*display:\s*inline-flex;/.test(desktopCollapseStyle),
|
||||
'Wide touch viewports should keep the sidebar toggle visible'
|
||||
);
|
||||
assert(
|
||||
!/\.app\.sidebar-collapsed \.sidebar:is\(:hover, :focus-within\)/.test(desktopCollapseStyle),
|
||||
'Wide fixed collapse should not enable hover preview on touch viewports'
|
||||
);
|
||||
const hoverPreviewStyle = styleSource.slice(hoverPreviewStyleStart, styleSource.indexOf('@media (prefers-reduced-motion: reduce)', hoverPreviewStyleStart));
|
||||
assert(
|
||||
/\.app\.sidebar-collapsed \.sidebar:is\(:hover, :focus-within\)/.test(hoverPreviewStyle),
|
||||
'Hover-capable desktops should retain temporary sidebar preview'
|
||||
);
|
||||
assert(
|
||||
styleSource.includes('margin-right: calc(var(--sidebar-rail-width) - var(--sidebar-width))')
|
||||
&& styleSource.includes('transform: translateX(calc(var(--sidebar-rail-width) - var(--sidebar-width)))'),
|
||||
@@ -641,7 +663,7 @@ function assertFrontendSidebarCollapseContract() {
|
||||
assert(
|
||||
source.includes('if (isSidebarDesktopCollapseMode())')
|
||||
&& source.includes("const expanded = isDrawer\n ? sidebar.classList.contains('open')\n : (isDesktopCollapsible ? !app.classList.contains('sidebar-collapsed') : true)"),
|
||||
'Unsupported wide-screen pointer modes should stay visually and accessibly expanded'
|
||||
'Wide touch viewports should use the fixed collapse state for labels and aria-expanded'
|
||||
);
|
||||
assert(
|
||||
styleSource.includes("html[data-theme='gilded'] .app.sidebar-collapsed .sidebar::after")
|
||||
@@ -895,7 +917,8 @@ function assertComposerSlashRoutingContract() {
|
||||
assert(source.includes('if (!msg.preserveComposerDraft) return false;'), 'Frontend should restore slash drafts only for explicit failure responses');
|
||||
assert(source.includes("if (msgInput.value !== '' || pendingAttachments.length > 0) return false;"), 'Frontend should not overwrite user input typed after a slash request');
|
||||
assert(source.includes('rememberPendingSlashDraft(requestId, text, currentSessionId, currentAgent);'), 'Frontend should remember slash draft before clearing the composer');
|
||||
assert(source.includes("send({ type: 'message', text, sessionId: currentSessionId, mode: currentMode, agent: currentAgent, requestId });"), 'Frontend slash sends should carry requestId');
|
||||
assert(source.includes("const clientMessageId = isGoalSetCommandText(text) ? createLocalId('user') : '';"), 'Frontend should allocate a stable user-message id for setting /goal');
|
||||
assert(source.includes('...(clientMessageId ? { clientMessageId } : {}),'), 'Frontend should send the Goal user-message id without changing other slash payloads');
|
||||
assert((source.match(/applyPendingSlashDraftResponse\(msg\);/g) || []).length >= 2, 'Frontend should handle slash draft restoration on system_message and error');
|
||||
|
||||
const frontendCommandsMatch = source.match(/const\s+SLASH_COMMANDS\s*=\s*\[([\s\S]*?)\n\s*\];/);
|
||||
@@ -919,6 +942,15 @@ function assertComposerSlashRoutingContract() {
|
||||
});
|
||||
assert(!knownSlashApi('/report/mcps?search') && !knownSlashApi('/help/topic'), 'Unknown slash paths should remain ordinary messages');
|
||||
|
||||
const goalSetSource = extractFunctionSource(source, 'isGoalSetCommandText');
|
||||
const goalSetApi = new Function(`
|
||||
${goalSetSource}
|
||||
return isGoalSetCommandText;
|
||||
`)();
|
||||
assert(goalSetApi('/goal improve benchmark coverage'), 'Frontend should recognize setting /goal as a Goal bubble command');
|
||||
assert(goalSetApi('/GOAL\nlong objective'), 'Frontend should recognize multiline and case-insensitive Goal objectives');
|
||||
assert(!goalSetApi('/goal') && !goalSetApi('/goal pause') && !goalSetApi('/goal resume') && !goalSetApi('/goal clear'), 'Frontend should keep Goal control commands out of user bubbles');
|
||||
|
||||
const tokenSource = extractFunctionSource(source, 'findActiveComposerToken');
|
||||
const tokenApi = new Function(`
|
||||
let msgInput = null;
|
||||
@@ -7032,17 +7064,40 @@ async function main() {
|
||||
assert(codexAppRetryMismatchUsers.length === 1, 'Codex App retry mismatch should not duplicate the user message');
|
||||
assert(!storedCodexAppAfterRetryMismatch.messages.some((message) => message.role === 'assistant' && /codexapp retry thread mismatch prompt/.test(String(message.content || ''))), 'Codex App retry mismatch should not persist a successful assistant response on the wrong thread');
|
||||
|
||||
ws.send(JSON.stringify({ type: 'message', text: '/goal improve benchmark coverage', sessionId: codexAppSession.sessionId, mode: 'yolo', agent: 'codexapp' }));
|
||||
const codexAppGoalRequestId = 'regression-goal-request';
|
||||
const codexAppGoalMessageId = 'regression-goal-user-message';
|
||||
ws.send(JSON.stringify({
|
||||
type: 'message',
|
||||
text: '/goal improve benchmark coverage',
|
||||
sessionId: codexAppSession.sessionId,
|
||||
mode: 'yolo',
|
||||
agent: 'codexapp',
|
||||
requestId: codexAppGoalRequestId,
|
||||
clientMessageId: codexAppGoalMessageId,
|
||||
}));
|
||||
const codexAppGoalBubble = await nextMessage(messages, ws, (msg) => (
|
||||
msg.type === 'session_message' &&
|
||||
msg.sessionId === codexAppSession.sessionId &&
|
||||
msg.message?.id === codexAppGoalMessageId
|
||||
), 5000);
|
||||
assert(codexAppGoalBubble.message.role === 'user', 'Setting /goal should render through the normal user-message role');
|
||||
assert(codexAppGoalBubble.message.content === 'improve benchmark coverage', 'Goal user bubble should contain only the objective');
|
||||
assert(codexAppGoalBubble.message.ccwebDisplayOnly === true, 'Goal user bubble should be display-only local history');
|
||||
assert(codexAppGoalBubble.message.ccwebGoalCommand?.action === 'set', 'Goal user bubble should carry dedicated Goal metadata');
|
||||
assert(codexAppGoalBubble.message.ccwebGoalCommand?.requestId === codexAppGoalRequestId, 'Goal metadata should preserve the stable request id');
|
||||
const codexAppGoalSyncing = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /正在同步 Goal/.test(msg.message || ''), 5000);
|
||||
assert(/正在同步 Goal/.test(codexAppGoalSyncing.message || ''), 'Codex App /goal should immediately show a syncing notice');
|
||||
assert(codexAppGoalSyncing.transient === true, 'Codex App /goal syncing notice should auto-dismiss');
|
||||
const codexAppGoalRunningList = await nextMessage(messages, ws, (msg) => (
|
||||
msg.type === 'session_list' &&
|
||||
Array.isArray(msg.sessions) &&
|
||||
msg.sessions.some((session) => session.id === codexAppSession.sessionId && session.isRunning)
|
||||
), 5000);
|
||||
assert(codexAppGoalRunningList.sessions.some((session) => session.id === codexAppSession.sessionId && session.isRunning), 'Codex App /goal RPC should mark the session running while waiting for app-server');
|
||||
const codexAppGoalSet = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /Goal active/.test(msg.message || '') && /improve benchmark coverage/.test(msg.message || ''));
|
||||
const codexAppGoalSet = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /Goal active/.test(msg.message || ''));
|
||||
assert(/Goal active/.test(codexAppGoalSet.message || ''), 'Codex App /goal should set an active goal');
|
||||
assert(!/improve benchmark coverage/.test(codexAppGoalSet.message || ''), 'Goal success notice should not repeat the full objective');
|
||||
assert(codexAppGoalSet.transient === true, 'Goal success notice should auto-dismiss');
|
||||
const codexAppGoalBackgroundDelta = await nextMessage(messages, ws, (msg) => (
|
||||
msg.type === 'text_delta' &&
|
||||
msg.sessionId === codexAppSession.sessionId &&
|
||||
@@ -7056,6 +7111,24 @@ async function main() {
|
||||
msg.sessions.some((session) => session.id === codexAppSession.sessionId && !session.isRunning)
|
||||
), 5000);
|
||||
assert(codexAppGoalIdleList.sessions.some((session) => session.id === codexAppSession.sessionId && !session.isRunning), 'Codex App /goal RPC should clear running state after app-server responds');
|
||||
ws.send(JSON.stringify({
|
||||
type: 'message',
|
||||
text: '/goal improve benchmark coverage',
|
||||
sessionId: codexAppSession.sessionId,
|
||||
mode: 'yolo',
|
||||
agent: 'codexapp',
|
||||
requestId: codexAppGoalRequestId,
|
||||
clientMessageId: codexAppGoalMessageId,
|
||||
}));
|
||||
const codexAppGoalDuplicate = await nextMessage(messages, ws, (msg) => (
|
||||
msg.type === 'system_message' &&
|
||||
msg.sessionId === codexAppSession.sessionId &&
|
||||
/Goal 设置请求已处理/.test(msg.message || '')
|
||||
), 5000);
|
||||
assert(codexAppGoalDuplicate.transient === true, 'Duplicate Goal command ids should receive a compact transient acknowledgement');
|
||||
await sleep(250);
|
||||
assert(!messages.some((msg) => msg.type === 'session_message' && msg.message?.id === codexAppGoalMessageId), 'Duplicate Goal command ids should not append another user bubble');
|
||||
assert(!messages.some((msg) => msg.type === 'text_delta' && /Goal background output: improve benchmark coverage/.test(msg.text || '')), 'Duplicate Goal command ids should not trigger another Goal RPC background turn');
|
||||
ws.send(JSON.stringify({ type: 'message', text: '/goal', sessionId: codexAppSession.sessionId, mode: 'yolo', agent: 'codexapp' }));
|
||||
const codexAppGoalShow = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /Goal active/.test(msg.message || '') && /improve benchmark coverage/.test(msg.message || ''));
|
||||
assert(/improve benchmark coverage/.test(codexAppGoalShow.message || ''), 'Codex App /goal should show the current goal');
|
||||
@@ -7072,7 +7145,11 @@ async function main() {
|
||||
const codexAppGoalEmpty = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /用法: \/goal <目标描述>/.test(msg.message || ''));
|
||||
assert(/\/goal <目标描述>/.test(codexAppGoalEmpty.message || ''), 'Codex App /goal should show usage when no goal exists');
|
||||
const storedCodexAppAfterGoal = JSON.parse(fs.readFileSync(path.join(sessionsDir, `${codexAppSession.sessionId}.json`), 'utf8'));
|
||||
assert(!storedCodexAppAfterGoal.messages.some((message) => message.role === 'user' && /^\/goal/.test(String(message.content || ''))), 'Codex App /goal slash commands should not be persisted as normal user messages');
|
||||
const storedGoalMessages = storedCodexAppAfterGoal.messages.filter((message) => message.id === codexAppGoalMessageId);
|
||||
assert(storedGoalMessages.length === 1, 'Codex App should persist exactly one Goal user bubble for a stable command id');
|
||||
assert(storedGoalMessages[0].role === 'user' && storedGoalMessages[0].content === 'improve benchmark coverage', 'Persisted Goal bubble should restore as the objective-only user message');
|
||||
assert(storedGoalMessages[0].ccwebDisplayOnly === true && storedGoalMessages[0].ccwebGoalCommand?.action === 'set', 'Persisted Goal bubble should remain marked as display-only Goal history');
|
||||
assert(!storedCodexAppAfterGoal.messages.some((message) => message.role === 'user' && /^\/goal/.test(String(message.content || ''))), 'Raw /goal commands should never enter ordinary user-message history');
|
||||
assert(storedCodexAppAfterGoal.messages.some((message) => (
|
||||
message.role === 'assistant' &&
|
||||
/Goal background output: improve benchmark coverage/.test(String(message.content || ''))
|
||||
|
||||
Reference in New Issue
Block a user