feat: move global takeover into master-agent menu
This commit is contained in:
@@ -52,6 +52,7 @@
|
||||
<activity android:name=".AiAccountsActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".OpenAiOnboardingActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".MasterAgentPromptActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".MasterAgentTakeoverActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".MasterAgentMemoryActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".OpsCenterActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".AboutActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
|
||||
@@ -12,7 +12,6 @@ import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -33,14 +32,12 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
private @Nullable String userPromptText;
|
||||
private @Nullable String projectPromptOverrideText;
|
||||
private @Nullable String backendOverrideText;
|
||||
private boolean globalTakeoverEnabled;
|
||||
private boolean clawSelectable;
|
||||
private @Nullable String clawReasonLabel;
|
||||
private final List<String> backendOverrideValues = new ArrayList<>();
|
||||
private EditText userPromptInput;
|
||||
private EditText projectPromptInput;
|
||||
private Spinner backendSpinner;
|
||||
private SwitchCompat globalTakeoverSwitch;
|
||||
private TextView previewTextView;
|
||||
|
||||
@Override
|
||||
@@ -94,7 +91,6 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
projectControls == null ? "" : projectControls.optString("promptOverride", "")
|
||||
);
|
||||
backendOverrideText = projectControls == null ? "" : projectControls.optString("backendOverride", "");
|
||||
globalTakeoverEnabled = projectControls != null && projectControls.optBoolean("globalTakeoverEnabled", false);
|
||||
clawSelectable = clawAvailability != null && clawAvailability.optBoolean("selectable", false);
|
||||
clawReasonLabel = clawAvailability == null ? "" : clawAvailability.optString("reasonLabel", "");
|
||||
|
||||
@@ -164,16 +160,6 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
backendSpinner
|
||||
));
|
||||
|
||||
globalTakeoverSwitch = new SwitchCompat(this);
|
||||
globalTakeoverSwitch.setText("开启");
|
||||
globalTakeoverSwitch.setChecked(globalTakeoverEnabled);
|
||||
globalTakeoverSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> refreshPreview());
|
||||
appendContent(BossUi.buildFormCell(
|
||||
this,
|
||||
"全局主 Agent 协同接管",
|
||||
"为线程会话默认开启主 Agent 协同推进。不会抢走你直接控制线程开发的能力。",
|
||||
globalTakeoverSwitch
|
||||
));
|
||||
if (!clawSelectable) {
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
@@ -250,12 +236,6 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
} else if (TextUtils.equals(backendOverrideText, "claw-runtime") && !clawSelectable) {
|
||||
builder.append("【执行后端】\n默认(Claw Runtime 当前不可用,运行时会自动回退)\n\n");
|
||||
}
|
||||
boolean globalTakeover = globalTakeoverSwitch != null
|
||||
? globalTakeoverSwitch.isChecked()
|
||||
: globalTakeoverEnabled;
|
||||
builder.append("【全局主 Agent 协同接管】\n")
|
||||
.append(globalTakeover ? "已开启" : "已关闭")
|
||||
.append("(不会抢走你直接控制线程开发)\n\n");
|
||||
if (builder.length() == 0) {
|
||||
return "当前没有任何提示词内容。";
|
||||
}
|
||||
@@ -272,7 +252,6 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
final String backendOverride = backendSpinner == null
|
||||
? ""
|
||||
: backendOverrideValues.get(backendSpinner.getSelectedItemPosition());
|
||||
final boolean globalTakeover = globalTakeoverSwitch != null && globalTakeoverSwitch.isChecked();
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
@@ -284,14 +263,6 @@ public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
BossApiClient.ApiResponse controlsResponse = apiClient.updateProjectTakeoverSettings(
|
||||
projectId,
|
||||
null,
|
||||
globalTakeover
|
||||
);
|
||||
if (!controlsResponse.ok()) {
|
||||
throw new IllegalStateException(controlsResponse.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage("提示词已保存");
|
||||
setResult(RESULT_OK);
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class MasterAgentTakeoverActivity extends BossScreenActivity {
|
||||
public static final String EXTRA_PROJECT_ID = "project_id";
|
||||
public static final String EXTRA_PROJECT_NAME = "project_name";
|
||||
|
||||
private String projectId;
|
||||
private String projectName;
|
||||
private boolean contentLoaded;
|
||||
private boolean globalTakeoverEnabled;
|
||||
private SwitchCompat globalTakeoverSwitch;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
projectId = getIntent().getStringExtra(EXTRA_PROJECT_ID);
|
||||
projectName = getIntent().getStringExtra(EXTRA_PROJECT_NAME);
|
||||
configureScreen("全局接管", projectName == null ? "主 Agent 协同推进" : projectName);
|
||||
setHeaderAction("保存", v -> saveTakeoverSettings());
|
||||
updateSaveAvailability();
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reload() {
|
||||
if (projectId == null || projectId.isEmpty()) {
|
||||
replaceContent(BossUi.buildEmptyCard(this, "缺少 projectId。"));
|
||||
setRefreshing(false);
|
||||
contentLoaded = false;
|
||||
updateSaveAvailability();
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.getProjectAgentControls(projectId);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> renderTakeoverSettings(response.json));
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
contentLoaded = false;
|
||||
updateSaveAvailability();
|
||||
replaceContent(BossUi.buildEmptyCard(this, "全局接管加载失败:" + error.getMessage()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void renderTakeoverSettings(JSONObject payload) {
|
||||
JSONObject controls = payload.optJSONObject("controls");
|
||||
globalTakeoverEnabled = controls != null && controls.optBoolean("globalTakeoverEnabled", false);
|
||||
|
||||
replaceContent();
|
||||
appendContent(BossUi.buildSimpleProfileHeader(
|
||||
this,
|
||||
projectName == null ? "主 Agent" : projectName,
|
||||
"全局主 Agent 协同推进",
|
||||
"为线程会话默认开启协同推进,不会抢走你继续直接控制线程开发的能力。"
|
||||
));
|
||||
|
||||
globalTakeoverSwitch = new SwitchCompat(this);
|
||||
globalTakeoverSwitch.setText("开启");
|
||||
globalTakeoverSwitch.setChecked(globalTakeoverEnabled);
|
||||
appendContent(BossUi.buildFormCell(
|
||||
this,
|
||||
"全局主 Agent 协同接管",
|
||||
"开启后,线程会话默认跟随全局协同推进;线程会话仍可单独覆盖。",
|
||||
globalTakeoverSwitch
|
||||
));
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
"说明",
|
||||
"主 Agent 会理解项目状态、给建议、补调度方案,但不会因为介入就抢走你继续直接控制线程开发的能力。",
|
||||
"线程级开关优先于这里的全局默认。"
|
||||
));
|
||||
|
||||
contentLoaded = true;
|
||||
updateSaveAvailability();
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
private void saveTakeoverSettings() {
|
||||
if (!contentLoaded) {
|
||||
showMessage("全局接管尚未加载完成,请先刷新成功后再保存。");
|
||||
return;
|
||||
}
|
||||
final boolean enabled = globalTakeoverSwitch != null && globalTakeoverSwitch.isChecked();
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.updateProjectTakeoverSettings(
|
||||
projectId,
|
||||
null,
|
||||
enabled
|
||||
);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage(enabled ? "已开启全局主 Agent 协同接管" : "已关闭全局主 Agent 协同接管");
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
});
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
showMessage("保存失败:" + error.getMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateSaveAvailability() {
|
||||
if (headerActionButton != null) {
|
||||
headerActionButton.setEnabled(contentLoaded);
|
||||
headerActionButton.setAlpha(contentLoaded ? 1f : 0.45f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
private ProjectChatUiState.SelectionState selectionState = ProjectChatUiState.emptySelection();
|
||||
private ActivityResultLauncher<Intent> conversationInfoLauncher;
|
||||
private ActivityResultLauncher<Intent> masterAgentPromptLauncher;
|
||||
private ActivityResultLauncher<Intent> masterAgentTakeoverLauncher;
|
||||
private ActivityResultLauncher<Intent> masterAgentMemoryLauncher;
|
||||
private ActivityResultLauncher<Intent> forwardTargetLauncher;
|
||||
private ActivityResultLauncher<String> imagePickerLauncher;
|
||||
@@ -181,6 +182,14 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
}
|
||||
}
|
||||
);
|
||||
masterAgentTakeoverLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK) {
|
||||
reload(true);
|
||||
}
|
||||
}
|
||||
);
|
||||
masterAgentMemoryLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
@@ -790,12 +799,23 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
masterAgentMemoryLauncher.launch(intent);
|
||||
}
|
||||
|
||||
private void openMasterAgentTakeoverSettings() {
|
||||
if (projectId == null || projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(this, MasterAgentTakeoverActivity.class);
|
||||
intent.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_ID, projectId);
|
||||
intent.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_NAME, initialProjectName);
|
||||
masterAgentTakeoverLauncher.launch(intent);
|
||||
}
|
||||
|
||||
private void showMasterAgentMoreMenu() {
|
||||
if (!isMasterAgentConversation()) {
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(this)
|
||||
.setItems(new CharSequence[]{"模型", "推理强度", "提示词", "记忆", "会话信息", "刷新"}, (dialog, which) -> {
|
||||
.setItems(new CharSequence[]{"模型", "推理强度", "全局接管", "提示词", "记忆", "会话信息", "刷新"}, (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0:
|
||||
showMasterAgentModelPicker();
|
||||
@@ -804,15 +824,18 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
showMasterAgentReasoningPicker();
|
||||
break;
|
||||
case 2:
|
||||
openMasterAgentPromptProfile();
|
||||
openMasterAgentTakeoverSettings();
|
||||
break;
|
||||
case 3:
|
||||
openMasterAgentMemories();
|
||||
openMasterAgentPromptProfile();
|
||||
break;
|
||||
case 4:
|
||||
openConversationInfo();
|
||||
openMasterAgentMemories();
|
||||
break;
|
||||
case 5:
|
||||
openConversationInfo();
|
||||
break;
|
||||
case 6:
|
||||
reload(true);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -54,8 +54,7 @@ public class MasterAgentPromptActivityTest {
|
||||
.put("userPrompt", new JSONObject().put("content", "用户私有主提示词"))
|
||||
.put("projectControls", new JSONObject()
|
||||
.put("promptOverride", "当前对话提示词")
|
||||
.put("backendOverride", "claw-runtime")
|
||||
.put("globalTakeoverEnabled", true));
|
||||
.put("backendOverride", "claw-runtime"));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
@@ -69,7 +68,6 @@ public class MasterAgentPromptActivityTest {
|
||||
assertTrue(viewTreeContainsText(content, "用户私有主提示词"));
|
||||
assertTrue(viewTreeContainsText(content, "当前对话提示词"));
|
||||
assertTrue(viewTreeContainsText(content, "执行后端"));
|
||||
assertTrue(viewTreeContainsText(content, "全局主 Agent 协同接管"));
|
||||
assertTrue(viewTreeContainsText(content, "合成预览"));
|
||||
}
|
||||
|
||||
@@ -286,17 +284,6 @@ public class MasterAgentPromptActivityTest {
|
||||
// JVM 单测不需要落 Android 侧身份缓存。
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse updateProjectTakeoverSettings(String projectId, Boolean takeoverEnabled, Boolean globalTakeoverEnabled) {
|
||||
try {
|
||||
return new ApiResponse(
|
||||
200,
|
||||
new JSONObject().put("ok", true)
|
||||
);
|
||||
} catch (Exception error) {
|
||||
throw new IllegalStateException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InMemorySharedPreferences implements android.content.SharedPreferences {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class MasterAgentTakeoverActivityTest {
|
||||
@Test
|
||||
public void renderTakeoverSettingsShowsStandaloneGlobalTakeoverSection() throws Exception {
|
||||
MasterAgentTakeoverActivity activity = Robolectric
|
||||
.buildActivity(
|
||||
MasterAgentTakeoverActivity.class,
|
||||
new Intent()
|
||||
.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||
.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_NAME, "主 Agent")
|
||||
)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject payload = new JSONObject()
|
||||
.put("controls", new JSONObject().put("globalTakeoverEnabled", true));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderTakeoverSettings",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, payload)
|
||||
);
|
||||
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "全局主 Agent 协同接管"));
|
||||
assertTrue(viewTreeContainsText(content, "线程会话默认跟随全局协同推进"));
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (text != null && text.toString().contains(expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -44,10 +44,11 @@ public class ProjectDetailActivityMasterAgentMenuTest {
|
||||
|
||||
assertMenuItem(listView, 0, "模型");
|
||||
assertMenuItem(listView, 1, "推理强度");
|
||||
assertMenuItem(listView, 2, "提示词");
|
||||
assertMenuItem(listView, 3, "记忆");
|
||||
assertMenuItem(listView, 4, "会话信息");
|
||||
assertMenuItem(listView, 5, "刷新");
|
||||
assertMenuItem(listView, 2, "全局接管");
|
||||
assertMenuItem(listView, 3, "提示词");
|
||||
assertMenuItem(listView, 4, "记忆");
|
||||
assertMenuItem(listView, 5, "会话信息");
|
||||
assertMenuItem(listView, 6, "刷新");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user