feat: complete WorkBuddy parity and Windows release workflow
This commit is contained in:
1 parent
143516b3a8
commit
d5512df549
434 files changed
+54977
-1854
No files matched your search
@@ -15,9 +15,11 @@
|
||||
// - 回收样式表:`contexts-BkjwYSZT.css`(`._branchLabel_*`)、`src-CyXHA0IC.css`(`.cr-context-usage*`)。
|
||||
// - 文案:已回收 i18n 表 `chatInput.contextUsage.*`(title 上下文用量 / used 已使用 /
|
||||
// tooltip "{percentage}% · {used} / {total} 上下文已使用" / needMessage / requesting / unavailable)。
|
||||
// - REA 保留的 `ContextUsageDisplay` 源码进一步确认:百分比固定一位小数,token 达 1K 后
|
||||
// 固定一位小数并显示为 K(例如 `20.0% · 12.8K / 64.0K`)。
|
||||
//
|
||||
// YCode 侧数据真实性:分支名由主进程真实执行 git;用量只用上游真实 usage(经 OMP 归一化)+
|
||||
// 模型自己的真实上下文窗口,拿不到就显示原版的「无法获取」文案,不填 0、不估算。
|
||||
// 该回合冻结的真实上下文窗口。历史样本没有分母时保持未知,不套当前模型配置。
|
||||
const fs = require('node:fs/promises')
|
||||
const path = require('node:path')
|
||||
const assert = require('node:assert/strict')
|
||||
@@ -29,6 +31,9 @@ const QA_PROMPT_TOKENS = 12800
|
||||
const QA_COMPLETION_TOKENS = 40
|
||||
const QA_CONTEXT_WINDOW = 64000
|
||||
const QA_EXPECTED_PERCENT = 20
|
||||
const QA_EXPECTED_PERCENT_TEXT = `${QA_EXPECTED_PERCENT.toFixed(1)}%`
|
||||
const QA_EXPECTED_USED_TEXT = '12.8K'
|
||||
const QA_EXPECTED_TOTAL_TEXT = '64.0K'
|
||||
const RING_CIRCUMFERENCE = 2 * Math.PI * 7
|
||||
|
||||
if (!process.versions.electron) {
|
||||
@@ -53,8 +58,19 @@ if (!process.versions.electron) {
|
||||
const now = Date.now()
|
||||
const sessions = [{ id: 'context-info-session', title: 'Context Info', cwd: repo, modelId: 'local-usage', createdAt: now, updatedAt: now + 1, messages: [
|
||||
{ id: 'context-user', role: 'user', content: '上下文信息验收', createdAt: now, status: 'done', requestId: 'context-run' },
|
||||
{ id: 'context-assistant', role: 'assistant', content: '收到。', createdAt: now + 1, status: 'done', requestId: 'context-run', tools: [], parts: [] }
|
||||
{ id: 'context-assistant', role: 'assistant', content: '收到。', createdAt: now + 1, status: 'done', requestId: 'context-run', tools: [], parts: [],
|
||||
contextUsage: { usage: { inputTokens: 8000, outputTokens: 40, totalTokens: 8040 } } }
|
||||
] }]
|
||||
const nativePath=path.join(data,'omp-sessions','session-native-context-session.jsonl')
|
||||
await fs.mkdir(path.dirname(nativePath),{recursive:true})
|
||||
await fs.writeFile(nativePath,[
|
||||
{id:'native-user',type:'message',message:{role:'user',content:[{type:'text',text:'old'}]}},
|
||||
{id:'native-reply',parentId:'native-user',type:'message',message:{role:'assistant',model:'old-model',usage:{input:4009,cacheRead:1152,output:125,totalTokens:5286},stopReason:'stop',timestamp:now-1000}}
|
||||
].map(value=>JSON.stringify(value)).join('\n'))
|
||||
sessions.push({id:'native-context-session',title:'Native Usage Recovery',cwd:repo,modelId:'local-usage',createdAt:now-1000,updatedAt:now-1000,messages:[
|
||||
{id:'legacy-user',role:'user',content:'old',createdAt:now-1000,status:'done',requestId:'legacy-native',nativeEntryId:'native-user',nativeSessionPath:nativePath},
|
||||
{id:'legacy-reply',role:'assistant',content:'old reply',createdAt:now-900,status:'done',requestId:'legacy-native',modelId:'local-usage'}
|
||||
]})
|
||||
await fs.writeFile(path.join(data, 'sessions.json'), JSON.stringify({ sessions }))
|
||||
const env = { ...process.env, YCODE_QA_DIRECTORY: directory, YCODE_USER_DATA_DIR: data, YCODE_OMP_HOME: home,
|
||||
YCODE_QA_REAL_OMP: process.env.YCODE_OMP_PATH || path.join(process.env.LOCALAPPDATA || '', 'Ygent', 'omp.exe'),
|
||||
@@ -133,7 +149,7 @@ if (!process.versions.electron) {
|
||||
const track=ring.querySelector('circle:not(.cr-context-usage__ring-progress)');const progress=ring.querySelector('.cr-context-usage__ring-progress');
|
||||
const wrapper=right.querySelector('.cr-context-usage-tooltip-trigger');const model=right.querySelector('.cr-model-selector');
|
||||
return {ring:{box:box(ring),radius:style.borderRadius,width:style.width,height:style.height,transition:style.transitionProperty+' '+style.transitionDuration,
|
||||
display:style.display,role:ring.getAttribute('role'),background:style.backgroundColor},
|
||||
display:style.display,role:ring.getAttribute('role'),background:style.backgroundColor,ariaLabel:ring.getAttribute('aria-label')},
|
||||
wrapper:wrapper?box(wrapper):null,
|
||||
svg:svg?{box:box(svg),viewBox:svg.getAttribute('viewBox'),className:svg.getAttribute('class')}:null,
|
||||
track:track?{r:track.getAttribute('r'),strokeWidth:track.getAttribute('stroke-width'),fill:track.getAttribute('fill'),stroke:getComputedStyle(track).stroke}:null,
|
||||
@@ -161,8 +177,15 @@ if (!process.versions.electron) {
|
||||
bar:bar?{box:box(bar),height:getComputedStyle(bar).height,radius:getComputedStyle(bar).borderRadius,fillWidth:popover.querySelector('.cr-context-usage-popover__bar-fill')?.style.width}:null,
|
||||
rows,bodyText:popover.textContent,tokenTertiary:probe('color','--cr-text-tertiary')}})()`
|
||||
|
||||
const requestingTipSource = `(()=>{const tip=document.querySelector('.cr-context-usage-requesting-tip');if(!tip)return null;
|
||||
const style=getComputedStyle(tip);const text=tip.querySelector('.cr-context-usage-requesting-tip__text');
|
||||
return {role:tip.getAttribute('role'),text:text?.textContent,background:style.backgroundColor,border:style.border,
|
||||
borderRadius:style.borderRadius,padding:style.padding,font:style.fontSize+'/'+style.lineHeight,
|
||||
popoverPresent:!!document.querySelector('.cr-context-usage-popover')}})()`
|
||||
|
||||
;(async () => {
|
||||
// 受控上游:最后一次 chunk 带真实 usage(provider 语义,含总 prompt tokens)。
|
||||
let omitUsage=false
|
||||
server = createServer(async (req, res) => {
|
||||
let raw = ''
|
||||
for await (const part of req) raw += part
|
||||
@@ -172,7 +195,7 @@ if (!process.versions.electron) {
|
||||
const chunk = payload => `data: ${JSON.stringify({ id: 'local-usage', object: 'chat.completion.chunk', created: 1, model: body.model, ...payload })}\n\n`
|
||||
res.write(chunk({ choices: [{ index: 0, delta: { role: 'assistant', content: 'USAGE_REPLY' }, finish_reason: null }] }))
|
||||
res.write(chunk({ choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
usage: { prompt_tokens: QA_PROMPT_TOKENS, completion_tokens: QA_COMPLETION_TOKENS, total_tokens: QA_PROMPT_TOKENS + QA_COMPLETION_TOKENS } }))
|
||||
...(omitUsage?{}:{usage: { prompt_tokens: QA_PROMPT_TOKENS, completion_tokens: QA_COMPLETION_TOKENS, total_tokens: QA_PROMPT_TOKENS + QA_COMPLETION_TOKENS }}) }))
|
||||
res.write('data: [DONE]\n\n')
|
||||
res.end()
|
||||
})
|
||||
@@ -250,20 +273,20 @@ if (!process.versions.electron) {
|
||||
check('ring track matches the recovered circle', [ring.track.r, ring.track.strokeWidth, ring.track.fill], ['7', '2', 'none'])
|
||||
check('ring progress circle matches the recovered geometry', [ring.progress.r, ring.progress.strokeWidth, ring.progress.strokeLinecap, ring.progress.transform], ['7', '2', 'round', 'rotate(-90 8 8)'])
|
||||
check('ring progress keeps the recovered dashoffset transition', ring.progress.transition, 'stroke-dashoffset 0.3s')
|
||||
check('no usage yet: ring stays empty', Math.round(ring.progress.dashOffset), Math.round(RING_CIRCUMFERENCE))
|
||||
check('legacy usage without a frozen denominator keeps the ring empty',
|
||||
Math.round(ring.progress.dashOffset), Math.round(RING_CIRCUMFERENCE))
|
||||
|
||||
await step('usage-popover-without-usage')
|
||||
await step('usage-requesting-tip-without-denominator')
|
||||
await read(`(()=>{document.querySelector('.cr-context-usage').click();return true})()`)
|
||||
await wait('!!document.querySelector(".cr-context-usage-popover")', 'usage popover')
|
||||
const emptyPopover = await read(popoverSource)
|
||||
snapshots.emptyPopover = emptyPopover
|
||||
check('popover keeps the recovered 280-wide card', [Math.round(emptyPopover.box[2]), emptyPopover.padding, emptyPopover.borderRadius, emptyPopover.font], [280, '16px', '16px', '12px/18px'])
|
||||
check('popover keeps the recovered header', [emptyPopover.title.text, emptyPopover.title.font, Math.round(emptyPopover.close[2]), Math.round(emptyPopover.close[3])], ['上下文用量', '12px/400/20px', 16, 16])
|
||||
check('no usage yet: popover says so instead of inventing numbers', emptyPopover.bodyText.includes('暂时无法获取用量明细,请稍后重试'), true)
|
||||
check('no usage yet: popover shows no percentage', emptyPopover.percent, null)
|
||||
await read(`(()=>{document.querySelector('.cr-context-usage-popover__close').click();return true})()`)
|
||||
await wait('!document.querySelector(".cr-context-usage-popover")', 'popover closed')
|
||||
check('close button dismisses the popover', await read('!document.querySelector(".cr-context-usage-popover")'), true)
|
||||
await wait('!!document.querySelector(".cr-context-usage-popover")', 'known usage with unknown denominator')
|
||||
const legacyPopover = await read(popoverSource)
|
||||
snapshots.legacyPopover = legacyPopover
|
||||
check('known token count is visible even without its historical window',legacyPopover.usedValue,'8.0K/ 未知')
|
||||
check('legacy unknown denominator is not replaced by the current model configuration',legacyPopover.percent.text,'—')
|
||||
check('legacy classifications remain unknown',legacyPopover.rows.map(row=>row.value),['—','—','—','—','—'])
|
||||
await read(`(()=>{document.querySelector('.cr-context-usage').click();return true})()`)
|
||||
await wait('!document.querySelector(".cr-context-usage-popover")', 'legacy popover closed')
|
||||
check('clicking the ring dismisses the usage panel', await read('!document.querySelector(".cr-context-usage-popover")'), true)
|
||||
|
||||
await step('real-turn-usage')
|
||||
await read(`(()=>{const input=[...document.querySelectorAll('.wb-composer__input')].find(node=>node.offsetParent!==null);
|
||||
@@ -280,6 +303,8 @@ if (!process.versions.electron) {
|
||||
check('ring dashoffset follows the real provider usage', Math.abs(usedRing.progress.dashOffset - expectedOffset) < 0.6, true)
|
||||
check('ring stroke uses the recovered progress colour', usedRing.progress.stroke, 'rgb(156, 163, 175)')
|
||||
check('ring track uses the recovered track colour', usedRing.track.stroke, 'rgba(128, 128, 128, 0.2)')
|
||||
check('ring accessible label uses the same recovered compact usage sentence', usedRing.ring.ariaLabel,
|
||||
`${QA_EXPECTED_PERCENT_TEXT} · ${QA_EXPECTED_USED_TEXT} / ${QA_EXPECTED_TOTAL_TEXT} 上下文已使用`)
|
||||
|
||||
await step('usage-tooltip')
|
||||
await hover('.cr-context-usage')
|
||||
@@ -289,7 +314,8 @@ if (!process.versions.electron) {
|
||||
background:style.backgroundColor,color:style.color,borderRadius:style.borderRadius,padding:style.padding,font:style.fontSize+'/'+style.lineHeight,
|
||||
arrow:!!node.querySelector('.wb-tooltip__arrow'),tokenBg:probe('backgroundColor','--wb-tooltip-bg'),tokenFg:probe('color','--wb-tooltip-text-color')}})()`)
|
||||
snapshots.tooltip = tooltip
|
||||
check('hover tooltip uses the recovered usage sentence', tooltip?.text, `${QA_EXPECTED_PERCENT}% · ${QA_PROMPT_TOKENS} / ${QA_CONTEXT_WINDOW} 上下文已使用`)
|
||||
check('hover tooltip uses the recovered one-decimal and compact-token formatting', tooltip?.text,
|
||||
`${QA_EXPECTED_PERCENT_TEXT} · ${QA_EXPECTED_USED_TEXT} / ${QA_EXPECTED_TOTAL_TEXT} 上下文已使用`)
|
||||
check('tooltip keeps the recovered base surface', [tooltip.background, tooltip.color, tooltip.borderRadius, tooltip.padding, tooltip.font, tooltip.arrow],
|
||||
[tooltip.tokenBg || 'rgba(0, 0, 0, 0.75)', tooltip.tokenFg || 'rgb(255, 255, 255)', '8px', '4px 8px', '12px/20px', true])
|
||||
|
||||
@@ -299,16 +325,38 @@ if (!process.versions.electron) {
|
||||
await pause(200)
|
||||
const usedPopover = await read(popoverSource)
|
||||
snapshots.usedPopover = usedPopover
|
||||
check('popover shows the real percentage', usedPopover.percent.text, `${QA_EXPECTED_PERCENT}%`)
|
||||
check('popover keeps the recovered one-decimal percentage', usedPopover.percent.text, QA_EXPECTED_PERCENT_TEXT)
|
||||
check('popover percent keeps 20/700/28', usedPopover.percent.font, '20px/700/28px')
|
||||
check('popover shows the real used/total pair', usedPopover.usedValue, `${QA_PROMPT_TOKENS} / ${QA_CONTEXT_WINDOW}`)
|
||||
check('popover keeps the recovered compact used/total pair', usedPopover.usedValue,
|
||||
`${QA_EXPECTED_USED_TEXT}/ ${QA_EXPECTED_TOTAL_TEXT}`)
|
||||
check('popover used label keeps the recovered copy', [usedPopover.usedLabel.text, usedPopover.usedLabel.color], ['已使用', usedPopover.tokenTertiary])
|
||||
check('popover bar keeps the recovered 8px track', [Math.round(usedPopover.bar.box[3]), usedPopover.bar.height, usedPopover.bar.radius], [8, '8px', '3px'])
|
||||
check('popover bar fill follows the same percentage', usedPopover.bar.fillWidth, `${QA_EXPECTED_PERCENT}%`)
|
||||
const prepared = await read(`window.ycode.session.messages('context-info-session').then(messages=>{const m=messages.filter(item=>item.role==='assistant').at(-1);return {breakdown:m?.contextBreakdown,requestId:m?.requestId}})`)
|
||||
snapshots.preparedInput=prepared
|
||||
check('real OMP provider hook records this request, not an unrelated historical sample',
|
||||
[prepared.breakdown?.scope,prepared.breakdown?.requestId,prepared.breakdown?.method],['provider-request',prepared.requestId,'utf8-estimate'])
|
||||
check('classification contains actual system, tool definitions, dialogue and skill catalogue',
|
||||
['system','tools','conversation','skills'].every(key=>prepared.breakdown?.counts[key]>0),true)
|
||||
check('category sum is independent from controlled upstream count',prepared.breakdown?.inputTokens!==QA_PROMPT_TOKENS,true)
|
||||
check('classification bar uses its own independent local estimate',Math.abs(parseFloat(usedPopover.bar.fillWidth)-Math.min(100,prepared.breakdown.inputTokens/QA_CONTEXT_WINDOW*100))<0.01,true)
|
||||
check('popover legend keeps the five recovered categories', usedPopover.rows.map(row => row.name),
|
||||
['系统提示词', '工具及子智能体', '对话消息', '连接器及MCP', '技能'])
|
||||
check('legend swatches keep the recovered 8px dots', usedPopover.rows.map(row => row.swatch), [[8, 8], [8, 8], [8, 8], [8, 8], [8, 8]])
|
||||
check('legend values stay unknown instead of fabricated', usedPopover.rows.map(row => row.value), ['—', '—', '—', '—', '—'])
|
||||
check('legend values are explicitly labeled local estimates',usedPopover.rows.every(row=>row.value.startsWith('≈')),true)
|
||||
await fs.writeFile(path.join(directory,'context-breakdown-light.png'),(await main.webContents.capturePage()).toPNG())
|
||||
|
||||
await step('frozen-window-survives-model-config-change')
|
||||
await read(`window.ycode.models.save(${JSON.stringify({ id: 'local-usage', previousId: 'local-usage', name: 'Local Usage', providerId: 'custom',
|
||||
model: 'model-usage', baseUrl, apiKey: 'LOCAL_TEST_KEY', supportsToolCall: true,
|
||||
maxInputTokens: QA_CONTEXT_WINDOW * 2, maxOutputTokens: 4096 })})`)
|
||||
await pause(250)
|
||||
const frozenRing = await read(ringSource)
|
||||
snapshots.frozenWindow = frozenRing
|
||||
check('historical usage keeps its own frozen denominator after model config changes',
|
||||
frozenRing.ring.ariaLabel,
|
||||
`${QA_EXPECTED_PERCENT_TEXT} · ${QA_EXPECTED_USED_TEXT} / ${QA_EXPECTED_TOTAL_TEXT} 上下文已使用`)
|
||||
check('historical usage does not silently inherit the newly configured model window',
|
||||
Math.abs(frozenRing.progress.dashOffset - expectedOffset) < 0.6, true)
|
||||
|
||||
await step('dark-theme-via-native-settings')
|
||||
// 真实路径:设置窗口切成深色,聊天区 `data-cr-theme` 随之变化,输入区 token 跟着切。
|
||||
@@ -327,8 +375,8 @@ if (!process.versions.electron) {
|
||||
snapshots.settingsWindow = await read(`({url:location.href,search:location.search,bodyClass:document.body.className,rootHtml:(document.querySelector('#root')?.innerHTML||'').slice(0,240)})`, settings)
|
||||
await wait('!!document.querySelector(".settings-window-root")', 'settings root', settings)
|
||||
await read(`(()=>{const tab=document.querySelector('[data-settings-tab="appearance"]');if(tab)tab.click();return true})()`, settings)
|
||||
await wait(`!!document.querySelector('.segmented')`, 'appearance panel', settings)
|
||||
await read(`(()=>{const node=[...document.querySelectorAll('.segmented__item')].find(item=>item.textContent.trim()==='深色');if(!node)throw new Error('missing dark option');node.click();return true})()`, settings)
|
||||
await wait(`!!document.querySelector('.appearance-card')`, 'appearance panel', settings)
|
||||
await read(`(()=>{const node=document.querySelector('.appearance-card[aria-label="深色"]');if(!node)throw new Error('missing dark option');node.click();return true})()`, settings)
|
||||
await wait(`document.documentElement.dataset.theme==='dark'`, 'main window dark theme')
|
||||
await wait(`document.querySelector('.chat-pane')?.dataset.crTheme==='dark'`, 'composer dark scope')
|
||||
await pause(250)
|
||||
@@ -339,16 +387,65 @@ if (!process.versions.electron) {
|
||||
tokenElevated:probe('backgroundColor','--cr-bg-elevated'),tokenActive:probe('backgroundColor','--cr-bg-active'),
|
||||
tokenTextPrimary:probe('color','--wb-color-text-primary'),scope:document.querySelector('.chat-pane')?.dataset.crTheme ?? null}})()`)
|
||||
snapshots.dark = { lightTokens, darkPopover }
|
||||
await fs.writeFile(path.join(directory,'context-breakdown-dark.png'),(await main.webContents.capturePage()).toPNG())
|
||||
check('native settings switches the composer to the dark scope', [lightTokens.scope, darkPopover.scope], ['light', 'dark'])
|
||||
check('dark popover uses the recovered dark elevated surface', darkPopover.popoverBackground, 'rgb(36, 38, 41)')
|
||||
check('dark popover bar uses the recovered dark active fill', darkPopover.bar, 'rgba(255, 255, 255, 0.1)')
|
||||
check('dark pill switches to the dark primary text colour', [darkPopover.pillColor, lightTokens.pillColor, darkPopover.pillColor === darkPopover.tokenTextPrimary], ['rgb(255, 255, 255)', 'rgb(0, 0, 0)', true])
|
||||
await read(`(()=>{const node=[...document.querySelectorAll('.segmented__item')].find(item=>item.textContent.trim()==='浅色');if(!node)throw new Error('missing light option');node.click();return true})()`, settings)
|
||||
await read(`(()=>{const node=document.querySelector('.appearance-card[aria-label="浅色"]');if(!node)throw new Error('missing light option');node.click();return true})()`, settings)
|
||||
await wait(`document.documentElement.dataset.theme==='light'`, 'main window light theme')
|
||||
await wait(`document.querySelector('.chat-pane')?.dataset.crTheme==='light'`, 'composer light scope')
|
||||
await read(`document.querySelector('.settings-window-close').click()`, settings)
|
||||
for (let index = 0; index < 200 && !settings.isDestroyed(); index++) await pause(20)
|
||||
check('light theme restored through the same settings window', await read(`[document.documentElement.dataset.theme,document.querySelector('.chat-pane').dataset.crTheme]`), ['light', 'light'])
|
||||
|
||||
await step('persisted-usage-after-renderer-reload')
|
||||
await wait(`window.ycode.chat.activeRun('context-info-session').then(run=>!run)`, 'real turn persistence barrier')
|
||||
const persistedUsage = await read(`window.ycode.session.messages('context-info-session').then(messages=>messages.filter(message=>message.role==='assistant').at(-1)?.contextUsage)`)
|
||||
snapshots.persistedUsage = persistedUsage
|
||||
check('actual turn stores its upstream input sample and original model window',
|
||||
[persistedUsage?.usage?.inputTokens, persistedUsage?.contextWindow], [QA_PROMPT_TOKENS, QA_CONTEXT_WINDOW])
|
||||
const reloaded = new Promise(resolve => main.webContents.once('did-finish-load', resolve))
|
||||
main.webContents.reload()
|
||||
await reloaded
|
||||
await wait(`document.querySelector('.cr-context-usage')?.getAttribute('aria-label')===${JSON.stringify(`${QA_EXPECTED_PERCENT_TEXT} · ${QA_EXPECTED_USED_TEXT} / ${QA_EXPECTED_TOTAL_TEXT} 上下文已使用`)}`,
|
||||
'restored usage with frozen denominator')
|
||||
snapshots.reloadedUsage = await read(ringSource)
|
||||
check('renderer reload recovers usage without adopting the newly doubled model window',
|
||||
snapshots.reloadedUsage.ring.ariaLabel,
|
||||
`${QA_EXPECTED_PERCENT_TEXT} · ${QA_EXPECTED_USED_TEXT} / ${QA_EXPECTED_TOTAL_TEXT} 上下文已使用`)
|
||||
await fs.writeFile(path.join(directory, 'context-usage-restored.png'), (await main.webContents.capturePage()).toPNG())
|
||||
await step('new-request-without-provider-usage')
|
||||
omitUsage=true
|
||||
await read(`(()=>{const input=[...document.querySelectorAll('.wb-composer__input')].find(node=>node.offsetParent!==null);
|
||||
Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype,'value').set.call(input,'NO_USAGE_NEW_REQUEST');input.dispatchEvent(new Event('input',{bubbles:true}));return true})()`)
|
||||
await read(`document.querySelector('.cr-input-toolbar__send').click()`)
|
||||
await wait(`window.ycode.session.messages('context-info-session').then(messages=>messages.filter(m=>m.role==='user').at(-1)?.content==='NO_USAGE_NEW_REQUEST')`,'new round created')
|
||||
await wait(`window.ycode.chat.activeRun('context-info-session').then(run=>!run)`,'no-usage round complete')
|
||||
const noUsage=await read(`window.ycode.session.messages('context-info-session').then(messages=>messages.filter(m=>m.role==='assistant').at(-1))`)
|
||||
snapshots.noUsage={contextUsage:noUsage.contextUsage,contextBreakdown:noUsage.contextBreakdown,requestId:noUsage.requestId}
|
||||
check('a new no-usage request cannot inherit the previous provider total',noUsage.contextUsage??null,null)
|
||||
check('the new request still retains its independently prepared classification',noUsage.contextBreakdown?.requestId,noUsage.requestId)
|
||||
await wait(`document.querySelector('.cr-context-usage')?.getAttribute('aria-label')?.startsWith('≈')`,'local estimate clearly identified')
|
||||
const noUsageReload=new Promise(resolve=>main.webContents.once('did-finish-load',resolve));main.webContents.reload();await noUsageReload
|
||||
await wait(`document.querySelector('.cr-context-usage')?.getAttribute('aria-label')?.startsWith('≈')`,'estimate restored without reviving old provider sample')
|
||||
check('reopening a no-usage round does not pick an older assistant sample',
|
||||
await read(`window.ycode.session.contextUsage('context-info-session').then(info=>[!!info.sample,info.breakdown?.requestId])`),[false,noUsage.requestId])
|
||||
|
||||
await step('native-history-usage-recovery')
|
||||
const nativeUpdatedBefore=await read(`window.ycode.session.list().then(sessions=>sessions.find(session=>session.id==='native-context-session').updatedAt)`)
|
||||
await wait(`!!document.querySelector('[data-session-id="native-context-session"]')`,'legacy sidebar item')
|
||||
await read(`document.querySelector('[data-session-id="native-context-session"]').click()`)
|
||||
await wait(`document.querySelector('.cr-context-usage')?.getAttribute('aria-label')?.includes('5.2K / 未知')`,'native sample recovered without a fabricated denominator')
|
||||
await read(`document.querySelector('.cr-context-usage').click()`)
|
||||
await wait(`document.querySelector('.yc-context-source')?.textContent.includes('原生记录恢复')`,'source of recovered sample visible')
|
||||
const recovered=await read(`window.ycode.session.messages('native-context-session').then(messages=>messages.at(-1).contextUsage)`)
|
||||
snapshots.nativeRecovered=recovered
|
||||
check('real IPC saves the recovered native sample with its provenance',
|
||||
[recovered.usage.inputTokens,recovered.contextWindow??null,recovered.source,recovered.nativeEntryId],[5161,null,'native-history','native-reply'])
|
||||
check('restoring usage metadata does not reorder the sidebar task',await read(`window.ycode.session.list().then(sessions=>sessions.find(session=>session.id==='native-context-session').updatedAt)`),nativeUpdatedBefore)
|
||||
await fs.writeFile(path.join(directory,'context-native-recovered.png'),(await main.webContents.capturePage()).toPNG())
|
||||
check('querying old records does not start a native agent',await fs.readdir(path.join(process.env.YCODE_USER_DATA_DIR,'context-usage')).then(files=>files.some(name=>name.includes('native-context-session'))),false)
|
||||
check('no renderer error logs', rendererErrors, [])
|
||||
|
||||
server.close()
|
||||
|
||||
Reference in new issue
Block a user