feat: summarize codex app server turns

This commit is contained in:
AI Bot
2026-06-03 10:17:07 +08:00
parent 74b333ba2f
commit 1ae81fa3af
11 changed files with 200 additions and 5 deletions

View File

@@ -1410,6 +1410,58 @@ function normalizeDiscoveryThreadSummary(threadListResult, loadedListResult, lim
};
}
function extractDiscoveryTurnStatus(turn) {
const status = turn?.status && typeof turn.status === "object" ? turn.status : turn?.status;
return safeProgressText(
typeof status === "string"
? status
: status?.type ?? status?.state ?? turn?.state ?? "unknown",
40,
);
}
function normalizeDiscoveryThreadTurnSummary(threadTurnResults, limit) {
const threadSummaries = threadTurnResults
.map(({ threadId, result }) => {
const rawTurns = asArray(result?.data).length > 0 ? asArray(result?.data) : asArray(result?.turns);
const turns = rawTurns
.map((turn) => {
const status = extractDiscoveryTurnStatus(turn);
return {
status,
updatedAt: trimToDefined(turn?.updatedAt ?? turn?.lastUpdatedAt ?? turn?.createdAt) || "",
};
})
.filter((turn) => turn.status || turn.updatedAt);
const latestTurn = turns
.slice()
.sort((left, right) => String(right.updatedAt).localeCompare(String(left.updatedAt)))[0];
return {
threadId,
turnCount: turns.length,
runningTurnCount: turns.filter((turn) => /active|running|streaming|in[_-]?progress/i.test(turn.status)).length,
completedTurnCount: turns.filter((turn) => /completed|complete|success|succeeded/i.test(turn.status)).length,
latestTurnStatus: latestTurn?.status || "unknown",
latestTurnUpdatedAt: latestTurn?.updatedAt || "",
};
})
.sort((left, right) => String(right.latestTurnUpdatedAt).localeCompare(String(left.latestTurnUpdatedAt)))
.slice(0, limit);
const latestUpdatedAt = threadSummaries
.map((thread) => thread.latestTurnUpdatedAt)
.filter(Boolean)
.sort()
.at(-1) || "";
return {
threadCount: threadSummaries.length,
totalTurnCount: threadSummaries.reduce((sum, thread) => sum + thread.turnCount, 0),
runningTurnCount: threadSummaries.reduce((sum, thread) => sum + thread.runningTurnCount, 0),
completedTurnCount: threadSummaries.reduce((sum, thread) => sum + thread.completedTurnCount, 0),
latestUpdatedAt,
threads: threadSummaries,
};
}
async function withCodexAppServerRpcSession(runnerConfig, callback) {
const cwd = runnerConfig.cwd || process.cwd();
let closed = false;
@@ -1559,6 +1611,22 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
const defaultModelId = models.find((model) => model.isDefault)?.id || models[0]?.id || "";
const fastModelId = pickFastModelId(models);
const deepModelId = models.find((model) => model.id !== fastModelId)?.id || defaultModelId;
const threadSummary = normalizeDiscoveryThreadSummary(threadListResult, loadedThreadsResult, limit);
const turnProbeLimit = Math.min(limit, 10);
const threadTurnResults = await Promise.all(
asArray(threadSummary.visibleThreads)
.slice(0, turnProbeLimit)
.map(async (thread) => ({
threadId: thread.id,
result: await safeRequest(request, "thread/turns/list", {
threadId: thread.id,
limit: turnProbeLimit,
sortDirection: "desc",
itemsView: "notLoaded",
}),
})),
);
return {
version: trimToDefined(runnerConfig.version),
discoveredAt: new Date().toISOString(),
@@ -1583,7 +1651,8 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
appConfigSummary: normalizeDiscoveryAppConfigSummary(configResult),
configRequirements: normalizeDiscoveryConfigRequirements(configRequirementsResult),
externalAgentMigration: normalizeDiscoveryExternalAgentMigration(externalAgentConfigResult),
threadSummary: normalizeDiscoveryThreadSummary(threadListResult, loadedThreadsResult, limit),
threadSummary,
threadTurnSummary: normalizeDiscoveryThreadTurnSummary(threadTurnResults, limit),
errors: [
modelResult?.__bossError ? `model/list:${safeRuntimeDiagnosticText(modelResult.__bossError)}` : undefined,
providerCapabilities?.__bossError
@@ -1621,6 +1690,9 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
loadedThreadsResult?.__bossError
? `thread/loaded/list:${safeRuntimeDiagnosticText(loadedThreadsResult.__bossError)}`
: undefined,
...threadTurnResults
.filter((entry) => entry.result?.__bossError)
.map((entry) => `thread/turns/list:${entry.threadId}:${safeRuntimeDiagnosticText(entry.result.__bossError)}`),
].filter(Boolean),
};
});