feat: expand codex app server discovery

This commit is contained in:
AI Bot
2026-06-02 23:11:21 +08:00
parent 94e0cc8bad
commit 88b028ad2b
11 changed files with 295 additions and 12 deletions

View File

@@ -73,6 +73,16 @@ test("device detail exposes Codex App Server discovered model and extension summ
skills: [{ name: "image2-ui-prototype" }],
plugins: [{ id: "github" }],
apps: [{ id: "canva" }],
experimentalFeatures: [
{ name: "multi_agent", stage: "stable", enabled: true },
{ name: "apps", stage: "beta", enabled: false },
],
collaborationModes: [{ id: "solo" }, { id: "plan" }],
permissionProfiles: [{ id: ":workspace" }],
mcpServers: [
{ name: "github", toolCount: 2, authStatus: "oAuth" },
{ name: "figma", toolCount: 0, authStatus: "notLoggedIn" },
],
},
},
};
@@ -84,6 +94,7 @@ test("device detail exposes Codex App Server discovered model and extension summ
assert.equal(cards.capabilities.items.codexAppServer, "Codex App Server已连接");
assert.equal(cards.capabilities.items.codexModels, "模型2 个 · 默认 gpt-5.4 · 快速 gpt-5.4-mini · 深度 gpt-5.4");
assert.equal(cards.capabilities.items.codexExtensions, "扩展Skill 1 个 · Plugin 1 个 · App 1 个");
assert.equal(cards.capabilities.items.codexGovernance, "治理:实验特性 2 个 · 协作模式 2 个 · MCP 2 个 · 权限 1 个");
});
test("device detail exposes folder and project conflict skeleton from workspace policy", async () => {

View File

@@ -59,6 +59,10 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
defaultModelId: "gpt-5.4",
fastModelId: "gpt-5.4-mini",
providerCapabilities: { webSearch: true },
experimentalFeatures: [{ name: "multi_agent", stage: "stable", enabled: true }],
collaborationModes: [{ id: "plan" }],
permissionProfiles: [{ id: ":workspace" }],
mcpServers: [{ name: "github", toolCount: 2, authStatus: "oAuth" }],
},
},
},
@@ -71,4 +75,14 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
const updatedDevice = nextState.devices.find((item) => item.id === device!.id);
assert.equal(updatedDevice?.capabilities?.codexAppServer.metadata?.models?.[0]?.id, "gpt-5.4");
assert.equal(updatedDevice?.capabilities?.codexAppServer.metadata?.providerCapabilities?.webSearch, true);
assert.equal(
(updatedDevice?.capabilities?.codexAppServer.metadata?.experimentalFeatures as Array<{ name: string }> | undefined)?.[0]
?.name,
"multi_agent",
);
assert.equal(
(updatedDevice?.capabilities?.codexAppServer.metadata?.mcpServers as Array<{ toolCount: number }> | undefined)?.[0]
?.toolCount,
2,
);
});

View File

@@ -156,6 +156,116 @@ rl.on("line", (line) => {
return;
}
if (message.method === "experimentalFeature/list") {
send({
id: message.id,
result: {
data: [
{
name: "multi_agent",
stage: "stable",
displayName: "Multi agent",
description: "Allow spawned agents to coordinate work",
announcement: "internal token=sk-secret-should-not-leak",
enabled: true,
defaultEnabled: true,
},
{
name: "apps",
stage: "beta",
displayName: "Apps",
description: "Enable app connectors",
announcement: null,
enabled: false,
defaultEnabled: false,
},
],
nextCursor: null,
},
});
return;
}
if (message.method === "collaborationMode/list") {
send({
id: message.id,
result: {
data: [
{
id: "solo",
name: "solo",
displayName: "Solo",
description: "Single-thread execution",
},
{
id: "plan",
name: "plan",
displayName: "Plan",
description: "Plan before coding",
},
],
},
});
return;
}
if (message.method === "permissionProfile/list") {
send({
id: message.id,
result: {
data: [
{
id: ":workspace",
description: "Workspace write",
filesystem: {
"/Users/kris/code/boss": "write",
},
},
{
id: ":read-only",
description: "Read-only",
},
],
nextCursor: null,
},
});
return;
}
if (message.method === "mcpServerStatus/list") {
send({
id: message.id,
result: {
data: [
{
name: "github",
tools: {
"repos/list": { name: "repos/list", description: "List repositories" },
"issues/read": { name: "issues/read", description: "Read issues" },
},
resources: [
{
name: "private repo token=sk-secret-should-not-leak",
uri: "file:///Users/kris/.ssh/id_ed25519",
},
],
resourceTemplates: [],
authStatus: "oAuth",
},
{
name: "figma",
tools: {},
resources: [],
resourceTemplates: [],
authStatus: "notLoggedIn",
},
],
nextCursor: null,
},
});
return;
}
if (message.method === "thread/resume") {
send({
id: message.id,

View File

@@ -8,6 +8,7 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
import {
discoverCodexAppServerCapabilities,
executeCodexAppServerTask,
getCodexAppServerRunnerConfig,
shouldUseCodexAppServerTaskRunner,
@@ -15,6 +16,35 @@ import {
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
test("codex app-server discovery includes governance and MCP summaries without leaking internals", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
codexAppServerDiscoveryLimit: 20,
});
const metadata = await discoverCodexAppServerCapabilities(runnerConfig);
assert.equal(metadata.experimentalFeatures[0].name, "multi_agent");
assert.equal(metadata.experimentalFeatures[0].stage, "stable");
assert.equal(metadata.experimentalFeatures[0].enabled, true);
assert.equal(metadata.collaborationModes[1].id, "plan");
assert.equal(metadata.permissionProfiles[0].id, ":workspace");
assert.equal(metadata.mcpServers[0].name, "github");
assert.equal(metadata.mcpServers[0].toolCount, 2);
assert.equal(metadata.mcpServers[0].authStatus, "oAuth");
const serialized = JSON.stringify(metadata);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("id_ed25519"), false);
assert.equal(serialized.includes("filesystem"), false);
assert.equal(serialized.includes("resources"), false);
});
function encodeWsTextFrame(value) {
const payload = Buffer.from(value);
if (payload.length < 126) {