feat: surface codex runtime status
This commit is contained in:
@@ -1353,6 +1353,83 @@ public final class BossUi {
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject modelRoute = progress == null ? null : progress.optJSONObject("modelRoute");
|
||||
JSONObject tokenUsage = progress == null ? null : progress.optJSONObject("tokenUsage");
|
||||
JSONArray mcpServers = progress == null ? null : progress.optJSONArray("mcpServers");
|
||||
JSONObject remoteControl = progress == null ? null : progress.optJSONObject("remoteControl");
|
||||
boolean hasRuntimeStatus = modelRoute != null || tokenUsage != null ||
|
||||
(mcpServers != null && mcpServers.length() > 0) || remoteControl != null;
|
||||
if (hasRuntimeStatus) {
|
||||
card.addView(divider(context));
|
||||
card.addView(sectionTitle(context, "运行状态"));
|
||||
if (modelRoute != null) {
|
||||
String fromModel = modelRoute.optString("fromModel", "").trim();
|
||||
String toModel = modelRoute.optString("toModel", "").trim();
|
||||
String reason = modelRoute.optString("reason", "").trim();
|
||||
if (!TextUtils.isEmpty(fromModel) && !TextUtils.isEmpty(toModel)) {
|
||||
card.addView(detailRow(
|
||||
context,
|
||||
"◇",
|
||||
"模型 " + fromModel + " → " + toModel,
|
||||
reason,
|
||||
false
|
||||
));
|
||||
}
|
||||
}
|
||||
if (tokenUsage != null) {
|
||||
int totalTokens = tokenUsage.optInt("totalTokens", 0);
|
||||
int modelContextWindow = tokenUsage.optInt("modelContextWindow", 0);
|
||||
int contextPercent = tokenUsage.optInt("contextPercent", 0);
|
||||
if (totalTokens > 0) {
|
||||
StringBuilder usage = new StringBuilder();
|
||||
usage.append("上下文 ").append(String.format(Locale.US, "%,d", totalTokens));
|
||||
if (modelContextWindow > 0) {
|
||||
usage.append(" / ").append(String.format(Locale.US, "%,d", modelContextWindow));
|
||||
}
|
||||
if (contextPercent > 0) {
|
||||
usage.append(" · ").append(contextPercent).append("%");
|
||||
}
|
||||
card.addView(detailRow(context, "◷", usage.toString(), "", false));
|
||||
}
|
||||
}
|
||||
if (mcpServers != null) {
|
||||
int shown = 0;
|
||||
for (int i = 0; i < mcpServers.length(); i += 1) {
|
||||
JSONObject server = mcpServers.optJSONObject(i);
|
||||
String name = server == null ? "" : server.optString("name", "").trim();
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
continue;
|
||||
}
|
||||
String status = server.optString("status", "").trim();
|
||||
String error = server.optString("error", "").trim();
|
||||
card.addView(detailRow(
|
||||
context,
|
||||
"M",
|
||||
TextUtils.isEmpty(status) ? "MCP " + name : "MCP " + name + " · " + status,
|
||||
error,
|
||||
"failed".equals(status)
|
||||
));
|
||||
shown += 1;
|
||||
if (shown >= 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remoteControl != null) {
|
||||
String status = remoteControl.optString("status", "").trim();
|
||||
String serverName = remoteControl.optString("serverName", "").trim();
|
||||
if (!TextUtils.isEmpty(status)) {
|
||||
card.addView(detailRow(
|
||||
context,
|
||||
"⌁",
|
||||
TextUtils.isEmpty(serverName) ? "远控 " + status : "远控 " + status + " · " + serverName,
|
||||
"",
|
||||
"errored".equals(status)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray warnings = progress == null ? null : progress.optJSONArray("warnings");
|
||||
if (warnings != null && warnings.length() > 0) {
|
||||
card.addView(divider(context));
|
||||
|
||||
@@ -1014,6 +1014,61 @@ public class ProjectDetailActivityUiTest {
|
||||
assertFalse(viewTreeContainsText(messageView, "v=0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executionProgressMessageRendersCodexRuntimeStatusSections() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "thread-runtime")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "Boss开发主线程");
|
||||
TestProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject message = new JSONObject()
|
||||
.put("id", "progress-runtime-1")
|
||||
.put("sender", "master")
|
||||
.put("senderLabel", "主 Agent")
|
||||
.put("body", "执行进度")
|
||||
.put("kind", "execution_progress")
|
||||
.put("sentAt", "2026-05-31T10: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("modelRoute", new JSONObject()
|
||||
.put("fromModel", "gpt-5.4-mini")
|
||||
.put("toModel", "gpt-5.4")
|
||||
.put("reason", "highRiskCyberActivity"))
|
||||
.put("tokenUsage", new JSONObject()
|
||||
.put("totalTokens", 3000)
|
||||
.put("modelContextWindow", 200000)
|
||||
.put("contextPercent", 2))
|
||||
.put("mcpServers", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("name", "github")
|
||||
.put("status", "failed")
|
||||
.put("error", "token=[redacted] failed to start")))
|
||||
.put("remoteControl", new JSONObject()
|
||||
.put("status", "connected")
|
||||
.put("serverName", "Mac Studio")
|
||||
.put("environmentId", "env-prod")));
|
||||
|
||||
View messageView = ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"buildMessageView",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, message)
|
||||
);
|
||||
|
||||
assertTrue(viewTreeContainsText(messageView, "运行状态"));
|
||||
assertTrue(viewTreeContainsText(messageView, "模型 gpt-5.4-mini → gpt-5.4"));
|
||||
assertTrue(viewTreeContainsText(messageView, "上下文 3,000 / 200,000 · 2%"));
|
||||
assertTrue(viewTreeContainsText(messageView, "MCP github · failed"));
|
||||
assertTrue(viewTreeContainsText(messageView, "token=[redacted] failed to start"));
|
||||
assertTrue(viewTreeContainsText(messageView, "远控 connected · Mac Studio"));
|
||||
assertFalse(viewTreeContainsText(messageView, "install-secret"));
|
||||
assertFalse(viewTreeContainsText(messageView, "sk-secret"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeRemoteExecutionProgressDoesNotRenderCodexSections() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
|
||||
Reference in New Issue
Block a user