feat: finish app-side master agent control surfaces
This commit is contained in:
@@ -1818,6 +1818,16 @@ public class MainActivity extends AppCompatActivity {
|
||||
intent.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_ID, "master-agent");
|
||||
intent.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||
break;
|
||||
case "master_agent_memory":
|
||||
intent = new Intent(this, MasterAgentMemoryActivity.class);
|
||||
intent.putExtra(MasterAgentMemoryActivity.EXTRA_PROJECT_ID, "master-agent");
|
||||
intent.putExtra(MasterAgentMemoryActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||
break;
|
||||
case "master_agent_takeover":
|
||||
intent = new Intent(this, MasterAgentTakeoverActivity.class);
|
||||
intent.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_ID, "master-agent");
|
||||
intent.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||
break;
|
||||
case "master_agent_evolution":
|
||||
intent = new Intent(this, MasterAgentEvolutionActivity.class);
|
||||
break;
|
||||
|
||||
@@ -19,6 +19,7 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity {
|
||||
private @Nullable String currentMode;
|
||||
private @Nullable String statusMessage;
|
||||
private boolean statusIsError;
|
||||
private boolean canManageEvolution = true;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -105,6 +106,7 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity {
|
||||
JSONArray signals = payload.optJSONArray("signals");
|
||||
JSONArray proposals = payload.optJSONArray("proposals");
|
||||
JSONArray rules = payload.optJSONArray("rules");
|
||||
canManageEvolution = payload.optBoolean("canManage", true);
|
||||
|
||||
currentMode = config == null ? "controlled" : config.optString("mode", "controlled");
|
||||
boolean autoApplyLowRiskRules = config != null && config.optBoolean("autoApplyLowRiskRules", false);
|
||||
@@ -116,7 +118,9 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity {
|
||||
this,
|
||||
"主 Agent 自动进化",
|
||||
"最近在学什么、打算怎么改、已经生效了什么",
|
||||
"支持在这里切换 controlled / autonomous,并直接审核待处理提案。"
|
||||
canManageEvolution
|
||||
? "支持在这里切换 controlled / autonomous,并直接审核待处理提案。"
|
||||
: "当前是只读视角,可以查看主 Agent 正在学习和生效的规则。"
|
||||
));
|
||||
|
||||
contentRoot.addView(BossUi.buildSoftPanel(
|
||||
@@ -127,13 +131,17 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity {
|
||||
));
|
||||
maybeRenderStatusBanner();
|
||||
|
||||
Button controlledButton = BossUi.buildMiniActionButton(this, "切到受控模式", false);
|
||||
controlledButton.setEnabled(!"controlled".equals(currentMode));
|
||||
controlledButton.setOnClickListener(v -> switchMode("controlled"));
|
||||
Button autonomousButton = BossUi.buildMiniActionButton(this, "切到全自动模式", true);
|
||||
autonomousButton.setEnabled(!"autonomous".equals(currentMode));
|
||||
autonomousButton.setOnClickListener(v -> switchMode("autonomous"));
|
||||
contentRoot.addView(BossUi.buildInlineActionRow(this, controlledButton, autonomousButton));
|
||||
if (canManageEvolution) {
|
||||
Button controlledButton = BossUi.buildMiniActionButton(this, "切到受控模式", false);
|
||||
controlledButton.setEnabled(!"controlled".equals(currentMode));
|
||||
controlledButton.setOnClickListener(v -> switchMode("controlled"));
|
||||
Button autonomousButton = BossUi.buildMiniActionButton(this, "切到全自动模式", true);
|
||||
autonomousButton.setEnabled(!"autonomous".equals(currentMode));
|
||||
autonomousButton.setOnClickListener(v -> switchMode("autonomous"));
|
||||
contentRoot.addView(BossUi.buildInlineActionRow(this, controlledButton, autonomousButton));
|
||||
} else {
|
||||
contentRoot.addView(BossUi.buildEmptyCard(this, "你当前没有管理权限,模式切换和提案审批仅管理员可操作。"));
|
||||
}
|
||||
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
@@ -192,11 +200,13 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity {
|
||||
null,
|
||||
null
|
||||
));
|
||||
Button rejectButton = BossUi.buildMiniActionButton(this, "拒绝", false);
|
||||
rejectButton.setOnClickListener(v -> reviewProposal(proposalId, false));
|
||||
Button approveButton = BossUi.buildMiniActionButton(this, "批准", true);
|
||||
approveButton.setOnClickListener(v -> reviewProposal(proposalId, true));
|
||||
contentRoot.addView(BossUi.buildInlineActionRow(this, rejectButton, approveButton));
|
||||
if (canManageEvolution) {
|
||||
Button rejectButton = BossUi.buildMiniActionButton(this, "拒绝", false);
|
||||
rejectButton.setOnClickListener(v -> reviewProposal(proposalId, false));
|
||||
Button approveButton = BossUi.buildMiniActionButton(this, "批准", true);
|
||||
approveButton.setOnClickListener(v -> reviewProposal(proposalId, true));
|
||||
contentRoot.addView(BossUi.buildInlineActionRow(this, rejectButton, approveButton));
|
||||
}
|
||||
}
|
||||
if (!rendered) {
|
||||
contentRoot.addView(BossUi.buildEmptyCard(this, "当前没有待审批提案。"));
|
||||
|
||||
@@ -104,7 +104,7 @@ public class MasterAgentMemoryActivity extends BossScreenActivity {
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
"记忆说明",
|
||||
"主 Agent 会自动沉淀长期有用的信息。你也可以在这里手动新增、编辑或删除。",
|
||||
"主 Agent 会自动沉淀长期有用的信息。你也可以在这里手动新增、编辑或归档。",
|
||||
"底层是结构化存储,项目记忆会显示真实 projectId。"
|
||||
));
|
||||
|
||||
@@ -242,23 +242,23 @@ public class MasterAgentMemoryActivity extends BossScreenActivity {
|
||||
));
|
||||
|
||||
if (memory != null) {
|
||||
builder.setNeutralButton("删除", (dialog, which) -> confirmDeleteMemory(memory));
|
||||
builder.setNeutralButton("归档", (dialog, which) -> confirmArchiveMemory(memory));
|
||||
}
|
||||
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void confirmDeleteMemory(JSONObject memory) {
|
||||
private void confirmArchiveMemory(JSONObject memory) {
|
||||
final String memoryId = memory.optString("memoryId", "");
|
||||
if (memoryId.isEmpty()) {
|
||||
showMessage("缺少 memoryId");
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("删除记忆")
|
||||
.setMessage("确定删除这条记忆吗?")
|
||||
.setTitle("归档记忆")
|
||||
.setMessage("确定归档这条记忆吗?归档后会从当前列表移除,不是永久删除。")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("删除", (dialog, which) -> deleteMemory(memoryId))
|
||||
.setPositiveButton("归档", (dialog, which) -> archiveMemory(memoryId))
|
||||
.show();
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ public class MasterAgentMemoryActivity extends BossScreenActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteMemory(String memoryId) {
|
||||
private void archiveMemory(String memoryId) {
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
@@ -338,14 +338,14 @@ public class MasterAgentMemoryActivity extends BossScreenActivity {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage("记忆已删除");
|
||||
showMessage("记忆已归档");
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
});
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
showMessage("记忆删除失败:" + error.getMessage());
|
||||
showMessage("记忆归档失败:" + error.getMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,6 +34,8 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
private @Nullable String backendOverrideText;
|
||||
private boolean clawSelectable;
|
||||
private @Nullable String clawReasonLabel;
|
||||
private boolean hermesSelectable;
|
||||
private @Nullable String hermesReasonLabel;
|
||||
private final List<String> backendOverrideValues = new ArrayList<>();
|
||||
private EditText userPromptInput;
|
||||
private EditText projectPromptInput;
|
||||
@@ -84,6 +86,7 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
userPrompt = payload.optJSONObject("userPrompt");
|
||||
projectControls = payload.optJSONObject("projectControls");
|
||||
JSONObject clawAvailability = payload.optJSONObject("clawAvailability");
|
||||
JSONObject hermesAvailability = payload.optJSONObject("hermesAvailability");
|
||||
adminPromptText = promptPolicy == null ? null : promptPolicy.optString("globalPrompt", "");
|
||||
userPromptText = userPrompt == null ? "" : userPrompt.optString("content", "");
|
||||
projectPromptOverrideText = payload.optString(
|
||||
@@ -93,6 +96,8 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
backendOverrideText = projectControls == null ? "" : projectControls.optString("backendOverride", "");
|
||||
clawSelectable = clawAvailability != null && clawAvailability.optBoolean("selectable", false);
|
||||
clawReasonLabel = clawAvailability == null ? "" : clawAvailability.optString("reasonLabel", "");
|
||||
hermesSelectable = hermesAvailability != null && hermesAvailability.optBoolean("selectable", false);
|
||||
hermesReasonLabel = hermesAvailability == null ? "" : hermesAvailability.optString("reasonLabel", "");
|
||||
|
||||
replaceContent();
|
||||
appendContent(BossUi.buildSimpleProfileHeader(
|
||||
@@ -138,6 +143,10 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
backendOverrideValues.add("claw-runtime");
|
||||
backendLabels.add("Claw Runtime");
|
||||
}
|
||||
if (hermesSelectable) {
|
||||
backendOverrideValues.add("hermes-runtime");
|
||||
backendLabels.add("Hermes Runtime");
|
||||
}
|
||||
|
||||
backendSpinner = new Spinner(this);
|
||||
backendSpinner.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, backendLabels));
|
||||
@@ -170,6 +179,16 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
: "恢复可用后,执行后端下拉框会重新出现 Claw Runtime。"
|
||||
));
|
||||
}
|
||||
if (!hermesSelectable) {
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
"Hermes Runtime 当前不可用",
|
||||
TextUtils.isEmpty(hermesReasonLabel) ? "当前环境未满足 Hermes Runtime 的启动条件。" : hermesReasonLabel,
|
||||
TextUtils.equals(backendOverrideText, "hermes-runtime")
|
||||
? "当前对话之前保存过 Hermes Runtime,运行时会自动回退到默认后端。"
|
||||
: "恢复可用后,执行后端下拉框会重新出现 Hermes Runtime。"
|
||||
));
|
||||
}
|
||||
|
||||
previewTextView = new TextView(this);
|
||||
previewTextView.setText(buildPreviewText());
|
||||
@@ -235,6 +254,8 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
builder.append("【执行后端】\n").append(backendValue).append("\n\n");
|
||||
} else if (TextUtils.equals(backendOverrideText, "claw-runtime") && !clawSelectable) {
|
||||
builder.append("【执行后端】\n默认(Claw Runtime 当前不可用,运行时会自动回退)\n\n");
|
||||
} else if (TextUtils.equals(backendOverrideText, "hermes-runtime") && !hermesSelectable) {
|
||||
builder.append("【执行后端】\n默认(Hermes Runtime 当前不可用,运行时会自动回退)\n\n");
|
||||
}
|
||||
if (builder.length() == 0) {
|
||||
return "当前没有任何提示词内容。";
|
||||
|
||||
@@ -19,7 +19,9 @@ public final class WechatSurfaceMapper {
|
||||
);
|
||||
|
||||
private static final List<MeMenuItem> ROOT_ME_MENU_ITEMS = Arrays.asList(
|
||||
new MeMenuItem("master_agent_prompt", "主 Agent 提示词 / 记忆", "配置全局主提示词、当前主提示词和用户记忆"),
|
||||
new MeMenuItem("master_agent_prompt", "主 Agent 提示词", "配置全局主提示词和当前对话提示词"),
|
||||
new MeMenuItem("master_agent_memory", "主 Agent 记忆", "维护用户通用记忆和项目记忆"),
|
||||
new MeMenuItem("master_agent_takeover", "全局接管", "设置主 Agent 是否默认协同推进线程"),
|
||||
new MeMenuItem("master_agent_evolution", "主 Agent 自动进化", "查看进化信号、提案与自动采纳规则"),
|
||||
new MeMenuItem("security", "账号与安全", "修改登录密码、设备安全与身份校验"),
|
||||
new MeMenuItem("settings", "设置", "默认首页、提醒方式与危险操作确认"),
|
||||
|
||||
@@ -40,7 +40,7 @@ public class BossUiRootSurfaceTest {
|
||||
ReflectionHelpers.callInstanceMethod(activity, "renderMeRoot");
|
||||
|
||||
LinearLayout content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
assertEquals("我的页应是资料头 + 8 条菜单", 9, content.getChildCount());
|
||||
assertEquals("我的页应是资料头 + 10 条菜单", 11, content.getChildCount());
|
||||
|
||||
View header = content.getChildAt(0);
|
||||
assertEquals("资料头不应保留浮层卡片感", 0f, header.getElevation(), 0.01f);
|
||||
@@ -49,7 +49,9 @@ public class BossUiRootSurfaceTest {
|
||||
assertTrue(viewTreeContainsText(header, "最高管理员"));
|
||||
assertTrue(viewTreeContainsText(header, "主控账号已启用安全保护"));
|
||||
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 提示词 / 记忆"));
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 提示词"));
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 记忆"));
|
||||
assertTrue(viewTreeContainsText(content, "全局接管"));
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 自动进化"));
|
||||
assertTrue(viewTreeContainsText(content, "账号与安全"));
|
||||
assertTrue(viewTreeContainsText(content, "设置"));
|
||||
|
||||
@@ -31,6 +31,38 @@ public class MainActivityMeEntryNavigationTest {
|
||||
assertEquals("主 Agent", started.getStringExtra(MasterAgentPromptActivity.EXTRA_PROJECT_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void masterAgentMemoryMeEntryOpensMemoryActivityForMasterAgent() {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"openMeEntry",
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "master_agent_memory")
|
||||
);
|
||||
|
||||
Intent started = Shadows.shadowOf(activity).getNextStartedActivity();
|
||||
assertEquals(MasterAgentMemoryActivity.class.getName(), started.getComponent().getClassName());
|
||||
assertEquals("master-agent", started.getStringExtra(MasterAgentMemoryActivity.EXTRA_PROJECT_ID));
|
||||
assertEquals("主 Agent", started.getStringExtra(MasterAgentMemoryActivity.EXTRA_PROJECT_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void masterAgentTakeoverMeEntryOpensTakeoverActivityForMasterAgent() {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"openMeEntry",
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "master_agent_takeover")
|
||||
);
|
||||
|
||||
Intent started = Shadows.shadowOf(activity).getNextStartedActivity();
|
||||
assertEquals(MasterAgentTakeoverActivity.class.getName(), started.getComponent().getClassName());
|
||||
assertEquals("master-agent", started.getStringExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_ID));
|
||||
assertEquals("主 Agent", started.getStringExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void masterAgentEvolutionMeEntryOpensEvolutionActivity() {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().get();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
@@ -91,6 +92,43 @@ public class MasterAgentEvolutionActivityTest {
|
||||
assertEquals(1, activity.reloadCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderDashboardHidesManageActionsForReadonlyView() throws Exception {
|
||||
TestMasterAgentEvolutionActivity activity = Robolectric
|
||||
.buildActivity(TestMasterAgentEvolutionActivity.class, new Intent())
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject payload = new JSONObject()
|
||||
.put("canManage", false)
|
||||
.put("config", new JSONObject()
|
||||
.put("mode", "controlled")
|
||||
.put("autoApplyLowRiskRules", false))
|
||||
.put("signals", new JSONArray())
|
||||
.put("proposals", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("proposalId", "proposal-1")
|
||||
.put("status", "pending_review")
|
||||
.put("proposalType", "fast_path_rule")
|
||||
.put("riskLevel", "low")
|
||||
.put("createdAt", "2026-04-16T12:01:00+08:00")
|
||||
.put("title", "新增 Fast Path")
|
||||
.put("summary", "把状态查询加入本地直答")))
|
||||
.put("rules", new JSONArray());
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderDashboard",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, payload)
|
||||
);
|
||||
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "当前是只读视角"));
|
||||
assertTrue(viewTreeContainsText(content, "你当前没有管理权限"));
|
||||
assertFalse(viewTreeContainsText(content, "切到全自动模式"));
|
||||
assertFalse(viewTreeContainsText(content, "批准"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderDashboardShowsStatusBannerAndFormattedTimes() throws Exception {
|
||||
TestMasterAgentEvolutionActivity activity = Robolectric
|
||||
|
||||
@@ -193,6 +193,86 @@ public class MasterAgentPromptActivityTest {
|
||||
assertTrue(viewTreeContainsText(content, "未检测到有效的 Claw 启动脚本"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderPromptProfileIncludesHermesRuntimeWhenSelectable() throws Exception {
|
||||
TestMasterAgentPromptActivity activity = Robolectric
|
||||
.buildActivity(
|
||||
TestMasterAgentPromptActivity.class,
|
||||
new Intent()
|
||||
.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||
.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_NAME, "主 Agent")
|
||||
)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject payload = new JSONObject()
|
||||
.put("promptPolicy", new JSONObject().put("globalPrompt", "全局主提示词"))
|
||||
.put("userPrompt", new JSONObject().put("content", "用户私有主提示词"))
|
||||
.put("projectControls", new JSONObject()
|
||||
.put("promptOverride", "当前对话提示词")
|
||||
.put("backendOverride", "hermes-runtime"))
|
||||
.put("clawAvailability", new JSONObject()
|
||||
.put("status", "ready")
|
||||
.put("selectable", true)
|
||||
.put("reasonLabel", "Claw Runtime 可用。"))
|
||||
.put("hermesAvailability", new JSONObject()
|
||||
.put("status", "ready")
|
||||
.put("selectable", true)
|
||||
.put("reasonLabel", "Hermes Runtime 可用。"));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderPromptProfile",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, payload)
|
||||
);
|
||||
|
||||
Spinner backendSpinner = ReflectionHelpers.getField(activity, "backendSpinner");
|
||||
assertEquals(3, backendSpinner.getAdapter().getCount());
|
||||
assertEquals(2, backendSpinner.getSelectedItemPosition());
|
||||
assertEquals("Hermes Runtime", String.valueOf(backendSpinner.getAdapter().getItem(2)));
|
||||
TextView previewTextView = ReflectionHelpers.getField(activity, "previewTextView");
|
||||
assertTrue(String.valueOf(previewTextView.getText()).contains("【执行后端】"));
|
||||
assertTrue(String.valueOf(previewTextView.getText()).contains("hermes-runtime"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderPromptProfileShowsHermesUnavailableHintWhenStoredOverrideCannotBeSelected() throws Exception {
|
||||
TestMasterAgentPromptActivity activity = Robolectric
|
||||
.buildActivity(
|
||||
TestMasterAgentPromptActivity.class,
|
||||
new Intent()
|
||||
.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||
.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_NAME, "主 Agent")
|
||||
)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject payload = new JSONObject()
|
||||
.put("promptPolicy", new JSONObject().put("globalPrompt", "全局主提示词"))
|
||||
.put("userPrompt", new JSONObject().put("content", "用户私有主提示词"))
|
||||
.put("projectControls", new JSONObject()
|
||||
.put("promptOverride", "当前对话提示词")
|
||||
.put("backendOverride", "hermes-runtime"))
|
||||
.put("hermesAvailability", new JSONObject()
|
||||
.put("status", "misconfigured")
|
||||
.put("selectable", false)
|
||||
.put("reason", "script_not_found")
|
||||
.put("reasonLabel", "未检测到有效的 Hermes 启动脚本,将自动回退到默认后端。"));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderPromptProfile",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, payload)
|
||||
);
|
||||
|
||||
Spinner backendSpinner = ReflectionHelpers.getField(activity, "backendSpinner");
|
||||
assertEquals(1, backendSpinner.getAdapter().getCount());
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "Hermes Runtime 当前不可用"));
|
||||
assertTrue(viewTreeContainsText(content, "未检测到有效的 Hermes 启动脚本"));
|
||||
assertTrue(viewTreeContainsText(content, "当前对话之前保存过 Hermes Runtime"));
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
|
||||
@@ -10,8 +10,12 @@ public class WechatSurfaceMapperMeMenuTest {
|
||||
WechatSurfaceMapper.MeMenuItem[] items = WechatSurfaceMapper.rootMeMenuItems();
|
||||
|
||||
assertEquals("master_agent_prompt", items[0].key);
|
||||
assertEquals("主 Agent 提示词 / 记忆", items[0].title);
|
||||
assertEquals("master_agent_evolution", items[1].key);
|
||||
assertEquals("主 Agent 自动进化", items[1].title);
|
||||
assertEquals("主 Agent 提示词", items[0].title);
|
||||
assertEquals("master_agent_memory", items[1].key);
|
||||
assertEquals("主 Agent 记忆", items[1].title);
|
||||
assertEquals("master_agent_takeover", items[2].key);
|
||||
assertEquals("全局接管", items[2].title);
|
||||
assertEquals("master_agent_evolution", items[3].key);
|
||||
assertEquals("主 Agent 自动进化", items[3].title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ public class WechatSurfaceMapperTest {
|
||||
@Test
|
||||
public void rootMeMenuTitles_matchLegacyWechatMenuWithOpsEntry() throws Exception {
|
||||
assertArrayEquals(
|
||||
new String[]{"主 Agent 提示词 / 记忆", "主 Agent 自动进化", "账号与安全", "设置", "运维与修复", "AI 账号", "技能", "关于"},
|
||||
new String[]{"主 Agent 提示词", "主 Agent 记忆", "全局接管", "主 Agent 自动进化", "账号与安全", "设置", "运维与修复", "AI 账号", "技能", "关于"},
|
||||
WechatSurfaceMapper.rootMeMenuTitles()
|
||||
);
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public class WechatSurfaceMapperTest {
|
||||
@Test
|
||||
public void mainPage_keepsOpsEntryInStableWechatMenuOrder() throws Exception {
|
||||
assertArrayEquals(
|
||||
new String[]{"主 Agent 提示词 / 记忆", "主 Agent 自动进化", "账号与安全", "设置", "运维与修复", "AI 账号", "技能", "关于"},
|
||||
new String[]{"主 Agent 提示词", "主 Agent 记忆", "全局接管", "主 Agent 自动进化", "账号与安全", "设置", "运维与修复", "AI 账号", "技能", "关于"},
|
||||
WechatSurfaceMapper.rootMeMenuTitles()
|
||||
);
|
||||
}
|
||||
@@ -380,18 +380,22 @@ public class WechatSurfaceMapperTest {
|
||||
public void meMenuItems_useStableKeysInsteadOfDisplayTitlesForRouting() throws Exception {
|
||||
WechatSurfaceMapper.MeMenuItem[] items = WechatSurfaceMapper.rootMeMenuItems();
|
||||
|
||||
assertEquals(8, items.length);
|
||||
assertEquals(10, items.length);
|
||||
assertEquals("master_agent_prompt", items[0].key);
|
||||
assertEquals("主 Agent 提示词 / 记忆", items[0].title);
|
||||
assertEquals("master_agent_evolution", items[1].key);
|
||||
assertEquals("主 Agent 自动进化", items[1].title);
|
||||
assertEquals("security", items[2].key);
|
||||
assertEquals("settings", items[3].key);
|
||||
assertEquals("ops", items[4].key);
|
||||
assertEquals("运维与修复", items[4].title);
|
||||
assertEquals("ai_accounts", items[5].key);
|
||||
assertEquals("skills", items[6].key);
|
||||
assertEquals("about", items[7].key);
|
||||
assertEquals("主 Agent 提示词", items[0].title);
|
||||
assertEquals("master_agent_memory", items[1].key);
|
||||
assertEquals("主 Agent 记忆", items[1].title);
|
||||
assertEquals("master_agent_takeover", items[2].key);
|
||||
assertEquals("全局接管", items[2].title);
|
||||
assertEquals("master_agent_evolution", items[3].key);
|
||||
assertEquals("主 Agent 自动进化", items[3].title);
|
||||
assertEquals("security", items[4].key);
|
||||
assertEquals("settings", items[5].key);
|
||||
assertEquals("ops", items[6].key);
|
||||
assertEquals("运维与修复", items[6].title);
|
||||
assertEquals("ai_accounts", items[7].key);
|
||||
assertEquals("skills", items[8].key);
|
||||
assertEquals("about", items[9].key);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user