feat: summarize codex app server turns
This commit is contained in:
@@ -118,6 +118,23 @@ test("device detail exposes Codex App Server discovered model and extension summ
|
||||
},
|
||||
],
|
||||
},
|
||||
threadTurnSummary: {
|
||||
threadCount: 2,
|
||||
totalTurnCount: 3,
|
||||
runningTurnCount: 1,
|
||||
completedTurnCount: 2,
|
||||
latestUpdatedAt: "2026-06-03T08:21:00.000Z",
|
||||
threads: [
|
||||
{
|
||||
threadId: "thr-active",
|
||||
turnCount: 2,
|
||||
runningTurnCount: 1,
|
||||
completedTurnCount: 1,
|
||||
latestTurnStatus: "running",
|
||||
latestTurnUpdatedAt: "2026-06-03T08:21:00.000Z",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -133,6 +150,7 @@ test("device detail exposes Codex App Server discovered model and extension summ
|
||||
assert.equal(cards.capabilities.items.codexAccount, "账号:chatgpt · 套餐 pro · 额度 42%");
|
||||
assert.equal(cards.capabilities.items.codexConfig, "配置:App 2 个 · 已启用 1 个 · 托管要求 2 个 · 外部迁移 3 项");
|
||||
assert.equal(cards.capabilities.items.codexThreads, "线程:3 个 · 已加载 2 个 · 活跃 1 个 · 最新 2026-06-03 16:20");
|
||||
assert.equal(cards.capabilities.items.codexTurns, "轮次:3 个 · 运行中 1 个 · 完成 2 个 · 最新 2026-06-03 16:21");
|
||||
});
|
||||
|
||||
test("device detail exposes folder and project conflict skeleton from workspace policy", async () => {
|
||||
|
||||
@@ -75,6 +75,13 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
|
||||
latestUpdatedAt: "2026-06-03T08:20:00.000Z",
|
||||
visibleThreads: [{ id: "thr-active", name: "Boss App Server rollout", loaded: true }],
|
||||
},
|
||||
threadTurnSummary: {
|
||||
threadCount: 2,
|
||||
totalTurnCount: 3,
|
||||
runningTurnCount: 1,
|
||||
latestUpdatedAt: "2026-06-03T08:21:00.000Z",
|
||||
threads: [{ threadId: "thr-active", turnCount: 2, runningTurnCount: 1 }],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -129,4 +136,20 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
|
||||
)?.visibleThreads?.[0]?.id,
|
||||
"thr-active",
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
updatedDevice?.capabilities?.codexAppServer.metadata?.threadTurnSummary as
|
||||
| { totalTurnCount?: number; threads?: Array<{ threadId?: string }> }
|
||||
| undefined
|
||||
)?.totalTurnCount,
|
||||
3,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
updatedDevice?.capabilities?.codexAppServer.metadata?.threadTurnSummary as
|
||||
| { threads?: Array<{ threadId?: string }> }
|
||||
| undefined
|
||||
)?.threads?.[0]?.threadId,
|
||||
"thr-active",
|
||||
);
|
||||
});
|
||||
|
||||
54
tests/fixtures/codex-app-server-runtime.mjs
vendored
54
tests/fixtures/codex-app-server-runtime.mjs
vendored
@@ -431,6 +431,60 @@ rl.on("line", (line) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "thread/turns/list") {
|
||||
const threadId = message.params?.threadId;
|
||||
if (threadId === "thr-active") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
data: [
|
||||
{
|
||||
id: "turn-active-2",
|
||||
status: "running",
|
||||
createdAt: "2026-06-03T08:18:00.000Z",
|
||||
updatedAt: "2026-06-03T08:21:00.000Z",
|
||||
userInput: "private active turn text should not leak",
|
||||
items: [{ type: "message", content: "private item content should not leak" }],
|
||||
},
|
||||
{
|
||||
id: "turn-active-1",
|
||||
status: "completed",
|
||||
createdAt: "2026-06-03T08:00:00.000Z",
|
||||
updatedAt: "2026-06-03T08:10:00.000Z",
|
||||
},
|
||||
],
|
||||
nextCursor: null,
|
||||
backwardsCursor: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (threadId === "thr-idle") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
data: [
|
||||
{
|
||||
id: "turn-idle-1",
|
||||
status: { type: "completed" },
|
||||
createdAt: "2026-06-03T07:00:00.000Z",
|
||||
updatedAt: "2026-06-03T07:30:00.000Z",
|
||||
userInput: "private idle turn text should not leak",
|
||||
},
|
||||
],
|
||||
nextCursor: null,
|
||||
backwardsCursor: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
send({
|
||||
id: message.id,
|
||||
result: { data: [], nextCursor: null, backwardsCursor: null },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "thread/resume") {
|
||||
send({
|
||||
id: message.id,
|
||||
|
||||
@@ -78,6 +78,19 @@ test("codex app-server discovery includes governance and MCP summaries without l
|
||||
loaded: true,
|
||||
updatedAt: "2026-06-03T08:20:00.000Z",
|
||||
});
|
||||
assert.equal(metadata.threadTurnSummary.threadCount, 2);
|
||||
assert.equal(metadata.threadTurnSummary.totalTurnCount, 3);
|
||||
assert.equal(metadata.threadTurnSummary.runningTurnCount, 1);
|
||||
assert.equal(metadata.threadTurnSummary.completedTurnCount, 2);
|
||||
assert.equal(metadata.threadTurnSummary.latestUpdatedAt, "2026-06-03T08:21:00.000Z");
|
||||
assert.deepEqual(metadata.threadTurnSummary.threads[0], {
|
||||
threadId: "thr-active",
|
||||
turnCount: 2,
|
||||
runningTurnCount: 1,
|
||||
completedTurnCount: 1,
|
||||
latestTurnStatus: "running",
|
||||
latestTurnUpdatedAt: "2026-06-03T08:21:00.000Z",
|
||||
});
|
||||
|
||||
const serialized = JSON.stringify(metadata);
|
||||
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
|
||||
@@ -90,6 +103,9 @@ test("codex app-server discovery includes governance and MCP summaries without l
|
||||
assert.equal(serialized.includes("AGENTS.md"), false);
|
||||
assert.equal(serialized.includes("secret user text should not leak"), false);
|
||||
assert.equal(serialized.includes("Old private thread"), false);
|
||||
assert.equal(serialized.includes("private active turn text should not leak"), false);
|
||||
assert.equal(serialized.includes("private item content should not leak"), false);
|
||||
assert.equal(serialized.includes("private idle turn text should not leak"), false);
|
||||
});
|
||||
|
||||
function encodeWsTextFrame(value) {
|
||||
|
||||
Reference in New Issue
Block a user