merge: sync feature/N7Web into feature/N7Web-Tech

# Conflicts:
#	apps/web-console/service-worker.js
#	src/cognitive_agent/application/mode_profile.py
#	src/cognitive_agent/application/mode_scene_catalog.py
#	src/cognitive_agent/application/seat_service.py
#	tests/e2e/test_seat_web_api.py
#	tests/unit/test_assistant_frontend_contract.py
#	tests/unit/test_vue_frontend_contract.py
This commit is contained in:
Miayui
2026-09-17 11:16:48 +08:00
31 changed files with 2925 additions and 127 deletions
+46
View File
@@ -323,6 +323,11 @@ const context = {
closeModeGallery(options) { calls.push(["close", options.restoreFocus]); },
hideModeActionPopover() {},
showToast(message) { calls.push(["toast", message]); },
hasExecutableModeCommandFlow(mode) {
const flow = mode?.command_flow;
return Boolean(flow?.source_request_id)
|| Boolean(flow?.plans && Object.keys(flow.plans).length);
},
executeSavedModeProfile(mode, options) {
calls.push(["execute", mode.id, options.command]);
return Promise.resolve(true);
@@ -359,6 +364,42 @@ console.log(JSON.stringify({
assert "executeSavedModeProfile" in snippet
def test_n7_mode_command_flow_is_directly_executable_without_source_request_id() -> None:
app = (WEB / "app.js").read_text(encoding="utf-8")
start = app.index("function hasExecutableModeCommandFlow")
end = app.index("\n\nasync function executeSavedModeProfile", start)
helper = app[start:end]
script = helper + r"""
console.log(JSON.stringify({
imported: hasExecutableModeCommandFlow({
id: "cinema",
command_flow: {
source_topic: "from_n7/scenes",
plans: { from_n7_scene: { spec: { commands: [] } } },
},
}),
generated: hasExecutableModeCommandFlow({
id: "reading-user",
command_flow: { source_request_id: "req-reading" },
}),
missing: hasExecutableModeCommandFlow({ id: "office" }),
}));
"""
completed = subprocess.run(
[node_runtime(), "-e", script],
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
assert json.loads(completed.stdout) == {
"imported": True,
"generated": True,
"missing": False,
}
def test_mode_card_without_saved_flow_falls_back_to_assistant_submission() -> None:
app = (WEB / "app.js").read_text(encoding="utf-8")
start = app.index("function applyModeCommand")
@@ -391,6 +432,11 @@ const context = {
setModeExecutionState(modeId, status) { calls.push(["state", modeId, status]); },
closeModeGallery(options) { calls.push(["close", options.restoreFocus]); },
showToast(message) { calls.push(["toast", message]); },
hasExecutableModeCommandFlow(mode) {
const flow = mode?.command_flow;
return Boolean(flow?.source_request_id)
|| Boolean(flow?.plans && Object.keys(flow.plans).length);
},
$(selector) {
return { "#utterance": input, "#requestForm": form, "#runButton": runButton }[selector];
},