feat: surface codex thread config progress
This commit is contained in:
@@ -1353,6 +1353,90 @@ public final class BossUi {
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject threadGoal = progress == null ? null : progress.optJSONObject("threadGoal");
|
||||
JSONObject threadSettings = progress == null ? null : progress.optJSONObject("threadSettings");
|
||||
JSONObject compaction = progress == null ? null : progress.optJSONObject("compaction");
|
||||
boolean hasThreadConfig = threadGoal != null || threadSettings != null || compaction != null;
|
||||
if (hasThreadConfig) {
|
||||
card.addView(divider(context));
|
||||
card.addView(sectionTitle(context, "线程配置"));
|
||||
if (threadGoal != null) {
|
||||
String status = threadGoal.optString("status", "").trim();
|
||||
String objective = threadGoal.optString("objective", "").trim();
|
||||
if ("cleared".equals(status)) {
|
||||
card.addView(detailRow(context, "◎", "目标已清除", "", false));
|
||||
} else if (!TextUtils.isEmpty(status) || !TextUtils.isEmpty(objective)) {
|
||||
card.addView(detailRow(
|
||||
context,
|
||||
"◎",
|
||||
TextUtils.isEmpty(status) ? "目标" : "目标 " + status,
|
||||
"",
|
||||
false
|
||||
));
|
||||
if (!TextUtils.isEmpty(objective)) {
|
||||
card.addView(detailRow(context, "", objective, "", false, true));
|
||||
}
|
||||
int tokensUsed = threadGoal.optInt("tokensUsed", 0);
|
||||
int tokenBudget = threadGoal.optInt("tokenBudget", 0);
|
||||
if (tokensUsed > 0 || tokenBudget > 0) {
|
||||
StringBuilder budget = new StringBuilder();
|
||||
budget.append("预算 ");
|
||||
if (tokensUsed > 0) {
|
||||
budget.append(String.format(Locale.US, "%,d", tokensUsed));
|
||||
} else {
|
||||
budget.append("0");
|
||||
}
|
||||
if (tokenBudget > 0) {
|
||||
budget.append(" / ").append(String.format(Locale.US, "%,d", tokenBudget));
|
||||
}
|
||||
card.addView(detailRow(context, "", budget.toString(), "", false, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (threadSettings != null) {
|
||||
String model = threadSettings.optString("model", "").trim();
|
||||
String provider = threadSettings.optString("modelProvider", "").trim();
|
||||
if (!TextUtils.isEmpty(model)) {
|
||||
card.addView(detailRow(
|
||||
context,
|
||||
"◇",
|
||||
TextUtils.isEmpty(provider) ? "模型 " + model : "模型 " + model + " · " + provider,
|
||||
"",
|
||||
false
|
||||
));
|
||||
}
|
||||
String approval = threadSettings.optString("approvalPolicy", "").trim();
|
||||
String sandbox = threadSettings.optString("sandboxPolicy", "").trim();
|
||||
if (!TextUtils.isEmpty(approval) || !TextUtils.isEmpty(sandbox)) {
|
||||
String label = !TextUtils.isEmpty(approval) && !TextUtils.isEmpty(sandbox)
|
||||
? "审批 " + approval + " · 沙箱 " + sandbox
|
||||
: !TextUtils.isEmpty(approval) ? "审批 " + approval : "沙箱 " + sandbox;
|
||||
card.addView(detailRow(context, "◇", label, "", false));
|
||||
}
|
||||
String collaborationMode = threadSettings.optString("collaborationMode", "").trim();
|
||||
String serviceTier = threadSettings.optString("serviceTier", "").trim();
|
||||
if (!TextUtils.isEmpty(collaborationMode) || !TextUtils.isEmpty(serviceTier)) {
|
||||
String label = !TextUtils.isEmpty(collaborationMode) && !TextUtils.isEmpty(serviceTier)
|
||||
? "协作 " + collaborationMode + " · " + serviceTier
|
||||
: !TextUtils.isEmpty(collaborationMode) ? "协作 " + collaborationMode : "服务 " + serviceTier;
|
||||
card.addView(detailRow(context, "◇", label, "", false));
|
||||
}
|
||||
}
|
||||
if (compaction != null) {
|
||||
String message = compaction.optString("message", "").trim();
|
||||
String status = compaction.optString("status", "").trim();
|
||||
if (!TextUtils.isEmpty(message) || !TextUtils.isEmpty(status)) {
|
||||
card.addView(detailRow(
|
||||
context,
|
||||
"◷",
|
||||
TextUtils.isEmpty(message) ? "上下文压缩 " + status : message,
|
||||
"",
|
||||
false
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject modelRoute = progress == null ? null : progress.optJSONObject("modelRoute");
|
||||
JSONObject tokenUsage = progress == null ? null : progress.optJSONObject("tokenUsage");
|
||||
JSONArray mcpServers = progress == null ? null : progress.optJSONArray("mcpServers");
|
||||
|
||||
@@ -1069,6 +1069,61 @@ public class ProjectDetailActivityUiTest {
|
||||
assertFalse(viewTreeContainsText(messageView, "sk-secret"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executionProgressMessageRendersCodexThreadGoalSettingsAndCompactionSections() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "thread-config")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "Boss开发主线程");
|
||||
TestProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject message = new JSONObject()
|
||||
.put("id", "progress-thread-config-1")
|
||||
.put("sender", "master")
|
||||
.put("senderLabel", "主 Agent")
|
||||
.put("body", "执行进度")
|
||||
.put("kind", "execution_progress")
|
||||
.put("sentAt", "2026-06-01T10:25:00+08:00")
|
||||
.put("executionProgress", new JSONObject()
|
||||
.put("status", "running")
|
||||
.put("steps", new JSONArray()
|
||||
.put(new JSONObject().put("text", "同步 Codex 线程配置").put("status", "running")))
|
||||
.put("threadGoal", new JSONObject()
|
||||
.put("objective", "完成 App Server 线程目标同步")
|
||||
.put("status", "active")
|
||||
.put("tokenBudget", 120000)
|
||||
.put("tokensUsed", 4800)
|
||||
.put("timeUsedSeconds", 600))
|
||||
.put("threadSettings", new JSONObject()
|
||||
.put("model", "gpt-5.5")
|
||||
.put("modelProvider", "openai")
|
||||
.put("approvalPolicy", "on-request")
|
||||
.put("sandboxPolicy", "workspaceWrite")
|
||||
.put("collaborationMode", "plan")
|
||||
.put("serviceTier", "fast")
|
||||
.put("cwd", "/Users/kris/code/boss/secret-project"))
|
||||
.put("compaction", new JSONObject()
|
||||
.put("status", "completed")
|
||||
.put("message", "上下文已压缩")));
|
||||
|
||||
View messageView = ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"buildMessageView",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, message)
|
||||
);
|
||||
|
||||
assertTrue(viewTreeContainsText(messageView, "线程配置"));
|
||||
assertTrue(viewTreeContainsText(messageView, "目标 active"));
|
||||
assertTrue(viewTreeContainsText(messageView, "完成 App Server 线程目标同步"));
|
||||
assertTrue(viewTreeContainsText(messageView, "模型 gpt-5.5 · openai"));
|
||||
assertTrue(viewTreeContainsText(messageView, "审批 on-request · 沙箱 workspaceWrite"));
|
||||
assertTrue(viewTreeContainsText(messageView, "协作 plan · fast"));
|
||||
assertTrue(viewTreeContainsText(messageView, "上下文已压缩"));
|
||||
assertFalse(viewTreeContainsText(messageView, "/Users/kris"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeRemoteExecutionProgressDoesNotRenderCodexSections() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
|
||||
Reference in New Issue
Block a user