fix: cover code agent m3 hwlab api requests
This commit is contained in:
@@ -2582,25 +2582,25 @@ function m3IoApiBaseUrlMissingBlocker({ route = HWLAB_M3_IO_API_ROUTE } = {}) {
|
||||
|
||||
function m3IoSkillReply(skillResult) {
|
||||
const lines = [
|
||||
`M3 IO Skill CLI result: status=${skillResult.status}; accepted=${skillResult.accepted}; route=${skillResult.route}; traceId=${skillResult.traceId}; operationId=${skillResult.operationId ?? "null"}.`
|
||||
`M3 IO Skill CLI result: 已通过 Code Agent -> Skill CLI -> HWLAB API ${skillResult.route} 处理请求;status=${skillResult.status}; accepted=${skillResult.accepted}; traceId=${skillResult.traceId}; operationId=${skillResult.operationId ?? "null"}。`
|
||||
];
|
||||
if (skillResult.audit?.summary) {
|
||||
lines.push(`Audit: ${skillResult.audit.summary}.`);
|
||||
lines.push(`Audit: ${skillResult.audit.summary}(audit 摘要)。`);
|
||||
}
|
||||
if (skillResult.evidence?.summary) {
|
||||
lines.push(`Evidence: ${skillResult.evidence.summary}.`);
|
||||
lines.push(`Evidence: ${skillResult.evidence.summary}(evidence 摘要)。`);
|
||||
}
|
||||
if (skillResult.blocker) {
|
||||
lines.push(`Blocker: ${skillResult.blocker.code}; ${skillResult.blocker.zh ?? skillResult.blocker.message}.`);
|
||||
lines.push(`Blocker: ${skillResult.blocker.code}; ${skillResult.blocker.zh ?? skillResult.blocker.message}(blocker)。`);
|
||||
}
|
||||
if (skillResult.action === "do.write") {
|
||||
lines.push(`DO1 requested value=${String(skillResult.command?.value)}; DI1 readback=${String(skillResult.result?.targetReadback?.value ?? "unknown")}.`);
|
||||
lines.push(`DO1 写入请求:res_boxsimu_1:DO1=${String(skillResult.command?.value)};DI1 readback:res_boxsimu_2:DI1=${String(skillResult.result?.targetReadback?.value ?? "unknown")}。`);
|
||||
} else if (skillResult.action === "di.read") {
|
||||
lines.push(`DI1 value=${String(skillResult.result?.value ?? "unknown")}.`);
|
||||
lines.push(`DI1 读取结果:res_boxsimu_2:DI1=${String(skillResult.result?.value ?? "unknown")}。`);
|
||||
} else if (skillResult.readonly === true || skillResult.route === HWLAB_M3_STATUS_API_ROUTE) {
|
||||
lines.push(`M3 status readback DI1=${String(skillResult.readback?.value ?? "unknown")}; readiness=${skillResult.readiness?.status ?? "unknown"}.`);
|
||||
lines.push(`M3 status readback:res_boxsimu_2:DI1=${String(skillResult.readback?.value ?? "unknown")};readiness=${skillResult.readiness?.status ?? "unknown"}。`);
|
||||
}
|
||||
lines.push(`Boundary: Code Agent -> Skill CLI -> HWLAB API ${skillResult.route}; no direct gateway/box/patch-panel call; no OpenAI fallback used. This is source/artifact control capability, not DEV-LIVE verification.`);
|
||||
lines.push(`Boundary/边界:本次只走 Code Agent -> Skill CLI -> HWLAB API ${skillResult.route};没有直连 gateway/box/patch-panel,也没有使用 OpenAI fallback 冒充 toolCall。这是受控操作结果,不是 M3 DEV-LIVE 验收结论。`);
|
||||
return boundToolOutput(lines.join("\n"), READONLY_TOOL_OUTPUT_LIMIT).text;
|
||||
}
|
||||
|
||||
|
||||
@@ -2360,7 +2360,7 @@ test("cloud api /v1/agent/chat routes M3 IO through Skill CLI to /v1/m3/io only"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
conversationId: "cnv_server-test-m3-skill",
|
||||
message: "请通过 M3 DO1 写入 false,并回读 DI1"
|
||||
message: "通过 HWLAB API 把 res_boxsimu_1 的 DO1 写成 false,然后读取 res_boxsimu_2 的 DI1。"
|
||||
})
|
||||
});
|
||||
assert.equal(response.status, 200);
|
||||
@@ -2381,6 +2381,9 @@ test("cloud api /v1/agent/chat routes M3 IO through Skill CLI to /v1/m3/io only"
|
||||
assert.equal(payload.providerTrace.fallbackUsed, false);
|
||||
assert.equal(payload.providerTrace.method, "POST");
|
||||
assert.equal(payload.providerTrace.readback.value, false);
|
||||
assert.match(payload.reply.content, /HWLAB API \/v1\/m3\/io/u);
|
||||
assert.match(payload.reply.content, /res_boxsimu_1:DO1=false/u);
|
||||
assert.match(payload.reply.content, /res_boxsimu_2:DI1=false/u);
|
||||
assert.equal(payload.runner.safety.directGatewayCallsAllowed, false);
|
||||
assert.equal(payload.runner.safety.directBoxSimuCallsAllowed, false);
|
||||
assert.equal(payload.runner.safety.directPatchPanelCallsAllowed, false);
|
||||
@@ -2478,6 +2481,9 @@ test("cloud api /v1/agent/chat routes Codex stdio M3 request through in-process
|
||||
assert.equal(payload.runnerTrace.route, "/v1/m3/io");
|
||||
assert.equal(payload.providerTrace.codexStdio, true);
|
||||
assert.equal(payload.providerTrace.fallbackUsed, false);
|
||||
assert.match(payload.reply.content, /HWLAB API \/v1\/m3\/io/u);
|
||||
assert.match(payload.reply.content, /res_boxsimu_1:DO1=true/u);
|
||||
assert.match(payload.reply.content, /res_boxsimu_2:DI1=true/u);
|
||||
assert.ok(payload.runner.toolPolicy.allowed.includes("POST /v1/m3/io"));
|
||||
assert.deepEqual(gatewayCalls.map((call) => new URL(call.url).pathname), [
|
||||
"/status",
|
||||
|
||||
@@ -510,7 +510,7 @@ async function runLocalContractSmoke() {
|
||||
{
|
||||
conversationId: "cnv_code-agent-chat-m3-skill",
|
||||
traceId: "trc_code-agent-chat-m3-skill",
|
||||
message: "请通过 M3 DO1 写入 true,并回读 DI1"
|
||||
message: "通过 HWLAB API 把 res_boxsimu_1 的 DO1 写成 true,然后读取 res_boxsimu_2 的 DI1。"
|
||||
},
|
||||
{
|
||||
now: () => "2026-05-22T00:04:55.000Z",
|
||||
@@ -583,13 +583,99 @@ async function runLocalContractSmoke() {
|
||||
assert.equal(m3SkillWrite.providerTrace.method, "POST");
|
||||
assert.equal(m3SkillWrite.providerTrace.readback.value, true);
|
||||
assert.equal(m3SkillWrite.runner.safety.directGatewayCallsAllowed, false);
|
||||
assert.match(m3SkillWrite.reply.content, /HWLAB API \/v1\/m3\/io/u);
|
||||
assert.match(m3SkillWrite.reply.content, /res_boxsimu_1:DO1=true/u);
|
||||
assert.match(m3SkillWrite.reply.content, /res_boxsimu_2:DI1=true/u);
|
||||
assert.equal(m3SkillCalls.length, 1);
|
||||
assert.equal(m3SkillCalls[0].url, "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667/v1/m3/io");
|
||||
assert.equal(m3SkillCalls[0].request.body.action, "do.write");
|
||||
assert.equal(m3SkillCalls[0].request.body.value, true);
|
||||
assert.equal(m3SkillCalls[0].url.includes("gateway-simu"), false);
|
||||
assert.equal(m3SkillCalls[0].url.includes("box-simu"), false);
|
||||
assert.equal(m3SkillCalls[0].url.includes("patch-panel"), false);
|
||||
logOk("M3 Skill CLI routes through HWLAB API only");
|
||||
|
||||
const m3SkillFalseCalls = [];
|
||||
const m3SkillWriteFalse = await handleCodeAgentChat(
|
||||
{
|
||||
conversationId: "cnv_code-agent-chat-m3-skill-false",
|
||||
traceId: "trc_code-agent-chat-m3-skill-false",
|
||||
message: "通过 HWLAB API 把 res_boxsimu_1 的 DO1 写成 false,然后读取 res_boxsimu_2 的 DI1。"
|
||||
},
|
||||
{
|
||||
now: () => "2026-05-22T00:04:56.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 M3 IO");
|
||||
},
|
||||
m3IoSkillRequestJson: async (url, request) => {
|
||||
m3SkillFalseCalls.push({ url, request });
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
accepted: true,
|
||||
action: "do.write",
|
||||
traceId: "trc_code-agent-chat-m3-skill-false",
|
||||
operationId: "op_m3_do_write_false_smoke",
|
||||
auditId: "aud_m3_do_write_false_smoke_succeeded",
|
||||
evidenceId: "evd_m3_do_write_false_smoke_succeeded",
|
||||
auditState: {
|
||||
status: "written_non_durable"
|
||||
},
|
||||
evidenceState: {
|
||||
status: "blocked",
|
||||
sourceKind: "BLOCKED",
|
||||
blocker: "runtime_durable_not_green",
|
||||
writeStatus: "written_non_durable"
|
||||
},
|
||||
durableStatus: {
|
||||
status: "degraded",
|
||||
durable: false,
|
||||
blocker: "runtime_durable_not_green"
|
||||
},
|
||||
result: {
|
||||
value: false,
|
||||
targetReadback: {
|
||||
status: "succeeded",
|
||||
value: false
|
||||
}
|
||||
},
|
||||
controlPath: {
|
||||
cloudApi: true,
|
||||
gatewaySimu: true,
|
||||
boxSimu: true,
|
||||
patchPanel: true,
|
||||
frontendBypass: false
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
validateCodeAgentChatSchema(m3SkillWriteFalse);
|
||||
assert.equal(m3SkillWriteFalse.status, "completed");
|
||||
assert.equal(m3SkillWriteFalse.provider, "hwlab-skill-cli");
|
||||
assert.equal(m3SkillWriteFalse.toolCalls[0].route, "/v1/m3/io");
|
||||
assert.equal(m3SkillWriteFalse.toolCalls[0].method, "POST");
|
||||
assert.equal(m3SkillWriteFalse.toolCalls[0].operationId, "op_m3_do_write_false_smoke");
|
||||
assert.equal(m3SkillWriteFalse.toolCalls[0].readback.value, false);
|
||||
assert.equal(m3SkillWriteFalse.providerTrace.fallbackUsed, false);
|
||||
assert.match(m3SkillWriteFalse.reply.content, /HWLAB API \/v1\/m3\/io/u);
|
||||
assert.match(m3SkillWriteFalse.reply.content, /res_boxsimu_1:DO1=false/u);
|
||||
assert.match(m3SkillWriteFalse.reply.content, /res_boxsimu_2:DI1=false/u);
|
||||
assert.equal(m3SkillFalseCalls.length, 1);
|
||||
assert.equal(m3SkillFalseCalls[0].url, "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667/v1/m3/io");
|
||||
assert.equal(m3SkillFalseCalls[0].request.body.action, "do.write");
|
||||
assert.equal(m3SkillFalseCalls[0].request.body.value, false);
|
||||
logOk("M3 Skill CLI handles user-direct false request through HWLAB API only");
|
||||
|
||||
const directToolCallPayload = JSON.parse(JSON.stringify(m3SkillWrite));
|
||||
directToolCallPayload.toolCalls[0].hwlabApi = {
|
||||
route: "/v1/m3/io",
|
||||
|
||||
Reference in New Issue
Block a user