feat: route chinese m3 io through controlled api

This commit is contained in:
Code Queue Review
2026-05-24 11:12:59 +00:00
parent 6f3205568c
commit 2c29ab242b
2 changed files with 488 additions and 5 deletions
+254 -5
View File
@@ -1118,6 +1118,46 @@ async function callM3IoSkillRunner({ intent, conversationId, sessionId, traceId,
}
async function executeM3IoSkillIntent({ intent, traceId, requestId, actorId, env, now, requestJson }) {
if (intent?.blocker) {
const blocker = m3IoIntentBlocker(intent.blocker, { traceId });
return {
commandArgs: [
"m3",
"io",
"--trace-id",
traceId,
"--request-id",
requestId,
"--actor-id",
actorId
],
skillResult: m3IoSkillBlockedBeforeRequestResult({
traceId,
requestId,
actorId,
blocker,
route: HWLAB_M3_IO_API_ROUTE,
method: "POST",
action: "m3.io.blocked",
hwlabApi: {
route: HWLAB_M3_IO_API_ROUTE,
redactedUrl: null,
source: "intent-validation",
baseUrlConfigured: Boolean(configuredCloudApiBaseUrl(env)),
requiredEnv: [...HWLAB_M3_IO_API_BASE_URL_ENVS],
recommendedEnv: HWLAB_M3_IO_API_BASE_URL_ENV,
missingConfig: [],
cloudApiOnly: true,
directGatewayCalls: false,
directBoxCalls: false,
directPatchPanelCalls: false
},
startedAt: nowIso(now),
finishedAt: nowIso(now)
})
};
}
if (!m3IoIntentHasSupportedAction(intent)) {
const blocker = m3IoIntentActionMissingBlocker({ traceId });
return {
@@ -1213,6 +1253,31 @@ function m3IoIntentActionMissingBlocker({ traceId }) {
};
}
function m3IoIntentBlocker(blocker, { traceId } = {}) {
const code = blocker?.code ?? "m3_io_scope_blocked";
const layer = blocker?.layer ?? "intent";
return {
code,
layer,
category: blocker?.category ?? "scope_blocked",
retryable: false,
source: "code-agent-m3-skill-cli",
summary: blocker?.summary ?? "M3 IO request is outside the allowed controlled virtual IO surface.",
message: blocker?.message ?? "Code Agent only supports the M3 virtual IO controlled cloud-api path.",
zh: blocker?.zh ?? "当前只支持 M3 虚拟 IO 受控链路。",
userMessage: blocker?.userMessage ?? "当前只支持 M3 虚拟 IO 受控链路:box-simu-1 DO1=true/false 写入,或 box-simu-2 DI1 读取。",
traceId,
route: HWLAB_M3_IO_API_ROUTE,
toolName: HWLAB_M3_IO_SKILL_NAME,
allowed: {
read: `${M3_IO_TOPOLOGY.targetResourceId}:${M3_IO_TOPOLOGY.targetPort}`,
write: `${M3_IO_TOPOLOGY.sourceResourceId}:${M3_IO_TOPOLOGY.sourcePort}=true|false`,
route: HWLAB_M3_IO_API_ROUTE
},
requested: blocker?.requested ?? null
};
}
function m3IoSkillBlockedBeforeRequestResult({
traceId,
requestId,
@@ -1745,6 +1810,18 @@ function m3IoStructuredResult({ skillResult = {}, stdioResult = {}, commandArgs
auditId: skillResult.auditId ?? skillResult.audit?.auditId ?? null,
evidenceId: skillResult.evidenceId ?? skillResult.evidence?.evidenceId ?? null
},
zh: m3IoChineseResult({
action,
isBlocker,
blocker,
route,
method,
do1Value,
di1Value,
trusted,
durable,
skillResult
}),
session: {
sessionId: stdioResult.session?.sessionId ?? null,
sessionMode: stdioResult.sessionMode ?? stdioResult.runner?.sessionMode ?? null,
@@ -1772,7 +1849,9 @@ function m3IoStructuredResult({ skillResult = {}, stdioResult = {}, commandArgs
trustBlocker: skillResult.trustBlocker ?? null,
note: trusted
? "operation/audit/evidence 已有可信持久化记录;仍不是 M3 DEV-LIVE 验收结论。"
: "本次不伪造 DEV-LIVE:控制链路结果与可信持久化状态分开展示。"
: skillResult.controlReady === true
? "控制链路可达,但可信记录未 green,不能作为 DEV-LIVE 可信闭环通过。"
: "本次不伪造 DEV-LIVE:控制链路结果与可信持久化状态分开展示。"
},
blocker,
blockers: skillResult.blockers ?? (blocker ? [blocker] : []),
@@ -1832,6 +1911,62 @@ function m3IoStructuredSummary({ action, isBlocker, blocker, route, do1Value, di
return `M3 IO 结果:${actionText}trusted=${trusted ? "true" : "false"}durable=${durable ? "true" : "false"}`;
}
function m3IoChineseResult({ action, isBlocker, blocker, route, method, do1Value, di1Value, trusted, durable, skillResult = {} }) {
const expectedValue = action === "do.write" ? do1Value : null;
const targetResource = action === "do.write"
? `${M3_IO_TOPOLOGY.sourceResourceId}:${M3_IO_TOPOLOGY.sourcePort}`
: `${M3_IO_TOPOLOGY.targetResourceId}:${M3_IO_TOPOLOGY.targetPort}`;
const actualResult = action === "do.write"
? `${M3_IO_TOPOLOGY.targetResourceId}:${M3_IO_TOPOLOGY.targetPort}=${formatM3Value(di1Value)}`
: action === "di.read"
? `${M3_IO_TOPOLOGY.targetResourceId}:${M3_IO_TOPOLOGY.targetPort}=${formatM3Value(di1Value)}`
: "未执行";
const controlReachable = skillResult.controlReady === true;
const trustedGreen = trusted === true;
const durableGreen = durable?.durable === true && !durable?.blocker;
return {
status: isBlocker ? "blocked" : trustedGreen && durableGreen ? "succeeded" : "degraded",
targetResource,
action: action === "do.write"
? "写入 DO1 并回读 DI1"
: action === "di.read"
? "读取 DI1"
: "M3 IO 请求",
expectedValue,
readValue: action === "di.read" ? di1Value : null,
actualResult,
source: `HWLAB cloud-api 受控链路 ${method} ${route}`,
traceId: skillResult.traceId ?? null,
operationId: skillResult.operationId ?? null,
auditId: skillResult.auditId ?? skillResult.audit?.auditId ?? null,
evidenceId: skillResult.evidenceId ?? skillResult.evidence?.evidenceId ?? null,
controlPath: {
reachable: controlReachable,
cloudApiRouteOnly: true,
route,
directGatewayCalls: false,
directBoxCalls: false,
directPatchPanelCalls: false
},
runtime: {
durableGreen,
trustedGreen,
durableStatus: durable?.status ?? null,
durableBlocker: durable?.blocker ?? null,
note: controlReachable && !trustedGreen
? "控制链路可达,但可信记录未 green,不能作为 DEV-LIVE 可信闭环通过。"
: trustedGreen
? "控制链路和可信记录均为 green;仍不在本接口内声明 live 已更新。"
: "控制链路或可信记录未 green。"
},
blocker: blocker?.code ? {
code: blocker.code,
layer: blocker.layer ?? null,
message: blocker.userMessage ?? blocker.zh ?? blocker.message ?? blocker.code
} : null
};
}
function redactM3IoCommandArgs(commandArgs = []) {
const redacted = [];
for (let index = 0; index < commandArgs.length; index += 1) {
@@ -2040,12 +2175,30 @@ function isSessionContextRequest(text) {
function detectM3IoIntent(text) {
const normalized = String(text ?? "");
if (!/\bM3\b|DO1|DI1|数字输出|数字输入|回读|读回|状态|status/u.test(normalized)) {
if (!/\bM\d+\b|\bM3\b|DO\s*\d+|DI\s*\d+|数字输出|数字输入|box[-_ ]?simu|res_boxsimu|回读|读回|状态|status|串口|serial|PWM|DAP/u.test(normalized)) {
return null;
}
if (isDirectM3HardwareAccessRequest(normalized)) {
return null;
}
const scopeBlocker = detectM3IoScopeBlocker(normalized);
if (scopeBlocker) {
return {
kind: "m3_io",
toolName: HWLAB_M3_IO_SKILL_NAME,
action: null,
blocker: scopeBlocker
};
}
const explicitTargetBlocker = detectM3IoTargetBlocker(normalized, "any");
if (explicitTargetBlocker) {
return {
kind: "m3_io",
toolName: HWLAB_M3_IO_SKILL_NAME,
action: null,
blocker: explicitTargetBlocker
};
}
if (/DEV-LIVE|验收|acceptance|(?:M3|M4|M5).{0,20}(?:通过|pass|green|accept)/iu.test(normalized)) {
return null;
}
@@ -2075,6 +2228,15 @@ function detectM3IoIntent(text) {
}
if (wantsRead && !wantsWrite) {
const targetBlocker = detectM3IoTargetBlocker(normalized, "di.read");
if (targetBlocker) {
return {
kind: "m3_io",
toolName: HWLAB_M3_IO_SKILL_NAME,
action: null,
blocker: targetBlocker
};
}
return {
kind: "m3_io",
toolName: HWLAB_M3_IO_SKILL_NAME,
@@ -2082,6 +2244,15 @@ function detectM3IoIntent(text) {
};
}
const targetBlocker = detectM3IoTargetBlocker(normalized, "do.write");
if (targetBlocker) {
return {
kind: "m3_io",
toolName: HWLAB_M3_IO_SKILL_NAME,
action: null,
blocker: targetBlocker
};
}
const value = parseM3WriteValue(normalized);
if (typeof value !== "boolean") {
return {
@@ -2098,6 +2269,79 @@ function detectM3IoIntent(text) {
};
}
function detectM3IoScopeBlocker(text) {
const value = String(text ?? "");
const unsupportedStage = value.match(/\bM(?!3\b)\d+\b/iu)?.[0] ?? null;
if (unsupportedStage && /(?:IO|DI|DO|控制|读取|读|写|状态|硬件|hardware)/iu.test(value)) {
return m3ScopeBlocker({
code: "m3_io_scope_blocked",
summary: `Unsupported hardware stage ${unsupportedStage} requested.`,
zh: `当前 Code Agent 只支持 M3 虚拟 IO,暂不支持 ${unsupportedStage} IO。`,
requested: unsupportedStage
});
}
const dangerous = value.match(/真实硬件|物理硬件|real\s+hardware|production\s+hardware|PROD|任意\s*shell|shell|bash|sh\s+-c|kubectl|数据库写入|写入数据库|database\s+write|SQL\s+write|绕过\s*(?:patch-panel|cloud-api|HWLAB API)|bypass\s+(?:patch-panel|cloud-api|HWLAB API)|批量脚本|脚本批量|batch\s+script|DAP|串口|serial|PWM/iu)?.[0] ?? null;
if (dangerous && /(?:M3|IO|DI|DO|box[-_ ]?simu|res_boxsimu|硬件|hardware|patch-panel|cloud-api|HWLAB API)/iu.test(value)) {
return m3ScopeBlocker({
code: "m3_io_scope_blocked",
summary: `Dangerous or out-of-scope M3 IO request was blocked: ${dangerous}.`,
zh: "当前只支持 M3 虚拟 IO 受控链路;不能操作真实硬件、shell、数据库、串口/DAP/PWM、批量脚本,或绕过 cloud-api/patch-panel。",
requested: dangerous
});
}
return null;
}
function detectM3IoTargetBlocker(text, action) {
const value = String(text ?? "");
const unsupportedPort = value.match(/\b(?:DO|DI)\s*(?!1\b)\d+\b|数字(?:输出|输入)\s*(?!1\b)\d+|PWM|DAP|串口|serial/iu)?.[0] ?? null;
if (unsupportedPort) {
return m3ScopeBlocker({
code: "m3_io_target_unsupported",
category: "target_unsupported",
summary: `Unsupported M3 IO target ${unsupportedPort} requested.`,
zh: "当前只支持 M3 阶段允许的 DO1/DI1,不能执行 DO2/DI2、DAP、串口或 PWM 等请求。",
requested: unsupportedPort
});
}
const mentionsBox1 = /(?:box[-_ ]?simu[-_ ]?1|boxsimu[_-]?1|res_boxsimu_1)/iu.test(value);
const mentionsBox2 = /(?:box[-_ ]?simu[-_ ]?2|boxsimu[_-]?2|res_boxsimu_2)/iu.test(value);
if (action === "do.write" && mentionsBox2 && !mentionsBox1) {
return m3ScopeBlocker({
code: "m3_io_target_unsupported",
category: "target_unsupported",
summary: "DO write target is not the allowed res_boxsimu_1:DO1 endpoint.",
zh: "DO 写入仅允许目标 box-simu-1 DO1;不能把 box-simu-2 当作 DO 写入目标。",
requested: "box-simu-2 DO1"
});
}
if (action === "di.read" && mentionsBox1 && !mentionsBox2) {
return m3ScopeBlocker({
code: "m3_io_target_unsupported",
category: "target_unsupported",
summary: "DI read target is not the allowed res_boxsimu_2:DI1 endpoint.",
zh: "DI 读取仅允许目标 box-simu-2 DI1;不能把 box-simu-1 当作 DI 读取目标。",
requested: "box-simu-1 DI1"
});
}
return null;
}
function m3ScopeBlocker({ code, category = "scope_blocked", summary, zh, requested }) {
return {
code,
layer: "intent",
category,
retryable: false,
summary,
message: "M3 IO request is outside the allowed controlled virtual IO surface.",
zh,
userMessage: `${zh} 当前只支持 box-simu-1 DO1=true/false 写入和 box-simu-2 DI1 读取,且必须走 cloud-api ${HWLAB_M3_IO_API_ROUTE}`,
requested
};
}
function isDirectM3HardwareAccessRequest(text) {
const value = String(text ?? "");
if (/(?:https?:\/\/[^\s"']*(?:gateway(?:-simu)?|box(?:-simu)?|patch-panel|hwlab-patch-panel)|\/invoke\b|\/sync\/tick\b|:7101\b|:7201\b|:7301\b)/iu.test(value)) {
@@ -2116,8 +2360,10 @@ function isM3TopologyRouteMention(text) {
}
function parseM3WriteValue(text) {
if (/\btrue\b|\b1\b|\bon\b|打开|置高|高电平|拉高|闭合/iu.test(text)) return true;
if (/\bfalse\b|\b0\b|\boff\b|关闭|置低|低电平|拉低|断开/iu.test(text)) return false;
if (/\bfalse\b|\boff\b|关闭|置低|低电平|拉低|断开/iu.test(text)) return false;
if (/\btrue\b|\bon\b|打开|置高|高电平|拉高|闭合/iu.test(text)) return true;
if (/(?:设置为|设为|写成|写入|置为|set\s+to|=|:)\s*0\b/iu.test(text)) return false;
if (/(?:设置为|设为|写成|写入|置为|set\s+to|=|:)\s*1\b/iu.test(text)) return true;
return null;
}
@@ -2267,6 +2513,9 @@ function m3IoSkillReply(skillResult, m3Io = null) {
if (m3Io?.blocker?.code) {
lines.push(`Blocker: ${m3Io.blocker.code}blocker=${m3Io.blocker.code}layer=${m3Io.blocker.layer ?? "unknown"}`);
}
if (m3Io?.trust?.note) {
lines.push(`可信记录:${m3Io.trust.note}`);
}
lines.push(`可信状态:trusted=${m3Io?.trust?.trusted === true ? "true" : "false"}durable=${m3Io?.trust?.durable === true ? "true" : "false"};这不是 M3 DEV-LIVE 验收结论。`);
return boundToolOutput(lines.join("\n"), READONLY_TOOL_OUTPUT_LIMIT).text;
}
@@ -2411,7 +2660,7 @@ function structuredCompletionBlocker(result, context = {}) {
}
function m3CompletionBlockerCode(primary = {}) {
if (["m3_gateway_session_unavailable", "runtime_durable_not_green", "skill_cli_api_base_missing", "m3_io_intent_action_missing"].includes(primary?.code)) {
if (["m3_gateway_session_unavailable", "runtime_durable_not_green", "skill_cli_api_base_missing", "m3_io_intent_action_missing", "m3_io_scope_blocked", "m3_io_target_unsupported"].includes(primary?.code)) {
return "m3_readiness_blocked";
}
return primary?.code ?? "m3_io_blocker";
@@ -1319,6 +1319,162 @@ test("Code Agent M3 true/false/read requests bypass Codex stdio and use Skill CL
assert.equal(calls.length, cases.length);
});
test("Code Agent parses exact Chinese M3 IO requests and returns structured Chinese result", async () => {
const calls = [];
const cases = [
{
suffix: "read_di1",
message: "读取 box-simu-2 DI1 状态",
action: "di.read",
value: true,
zhAction: "读取 DI1",
targetResource: "res_boxsimu_2:DI1"
},
{
suffix: "write_do1_true",
message: "把 box-simu-1 DO1 设置为 true",
action: "do.write",
value: true,
zhAction: "写入 DO1 并回读 DI1",
targetResource: "res_boxsimu_1:DO1"
},
{
suffix: "write_do1_false",
message: "把 box-simu-1 DO1 设置为 false",
action: "do.write",
value: false,
zhAction: "写入 DO1 并回读 DI1",
targetResource: "res_boxsimu_1:DO1"
}
];
for (const item of cases) {
const payload = await handleCodeAgentChat(
{
conversationId: `cnv_m3_exact_zh_${item.suffix}`,
traceId: `trc_m3_exact_zh_${item.suffix}`,
message: item.message
},
{
now: () => "2026-05-24T10:00:00.000Z",
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_WORKSPACE: process.cwd(),
HWLAB_CODE_AGENT_HWLAB_API_BASE_URL: "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
OPENAI_API_KEY: "must-not-be-used"
},
callProvider: async () => {
throw new Error("OpenAI fallback must not be used for exact Chinese M3 IO");
},
m3IoSkillRequestJson: async (url, request) => {
calls.push({ suffix: item.suffix, url, request });
assert.equal(new URL(url).pathname, HWLAB_M3_IO_API_ROUTE);
assert.equal(new URL(url).hostname, "hwlab-cloud-api.hwlab-dev.svc.cluster.local");
assert.equal(request.method, "POST");
assert.equal(request.body.action, item.action);
assert.equal(request.body.source, "hwlab-agent-runtime.m3-io");
if (item.action === "do.write") {
assert.equal(request.body.resourceId, "res_boxsimu_1");
assert.equal(request.body.port, "DO1");
assert.equal(request.body.value, item.value);
} else {
assert.equal(request.body.resourceId, "res_boxsimu_2");
assert.equal(request.body.port, "DI1");
assert.equal(Object.hasOwn(request.body, "value"), false);
}
return {
ok: true,
status: 200,
body: {
serviceId: "hwlab-cloud-api",
contractVersion: "m3-io-control-v1",
status: "succeeded",
accepted: true,
action: item.action,
traceId: `trc_m3_exact_zh_${item.suffix}`,
operationId: `op_m3_exact_zh_${item.suffix}`,
auditId: `aud_m3_exact_zh_${item.suffix}`,
evidenceId: `evd_m3_exact_zh_${item.suffix}`,
auditState: {
status: item.action === "do.write" ? "written_non_durable" : "read"
},
evidenceState: {
status: "blocked",
sourceKind: "BLOCKED",
blocker: "runtime_durable_not_green",
writeStatus: item.action === "do.write" ? "written_non_durable" : "not_written"
},
durableStatus: {
status: "degraded",
durable: false,
blocker: "runtime_durable_not_green"
},
result: item.action === "do.write"
? {
value: item.value,
targetReadback: {
status: "succeeded",
value: item.value,
resourceId: "res_boxsimu_2",
port: "DI1"
}
}
: {
status: "succeeded",
value: item.value,
resourceId: "res_boxsimu_2",
port: "DI1"
},
controlPath: {
status: "succeeded",
cloudApi: true,
gatewaySimu: true,
boxSimu: true,
patchPanel: true,
frontendBypass: false
}
}
};
}
}
);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.provider, "hwlab-skill-cli");
assert.equal(payload.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.ready);
assert.equal(payload.responseType, "m3_io_result");
assert.equal(payload.m3Io.zh.status, "degraded");
assert.equal(payload.m3Io.zh.targetResource, item.targetResource);
assert.equal(payload.m3Io.zh.action, item.zhAction);
assert.equal(payload.m3Io.zh.traceId, `trc_m3_exact_zh_${item.suffix}`);
assert.equal(payload.m3Io.zh.operationId, `op_m3_exact_zh_${item.suffix}`);
assert.equal(payload.m3Io.zh.auditId, `aud_m3_exact_zh_${item.suffix}`);
assert.equal(payload.m3Io.zh.evidenceId, `evd_m3_exact_zh_${item.suffix}`);
assert.equal(payload.m3Io.zh.controlPath.reachable, true);
assert.equal(payload.m3Io.zh.controlPath.cloudApiRouteOnly, true);
assert.equal(payload.m3Io.zh.runtime.durableGreen, false);
assert.equal(payload.m3Io.zh.runtime.trustedGreen, false);
assert.equal(payload.m3Io.zh.runtime.durableBlocker, "runtime_durable_not_green");
assert.match(payload.m3Io.zh.runtime.note, /控制链路可达,但可信记录未 green/u);
assert.match(payload.reply.content, /控制链路可达,但可信记录未 green/u);
assert.equal(payload.toolCalls[0].route, HWLAB_M3_IO_API_ROUTE);
assert.equal(payload.toolCalls[0].method, "POST");
assert.equal(payload.toolCalls[0].hwlabApi.cloudApiOnly, true);
assert.equal(payload.toolCalls[0].directGatewayCalls, false);
assert.equal(payload.toolCalls[0].directBoxCalls, false);
assert.equal(payload.toolCalls[0].directPatchPanelCalls, false);
assert.equal(payload.providerTrace.fallbackUsed, false);
}
assert.equal(calls.length, cases.length);
for (const call of calls) {
assert.equal(call.url.includes("gateway"), false);
assert.equal(call.url.includes("box-simu"), false);
assert.equal(call.url.includes("patch-panel"), false);
}
});
test("Code Agent ambiguous M3 IO control request returns structured blocker without Codex stdio", async () => {
const codexStdioCalls = [];
const payload = await handleCodeAgentChat(
@@ -1385,6 +1541,84 @@ test("Code Agent ambiguous M3 IO control request returns structured blocker with
assert.equal(codexStdioCalls.length, 0);
});
test("Code Agent blocks out-of-scope or dangerous Chinese M3 IO requests before HWLAB API call", async () => {
const messages = [
{
suffix: "real_hardware",
message: "把真实硬件 M3 DO1 设置为 true",
code: "m3_io_scope_blocked"
},
{
suffix: "shell",
message: "用任意 shell 批量脚本绕过 patch-panel 写入 M3 DO1 true",
code: "m3_io_scope_blocked"
},
{
suffix: "di2",
message: "读取 box-simu-2 DI2 状态",
code: "m3_io_target_unsupported"
},
{
suffix: "wrong_box",
message: "把 box-simu-2 DO1 设置为 true",
code: "m3_io_target_unsupported"
},
{
suffix: "serial_pwm",
message: "通过串口读取 M3 PWM 状态",
code: "m3_io_scope_blocked"
}
];
for (const item of messages) {
const payload = await handleCodeAgentChat(
{
conversationId: `cnv_m3_dangerous_zh_${item.suffix}`,
traceId: `trc_m3_dangerous_zh_${item.suffix}`,
message: item.message
},
{
now: () => "2026-05-24T10:01:00.000Z",
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_WORKSPACE: process.cwd(),
HWLAB_CODE_AGENT_HWLAB_API_BASE_URL: "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
OPENAI_API_KEY: "must-not-be-used"
},
callProvider: async () => {
throw new Error("OpenAI fallback must not be used for dangerous M3 IO");
},
m3IoSkillRequestJson: async () => {
throw new Error("dangerous or out-of-scope M3 IO must block before HWLAB API request");
}
}
);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.provider, "hwlab-skill-cli");
assert.equal(payload.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.blocked);
assert.equal(payload.responseType, "m3_io_blocker");
assert.equal(payload.blocker.code, "m3_readiness_blocked");
assert.equal(payload.toolCalls.length, 1);
assert.equal(payload.toolCalls[0].status, "blocked");
assert.equal(payload.toolCalls[0].route, HWLAB_M3_IO_API_ROUTE);
assert.equal(payload.toolCalls[0].blocker.code, item.code);
assert.equal(payload.toolCalls[0].hwlabApi.source, "intent-validation");
assert.equal(payload.toolCalls[0].accepted, false);
assert.equal(payload.toolCalls[0].operationId, null);
assert.equal(payload.toolCalls[0].directGatewayCalls, false);
assert.equal(payload.toolCalls[0].directBoxCalls, false);
assert.equal(payload.toolCalls[0].directPatchPanelCalls, false);
assert.equal(payload.providerTrace.fallbackUsed, false);
assert.equal(payload.m3Io.zh.status, "blocked");
assert.equal(payload.m3Io.zh.controlPath.cloudApiRouteOnly, true);
assert.match(payload.m3Io.zh.blocker.message, /当前只支持/u);
assert.match(payload.reply.content, /M3 IO 阻塞/u);
assert.match(payload.reply.content, /Blocker:/u);
}
});
test("Code Agent M3 status uses Skill CLI GET /v1/m3/status and keeps route evidence", async () => {
const calls = [];
const payload = await handleCodeAgentChat(