diff --git a/apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx b/apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx
index 40c0a8606..843ef706e 100644
--- a/apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx
+++ b/apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx
@@ -3,14 +3,13 @@ import {
type ErrorInfo,
type FormEvent,
type ReactNode,
- useCallback,
useEffect,
useState,
} from 'react';
import type { AuthUser } from '../../../../packages/shared/src/contracts/auth';
import brandIcon from '../../../../packages/shared/src/icons/taonier-product-ip.png';
-import { ErrorReportDialog } from '../components/error-report/ErrorReportDialog';
+import { ErrorReportCenter } from '../components/error-report/ErrorReportDialog';
import {
clearStoredAuthAccessToken,
getClientAuthErrorMessage,
@@ -34,7 +33,7 @@ import {
import {
captureClientError,
installWebviewLogBridge,
- OPEN_ERROR_REPORT_EVENT,
+ shouldCaptureClientError,
} from '../services/errorReporting';
import {
beginPlatformSessionClearTransition,
@@ -50,7 +49,6 @@ import {
type ClientRuntimeErrorBoundaryProps = {
children: ReactNode;
onLogout: () => void;
- onReport?: () => void;
};
type ClientRuntimeErrorBoundaryState = {
@@ -95,11 +93,6 @@ export class ClientRuntimeErrorBoundary extends Component<
- {this.props.onReport ? (
-
- ) : null}
);
@@ -123,7 +116,6 @@ export function AuthenticatedClient({
const [loginBusy, setLoginBusy] = useState(false);
const [codeBusy, setCodeBusy] = useState(false);
const [codeCooldownSeconds, setCodeCooldownSeconds] = useState(0);
- const [errorReportOpen, setErrorReportOpen] = useState(false);
const initialServerSelection = getClientServerSelection();
const [serverSelection, setServerSelection] = useState(
initialServerSelection,
@@ -131,13 +123,6 @@ export function AuthenticatedClient({
const [customServerUrl, setCustomServerUrl] = useState(
initialServerSelection.customBaseUrl,
);
- const openManualErrorReport = useCallback(async () => {
- await captureClientError(new Error('用户主动报告问题'), {
- source: 'manual',
- });
- setErrorReportOpen(true);
- }, []);
-
useEffect(() => {
const uninstallWebviewLogBridge = installWebviewLogBridge();
const handleError = (event: ErrorEvent) => {
@@ -157,15 +142,6 @@ export function AuthenticatedClient({
};
}, []);
- useEffect(() => {
- window.addEventListener(OPEN_ERROR_REPORT_EVENT, openManualErrorReport);
- return () =>
- window.removeEventListener(
- OPEN_ERROR_REPORT_EVENT,
- openManualErrorReport,
- );
- }, [openManualErrorReport]);
-
function persistServerSelection() {
try {
const next = setClientServerSelection({
@@ -357,10 +333,11 @@ export function AuthenticatedClient({
setCodeCooldownSeconds(Math.max(0, Math.floor(response.cooldownSeconds)));
setLoginStatus(`验证码已发送,${response.expiresInSeconds} 秒内有效`);
} catch (error) {
- void captureClientError(error, {
- source: 'auth',
- action: 'send-login-code',
- });
+ if (shouldCaptureClientError(error))
+ void captureClientError(error, {
+ source: 'auth',
+ action: 'send-login-code',
+ });
setLoginStatus(error instanceof Error ? error.message : String(error));
} finally {
setCodeBusy(false);
@@ -419,7 +396,8 @@ export function AuthenticatedClient({
setCode('');
setPassword('');
} catch (error) {
- void captureClientError(error, { source: 'auth', action: 'login' });
+ if (shouldCaptureClientError(error))
+ void captureClientError(error, { source: 'auth', action: 'login' });
setLoginStatus(error instanceof Error ? error.message : String(error));
} finally {
setLoginBusy(false);
@@ -589,16 +567,10 @@ export function AuthenticatedClient({
return (
<>
- void openManualErrorReport()}
- >
+
{children({ user: authUser, logout })}
- setErrorReportOpen(false)}
- />
+
>
);
}
diff --git a/apps/ai-game-creator-shell/src/features/project-summary/projectDeliverySummaries.ts b/apps/ai-game-creator-shell/src/features/project-summary/projectDeliverySummaries.ts
index 61ad15279..0e3d4091c 100644
--- a/apps/ai-game-creator-shell/src/features/project-summary/projectDeliverySummaries.ts
+++ b/apps/ai-game-creator-shell/src/features/project-summary/projectDeliverySummaries.ts
@@ -159,7 +159,7 @@ export function summarizeProjectKnownIssues(
};预览 ${previewSummary}`,
`- 已知问题:${knownIssueSummary}`,
'- 试玩限制:本地 Web 原型;小范围 5-10 分钟试玩;不承诺账号、云存档、排行榜、付费或长期兼容',
- '- 反馈入口:单个问题走 /bug-report;整体体验走 /feedback;版本变化走 /release-notes',
+ '- 反馈入口:客户端问题由自动诊断提示;整体体验走 /feedback;版本变化走 /release-notes',
'- 发送前检查:可试玩状态先 /run;交付口径看 /share;对外资料看 /media-kit',
'- 边界:只准备已知问题清单;不读取文件;不启动或打开预览;不导出试玩包;不上传云端;不发布作品;不写项目',
`- 建议:${draftCommand}`,
diff --git a/apps/ai-game-creator-shell/src/features/project-summary/projectGuidanceSummaries.ts b/apps/ai-game-creator-shell/src/features/project-summary/projectGuidanceSummaries.ts
index ca30a9ab9..2bf3d12f9 100644
--- a/apps/ai-game-creator-shell/src/features/project-summary/projectGuidanceSummaries.ts
+++ b/apps/ai-game-creator-shell/src/features/project-summary/projectGuidanceSummaries.ts
@@ -75,7 +75,6 @@ export function summarizeNextProjectActions(
addSuggestion('查看隐私与导出边界', '/privacy');
addSuggestion('查看首批试玩对象', '/audience');
addSuggestion('准备试玩邀请文案', '/invite');
- addSuggestion('报告问题', '/bug-report');
addSuggestion('准备试玩问卷问题', '/survey');
addSuggestion('准备封面与缩略图检查', '/cover');
addSuggestion('准备宣传截图清单', '/screenshots');
diff --git a/apps/ai-game-creator-shell/src/features/project-summary/projectPlaytestSummaries.ts b/apps/ai-game-creator-shell/src/features/project-summary/projectPlaytestSummaries.ts
index 18442f17c..53c928a77 100644
--- a/apps/ai-game-creator-shell/src/features/project-summary/projectPlaytestSummaries.ts
+++ b/apps/ai-game-creator-shell/src/features/project-summary/projectPlaytestSummaries.ts
@@ -290,8 +290,8 @@ export function summarizeProjectPlaytestSurvey(
'- 使用场景:发给首批 3-5 位测试者;每人 5-10 分钟;先自由玩一局再回答',
'- 问题清单:1. 30 秒内你觉得目标是什么?2. 第一次操作哪里最卡?3. 胜负/重开是否清楚?4. 难度/节奏感觉如何?5. 最想保留和最想改的各一项?',
'- 记录格式:每题 1-5 分 + 一句话;补充设备、输入方式、是否愿意再玩一局',
- '- 追踪方式:单个问题走 /bug-report;整体反馈走 /feedback;下一轮改动走 /revise',
- '- 参考:/invite;/audience;/feedback;/bug-report',
+ '- 追踪方式:客户端问题由自动诊断提示;整体反馈走 /feedback;下一轮改动走 /revise',
+ '- 参考:/invite;/audience;/feedback',
'- 边界:只准备试玩问卷;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
`- 建议:${draftCommand}`,
].join('\n'),
@@ -557,7 +557,7 @@ export function summarizeProjectPlaytestFaq(
};预览 ${previewSummary}`,
'- 问答清单:1. 这是什么?2. 怎么开始和重开?3. 需要反馈什么?4. 打不开或卡住怎么办?5. 能不能转发或公开?',
'- 回答口径:早期本地 Web 原型;5-10 分钟试玩;重点反馈目标理解、操作手感、难度、bug 和还想不想再玩',
- '- 测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;单个问题走 /bug-report',
+ '- 测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;客户端问题由自动诊断提示',
'- 交付搭配:/invite;/share;/screenshots;/trailer',
'- 边界:只准备试玩常见问答;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
`- 建议:${draftCommand}`,
@@ -624,7 +624,7 @@ export function summarizeProjectCommunityPost(
`- 短文案:我做了一个早期 Web 小游戏原型《${nextManifest.name}》,核心目标是${goal}。想找 3-5 位朋友试玩 5 分钟,重点看能不能理解目标、操作顺不顺、还想不想再来一局。`,
'- 长文案结构:一句玩法目标;一张截图或短视频;试玩方式;希望收到的三类反馈;已知限制',
'- 标签建议:#Web小游戏 #原型试玩 #AI游戏创作 #本地试玩',
- '- CTA:愿意试玩请回复;遇到问题按 /faq 或 /bug-report 的口径反馈',
+ '- CTA:愿意试玩请回复;遇到问题按 /faq 的口径反馈,客户端问题会自动提示',
'- 参考:/faq;/screenshots;/trailer;/store;/share',
'- 边界:只准备社区发布文案;不上传云端;不发布作品;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
`- 建议:${draftCommand}`,
diff --git a/apps/ai-game-creator-shell/src/features/project-summary/projectQualitySummaries.ts b/apps/ai-game-creator-shell/src/features/project-summary/projectQualitySummaries.ts
index 0057fbb32..d95a6428f 100644
--- a/apps/ai-game-creator-shell/src/features/project-summary/projectQualitySummaries.ts
+++ b/apps/ai-game-creator-shell/src/features/project-summary/projectQualitySummaries.ts
@@ -290,7 +290,7 @@ export function summarizeProjectCompatibilityNotes(
`- 输入兼容:${inputSummary}`,
'- 推荐环境:桌面 Chrome / Edge 最新版;本机 127.0.0.1 预览;移动浏览器只做早期体验',
'- 不承诺:旧浏览器、低端设备、离线模式、云存档、账号同步、手柄或多端数据一致',
- '- 反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;问题记录走 /bug-report',
+ '- 反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;客户端检测到问题时会自动提示',
'- 参考:/mobile;/accessibility;/performance;/known-issues',
'- 边界:只准备兼容性说明;不读取文件;不启动或打开预览;不导出试玩包;不上传云端;不发布作品;不写项目',
`- 建议:${draftCommand}`,
diff --git a/apps/ai-game-creator-shell/src/features/project-summary/projectSummaryConstants.ts b/apps/ai-game-creator-shell/src/features/project-summary/projectSummaryConstants.ts
index 1e9418aa5..8329268ae 100644
--- a/apps/ai-game-creator-shell/src/features/project-summary/projectSummaryConstants.ts
+++ b/apps/ai-game-creator-shell/src/features/project-summary/projectSummaryConstants.ts
@@ -123,7 +123,6 @@ export const chatCommandHelp = [
'/privacy:查看隐私与导出边界',
'/audience:查看首批试玩对象',
'/invite:准备试玩邀请文案',
- '/bug-report:报告问题',
'/survey:准备试玩问卷问题',
'/cover:准备封面与缩略图检查',
'/screenshots:准备宣传截图清单',
diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/projectSummaryCommands.ts b/apps/ai-game-creator-shell/src/features/project-workspace/projectSummaryCommands.ts
index 25ada417a..065975c6a 100644
--- a/apps/ai-game-creator-shell/src/features/project-workspace/projectSummaryCommands.ts
+++ b/apps/ai-game-creator-shell/src/features/project-workspace/projectSummaryCommands.ts
@@ -9,7 +9,6 @@ import type {
AgentStatusCard,
ChatMessage,
} from '../../app/types';
-import { OPEN_ERROR_REPORT_EVENT } from '../../services/errorReporting';
import {
summarizeAgentReviewState,
summarizeAgentRunBudget,
@@ -506,20 +505,6 @@ export function handleProjectSummaryChatCommand({
return true;
}
- if (prompt === '/bug-report') {
- setMessages((current) => [
- ...current,
- {
- role: 'assistant',
- text: '已打开“报告问题”面板,请填写遇到的问题。',
- },
- ]);
- if (typeof window !== 'undefined') {
- window.dispatchEvent(new CustomEvent(OPEN_ERROR_REPORT_EVENT));
- }
- return true;
- }
-
if (prompt === '/survey') {
if (!requireChatProjectForUserAction()) {
return true;
diff --git a/apps/ai-game-creator-shell/tests/appSurface/developer-agent-window.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/developer-agent-window.suite.ts
index 7a9d2cad7..edb340240 100644
--- a/apps/ai-game-creator-shell/tests/appSurface/developer-agent-window.suite.ts
+++ b/apps/ai-game-creator-shell/tests/appSurface/developer-agent-window.suite.ts
@@ -5072,7 +5072,6 @@ export function registerDeveloperToolsTests() {
expect(screen.getByText(/\/privacy:查看隐私与导出边界/)).not.toBeNull();
expect(screen.getByText(/\/audience:查看首批试玩对象/)).not.toBeNull();
expect(screen.getByText(/\/invite:准备试玩邀请文案/)).not.toBeNull();
- expect(screen.getByText(/\/bug-report:报告问题/)).not.toBeNull();
expect(screen.getByText(/\/survey:准备试玩问卷问题/)).not.toBeNull();
expect(screen.getByText(/\/cover:准备封面与缩略图检查/)).not.toBeNull();
expect(screen.getByText(/\/screenshots:准备宣传截图清单/)).not.toBeNull();
diff --git a/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-playtest-and-release-shortcuts.ts b/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-playtest-and-release-shortcuts.ts
index 76ea3bb15..cbcc6989f 100644
--- a/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-playtest-and-release-shortcuts.ts
+++ b/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-playtest-and-release-shortcuts.ts
@@ -1,4 +1,3 @@
-import { OPEN_ERROR_REPORT_EVENT } from '../../../../src/services/errorReporting';
import { expect, fireEvent, screen, submitChat, within } from '../../harness';
import type { PreviewShortcutInvoke } from './invoke-mock';
@@ -47,7 +46,6 @@ export async function assertPlaytestAndReleaseShortcutFlow(
expect(screen.getByText(/查看隐私与导出边界:\/privacy/)).not.toBeNull();
expect(screen.getByText(/查看首批试玩对象:\/audience/)).not.toBeNull();
expect(screen.getByText(/准备试玩邀请文案:\/invite/)).not.toBeNull();
- expect(screen.getByText(/报告问题:\/bug-report/)).not.toBeNull();
expect(screen.getByText(/准备试玩问卷问题:\/survey/)).not.toBeNull();
expect(screen.getByText(/准备封面与缩略图检查:\/cover/)).not.toBeNull();
expect(screen.getByText(/准备宣传截图清单:\/screenshots/)).not.toBeNull();
@@ -648,20 +646,6 @@ export async function assertPlaytestAndReleaseShortcutFlow(
([command]) => command === 'control_agent_run',
).length,
};
- let openedErrorReport = false;
- const onErrorReportOpen = () => {
- openedErrorReport = true;
- };
- window.addEventListener(OPEN_ERROR_REPORT_EVENT, onErrorReportOpen);
- try {
- submitChat('/bug-report');
- expect(
- await screen.findByText('已打开“报告问题”面板,请填写遇到的问题。'),
- ).not.toBeNull();
- expect(openedErrorReport).toBe(true);
- } finally {
- window.removeEventListener(OPEN_ERROR_REPORT_EVENT, onErrorReportOpen);
- }
expect(
invoke.mock.calls.filter(
([command]) => command === 'get_local_game_manifest',
@@ -753,10 +737,10 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'记录格式:每题 1-5 分 + 一句话;补充设备、输入方式、是否愿意再玩一局',
);
expect(surveyMessage.textContent).toContain(
- '追踪方式:单个问题走 /bug-report;整体反馈走 /feedback;下一轮改动走 /revise',
+ '追踪方式:客户端问题由自动诊断提示;整体反馈走 /feedback;下一轮改动走 /revise',
);
expect(surveyMessage.textContent).toContain(
- '参考:/invite;/audience;/feedback;/bug-report',
+ '参考:/invite;/audience;/feedback',
);
expect(surveyMessage.textContent).toContain(
'边界:只准备试玩问卷;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
@@ -1185,7 +1169,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'回答口径:早期本地 Web 原型;5-10 分钟试玩;重点反馈目标理解、操作手感、难度、bug 和还想不想再玩',
);
expect(faqMessage.textContent).toContain(
- '测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;单个问题走 /bug-report',
+ '测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;客户端问题由自动诊断提示',
);
expect(faqMessage.textContent).toContain(
'交付搭配:/invite;/share;/screenshots;/trailer',
@@ -1296,7 +1280,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'标签建议:#Web小游戏 #原型试玩 #AI游戏创作 #本地试玩',
);
expect(postMessage.textContent).toContain(
- 'CTA:愿意试玩请回复;遇到问题按 /faq 或 /bug-report 的口径反馈',
+ 'CTA:愿意试玩请回复;遇到问题按 /faq 的口径反馈,客户端问题会自动提示',
);
expect(postMessage.textContent).toContain(
'参考:/faq;/screenshots;/trailer;/store;/share',
@@ -1735,7 +1719,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'试玩限制:本地 Web 原型;小范围 5-10 分钟试玩;不承诺账号、云存档、排行榜、付费或长期兼容',
);
expect(knownIssueMessage.textContent).toContain(
- '反馈入口:单个问题走 /bug-report;整体体验走 /feedback;版本变化走 /release-notes',
+ '反馈入口:客户端问题由自动诊断提示;整体体验走 /feedback;版本变化走 /release-notes',
);
expect(knownIssueMessage.textContent).toContain(
'发送前检查:可试玩状态先 /run;交付口径看 /share;对外资料看 /media-kit',
diff --git a/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-project-and-design-shortcuts.ts b/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-project-and-design-shortcuts.ts
index 97418c335..1aba097b3 100644
--- a/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-project-and-design-shortcuts.ts
+++ b/apps/ai-game-creator-shell/tests/appSurface/project-preview/preview-shortcuts/assert-project-and-design-shortcuts.ts
@@ -916,7 +916,7 @@ export async function assertProjectAndDesignShortcutFlow(
'不承诺:旧浏览器、低端设备、离线模式、云存档、账号同步、手柄或多端数据一致',
);
expect(compatibilityMessage.textContent).toContain(
- '反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;问题记录走 /bug-report',
+ '反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;客户端检测到问题时会自动提示',
);
expect(compatibilityMessage.textContent).toContain(
'参考:/mobile;/accessibility;/performance;/known-issues',
diff --git a/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md b/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md
index 4c6f8e259..bd85e77cb 100644
--- a/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md
+++ b/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md
@@ -11,7 +11,8 @@ AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事
- 指纹计算可使用调用方的 page/action 及脱敏后的首个调用点作为进程内区分输入,但这些上下文不会作为事件字段上传;消息与 stack 在入池前统一脱敏,WebCrypto 失败时降级为稳定可读指纹,采集本身不得产生新的未处理拒绝。
- 客户端 API 自动采集只覆盖网络错误、408 和 5xx;预期的 4xx 登录/鉴权失败不进入错误报告池。
- Rust 侧通过 `app_log!` 将普通文本日志同时输出到 stderr 和 AppData `diagnostics/application.log`,超出 256 KiB 滚动到 `application.previous.log`;WebView 的 console 输出通过 `append_application_log` 镜像到同一 raw log,并在客户端桥接处再次脱敏;`read_diagnostic_logs` 只读取应用级日志。
-- 报告面板允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。
+- 报告面板只由自动诊断通知中的“查看并报告”打开,不提供聊天命令、崩溃页按钮或其他手动入口;默认选中最新事件,其他事件可勾选。允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。
+- 错误事件先在当前进程内存池按 fingerprint 合并,经过 5 秒聚合后只显示一条非阻塞通知;通知支持“查看并报告”和“忽略”,同一 fingerprint 在本次运行中只提醒一次。通知不直接打开阻塞式报告面板。
- 上传失败只在当前进程显示失败并允许用户再次提交,不跨重启恢复事件池,不后台自动重试。
## HTTP 与存储