feat: show code agent message attribution metadata

This commit is contained in:
Code Queue Review
2026-05-23 16:20:09 +00:00
parent 069a54920e
commit cac46e45f7
6 changed files with 453 additions and 29 deletions
@@ -271,6 +271,10 @@ const workbenchMarkers = Object.freeze([
]);
const requiredCodeAgentEvidenceTerms = Object.freeze([
"消息归因",
"字段缺失:证据不足",
"只是文本回答",
"当前不能执行",
"provider",
"model",
"backend",
@@ -291,11 +295,14 @@ const requiredCodeAgentEvidenceTerms = Object.freeze([
"message",
"providerTrace",
"openai-responses",
"openai-responses-fallback",
"text-chat-only / 只是文本回答",
"codex-stdio",
"codex-cli",
"codex-mcp-stdio-long-lived",
"repo-owned-codex-mcp-stdio-session",
"controlled-readonly-session-registry",
"read-only-session-tools:只读工具",
"not-codex-stdio",
"not-write-capable",
"not-durable-session",
@@ -304,6 +311,7 @@ const requiredCodeAgentEvidenceTerms = Object.freeze([
"SOURCE fixture",
"Code Agent SOURCE 回复",
"message-evidence-chip",
"message-attribution-chip",
"hwlab-skill-cli",
"hwlab-m3-io-skill-cli",
"message-m3-evidence"
@@ -2411,6 +2419,9 @@ function hasCodeAgentCompletedEvidenceVisibility({ app, codeAgentM3Evidence }) {
/conversationId:\s*result\.conversationId \|\| result\.sessionId \|\| state\.conversationId/u.test(app) &&
/providerTrace:\s*result\.providerTrace/u.test(app) &&
/function\s+providerTraceSummary\s*\(/u.test(app) &&
/function\s+messageAttributionPanel\s*\(/u.test(app) &&
/codeAgentAttributionFromMessage/u.test(app) &&
/attribution\.fields\.map\(\(field\) => field\.chip\)/u.test(messageEvidenceBody) &&
/recordField\("provider",\s*message\.provider\)/u.test(messageEvidenceBody) &&
/recordField\("model",\s*message\.model\)/u.test(messageEvidenceBody) &&
/recordField\("backend",\s*message\.backend\)/u.test(messageEvidenceBody) &&
@@ -2428,6 +2439,9 @@ function hasCodeAgentCompletedEvidenceVisibility({ app, codeAgentM3Evidence }) {
/recordField\("toolCalls",\s*toolCallsSummary\(message\.toolCalls\)\)/u.test(messageEvidenceBody) &&
/recordField\("skills",\s*skillsSummary\(message\.skills\)\)/u.test(messageEvidenceBody) &&
/recordField\("runnerTrace",\s*runnerTraceSummary\(message\.runnerTrace\)\)/u.test(messageEvidenceBody) &&
/recordField\("operationId",\s*operationIdSummary\(message\)\)/u.test(messageEvidenceBody) &&
/recordField\("audit",\s*auditSummary\(message\)\)/u.test(messageEvidenceBody) &&
/recordField\("evidence",\s*evidenceSummary\(message\)\)/u.test(messageEvidenceBody) &&
/recordField\("conversation",\s*message\.conversationId\)/u.test(messageEvidenceBody) &&
/recordField\("trace",\s*message\.traceId\)/u.test(messageEvidenceBody) &&
/recordField\("message",\s*message\.messageId\)/u.test(messageEvidenceBody) &&
@@ -2517,8 +2531,12 @@ function hasCodeAgentConversationUxStates({ app, styles }) {
/function\s+isSourceFixtureCompletion\s*\(/u.test(app) &&
/function\s+isSourceFixtureCompletedChatMessage\s*\(/u.test(app) &&
/function\s+messageEvidencePanel\s*\(/u.test(app) &&
/function\s+messageAttributionPanel\s*\(/u.test(app) &&
/function\s+messageEvidenceSummary\s*\(/u.test(app) &&
/function\s+boundedEvidenceField\s*\(/u.test(app) &&
/codeAgentAttributionFromMessage/u.test(app) &&
/字段缺失:证据不足/u.test(app) &&
/当前不能执行/u.test(app) &&
/DEFAULT_API_TIMEOUT_MS\s*=\s*4500/u.test(app) &&
/DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*150000/u.test(app) &&
/timeoutMs:\s*CODE_AGENT_TIMEOUT_MS/u.test(app) &&
@@ -2533,6 +2551,8 @@ function hasCodeAgentConversationUxStates({ app, styles }) {
/message-user/u.test(styles) &&
/message-agent/u.test(styles) &&
/message-m3-evidence\s*\{/u.test(styles) &&
/message-attribution\s*\{/u.test(styles) &&
/message-attribution-chip/u.test(styles) &&
/message-m3-rows\s*\{/u.test(styles) &&
/copy-chip\s*\{/u.test(styles) &&
/message-evidence\s*\{/u.test(styles) &&
@@ -3652,6 +3672,20 @@ async function inspectJourneyUi(page) {
sourceMessageHasNonSensitiveMeta: Boolean(
[...document.querySelectorAll(".message-card.status-source .message-meta")]
.some((element) => /SOURCE|source-fixture|trc_/u.test(element.textContent ?? ""))
),
attributionText: [...document.querySelectorAll(".message-card.message-agent .message-attribution")]
.map((element) => element.textContent ?? "")
.join("\n"),
attributionHasTraceId: [...document.querySelectorAll(".message-card.message-agent .message-attribution [data-field='traceId']")]
.some((element) => /trc_/u.test(element.textContent ?? "")),
attributionShowsToolCalls: [...document.querySelectorAll(".message-card.message-agent .message-attribution [data-field='toolCalls']")]
.some((element) => /toolCalls=/u.test(element.textContent ?? "")),
attributionMissingFieldsStructured: [...document.querySelectorAll(".message-card.message-agent .message-attribution-chip.is-missing")]
.some((element) => /字段缺失证据不足/u.test(element.textContent ?? "")),
attributionFallbackNotRunnerControl: !/openai-responses-fallback[\s\S]{0,80}(真实 runner|Codex stdio 长会话|workspace-write)/u.test(
[...document.querySelectorAll(".message-card.message-agent .message-attribution")]
.map((element) => element.textContent ?? "")
.join("\n")
)
};
});
@@ -4022,6 +4056,10 @@ async function runLocalAgentFixtureSmoke({
ui.agentChatStatus === "SOURCE 回复" &&
ui.sourceMessageVisible &&
ui.sourceMessageHasNonSensitiveMeta &&
ui.attributionHasTraceId &&
ui.attributionShowsToolCalls &&
ui.attributionMissingFieldsStructured &&
ui.attributionFallbackNotRunnerControl &&
!ui.completedMessageVisible &&
!ui.failedMessageVisible
);
@@ -5086,6 +5124,25 @@ async function handleLocalAgentFixtureApi({ request, response, url, options = {}
model: "gpt-source-fixture",
backend: "local-source-fixture",
projectId: stringOrFallback(body?.projectId, gateSummary.topology.projectId),
runner: {
kind: "openai-responses-fallback",
codexStdio: false,
writeCapable: false,
durableSession: false
},
capabilityLevel: "text-chat-only",
sessionMode: "provider-text-request",
toolCalls: [],
skills: {
status: "not_requested",
count: 0,
items: []
},
runnerTrace: {
traceId: stringOrFallback(body?.traceId, "trc_source_fixture_browser"),
runnerKind: "openai-responses-fallback",
events: ["fixture:text-chat-only"]
},
reply: {
messageId,
role: "assistant",