diff --git a/scripts/src/dev-cloud-workbench-smoke-lib.mjs b/scripts/src/dev-cloud-workbench-smoke-lib.mjs index 3770ea5b..8706f280 100644 --- a/scripts/src/dev-cloud-workbench-smoke-lib.mjs +++ b/scripts/src/dev-cloud-workbench-smoke-lib.mjs @@ -72,6 +72,9 @@ const defaultHomepageForbiddenTerms = Object.freeze([ "验收", "M0-M5", "BLOCKED", + "SOURCE", + "DRY-RUN", + "DEV-LIVE", "db-live", "m3-hardware-loop-runtime", "执行轨迹", @@ -133,7 +136,7 @@ const requiredHardwareStatusTerms = Object.freeze([ "Patch Panel 连线", "DO1 -> patch-panel -> DI1", "AI/AO/FREQ", - "future capability", + "后续能力", "SOURCE", "DEV-LIVE", "BLOCKED" @@ -1109,14 +1112,12 @@ function beforeSection(source, viewId) { } function defaultHomepageHidesBackstage({ html }) { - const workspaceHtml = sectionSource(html, "workspace"); - const visibleShellHtml = `${beforeSection(html, "help")}\n${workspaceHtml}`; - return defaultHomepageForbiddenTerms.every((term) => !visibleShellHtml.includes(term)); + const firstViewportText = visibleTextFromHtml(defaultFirstViewportHtml(html)); + return defaultHomepageForbiddenTerms.every((term) => !textHasForbiddenTerm(firstViewportText, term)); } function defaultWorkspaceIsSanitized({ html, app }) { - const workspaceHtml = sectionSource(html, "workspace"); - const defaultShell = `${beforeSection(html, "help")}\n${workspaceHtml}`; + const defaultShellText = visibleTextFromHtml(defaultFirstViewportHtml(html)); const userFacingBodies = [ functionBody(app, "codeAgentStatusMessage"), functionBody(app, "codeAgentPromptText"), @@ -1127,14 +1128,47 @@ function defaultWorkspaceIsSanitized({ html, app }) { functionBody(app, "renderWiringList") ].join("\n"); return ( - !/OPENAI_API_KEY|hwlab-code-agent-provider|Secret|secretRef|m3-route-required|waiting-for-dev-live/u.test(defaultShell) && - !/OPENAI_API_KEY|hwlab-code-agent-provider|Secret|secretRef|m3-route-required|waiting-for-dev-live|BLOCKED 凭证缺口/u.test(userFacingBodies) && + defaultHomepageForbiddenTerms.every((term) => !textHasForbiddenTerm(defaultShellText, term)) && + !/OPENAI_API_KEY|hwlab-code-agent-provider|Secret|secretRef|m3-route-required|waiting-for-dev-live|BLOCKED 凭证缺口|SOURCE fixture|DEV-LIVE/u.test(userFacingBodies) && /服务受阻/u.test(functionBody(app, "renderAgentChatStatus")) && /Code Agent 服务暂不可用/u.test(functionBody(app, "codeAgentBlockedSummary")) && /等待可信记录/u.test(functionBody(app, "renderWiringList")) ); } +function defaultFirstViewportHtml(html) { + return `${beforeSection(html, "help")}\n${rightSidebarDefaultHtml(html)}`; +} + +function rightSidebarDefaultHtml(html) { + const start = html.search(/]*\bclass=["'][^"']*\bright-sidebar\b[^"']*["'][^>]*>/u); + if (start === -1) return ""; + const wiringStart = html.slice(start).search(/]*\bid=["']panel-wiring["'][^>]*>/u); + if (wiringStart !== -1) return html.slice(start, start + wiringStart); + const end = html.slice(start).search(/<\/aside>/u); + return end === -1 ? html.slice(start) : html.slice(start, start + end); +} + +function visibleTextFromHtml(fragment) { + return fragment + .replace(//giu, " ") + .replace(//giu, " ") + .replace(/<[^>]+>/gu, " ") + .replace(/ /gu, " ") + .replace(/>/gu, ">") + .replace(/</gu, "<") + .replace(/&/gu, "&") + .replace(/\s+/gu, " ") + .trim(); +} + +function textHasForbiddenTerm(text, term) { + if (/^[A-Za-z][A-Za-z0-9-]*$/u.test(term)) { + return new RegExp(`\\b${escapeRegExp(term)}\\b`, "u").test(text); + } + return text.includes(term); +} + function hasCompletePrimaryNavigation(html) { const navLabelsPresent = ["工作台", "内部复核", "使用说明", "资源树"].every((label) => html.includes(`>${label}<`)); const legacyLabelsAbsent = incompletePrimaryNavLabels.every((label) => !html.includes(`>${label}<`)); @@ -1190,7 +1224,7 @@ function hardwareStatusStructureContract({ html, app, styles }) { /function\s+hasTrustedM3LiveEvidence\s*\(/u.test(app) && /function\s+linkStatusLabel\s*\(/u.test(app) && /function\s+normalizePort\s*\(/u.test(app) && - /Web 不提供硬件写 RPC;不会新增伪硬件写操作。/u.test(app) && + /页面不直连模拟器,也不伪造硬件状态。/u.test(app) && /\.hardware-section\s*\{/u.test(styles) && /\.hardware-row\s*\{/u.test(styles) ); @@ -1370,8 +1404,8 @@ function hasCodeAgentReadinessVisibility({ html, app }) { blockedCodeAgentUiLabels.some((label) => app.includes(label)) && /provider_unavailable/u.test(app) && /OPENAI_API_KEY/u.test(app) && - /只有真实 completed 回复才标 DEV-LIVE/u.test(app) && - /不能因为只有 conversationId 标为开发实况/u.test(app) && + /只有真实完成回复才显示为完成/u.test(app) && + /不能因为只有会话编号就当成实况完成/u.test(app) && /isTrustedCodeAgentProvider/u.test(app) && /Boolean\(value\?\.model\)/u.test(app) && /Boolean\(value\?\.backend\)/u.test(app) && @@ -1492,8 +1526,8 @@ function m3FixtureCountsNotDevLive(app) { /hasTrustedPatchPanelLiveReport/u.test(liveEvidenceBody) && /serviceId\s*!==\s*M3_TRUSTED_ROUTE\.patchPanelServiceId/u.test(patchPanelReportBody) && /trustedM3Link\(\[link\]\)/u.test(patchPanelReportBody) && - /counts 不是 patch-panel live report/u.test(linkDetailBody) && - /SOURCE fixture 包含目标接线/u.test(linkDetailBody) + /只读运行计数已返回,但还不能证明接线盘闭环/u.test(linkDetailBody) && + /来源拓扑包含目标接线/u.test(linkDetailBody) ); } @@ -1869,7 +1903,7 @@ async function inspectLiveDom(url) { browser = await chromium.launch({ headless: true }); const page = await browser.newPage({ viewport: { width: 1366, height: 768 } }); await page.goto(url, { waitUntil: "domcontentloaded", timeout: 8000 }); - const dom = await page.evaluate(async () => { + const dom = await page.evaluate(async (forbiddenTerms) => { const workspace = document.querySelector('[data-view="workspace"]'); const gate = document.querySelector('[data-view="gate"]'); const help = document.querySelector('[data-view="help"]'); @@ -1884,10 +1918,34 @@ async function inspectLiveDom(url) { const box = element.getBoundingClientRect(); return style.visibility !== "hidden" && style.display !== "none" && box.width > 0 && box.height > 0; }; + const visibleTextInFirstViewport = () => { + const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); + const chunks = []; + for (let node = walker.nextNode(); node; node = walker.nextNode()) { + const textContent = node.textContent?.replace(/\s+/gu, " ").trim(); + if (!textContent) continue; + const parent = node.parentElement; + if (!parent) continue; + const style = getComputedStyle(parent); + if (style.visibility === "hidden" || style.display === "none") continue; + const box = parent.getBoundingClientRect(); + const inViewport = box.width > 0 && box.height > 0 && box.bottom > 0 && box.right > 0 && box.top < window.innerHeight && box.left < window.innerWidth; + if (inViewport) chunks.push(textContent); + } + return chunks.join(" ").replace(/\s+/gu, " ").trim(); + }; + const textHasForbiddenTerm = (candidateText, term) => { + if (/^[A-Za-z][A-Za-z0-9-]*$/u.test(term)) { + const escaped = term.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); + return new RegExp(`\\b${escaped}\\b`, "u").test(candidateText); + } + return candidateText.includes(term); + }; window.scrollTo(0, 240); document.documentElement.scrollTop = 240; document.body.scrollTop = 240; await new Promise((resolve) => requestAnimationFrame(resolve)); + const firstViewportText = visibleTextInFirstViewport(); const rootAfterScrollAttempt = { windowScrollY: window.scrollY, htmlScrollTop: document.documentElement.scrollTop, @@ -1903,6 +1961,8 @@ async function inspectLiveDom(url) { diagnosticsHidden: diagnostics ? diagnostics.hidden : null, outerScrollLocked: document.documentElement.scrollHeight <= document.documentElement.clientHeight + 2, rootAfterScrollAttempt, + firstViewportForbiddenPresent: forbiddenTerms.filter((term) => textHasForbiddenTerm(firstViewportText, term)), + firstViewportTextSample: firstViewportText.slice(0, 400), labelsPresent: ["硬件资源", "Agent 对话", "工作清单", "可信记录", "使用说明"].every((label) => text.includes(label)), coreControlsVisible: { commandInput: visible("#command-input"), @@ -1914,7 +1974,7 @@ async function inspectLiveDom(url) { recordsTab: visible("#tab-records") } }; - }); + }, [...defaultHomepageForbiddenTerms]); const pass = dom.title === "HWLAB 云工作台" && dom.bodyOverflow === "hidden" && @@ -1927,12 +1987,18 @@ async function inspectLiveDom(url) { dom.rootAfterScrollAttempt.windowScrollY === 0 && dom.rootAfterScrollAttempt.htmlScrollTop === 0 && dom.rootAfterScrollAttempt.bodyScrollTop === 0 && + dom.firstViewportForbiddenPresent.length === 0 && dom.labelsPresent && Object.values(dom.coreControlsVisible).every(Boolean); return { status: pass ? "pass" : "blocked", summary: pass ? "Browser DOM confirms the default workbench, core controls, and outer scroll lock." : "Browser DOM did not satisfy the workbench contract.", - evidence: [`title=${dom.title}`, `bodyOverflow=${dom.bodyOverflow}`, `htmlOverflow=${dom.htmlOverflow}`], + evidence: [ + `title=${dom.title}`, + `bodyOverflow=${dom.bodyOverflow}`, + `htmlOverflow=${dom.htmlOverflow}`, + `firstViewportForbidden=${dom.firstViewportForbiddenPresent.join(",") || "none"}` + ], observations: dom }; } catch (error) { diff --git a/web/hwlab-cloud-web/app.mjs b/web/hwlab-cloud-web/app.mjs index ec1a6d11..de6c735c 100644 --- a/web/hwlab-cloud-web/app.mjs +++ b/web/hwlab-cloud-web/app.mjs @@ -17,7 +17,7 @@ const STATUS_LABELS = Object.freeze({ active: "活动", available: "可用", blocked_after_cloud_api: "cloud-api 后阻塞", - blocked: "阻塞 BLOCKED", + blocked: "待处理", completed: "完成", connected: "已连接", degraded: "降级", @@ -36,9 +36,9 @@ const STATUS_LABELS = Object.freeze({ recorded: "已记录", sent: "已发送", requires: "依赖", - source: "来源 SOURCE", - "dry-run": "演练 DRY-RUN", - "dev-live": "开发实况 DEV-LIVE" + source: "来源", + "dry-run": "演练", + "dev-live": "实况已验证" }); const M3_TRUSTED_ROUTE = Object.freeze({ @@ -913,7 +913,7 @@ function renderUnblockList() { }, { title: "证据升级规则", - detail: "SOURCE、LOCAL、DRY-RUN 与 fixture 只能作为解释材料,不能升级为 DEV-LIVE 验收证据。", + detail: "源码、本地与演练记录只能作为解释材料,不能升级为实况验收证据。", tone: "dry-run" } ]; @@ -931,7 +931,7 @@ function renderHardwareStatus(summary) { const patchTone = m3Live ? "live" : patchPanel.state === "active" ? "source" : patchPanel.state; const linkTone = m3Live ? "live" : sourceLink || patchPanel.activeConnectionCount > 0 ? "degraded" : "blocked"; - el.hardwareSourceStatus.textContent = m3Live ? "DEV-LIVE" : "SOURCE"; + el.hardwareSourceStatus.textContent = m3Live ? "实况已验证" : "只读核对"; el.hardwareSourceStatus.className = `state-tag tone-${toneClass(m3Live ? "dev-live" : "source")}`; replaceChildren( @@ -943,8 +943,8 @@ function renderHardwareStatus(summary) { label: "Gateway-SIMU", value: counts ? `${counts.gatewaySessions ?? 0} 会话` : "离线查看", detail: counts - ? `${counts.gatewaySessions ?? 0} 个 Gateway 会话来自只读 runtime counts;不是 M3 DEV-LIVE 证据。` - : `${gateSummary.gatewaySessionCount} 个 Gateway 会话来自 SOURCE fixture。`, + ? `${counts.gatewaySessions ?? 0} 个 Gateway 会话来自只读运行计数;仅用于资源核对。` + : `${gateSummary.gatewaySessionCount} 个 Gateway 会话来自内置拓扑。`, sourceKind: runtimeCountsSourceKind, tone: runtimeCountsTone }, @@ -952,8 +952,8 @@ function renderHardwareStatus(summary) { label: "BOX-SIMU", value: counts ? `${counts.boxResources ?? 0} 资源` : "离线查看", detail: counts - ? `${counts.boxResources ?? 0} 个 BOX 资源来自只读 runtime counts;不是 M3 DEV-LIVE 证据。` - : `${gateSummary.boxResourceCount} 个 BOX 资源来自 SOURCE fixture。`, + ? `${counts.boxResources ?? 0} 个 BOX 资源来自只读运行计数;仅用于资源核对。` + : `${gateSummary.boxResourceCount} 个 BOX 资源来自内置拓扑。`, sourceKind: runtimeCountsSourceKind, tone: runtimeCountsTone } @@ -964,22 +964,22 @@ function renderHardwareStatus(summary) { rows: [ { label: "DO1", - value: m3Live ? "verified" : "not wired", - detail: m3Live ? m3LiveDetail(liveM3Evidence) : "BLOCKED:没有 DO1 电平的 live M3 operation/trace/audit/evidence 证据。", + value: m3Live ? "已验证" : "待接线", + detail: m3Live ? m3LiveDetail(liveM3Evidence) : "尚未看到 DO1 电平的完整可信记录。", sourceKind: m3Live ? "DEV-LIVE" : "BLOCKED", tone: m3Live ? "live" : "blocked" }, { label: "DI1", - value: m3Live ? "verified" : "not wired", - detail: m3Live ? m3LiveDetail(liveM3Evidence) : "BLOCKED:没有 DI1 回读的 live M3 operation/trace/audit/evidence 证据。", + value: m3Live ? "已验证" : "待回读", + detail: m3Live ? m3LiveDetail(liveM3Evidence) : "尚未看到 DI1 回读的完整可信记录。", sourceKind: m3Live ? "DEV-LIVE" : "BLOCKED", tone: m3Live ? "live" : "blocked" }, { label: "AI/AO/FREQ", - value: "future capability", - detail: "当前未声明模拟量/频率闭环,按 future capability 处理。", + value: "后续能力", + detail: "当前未声明模拟量或频率闭环,按后续能力处理。", sourceKind: "SOURCE", tone: "disabled" } @@ -992,8 +992,8 @@ function renderHardwareStatus(summary) { label: "hwlab-patch-panel", value: statusLabel(patchPanel.state), detail: m3Live - ? `DEV-LIVE patch-panel live report ${liveM3Evidence.patchPanelLiveReport.reportId ?? liveM3Evidence.patchPanelLiveReport.patchPanelStatusId} 已绑定 M3 route。` - : `${patchPanel.activeConnectionCount} 条 SOURCE fixture 接线;工作台只展示 required M3 route,未升级为 DEV-LIVE。`, + ? `接线盘实况记录 ${liveM3Evidence.patchPanelLiveReport.reportId ?? liveM3Evidence.patchPanelLiveReport.patchPanelStatusId} 已绑定目标链路。` + : `${patchPanel.activeConnectionCount} 条内置接线记录;工作台只展示目标链路,未升级为实况验收。`, sourceKind: m3Live ? "DEV-LIVE" : "SOURCE", tone: patchTone }, @@ -1011,10 +1011,10 @@ function renderHardwareStatus(summary) { rows: [ { label: "硬件控制", - value: state.m3Control.contract?.status === "available" ? "cloud-api 受控" : "阻塞", + value: state.m3Control.contract?.status === "available" ? "受控路径" : "等待探测", detail: state.m3Control.contract?.status === "available" - ? "M3 控制只调用 same-origin /v1/m3/io,由 cloud-api 转发 gateway-simu/box-simu 并触发 patch-panel;Web 不直连 gateway/box-simu。Web 不提供硬件写 RPC;不会新增伪硬件写操作。" - : "M3 控制路径未就绪;Web 不提供直连 gateway/box-simu 或伪硬件状态写入。", + ? "控制只通过受控后端执行,并经由 gateway-simu、box-simu 与 patch-panel;页面不直连模拟器,也不伪造硬件状态。" + : "控制路径未就绪;页面不直连模拟器,也不伪造硬件状态。", sourceKind: state.m3Control.contract?.status === "available" ? "SOURCE" : "BLOCKED", tone: state.m3Control.contract?.status === "available" ? "source" : "blocked" } @@ -1040,7 +1040,7 @@ function hardwareRow(item) { row.dataset.sourceKind = item.sourceKind; const head = document.createElement("div"); head.className = "hardware-row-head"; - head.append(textSpan(item.label, "hardware-row-label"), badge(item.sourceKind, item.sourceKind)); + head.append(textSpan(item.label, "hardware-row-label"), badge(item.badgeLabel ?? hardwareEvidenceLabel(item.sourceKind), item.sourceKind)); row.append(head); row.append(textSpan(item.value, `hardware-row-value tone-${toneClass(item.tone)}`)); row.append(textSpan(item.detail, "hardware-row-detail")); @@ -1060,18 +1060,30 @@ function trustedM3Link(connections) { function m3LinkDetail({ liveM3Evidence, sourceLink, patchPanel, counts }) { if (hasTrustedM3LiveEvidence(liveM3Evidence)) { - return `DEV-LIVE M3:${M3_TRUSTED_ROUTE.fromResourceId}:${M3_TRUSTED_ROUTE.fromPort} -> ${M3_TRUSTED_ROUTE.patchPanelServiceId} -> ${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort};${m3LiveDetail(liveM3Evidence)}。`; + return `实况链路:${M3_TRUSTED_ROUTE.fromResourceId}:${M3_TRUSTED_ROUTE.fromPort} -> ${M3_TRUSTED_ROUTE.patchPanelServiceId} -> ${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort};${m3LiveDetail(liveM3Evidence)}。`; } if (sourceLink) { - return `BLOCKED:SOURCE fixture 包含目标接线 ${sourceLink.fromResourceId}:${sourceLink.fromPort} -> ${patchPanel.serviceId} -> ${sourceLink.toResourceId}:${sourceLink.toPort},但缺少 patch-panel live report 与 operation/trace/audit/evidence IDs。`; + return `来源拓扑包含目标接线 ${sourceLink.fromResourceId}:${sourceLink.fromPort} -> ${patchPanel.serviceId} -> ${sourceLink.toResourceId}:${sourceLink.toPort},仍在等待完整可信记录。`; } if (counts) { - return "BLOCKED:runtime counts 已返回,但 counts 不是 patch-panel live report,不能证明 M3 DEV-LIVE。"; + return "只读运行计数已返回,但还不能证明接线盘闭环。"; } if (patchPanel.activeConnectionCount > 0) { - return `SOURCE 仅看到非 M3 patch-panel fixture 接线;required M3 route ${M3_TRUSTED_ROUTE.fromResourceId}:${M3_TRUSTED_ROUTE.fromPort} -> ${M3_TRUSTED_ROUTE.patchPanelServiceId} -> ${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort} 仍 BLOCKED,未冒充 live。`; + return `当前只看到非目标接线;目标链路 ${M3_TRUSTED_ROUTE.fromResourceId}:${M3_TRUSTED_ROUTE.fromPort} -> ${M3_TRUSTED_ROUTE.patchPanelServiceId} -> ${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort} 仍在等待可信记录。`; } - return "BLOCKED:当前没有 DO1 -> patch-panel -> DI1 的可信链路证据。"; + return "当前还没有 DO1 -> patch-panel -> DI1 的可信链路记录。"; +} + +function hardwareEvidenceLabel(sourceKind) { + const normalized = String(sourceKind ?? "").toUpperCase(); + return ( + { + SOURCE: "来源拓扑", + BLOCKED: "待可信记录", + "DEV-LIVE": "实况已验证", + "DRY-RUN": "演练记录" + }[normalized] ?? statusLabel(sourceKind) + ); } function linkStatusLabel(status) { @@ -1202,13 +1214,13 @@ function renderM3ControlStatus() { ? "执行中" : operationSucceeded ? operation.evidenceState?.status === "green" - ? "控制可达 / 证据 green" - : "控制可达 / 证据 blocked" + ? "控制可达 / 记录完整" + : "控制可达 / 等待记录" : operationBlocked - ? "操作 BLOCKED" + ? "操作待处理" : contractAvailable - ? "cloud-api 受控" - : "BLOCKED"; + ? "受控路径可用" + : "等待探测"; const tone = pending ? "dry-run" : operationSucceeded @@ -1244,16 +1256,16 @@ function controlRows() { : []; return [ { - title: "cloud-api 控制路径", + title: "控制路径", detail: state.m3Control.contract?.status === "available" - ? "按钮只调用 /v1/m3/io;cloud-api 再通过 gateway-simu -> box-simu -> hwlab-patch-panel 执行 M3 DO/DI。" - : state.m3Control.contract?.blockedReason ?? "等待 /v1/m3/io 合同探测;不可用时不会直连 gateway/box-simu。", + ? "按钮只走受控后端,再通过 gateway-simu -> box-simu -> hwlab-patch-panel 执行 M3 DO/DI。" + : "等待受控路径探测;不可用时不会直连 gateway/box-simu。", tone: state.m3Control.contract?.status === "available" ? "source" : "blocked" }, ...operationCards, { title: "读取目标", - detail: `${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort} 只能通过 /v1/m3/io 的 di.read 读取;不能从浏览器直连 box-simu。`, + detail: `${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort} 只能通过受控路径读取;浏览器不直连 box-simu。`, tone: "source" }, { @@ -1263,7 +1275,7 @@ function controlRows() { }, { title: "只读探测", - detail: "技术复核探测仍保持 same-origin read-only,并放在二级页面。", + detail: "内部复核探测保持只读,并放在二级页面。", tone: "source" } ]; @@ -1328,9 +1340,9 @@ function renderWiringList(summary = null) { tr.append(cell(M3_TRUSTED_ROUTE.toResourceId, "mono wrap")); tr.append(cell(M3_TRUSTED_ROUTE.toPort, "mono")); const status = document.createElement("td"); - status.append(badge(m3Live ? "开发实况 DEV-LIVE" : "阻塞 BLOCKED", m3Live ? "dev-live" : "blocked")); + status.append(badge(m3Live ? "实况已验证" : "待可信记录", m3Live ? "dev-live" : "blocked")); tr.append(status); - tr.append(cell(m3Live ? "DEV-LIVE" : "来源拓扑", "wrap")); + tr.append(cell(m3Live ? "实况可信记录" : "来源拓扑", "wrap")); tr.append(cell(traceEvidence, "mono wrap")); replaceChildren(el.wiringBody, tr); @@ -2010,8 +2022,8 @@ function codeAgentStatusMessage(availability) { if (String(availability?.sourceKind ?? availability?.evidenceLevel ?? "").toUpperCase() === "SOURCE") { return { role: "system", - title: "Code Agent 状态:SOURCE fixture", - text: "当前只用于本地浏览器 smoke:可以验证发送、处理中、回复渲染和 evidence 展示,但不会标记为 DEV-LIVE。", + title: "Code Agent 状态:本地验证记录", + text: "当前只用于本地浏览器验证:可以检查发送、处理中、回复渲染和证据展示,但不会标记为实况完成。", status: "source" }; } @@ -2019,38 +2031,38 @@ function codeAgentStatusMessage(availability) { return { role: "system", title: "Code Agent 状态:真实通道已接入", - text: "真实 /v1/agent/chat 后端已接入;发送后只有实际 completed 回复才会标记为 DEV-LIVE。", + text: "受控对话通道已接入;发送后只有真实完成的回复才会标记为已完成。", status: "source" }; } return { role: "system", title: "Code Agent 状态:等待只读探测", - text: "正在读取 same-origin /v1 与 /health/live;首屏不会宣称 Code Agent 可用,也不会把失败或静态内容标成 DEV-LIVE。", + text: "正在确认服务状态;首屏不会宣称 Code Agent 可用,也不会把失败或静态内容当成真实回复。", status: "source" }; } function codeAgentPromptText(availability) { if (availability?.status === "blocked") { - return "可以继续输入并保留草稿;发送会走真实 /v1/agent/chat。服务恢复前会显示结构化失败,不会冒充真实 Codex 回复。"; + return "可以继续输入并保留草稿;发送会走受控对话通道。服务恢复前会显示结构化失败,不会冒充真实 Codex 回复。"; } - return "请在底部输入中文消息并点击发送;完成态只来自真实 /v1/agent/chat completed 回复。"; + return "请在底部输入中文消息并点击发送;完成态只来自真实完成的后端回复。"; } function codeAgentControlSummary(availability) { if (latestCompletedAgentMessage()) { return state.conversationId - ? `当前会话 ${state.conversationId};最近一次真实 completed 回复来自 cloud-api 受控 Code Agent 接口,并带有 provider/model/trace/conversation 证据。` - : "最近一次真实 completed 回复来自 cloud-api 受控 Code Agent 接口,并带有 provider/model/trace/conversation 证据。"; + ? `当前会话 ${state.conversationId};最近一次真实完成回复来自受控 Code Agent 接口,并带有可核验记录。` + : "最近一次真实完成回复来自受控 Code Agent 接口,并带有可核验记录。"; } if (isSourceFixtureCompletedChatMessage(latestChatResult())) { - return "最近一次为 SOURCE fixture 回复;它证明本地浏览器对话 UX,不作为 DEV-LIVE 或 #143 完成证据。"; + return "最近一次为本地验证回复;它只证明浏览器对话体验,不作为实况完成证据。"; } if (availability?.status === "blocked") { return codeAgentBlockedSummary(availability); } - return "输入区会调用 cloud-api 受控 Code Agent 接口;只有真实 completed 回复才标 DEV-LIVE,不能因为只有 conversationId 标为开发实况。"; + return "输入区会调用受控 Code Agent 接口;只有真实完成回复才显示为完成,不能因为只有会话编号就当成实况完成。"; } function codeAgentBlockedSummary(availability) { @@ -2071,10 +2083,10 @@ function codeAgentAvailabilitySummary() { return "同源 API 可响应;Code Agent 服务暂不可用,工作台保持只读和可重试。"; } if (String(state.codeAgentAvailability?.sourceKind ?? state.codeAgentAvailability?.evidenceLevel ?? "").toUpperCase() === "SOURCE") { - return "同源 API 可响应;当前 Code Agent 回复来源是 SOURCE fixture,仅用于本地浏览器 smoke,不是 DEV-LIVE。"; + return "同源 API 可响应;当前 Code Agent 回复来源是本地验证记录,仅用于浏览器验证,不是实况完成。"; } if (state.codeAgentAvailability?.status === "available") { - return "同源 API 可响应;Code Agent 真实通道已接入,只有 completed 回复才标 DEV-LIVE。"; + return "同源 API 可响应;Code Agent 真实通道已接入,只有真实完成回复才显示为完成。"; } return "同源只读接口可响应。技术复核详情已放在二级页面。"; } diff --git a/web/hwlab-cloud-web/index.html b/web/hwlab-cloud-web/index.html index 980fce23..6dbcc909 100644 --- a/web/hwlab-cloud-web/index.html +++ b/web/hwlab-cloud-web/index.html @@ -211,35 +211,35 @@

运行界面

硬件状态:BOX-SIMU / Gateway-SIMU / hwlab-patch-panel

- SOURCE + 只读核对
- SOURCE 兜底状态 + 只读资源概览
- Gateway/Box SOURCE fixture - SOURCE + Gateway/Box 资源 + 来源拓扑
离线查看 - 等待 app.mjs 或同源只读 counts 覆盖;当前仅展示 SOURCE 拓扑摘要。 + 等待同源只读数据覆盖;当前展示内置拓扑摘要。
hwlab-patch-panel - SOURCE + 来源拓扑
- 来源拓扑 + 目标接线 res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1 仅为源码兜底显示。
DO1 -> patch-panel -> DI1 - BLOCKED + 待可信记录
- m3-route-required / waiting-for-dev-live - 缺少 patch-panel live report 与 operation/trace/audit/evidence IDs,不能标为 DEV-LIVE。 + 等待可信记录 + 尚未看到完整可信记录,不能作为实况闭环证据。
@@ -297,7 +297,7 @@