Merge origin/feature/N7Web into feature/N7Web

Integrate the remote firmware workbench, OTA delivery, and timed mode schedules while preserving the local N7 scene and runtime changes.
This commit is contained in:
Miayui
2026-09-17 12:46:51 +08:00
135 changed files with 30180 additions and 513 deletions
@@ -595,6 +595,58 @@ test("the change-into predicate carries only a same-name opaque draft reference"
assert.equal(Object.hasOwn(anotherMode, "mode_profile_draft_context"), false);
});
for (const phrase of [
"在午休模式中加入空调、氛围灯、阅读灯、车窗等设备动作",
"请帮我在午休模式里添加空调和车窗动作",
"给午休模式增加氛围灯",
"为午休模式补充阅读灯",
"往午休模式内加入空调,温度26度",
"向午休模式中加上关闭车窗",
"午休模式中再加入空调温度26度",
"午休模式再补充车窗和阅读灯",
"把关闭车窗加入到午休模式中",
"请将空调26度和柔和氛围灯添加到午休模式",
]) {
test(`device additions carry only the same-name opaque draft: ${phrase}`, async () => {
const events = modeDraftEvents("req-draft-add-devices", 24);
const harness = createHarness(events);
harness.input.value = "构建一个午休模式";
await harness.sandbox.CognitiveAgentAssistant.submit();
events.splice(0, events.length, { type: "answer.completed", answer: "已收到草稿补充请求" });
harness.input.value = phrase;
await harness.sandbox.CognitiveAgentAssistant.submit();
const request = JSON.parse(harness.requestCalls.at(-1).options.body);
assert.equal(request.message, phrase);
assert.deepEqual(request.mode_profile_draft_context, {
name: "午休模式", source_request_id: "req-draft-add-devices", vehicle_id: "demo-vehicle",
});
assert.equal(harness.requestCalls.every((call) => call.url === "/api/assistant/stream"), true);
});
}
for (const phrase of [
"在露营模式中加入空调和车窗",
"把空调添加到露营模式中",
"在午休模式中加入空调可以吗?",
"午休模式中加入哪些设备合适?",
"在午休模式中不要加入空调",
"在午休模式中先别添加车窗",
"打开空调",
"设置空调出风模式为吹脚",
]) {
test(`non-update or other-mode additions do not borrow a draft: ${phrase}`, async () => {
const events = modeDraftEvents("req-draft-add-isolation", 24);
const harness = createHarness(events);
harness.input.value = "构建一个午休模式";
await harness.sandbox.CognitiveAgentAssistant.submit();
events.splice(0, events.length, { type: "answer.completed", answer: "已收到" });
harness.input.value = phrase;
await harness.sandbox.CognitiveAgentAssistant.submit();
const request = JSON.parse(harness.requestCalls.at(-1).options.body);
assert.equal(Object.hasOwn(request, "mode_profile_draft_context"), false);
});
}
test("shade position rows explain the target opening at semantic boundaries", async () => {
const positions = [0, 30, 50, 100];
const events = [