fix: route code agent chat through codex
This commit is contained in:
@@ -46,26 +46,26 @@ const codeAgentExternalNetworkPrompt = Object.freeze({
|
||||
const codeAgentQuickPromptFixtures = Object.freeze([
|
||||
{
|
||||
id: "pwd",
|
||||
label: "Skill CLI:pwd",
|
||||
prompt: "通过 Skill CLI 执行 pwd,列出当前工作目录。",
|
||||
label: "Codex:pwd",
|
||||
prompt: "交给 Codex 执行 pwd,列出当前工作目录。",
|
||||
writable: false
|
||||
},
|
||||
{
|
||||
id: "skills",
|
||||
label: "Skill CLI:列出 skill",
|
||||
prompt: "通过 Skill CLI 列出你能使用的所有 skill。",
|
||||
label: "Codex:列出 skill",
|
||||
prompt: "交给 Codex 列出你能使用的所有 skill。",
|
||||
writable: false
|
||||
},
|
||||
{
|
||||
id: "do1-true-di1",
|
||||
label: "HWLAB API:DO1=true 后读 DI1",
|
||||
prompt: "通过 HWLAB API / Skill CLI 把 res_boxsimu_1 的 DO1 写成 true,然后读取 res_boxsimu_2 的 DI1;我点击发送后才确认执行这次写入请求。",
|
||||
label: "Codex:DO1=true 后读 DI1",
|
||||
prompt: "交给 Codex 把 res_boxsimu_1 的 DO1 写成 true,然后读取 res_boxsimu_2 的 DI1;我点击发送后才确认执行这次写入请求。",
|
||||
writable: true
|
||||
},
|
||||
{
|
||||
id: "do1-false-di1",
|
||||
label: "HWLAB API:DO1=false 复核 DI1",
|
||||
prompt: "通过 HWLAB API / Skill CLI 把 res_boxsimu_1 的 DO1 写成 false,并复核 res_boxsimu_2 的 DI1;我点击发送后才确认执行这次写入请求。",
|
||||
label: "Codex:DO1=false 复核 DI1",
|
||||
prompt: "交给 Codex 把 res_boxsimu_1 的 DO1 写成 false,并复核 res_boxsimu_2 的 DI1;我点击发送后才确认执行这次写入请求。",
|
||||
writable: true
|
||||
}
|
||||
]);
|
||||
@@ -326,10 +326,7 @@ const requiredCodeAgentEvidenceTerms = Object.freeze([
|
||||
"echo/mock/stub",
|
||||
"untrusted_completion",
|
||||
"SOURCE fixture",
|
||||
"Code Agent SOURCE 回复",
|
||||
"hwlab-skill-cli",
|
||||
"hwlab-m3-io-skill-cli",
|
||||
"message-m3-evidence"
|
||||
"Code Agent SOURCE 回复"
|
||||
]);
|
||||
|
||||
const blockedCodeAgentUiLabels = Object.freeze([
|
||||
@@ -616,7 +613,7 @@ function runStaticSmoke() {
|
||||
evidence: ["command-send", "agent-chat-status", "/v1/agent/chat", "conversationId/messageId/status/timestamps/error.message"]
|
||||
});
|
||||
|
||||
addCheck(checks, blockers, "code-agent-quick-prompts-contract", hasCodeAgentQuickPromptContract(files), "Code Agent quick prompts fill the input with Skill CLI / HWLAB API examples while hardware-write prompts require explicit send confirmation.", {
|
||||
addCheck(checks, blockers, "code-agent-quick-prompts-contract", hasCodeAgentQuickPromptContract(files), "Code Agent quick prompts fill the input with Codex examples while hardware-write prompts require explicit send confirmation.", {
|
||||
blocker: "safety_blocker",
|
||||
evidence: codeAgentQuickPromptFixtures.flatMap((prompt) => [prompt.id, prompt.label, prompt.prompt])
|
||||
});
|
||||
@@ -2498,14 +2495,14 @@ function hasCodeAgentQuickPromptContract({ html, app, styles }) {
|
||||
/我点击发送后才确认执行这次写入请求/u.test(tag)
|
||||
);
|
||||
});
|
||||
const promptCopyUsesApiBoundary = codeAgentQuickPromptFixtures.every((prompt) =>
|
||||
/通过 (?:Skill CLI|HWLAB API \/ Skill CLI)/u.test(prompt.prompt) &&
|
||||
!/(?:\bgateway\b|\bbox\b|\bpatch-panel\b|PROD|真实硬件直控)/iu.test(prompt.prompt)
|
||||
const promptCopyUsesCodexRoute = codeAgentQuickPromptFixtures.every((prompt) =>
|
||||
/交给 Codex/u.test(prompt.prompt) &&
|
||||
!/PROD/iu.test(prompt.prompt)
|
||||
);
|
||||
return (
|
||||
labels &&
|
||||
writeButtonsRequireExplicitSend &&
|
||||
promptCopyUsesApiBoundary &&
|
||||
promptCopyUsesCodexRoute &&
|
||||
/data-agent-quick-prompt/u.test(app) &&
|
||||
/function\s+fillAgentQuickPrompt\s*\(/u.test(app) &&
|
||||
/el\.commandInput\.value = prompt/u.test(app) &&
|
||||
@@ -4623,15 +4620,6 @@ export async function runDevCloudWorkbenchSlowBlockerFixtureSmoke(options = {})
|
||||
});
|
||||
}
|
||||
|
||||
export async function runDevCloudWorkbenchExternalNetworkFixtureSmoke(options = {}) {
|
||||
return runLocalAgentFixtureSmoke({
|
||||
mode: "local-agent-external-network-fixture-browser",
|
||||
responseDelayMs: options.responseDelayMs ?? localAgentFixtureDelayMs,
|
||||
agentFixtureMode: "external-network-blocker",
|
||||
expectTimeout: false
|
||||
});
|
||||
}
|
||||
|
||||
export async function runDevCloudWorkbenchSessionContinuityFixtureSmoke(options = {}) {
|
||||
let chromium;
|
||||
try {
|
||||
@@ -4891,7 +4879,7 @@ export async function runDevCloudWorkbenchQuickPromptsFixtureSmoke() {
|
||||
{
|
||||
id: "quick-prompts-copy-boundary",
|
||||
status: promptResults.every((result) => result.copyBoundaryOk) ? "pass" : "blocked",
|
||||
summary: "Quick prompt labels and filled text say HWLAB API / Skill CLI and avoid direct gateway/box/patch-panel or PROD claims.",
|
||||
summary: "Quick prompt labels and filled text say Codex and avoid PROD claims.",
|
||||
observations: promptResults.map(({ viewport, id, label, inputValue, copyBoundaryOk, forbiddenHits }) => ({
|
||||
viewport,
|
||||
id,
|
||||
@@ -5068,8 +5056,6 @@ async function runLocalAgentFixtureSmoke({
|
||||
} else {
|
||||
const prompts = agentFixtureMode === "slow-blocker"
|
||||
? [codeAgentE2ePrompts[1]]
|
||||
: agentFixtureMode === "external-network-blocker"
|
||||
? [codeAgentExternalNetworkPrompt]
|
||||
: codeAgentE2ePrompts;
|
||||
for (const prompt of prompts) {
|
||||
promptResults.push(await runCodeAgentPromptJourney(page, prompt, { sourceFixture: true }));
|
||||
@@ -5103,7 +5089,7 @@ async function runLocalAgentFixtureSmoke({
|
||||
ui.timeoutActionPanelContained &&
|
||||
!ui.completedMessageVisible
|
||||
)
|
||||
: agentFixtureMode === "slow-blocker" || agentFixtureMode === "external-network-blocker"
|
||||
: agentFixtureMode === "slow-blocker"
|
||||
? (
|
||||
promptResults.length === 1 &&
|
||||
promptResults[0]?.status === "blocked" &&
|
||||
@@ -5111,9 +5097,7 @@ async function runLocalAgentFixtureSmoke({
|
||||
promptResults[0]?.legacyWindow?.permanentFailureAround4500ms === false &&
|
||||
promptResults[0]?.legacyWindow?.pendingChineseVisible === true &&
|
||||
promptResults[0]?.legacyWindow?.pendingTraceShowsEvents === true &&
|
||||
(agentFixtureMode === "external-network-blocker"
|
||||
? ["能力未开放", "Runner 受阻", "服务受阻"].includes(ui.agentChatStatus)
|
||||
: ui.agentChatStatus === "Runner 受阻") &&
|
||||
ui.agentChatStatus === "Runner 受阻" &&
|
||||
ui.failedMessageVisible &&
|
||||
ui.failedMessageHasTrace &&
|
||||
ui.retryInputPreserved &&
|
||||
@@ -5188,7 +5172,7 @@ async function runLocalAgentFixtureSmoke({
|
||||
status: pass ? "pass" : "blocked",
|
||||
summary: expectTimeout
|
||||
? "Local SOURCE fixture proves a bounded Code Agent timeout stays user-visible/BLOCKED in the UI, preserves trace context, and keeps the user's input for retry."
|
||||
: agentFixtureMode === "slow-blocker" || agentFixtureMode === "external-network-blocker"
|
||||
: agentFixtureMode === "slow-blocker"
|
||||
? "Local SOURCE fixture returns a delayed structured blocker and the conversation shows the Chinese reason with trace evidence while preserving the input."
|
||||
: "Local SOURCE fixture sends input and reaches a user-visible replied state without marking the fixture as DEV-LIVE.",
|
||||
observations: expectTimeout ? {
|
||||
@@ -5239,15 +5223,11 @@ async function runLocalAgentFixtureSmoke({
|
||||
summary: "390x844 mobile view shows Chinese Code Agent pending status, trace/session context, and no pending-card text overflow before the delayed response arrives.",
|
||||
observations: mobilePending
|
||||
});
|
||||
} else if (!expectTimeout && (agentFixtureMode === "slow-blocker" || agentFixtureMode === "external-network-blocker")) {
|
||||
} else if (!expectTimeout && agentFixtureMode === "slow-blocker") {
|
||||
checks.push({
|
||||
id: agentFixtureMode === "external-network-blocker"
|
||||
? "local-agent-fixture-external-network-blocker"
|
||||
: "local-agent-fixture-slow-structured-blocker",
|
||||
id: "local-agent-fixture-slow-structured-blocker",
|
||||
status: pass ? "pass" : "blocked",
|
||||
summary: agentFixtureMode === "external-network-blocker"
|
||||
? "External network blocker for a GitHub prompt displays Runner 受阻 with network trace, keeps the user prompt, and does not restore main evidence/facts noise."
|
||||
: "Delayed structured blocker arrives after the legacy 4500ms window, displays Runner 受阻 instead of a generic 后端失败 state, keeps trace visible, and leaves the user prompt for continuing the same conversation.",
|
||||
summary: "Delayed structured blocker arrives after the legacy 4500ms window, displays Runner 受阻 instead of a generic 后端失败 state, keeps trace visible, and leaves the user prompt for continuing the same conversation.",
|
||||
observations: {
|
||||
legacyFailureWindowMs,
|
||||
fixtureDelayMs: responseDelayMs,
|
||||
@@ -5738,7 +5718,7 @@ async function inspectQuickPromptsViewport(browser, url, viewport) {
|
||||
const button = document.querySelector(`[data-agent-quick-prompt="${id}"]`);
|
||||
const input = document.querySelector("#command-input");
|
||||
const text = `${button?.textContent ?? ""}\n${button?.getAttribute("title") ?? ""}\n${button?.dataset.promptText ?? ""}`;
|
||||
const forbiddenPattern = /\b(?:PROD|gateway|box|patch-panel|真实硬件直控)\b/iu;
|
||||
const forbiddenPattern = /\bPROD\b/iu;
|
||||
const forbiddenHits = [...new Set(text.match(forbiddenPattern) ?? [])];
|
||||
return {
|
||||
label: button?.textContent?.replace(/\s+/gu, " ").trim() ?? "",
|
||||
@@ -5748,7 +5728,7 @@ async function inspectQuickPromptsViewport(browser, url, viewport) {
|
||||
focused: document.activeElement === input,
|
||||
explicitSendRequired: button?.dataset.requiresExplicitSend === "true",
|
||||
copyBoundaryOk:
|
||||
/(?:HWLAB API|Skill CLI)/u.test(text) &&
|
||||
/Codex/u.test(text) &&
|
||||
!forbiddenPattern.test(text) &&
|
||||
(button?.dataset.requiresExplicitSend !== "true" || /只填充输入框|点击发送后才确认/u.test(text)),
|
||||
forbiddenHits
|
||||
@@ -6899,136 +6879,6 @@ async function handleLocalAgentFixtureApi({ request, response, url, options = {}
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
if (options.agentFixtureMode === "external-network-blocker") {
|
||||
const blocker = {
|
||||
code: "external_network_blocked",
|
||||
category: "capability_unavailable",
|
||||
layer: "network",
|
||||
userMessage: "当前 Code Agent 运行策略禁止外部网络访问;未访问外网,也不会伪造成功。",
|
||||
retryable: false,
|
||||
traceId,
|
||||
sessionId: conversationId,
|
||||
conversationId,
|
||||
stage: "policy",
|
||||
elapsedMs: options.agentDelayMs ?? 0,
|
||||
lastEvent: { seq: 4, traceId, type: "network", stage: "policy", status: "blocked", label: "network:blocked", errorCode: "external_network_blocked" }
|
||||
};
|
||||
jsonResponse(response, 200, {
|
||||
conversationId,
|
||||
sessionId: conversationId,
|
||||
messageId,
|
||||
status: "failed",
|
||||
sourceKind: "SOURCE",
|
||||
evidenceLevel: "SOURCE",
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
traceId,
|
||||
provider: "codex-stdio",
|
||||
model: "codex-stdio",
|
||||
backend: "local-source-fixture/codex-stdio-external-network-blocker",
|
||||
projectId: stringOrFallback(body?.projectId, gateSummary.topology.projectId),
|
||||
workspace: "/workspace/hwlab",
|
||||
sandbox: "workspace-write",
|
||||
runner: {
|
||||
kind: "codex-mcp-stdio-runner",
|
||||
writeCapable: true,
|
||||
codexStdio: true,
|
||||
longLivedSession: true,
|
||||
durableSession: true
|
||||
},
|
||||
capabilityLevel: "blocked",
|
||||
sessionMode: "codex-mcp-stdio-long-lived",
|
||||
session: {
|
||||
sessionId: conversationId,
|
||||
conversationId,
|
||||
status: "idle",
|
||||
turn: 1,
|
||||
lastTraceId: traceId
|
||||
},
|
||||
sessionReuse: {
|
||||
conversationId,
|
||||
sessionId: conversationId,
|
||||
mapped: true,
|
||||
reused: true,
|
||||
turn: 1,
|
||||
status: "idle"
|
||||
},
|
||||
error: {
|
||||
code: "external_network_blocked",
|
||||
category: "capability_unavailable",
|
||||
layer: "network",
|
||||
message: "SOURCE external network blocker: runtime policy disabled external network checks.",
|
||||
userMessage: blocker.userMessage,
|
||||
retryable: false,
|
||||
blocker
|
||||
},
|
||||
blocker,
|
||||
blockers: [blocker],
|
||||
toolCalls: [
|
||||
{
|
||||
name: "external.network.check",
|
||||
type: "network-check",
|
||||
status: "blocked",
|
||||
exitCode: 2,
|
||||
traceId,
|
||||
stderrSummary: "external_network_blocked",
|
||||
targetUrl: "https://github.com/",
|
||||
blocker
|
||||
}
|
||||
],
|
||||
skills: {
|
||||
status: "not_requested",
|
||||
items: [],
|
||||
count: 0,
|
||||
totalCount: 0,
|
||||
blockers: []
|
||||
},
|
||||
runnerTrace: {
|
||||
traceId,
|
||||
runnerKind: "codex-mcp-stdio-runner",
|
||||
sessionMode: "codex-mcp-stdio-long-lived",
|
||||
sessionId: conversationId,
|
||||
sessionStatus: "idle",
|
||||
status: "blocked",
|
||||
elapsedMs: options.agentDelayMs ?? 0,
|
||||
waitingFor: "network-policy",
|
||||
events: [
|
||||
{ seq: 1, traceId, type: "session", stage: "created", status: "completed", label: "session:created" },
|
||||
{ seq: 2, traceId, type: "prompt", stage: "sent", status: "completed", label: "prompt:sent", toolName: "external.network.check" },
|
||||
{ seq: 3, traceId, type: "network", stage: "policy", status: "started", label: "network:started", toolName: "external.network.check" },
|
||||
blocker.lastEvent,
|
||||
{ seq: 5, traceId, type: "tool_call", stage: "tool_call", status: "blocked", label: "tool:external.network.check:blocked", toolName: "external.network.check", errorCode: "external_network_blocked" }
|
||||
],
|
||||
lastEvent: { seq: 5, traceId, type: "tool_call", stage: "tool_call", status: "blocked", label: "tool:external.network.check:blocked", toolName: "external.network.check", errorCode: "external_network_blocked" }
|
||||
},
|
||||
conversationFacts: {
|
||||
conversationId,
|
||||
sessionId: conversationId,
|
||||
sessionStatus: "idle",
|
||||
sessionMode: "codex-mcp-stdio-long-lived",
|
||||
capabilityLevel: "blocked",
|
||||
runnerKind: "codex-mcp-stdio-runner",
|
||||
workspace: "/workspace/hwlab",
|
||||
sandbox: "workspace-write",
|
||||
turnCount: 1,
|
||||
latestTraceId: traceId,
|
||||
traceIds: [traceId],
|
||||
latestSkills: null,
|
||||
recentToolCalls: [{ name: "external.network.check", status: "blocked" }],
|
||||
facts: [],
|
||||
valuesRedacted: true,
|
||||
secretMaterialStored: false
|
||||
},
|
||||
providerTrace: {
|
||||
source: "SOURCE-local-browser-fixture",
|
||||
sourceKind: "SOURCE",
|
||||
transport: "stdio+controlled-network",
|
||||
responseId: "rsp_source_fixture_external_network_blocker",
|
||||
redacted: true
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (options.agentFixtureMode === "slow-blocker") {
|
||||
jsonResponse(response, 200, {
|
||||
conversationId,
|
||||
|
||||
Reference in New Issue
Block a user