feat: surface codex app server governance summaries
This commit is contained in:
@@ -83,6 +83,7 @@
|
||||
- 当前默认本机配置已把 `browserAutomation / computerUse` 两项能力直接上报为在线起步态,所以 Boss App 里这台 Mac 会显示“可做浏览器控制 / 桌面控制”;如果某条链路要临时收起,只需要改 `local-agent/config.cloud.json`
|
||||
- 当前 `local-agent` 已新增 `Codex App Server` runner:boss-agent 默认打开 `codexAppServerEnabled`,通过 `codex app-server` stdio 接入 `conversation_reply / dispatch_execution`,也可灰度切到 `ws://127.0.0.1:<port>` 或 `unix://PATH` 本机长驻 App Server;WebSocket/Unix WebSocket handshake 支持 `Authorization: Bearer <token>`,优先用 `codexAppServerAuthTokenFile` 保存本地 token。失败时只在 turn 未启动前回退 `codex exec resume`,避免重复执行同一轮对话。设备 heartbeat 会单独上报 `codexAppServer` capability,并按 `codexAppServerDiscoveryTtlMs` 缓存 `model/list / skills/list / plugin/list / app/list / modelProvider/capabilities/read` 的能力摘要,供 APP/后台模型选择和治理页读取。2026-05-31 起,runner 会吸收 App Server 的 plan / diff / item / subagent 事件并归一到 Boss `execution_progress` 进度卡,执行中通过 `POST /api/v1/master-agent/tasks/[taskId]/progress` 实时刷新;后续已补 `approvals / warnings / fileChanges / threadStatus / realtime / modelRoute / tokenUsage / mcpServers / remoteControl / threadGoal / threadSettings / compaction / accountStatus / modelVerification / threadCollaboration / toolActivities / reasoningSummary / windowsSandbox` 等结构化摘要。Android 原生进度卡可显示线程状态、实时状态、线程配置、线程协作、工具活动、思考摘要、账号状态、运行状态、Windows 沙箱状态、安全提醒、审批状态和文件变更摘要,且不展示完整命令、diff、系统提示词、密钥、SDP、音频原始数据、raw realtime item、remote installationId、本地绝对路径或 Windows sandbox sourcePath。本机 `codex-cli 0.135.0-alpha.1` 协议快照已生成在 `docs/protocol-snapshots/codex-app-server/0.135.0-alpha.1/`。同日新增第一版 Inter-Thread Broker:任务携带源/目标 Codex 线程时可通过 `thread/read -> thread/inject_items -> turn/start` 完成受控线程协作;服务端新增 `POST /api/v1/projects/[projectId]/thread-collaboration` 作为 APP/后台可调用入口;任务携带 `targetCodexTurnId` 时 runner 会改用 `turn/steer` 干预活跃 turn。
|
||||
- 当前 App Server heartbeat discovery 已扩展到 `experimentalFeature/list / collaborationMode/list / permissionProfile/list / mcpServerStatus/list`,设备详情页会展示“治理:实验特性 / 协作模式 / MCP / 权限”摘要;MCP 只保留服务名、工具数量、资源数量和认证状态,permission profile 只保留 id/description,不保存本地路径、resource URI、文件规则、token 或工具参数。
|
||||
- 当前 App Server heartbeat discovery 已继续扩展到 `account/read / account/rateLimits/read / config/read / configRequirements/read / externalAgentConfig/detect`,设备详情页会展示账号、套餐、额度、App 配置、托管要求和外部 Agent 迁移候选摘要;该链路只保存计数、开关和状态,不保存邮箱、API key、完整 config、本地路径、迁移描述或外部 Agent 原始内容。
|
||||
- `GET http://127.0.0.1:4317/api/v1/skills` 正常,已返回本机扫描到的 Codex Skill
|
||||
- `POST http://127.0.0.1:4317/api/v1/heartbeat` 正常,且会顺带触发 `thread-context` 上报
|
||||
- `local-agent` 当前每 5 秒轮询一次本机 Skill lifecycle 请求;默认打开 `skillLifecycleEnabled=true`。远程 `install` 或带 `sourceUrl` 的更新必须命中 `skillLifecycleAllowedSources` 或 `skillLifecycleTrustedSources`,为空时只允许既有本地 Skill 的 `update / rollback / uninstall / version_lock`;请求携带 `checksum / expectedChecksum` 时会校验 `manifest.json` 或 `SKILL.md` 的 sha256,失败会清理半安装目录或尽量恢复备份。卸载 / 更新 / 回滚前会在 `skillsDir/.boss-skill-backups` 保留备份,卸载仍限制在 `skillsDir` 目录内,版本锁写入 `.boss-skill-locks.json`
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
- Web 和原生 Android 当前都已经接上“新设备导入草稿 -> 勾选 -> 决议预览 -> 应用导入”的前台页面;已绑定生产设备继续保留 heartbeat 自动导入链路
|
||||
- 原生首页的刷新失败策略当前已改成按当前 tab 独立判错,不会再因为 `设备 / 设置 / OTA` 的旁路请求失败把会话页刷新一并判成失败
|
||||
- 当前量产方向已经明确为“Boss 企业控制面 + 可插拔执行协议”:多租户、权限、审批、审计、备份、回退和 Skill 治理由 Boss 承担,Codex App Server / Codex MCP / Codex CLI / Computer Use / 业务系统 API 都作为 provider 接入;详见 `docs/architecture/enterprise_ai_ops_architecture_cn.md`
|
||||
- 当前 Codex App Server 已完成十三批接入:boss-agent 默认开启 `local-agent/codex-app-server-runner.mjs` 作为 Codex 绑定入口,优先走 `codex app-server` stdio,也可灰度连接 `ws://127.0.0.1:<port>` 或 `unix://PATH` 同机长驻 App Server;长驻连接支持 `Authorization: Bearer <token>`,配置上优先使用 `codexAppServerAuthTokenFile`。turn 启动前失败才回退 CLI,turn 启动后不重复执行;桌面远程控制默认先走 `codex-computer-use`,失败后回退 `cua-driver-computer-use`。2026-05-31 已按本机 `codex-cli 0.135.0-alpha.1` 生成协议快照 `docs/protocol-snapshots/codex-app-server/0.135.0-alpha.1/`,并把 `turn/plan/updated`、`turn/diff/updated`、`item/started|completed`、`thread/started`、`item/*/requestApproval`、`item/autoApprovalReview/*`、`guardianWarning`、`serverRequest/resolved`、`item/fileChange/patchUpdated`、`thread/status/changed`、`thread/realtime/*`、`model/rerouted`、`thread/tokenUsage/updated`、`mcpServer/startupStatus/updated`、`remoteControl/status/changed`、`windowsSandbox/setupCompleted`、`thread/goal/*`、`thread/settings/updated`、`thread/compacted`、`account/updated`、`account/rateLimits/updated`、`model/verification`、`warning`、`configWarning`、`deprecationNotice`、`ThreadItem.collabToolCall`、`ThreadItem.contextCompaction`、`mcpToolCall`、`dynamicToolCall`、`webSearch`、`imageView`、`imageGeneration`、`hook/started|completed`、`enteredReviewMode`、`exitedReviewMode`、`commandExecution`、`ThreadItem.plan`、`ThreadItem.reasoning.summary` 归一到 Boss `execution_progress` 卡片;realtime 只保留状态、文本摘要和计数,运行状态只保留模型切换、上下文用量、MCP 状态、远控连接摘要和 Windows 沙箱准备状态,线程配置只保留目标、模型、审批、沙箱、协作模式和压缩状态,线程协作只保留工具名、状态、目标类型和智能体状态,工具活动只保留类型、名称、状态和安全摘要,图像生成只保留状态与安全文件名,钩子生命周期只保留事件名、处理器类型、状态、来源、执行模式和耗时,思考摘要只保留官方 summary 文本和状态,账号状态只保留认证方式、套餐、额度窗口、积分余额和模型校验摘要,不保存 SDP、音频原始数据、raw item、remote installationId、cwd、turnId、配置文件路径、collab 源/目标线程 ID、collab prompt、tool arguments/result/contentItems、web URL token、命令正文/输出、raw reasoning content、reasoning item id、imageGeneration revisedPrompt/result、hook sourcePath/statusMessage/entries、Windows sandbox sourcePath/samplePaths/本地绝对路径或未清洗密钥。heartbeat 已能缓存 `model/list / skills/list / plugin/list / app/list / modelProvider/capabilities/read / experimentalFeature/list / collaborationMode/list / permissionProfile/list / mcpServerStatus/list` 的能力摘要;同批已补 `turn/steer` 活跃 turn 干预和 `POST /api/v1/projects/[projectId]/thread-collaboration` 服务端线程协作排队入口。MCP 与权限 discovery 只保留安全摘要,不保存 resource URI、权限文件规则、工具参数、本地路径或 token。
|
||||
- 当前 Codex App Server 已完成十四批接入:boss-agent 默认开启 `local-agent/codex-app-server-runner.mjs` 作为 Codex 绑定入口,优先走 `codex app-server` stdio,也可灰度连接 `ws://127.0.0.1:<port>` 或 `unix://PATH` 同机长驻 App Server;长驻连接支持 `Authorization: Bearer <token>`,配置上优先使用 `codexAppServerAuthTokenFile`。turn 启动前失败才回退 CLI,turn 启动后不重复执行;桌面远程控制默认先走 `codex-computer-use`,失败后回退 `cua-driver-computer-use`。2026-05-31 已按本机 `codex-cli 0.135.0-alpha.1` 生成协议快照 `docs/protocol-snapshots/codex-app-server/0.135.0-alpha.1/`,并把 `turn/plan/updated`、`turn/diff/updated`、`item/started|completed`、`thread/started`、`item/*/requestApproval`、`item/autoApprovalReview/*`、`guardianWarning`、`serverRequest/resolved`、`item/fileChange/patchUpdated`、`thread/status/changed`、`thread/realtime/*`、`model/rerouted`、`thread/tokenUsage/updated`、`mcpServer/startupStatus/updated`、`remoteControl/status/changed`、`windowsSandbox/setupCompleted`、`thread/goal/*`、`thread/settings/updated`、`thread/compacted`、`account/updated`、`account/rateLimits/updated`、`model/verification`、`warning`、`configWarning`、`deprecationNotice`、`ThreadItem.collabToolCall`、`ThreadItem.contextCompaction`、`mcpToolCall`、`dynamicToolCall`、`webSearch`、`imageView`、`imageGeneration`、`hook/started|completed`、`enteredReviewMode`、`exitedReviewMode`、`commandExecution`、`ThreadItem.plan`、`ThreadItem.reasoning.summary` 归一到 Boss `execution_progress` 卡片;realtime 只保留状态、文本摘要和计数,运行状态只保留模型切换、上下文用量、MCP 状态、远控连接摘要和 Windows 沙箱准备状态,线程配置只保留目标、模型、审批、沙箱、协作模式和压缩状态,线程协作只保留工具名、状态、目标类型和智能体状态,工具活动只保留类型、名称、状态和安全摘要,图像生成只保留状态与安全文件名,钩子生命周期只保留事件名、处理器类型、状态、来源、执行模式和耗时,思考摘要只保留官方 summary 文本和状态,账号状态只保留认证方式、套餐、额度窗口、积分余额和模型校验摘要,不保存 SDP、音频原始数据、raw item、remote installationId、cwd、turnId、配置文件路径、collab 源/目标线程 ID、collab prompt、tool arguments/result/contentItems、web URL token、命令正文/输出、raw reasoning content、reasoning item id、imageGeneration revisedPrompt/result、hook sourcePath/statusMessage/entries、Windows sandbox sourcePath/samplePaths/本地绝对路径或未清洗密钥。heartbeat 已能缓存 `model/list / skills/list / plugin/list / app/list / modelProvider/capabilities/read / experimentalFeature/list / collaborationMode/list / permissionProfile/list / mcpServerStatus/list / account/read / account/rateLimits/read / config/read / configRequirements/read / externalAgentConfig/detect` 的能力摘要;同批已补 `turn/steer` 活跃 turn 干预和 `POST /api/v1/projects/[projectId]/thread-collaboration` 服务端线程协作排队入口。MCP、权限、账号、配置和外部 Agent discovery 只保留安全摘要,不保存 resource URI、权限文件规则、工具参数、邮箱、完整 config、本地路径、迁移描述或 token。
|
||||
- 当前 boss-agent 已支持 Mac OTA:`local-agent/boss-agent-ota-runner.mjs` 默认开启,每 5 分钟检查服务端最新包;状态页可手动检查或下载并安装,安装时保留原绑定配置,只更新版本号和本机 runtime 路径。最新验证版本为 `20260516221619`,已在 MacBook Air `macbook-air` 上确认 OTA 下载校验、暂存、覆盖安装后不会误切到默认 `config.cloud.json`。正式分发脚本已预留 Developer ID 公证路径:`BOSS_AGENT_NOTARIZE=1` 配合 notary profile 或 Apple ID 凭据。
|
||||
- 当前量产治理已补设备撤权和任务可靠性底座:`revoke_device` 会清空设备 token、标记离线并阻断 heartbeat / 任务认领 / Skill 同步 / 日志上报 / boss-agent OTA;`MasterAgentTask` claim 会记录 attempt 和 lease,运行中任务可按租约重试,超过上限转 `timed_out`,用户或管理员可通过 cancel 接口转 `canceled` 且迟到 complete 不覆盖终态。
|
||||
- 当前群聊 `dispatch_execution` 完成回写已补幂等,重复完成不会再向群聊重复追加结果
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
- 当前 Codex Desktop 同步新增常驻刷新桥:`scripts/codex-desktop-refresh-bridge-daemon.mjs` 通过 launchd 监听 `127.0.0.1:4318`,暴露 `POST /api/v1/codex-desktop/refresh`、`GET /api/v1/codex-desktop/events`、`GET /api/v1/codex-desktop/events/recent` 和 `GET /api/v1/codex-desktop/capabilities`;`local-agent` 会优先调用 refresh endpoint,失败时回退到 `scripts/codex-desktop-refresh-hint.mjs` 命令式刷新。SSE 事件只包含线程引用、消息 ID、状态、deep link 等安全元数据,不包含用户正文或内部 prompt;`scripts/codex-desktop-event-consumer.mjs` 可作为 Desktop 插件/IPC 接入前的订阅 smoke;`scripts/codex-desktop-integration-probe.mjs` 负责只读探测 Codex.app 能力
|
||||
- 当前新增 Codex App Server runner:`local-agent/codex-app-server-runner.mjs`。boss-agent 默认配置 `codexAppServerEnabled=true`,会接管 `conversation_reply / dispatch_execution`;它默认通过 stdio 启动 `codex app-server`,也支持 `codexAppServerTransport=ws + codexAppServerUrl=ws://127.0.0.1:<port>` 或 `codexAppServerTransport=unix + codexAppServerUrl=unix:///absolute/path.sock` 连接同机长驻 App Server,bearer token 可通过 `codexAppServerAuthTokenFile` 读取并在握手时发送 `Authorization: Bearer <token>`。runner 执行 `initialize -> thread/resume|thread/start -> turn/start|turn/steer`,并把 `item/agentMessage/delta` 或 `item/completed` 归一成 Boss 任务回复;当 App Server 对单个 JSON-RPC 请求返回 `-32001 / retry later` 时,runner 会做最多 3 次指数退避重试。turn 启动前失败可回退 CLI,turn 启动后失败不回退,避免重复执行。2026-05-31 起,runner 会把 `turn/plan/updated`、`turn/diff/updated`、`item/started|completed`、`thread/started` 归一成 `executionProgress.steps / branch / artifacts / agents`,把 `item/*/requestApproval`、`item/autoApprovalReview/*`、`guardianWarning`、`serverRequest/resolved`、`item/fileChange/patchUpdated` 归一成 `executionProgress.approvals / warnings / fileChanges`,把 `thread/status/changed`、`thread/realtime/started|transcript|outputAudio|itemAdded|error|closed` 归一成 `executionProgress.threadStatus / realtime`,把 `model/rerouted`、`thread/tokenUsage/updated`、`mcpServer/startupStatus/updated`、`remoteControl/status/changed` 归一成 `executionProgress.modelRoute / tokenUsage / mcpServers / remoteControl`,并把 `thread/goal/*`、`thread/settings/updated`、`thread/compacted`、`account/updated`、`account/rateLimits/updated`、`model/verification`、`warning`、`configWarning`、`deprecationNotice`、`ThreadItem.collabToolCall`、`ThreadItem.contextCompaction`、`mcpToolCall`、`dynamicToolCall`、`webSearch`、`imageView`、`imageGeneration`、`hook/started|completed`、`windowsSandbox/setupCompleted`、`enteredReviewMode`、`exitedReviewMode`、`commandExecution`、`ThreadItem.plan`、`ThreadItem.reasoning.summary` 归一成线程配置、账号状态、模型校验、安全提醒、线程协作、上下文压缩、工具活动、图片产物、钩子生命周期、Windows 沙箱准备状态、计划步骤和思考摘要;服务端 complete 回写会与本地 Git/GitHub 进度合并,且不保存 SDP、音频 base64、raw realtime item、remote installationId、cwd、turnId、配置路径、collab 源/目标线程 ID、collab prompt、tool arguments/result/contentItems、web URL token、命令正文/输出、raw reasoning content、reasoning item id、imageGeneration revisedPrompt/result、hook sourcePath/statusMessage/entries、Windows sandbox sourcePath/samplePaths/本地绝对路径或未清洗的 MCP 错误。heartbeat 同时支持按 TTL 拉取 `model/list / skills/list / plugin/list / app/list / modelProvider/capabilities/read`,并把摘要保存在 `capabilities.codexAppServer.metadata`。
|
||||
- App Server heartbeat discovery 现在还会按 TTL 拉取 `experimentalFeature/list / collaborationMode/list / permissionProfile/list / mcpServerStatus/list`,写入 `capabilities.codexAppServer.metadata.experimentalFeatures / collaborationModes / permissionProfiles / mcpServers`。这些字段用于 APP/后台治理页展示 Codex 当前可用的实验特性、多 Agent/协作模式、权限 profile 和 MCP 服务健康;MCP 请求固定使用 `detail=toolsAndAuthOnly`,服务端状态里不保存 resource URI、工具参数、permission profile 文件规则、本地路径或密钥。
|
||||
- App Server heartbeat discovery 现在还会按 TTL 拉取 `account/read / account/rateLimits/read / config/read / configRequirements/read / externalAgentConfig/detect`,写入 `capabilities.codexAppServer.metadata.accountSummary / rateLimitSummary / appConfigSummary / configRequirements / externalAgentMigration`。这些字段用于 APP/后台展示账号、额度、App 配置、企业托管要求和外部 Agent 迁移候选摘要;当前只做观测,不通过 Boss 远程写 `config.toml` 或执行外部 Agent 导入,且不保存邮箱、完整 config、API key、本地路径或迁移描述。
|
||||
- 当前 Codex App Server runner 已新增第一版 Boss Inter-Thread Broker:任务携带 `intentCategory=thread_collaboration`、`sourceCodexThreadRef` 和 `targetCodexThreadRef` 时,会先 `thread/read` 源线程,再通过 `thread/inject_items` 向目标线程注入受控摘要,最后 `turn/start` 目标线程;服务端入口是 `POST /api/v1/projects/[projectId]/thread-collaboration`,负责权限、源/目标线程校验和任务排队。这不是假设官方线程 P2P,而是 Boss 自己做线程协作编排。
|
||||
- 当前 boss-agent Mac OTA 已接入:`local-agent/boss-agent-ota-runner.mjs` 会用设备 token 调 Boss 服务端 `/api/v1/boss-agent/ota` 检查最新 Mac 运行包,`/api/v1/boss-agent/ota/apply` 会下载 `boss-agent-mac-latest.zip`、校验 sha256、暂存安装 wrapper,并拉起本机安装器;安装脚本会保留绑定配置并只更新版本号与本机 runtime 路径。安装器会优先沿用当前 LaunchAgent active config,并保留所有 `config*.json`,避免多电脑场景中误绑定到默认设备配置。当前最新验证包为 `20260516221619`;构建脚本支持 `BOSS_AGENT_NOTARIZE=1` 的 Developer ID 公证路径。
|
||||
- 当前 `local-agent` 还新增了两条统一电脑控制 runtime:
|
||||
|
||||
@@ -20,9 +20,9 @@ Boss 不能直接把 App Server 原始 Thread / Turn / Item 字段写进业务
|
||||
|
||||
2026-05-31 已继续把 `turn/plan/updated`、`turn/diff/updated`、`item/started|completed`、`thread/started` 这类协议事件归一化为 Boss `execution_progress` 的步骤、分支变更、产物和后台智能体。同日第二批补齐 `item/*/requestApproval`、`item/autoApprovalReview/*`、`guardianWarning`、`serverRequest/resolved` 和 `item/fileChange/patchUpdated` 的安全摘要映射,APP 只展示审批状态、风险提醒和文件路径,不展示完整命令、diff、系统提示词或密钥。第三批已把 `thread/status/changed` 与 `thread/realtime/*` 归一成 `executionProgress.threadStatus / realtime`,APP 只展示活跃/等待审批/等待用户输入、realtime 文本摘要、音频片段计数和关闭/错误原因;第四批已把 `model/rerouted`、`thread/tokenUsage/updated`、`mcpServer/startupStatus/updated` 和 `remoteControl/status/changed` 归一成 `executionProgress.modelRoute / tokenUsage / mcpServers / remoteControl`,用于 APP “运行状态”区块。
|
||||
|
||||
2026-06-01 第五批已把 `thread/goal/updated|cleared`、`thread/settings/updated` 和 `thread/compacted` 归一成 `executionProgress.threadGoal / threadSettings / compaction`,用于 APP “线程配置”区块;第六批已把 `account/updated`、`account/rateLimits/updated`、`model/verification`、`warning`、`configWarning`、`deprecationNotice` 归一成 `executionProgress.accountStatus / modelVerification / warnings`;第七批已把官方 `ThreadItem.collabToolCall` 归一成 `executionProgress.threadCollaboration`,并按官方建议把新版 `ThreadItem.contextCompaction` 映射回 `executionProgress.compaction`;第八批已把 `mcpToolCall`、`dynamicToolCall`、`webSearch`、`imageView`、`enteredReviewMode`、`exitedReviewMode` 和 `commandExecution` 归一成 `executionProgress.toolActivities`;第九批已把官方 `ThreadItem.plan` 的最终 `item/completed` 文本映射为 `executionProgress.steps`,并把 `ThreadItem.reasoning.summary` 映射为 `executionProgress.reasoningSummary`;第十批已把 `ThreadItem.imageGeneration` 安全映射为 `executionProgress.toolActivities` 的图像生成活动和 `executionProgress.artifacts` 的图片产物;第十一批已把 `hook/started|completed` 安全映射为 `executionProgress.toolActivities` 的钩子活动,供 APP 以“钩子”轻卡展示企业治理和插件生命周期状态;第十二批已把 `windowsSandbox/setupCompleted` 安全映射为 `executionProgress.windowsSandbox`,供 APP 在“运行状态”里展示 Windows 沙箱准备状态、setup mode 和脱敏错误摘要;第十三批已把 heartbeat discovery 扩展到 `experimentalFeature/list`、`collaborationMode/list`、`permissionProfile/list` 和 `mcpServerStatus/list`,供设备详情、APP 和 PC 后台看到实验特性、协作模式、权限 Profile 与 MCP 服务摘要。
|
||||
2026-06-01 第五批已把 `thread/goal/updated|cleared`、`thread/settings/updated` 和 `thread/compacted` 归一成 `executionProgress.threadGoal / threadSettings / compaction`,用于 APP “线程配置”区块;第六批已把 `account/updated`、`account/rateLimits/updated`、`model/verification`、`warning`、`configWarning`、`deprecationNotice` 归一成 `executionProgress.accountStatus / modelVerification / warnings`;第七批已把官方 `ThreadItem.collabToolCall` 归一成 `executionProgress.threadCollaboration`,并按官方建议把新版 `ThreadItem.contextCompaction` 映射回 `executionProgress.compaction`;第八批已把 `mcpToolCall`、`dynamicToolCall`、`webSearch`、`imageView`、`enteredReviewMode`、`exitedReviewMode` 和 `commandExecution` 归一成 `executionProgress.toolActivities`;第九批已把官方 `ThreadItem.plan` 的最终 `item/completed` 文本映射为 `executionProgress.steps`,并把 `ThreadItem.reasoning.summary` 映射为 `executionProgress.reasoningSummary`;第十批已把 `ThreadItem.imageGeneration` 安全映射为 `executionProgress.toolActivities` 的图像生成活动和 `executionProgress.artifacts` 的图片产物;第十一批已把 `hook/started|completed` 安全映射为 `executionProgress.toolActivities` 的钩子活动,供 APP 以“钩子”轻卡展示企业治理和插件生命周期状态;第十二批已把 `windowsSandbox/setupCompleted` 安全映射为 `executionProgress.windowsSandbox`,供 APP 在“运行状态”里展示 Windows 沙箱准备状态、setup mode 和脱敏错误摘要;第十三批已把 heartbeat discovery 扩展到 `experimentalFeature/list`、`collaborationMode/list`、`permissionProfile/list` 和 `mcpServerStatus/list`,供设备详情、APP 和 PC 后台看到实验特性、协作模式、权限 Profile 与 MCP 服务摘要;第十四批已把 `account/read`、`account/rateLimits/read`、`config/read`、`configRequirements/read` 和 `externalAgentConfig/detect` 纳入 heartbeat discovery,用于展示账号、套餐、额度、App 配置、托管要求和外部 Agent 迁移候选摘要。
|
||||
|
||||
`thread/realtime/sdp`、音频 base64、原始 realtime item、remote installationId、thread settings 的 `cwd`、compaction `turnId`、collaboration settings 内部 prompt、collabToolCall 源/目标线程 ID、tool arguments/result/contentItems、web URL token、命令正文/输出、raw reasoning `content`、reasoning item id、imageGeneration 原始 result/revisedPrompt、hook id/sourcePath/statusMessage/entries、Windows sandbox sourcePath/samplePaths、本地绝对路径、permission profile 文件规则、MCP resource URI 和未清洗的 MCP 错误不入账。
|
||||
`thread/realtime/sdp`、音频 base64、原始 realtime item、remote installationId、thread settings 的 `cwd`、compaction `turnId`、collaboration settings 内部 prompt、collabToolCall 源/目标线程 ID、tool arguments/result/contentItems、web URL token、命令正文/输出、raw reasoning `content`、reasoning item id、imageGeneration 原始 result/revisedPrompt、hook id/sourcePath/statusMessage/entries、Windows sandbox sourcePath/samplePaths、本地绝对路径、permission profile 文件规则、MCP resource URI、账号邮箱、API key、完整 config、外部 Agent 迁移描述和未清洗的 MCP 错误不入账。
|
||||
|
||||
官方文档入口:`https://developers.openai.com/codex/app-server`
|
||||
|
||||
@@ -44,7 +44,7 @@ Boss 不能直接把 App Server 原始 Thread / Turn / Item 字段写进业务
|
||||
- 本机协议快照已生成到 `docs/protocol-snapshots/codex-app-server/0.135.0-alpha.1/`,共识别 137 个协议方法;确认支持 `thread/inject_items`、`thread/rollback`、`thread/goal/*`、`turn/steer`、`command/exec`、`thread/realtime/*`、`account/*`、`model/verification`、`configWarning`、`deprecationNotice`、`model/list`
|
||||
- Boss 当前默认仍以 `stdio` 作为本机 agent 接入方式;`ws://127.0.0.1:<port>` 和 `unix://PATH` 本地长驻 transport 已可灰度接入,WebSocket/Unix WebSocket handshake 支持 `Authorization: Bearer <token>`;非 loopback signed bearer/JWT、自动重连和健康探测仍保留为后续增强,不直接替换当前稳定链路
|
||||
- 官方文档提示 WebSocket ingress 满载时会返回 JSON-RPC `-32001 / Server overloaded; retry later.`;Boss runner 已对该错误做最多 3 次指数退避重试,避免长驻连接瞬时拥塞直接把用户任务打失败
|
||||
- Boss heartbeat 已新增 App Server 能力发现缓存:按 `codexAppServerDiscoveryTtlMs` 拉取 `model/list`、`modelProvider/capabilities/read`、`skills/list`、`plugin/list`、`app/list`、`experimentalFeature/list`、`collaborationMode/list`、`permissionProfile/list` 和 `mcpServerStatus/list`,归一成设备 `capabilities.codexAppServer.metadata`;发现失败只记录 warn,不阻塞心跳。MCP discovery 使用 `detail=toolsAndAuthOnly`,只保留服务名、工具数量、资源数量和认证状态,不保存 resource URI 或工具参数。
|
||||
- Boss heartbeat 已新增 App Server 能力发现缓存:按 `codexAppServerDiscoveryTtlMs` 拉取 `model/list`、`modelProvider/capabilities/read`、`skills/list`、`plugin/list`、`app/list`、`experimentalFeature/list`、`collaborationMode/list`、`permissionProfile/list`、`mcpServerStatus/list`、`account/read`、`account/rateLimits/read`、`config/read`、`configRequirements/read` 和 `externalAgentConfig/detect`,归一成设备 `capabilities.codexAppServer.metadata`;发现失败只记录 warn,不阻塞心跳。MCP discovery 使用 `detail=toolsAndAuthOnly`,账号和配置 discovery 只保留安全摘要,不保存邮箱、resource URI、工具参数、完整 config、本地路径或迁移描述。
|
||||
- Boss 第一批只用 App Server 做任务级 provider,不直接复用 ChatGPT Mobile 到 Codex App 的官方 relay;官方移动控制链路仍属于 ChatGPT App 与 Codex App 同账号/工作区之间的产品能力,不是第三方 Boss 可以稳定依赖的私有通道
|
||||
|
||||
下一轮再核对版本时,不要只看 npm 包版本号;必须同时读取 App Server schema / TypeScript 定义,并把 protocol snapshot 保存到 `docs/protocol-snapshots/codex-app-server/<version>/`。
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
- 当前生产主链仍然沿用 `local-agent -> codex exec resume -> /api/v1/master-agent/tasks/[taskId]/complete`,执行底座重构以“先抽象、不改行为”为准
|
||||
- 当前 Codex server 调研结论已记录在 `docs/architecture/codex_server_progress_card_cn.md`:长期优先方向更新为 `Codex App Server / Remote Control -> Inter-Thread Broker -> CodexMcpBackendAdapter -> codex exec resume` 的分层 provider 策略;当前 boss-agent 默认打开 `Codex App Server` runner 作为 Codex 绑定入口,Boss 仍保留 `codex exec resume` 兜底,并继续用 `execution_progress` 结构化进度卡作为 APP 可见执行态。本机 `codex-cli 0.135.0-alpha.1` 协议快照已生成到 `docs/protocol-snapshots/codex-app-server/0.135.0-alpha.1/`,确认支持 WebSocket auth、`thread/inject_items`、`turn/steer`、`thread/realtime/*`、`thread/goal/*`、`thread/settings/updated`、`thread/compacted`、`ThreadItem.contextCompaction`、`ThreadItem.collabToolCall`、`account/*`、`model/verification`、`configWarning`、`deprecationNotice`、`command/exec` 和 `model/list`
|
||||
- 当前 App Server 能力发现已新增治理摘要:local-agent 会在 heartbeat discovery 中拉取 `experimentalFeature/list / collaborationMode/list / permissionProfile/list / mcpServerStatus/list`,并把实验特性、协作模式、权限 Profile 与 MCP 服务状态写入设备 `codexAppServer.metadata`;设备详情页会显示“治理”摘要。该链路只保留安全摘要,不保存 MCP resource URI、permission profile 文件规则、本地路径、token 或工具参数。
|
||||
- 当前 App Server 能力发现已新增账号与配置摘要:local-agent 会在 heartbeat discovery 中拉取 `account/read / account/rateLimits/read / config/read / configRequirements/read / externalAgentConfig/detect`,并把账号登录方式、套餐、额度使用率、App 配置计数、托管要求数量和外部 Agent 迁移候选数量写入设备 `codexAppServer.metadata`;设备详情页会显示“账号 / 配置”摘要。该链路只读不写,不保存账号邮箱、完整 config、API key、本地路径或迁移描述。
|
||||
- 当前量产 B+ 架构开发文档已新增:`docs/architecture/enterprise_ai_ops_architecture_cn.md`。该文档把 PPT 中的主 Agent / 业务 Agent / 老板端 / 经理端 / 员工端 / 治理层 / 系统层 / 设备层 / 执行层 / 接入层整理成后续产品架构约束,并明确数据库备份、业务回退、Codex 协议扩展和 Skill 治理方向;它是规划文档,不代表当前全部已落地
|
||||
- 当前 `claw-code` 已以最小 `ClawBackendAdapter` 形式接入执行底座,但默认关闭;只有显式配置 `BOSS_CLAW_*` 且可用性探测通过时,`master-agent` 当前对话中才会出现并允许选择 `claw-runtime`
|
||||
- 当前已新增最小 `Telegram Gateway`:Boss 当前可直接暴露 Telegram webhook,把 Telegram 私聊或受控群聊文本桥接进 `master-agent` 或按群 / Topic 路由到指定 Boss 项目,并在主 Agent 异步任务完成后自动回推 Telegram;配置入口已接到 Web `/me/telegram` 和原生 Android `我的 > Telegram 接入`
|
||||
|
||||
@@ -1287,6 +1287,68 @@ function normalizeDiscoveryMcpServers(result) {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function normalizeDiscoveryAccountSummary(result) {
|
||||
const account = result?.account && typeof result.account === "object" ? result.account : null;
|
||||
const authMode = trimToDefined(account?.type) || trimToDefined(result?.authMode) || "none";
|
||||
return {
|
||||
signedIn: Boolean(account),
|
||||
authMode,
|
||||
planType: safeProgressText(account?.planType ?? result?.planType, 64) || "",
|
||||
requiresOpenaiAuth: Boolean(result?.requiresOpenaiAuth),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDiscoveryRateLimitSummary(result) {
|
||||
const buckets = Object.values(
|
||||
result?.rateLimitsByLimitId && typeof result.rateLimitsByLimitId === "object"
|
||||
? result.rateLimitsByLimitId
|
||||
: {},
|
||||
);
|
||||
const rateLimits = buckets.length > 0 ? buckets : result?.rateLimits ? [result.rateLimits] : [];
|
||||
const usedPercents = rateLimits
|
||||
.map((bucket) => Number(bucket?.primary?.usedPercent))
|
||||
.filter((value) => Number.isFinite(value))
|
||||
.map((value) => Math.max(0, Math.min(100, Math.round(value))));
|
||||
const reached = rateLimits.some((bucket) => Boolean(trimToDefined(bucket?.rateLimitReachedType)));
|
||||
return {
|
||||
bucketCount: rateLimits.length,
|
||||
maxUsedPercent: usedPercents.length > 0 ? Math.max(...usedPercents) : 0,
|
||||
reached,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDiscoveryAppConfigSummary(result) {
|
||||
const apps = result?.config?.apps && typeof result.config.apps === "object" ? result.config.apps : {};
|
||||
const defaultConfig = apps._default && typeof apps._default === "object" ? apps._default : {};
|
||||
const appEntries = Object.entries(apps).filter(([key]) => key !== "_default");
|
||||
return {
|
||||
appCount: appEntries.length,
|
||||
enabledAppCount: appEntries.filter(([, value]) => value && typeof value === "object" && value.enabled === true)
|
||||
.length,
|
||||
defaultEnabled: defaultConfig.enabled !== false,
|
||||
destructiveEnabled: Boolean(defaultConfig.destructive_enabled),
|
||||
openWorldEnabled: Boolean(defaultConfig.open_world_enabled),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDiscoveryConfigRequirements(result) {
|
||||
return {
|
||||
managed: Boolean(result?.managed),
|
||||
requirementCount: asArray(result?.requirements).length,
|
||||
warningCount: asArray(result?.warnings).length,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDiscoveryExternalAgentMigration(result) {
|
||||
const items = asArray(result?.items);
|
||||
return {
|
||||
itemCount: items.length,
|
||||
homeItemCount: items.filter((item) => item?.cwd === null).length,
|
||||
projectItemCount: items.filter((item) => trimToDefined(item?.cwd)).length,
|
||||
itemTypes: Array.from(new Set(items.map((item) => trimToDefined(item?.itemType)).filter(Boolean))).sort(),
|
||||
};
|
||||
}
|
||||
|
||||
async function withCodexAppServerRpcSession(runnerConfig, callback) {
|
||||
const cwd = runnerConfig.cwd || process.cwd();
|
||||
let closed = false;
|
||||
@@ -1403,6 +1465,11 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
|
||||
collaborationModesResult,
|
||||
permissionProfilesResult,
|
||||
mcpServersResult,
|
||||
accountResult,
|
||||
rateLimitsResult,
|
||||
configResult,
|
||||
configRequirementsResult,
|
||||
externalAgentConfigResult,
|
||||
] = await Promise.all([
|
||||
safeRequest(request, "model/list", { includeHidden: false, limit }),
|
||||
safeRequest(request, "modelProvider/capabilities/read", {}),
|
||||
@@ -1413,6 +1480,11 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
|
||||
safeRequest(request, "collaborationMode/list", {}),
|
||||
safeRequest(request, "permissionProfile/list", { cwd, limit }),
|
||||
safeRequest(request, "mcpServerStatus/list", { limit, detail: "toolsAndAuthOnly" }),
|
||||
safeRequest(request, "account/read", { refreshToken: false }),
|
||||
safeRequest(request, "account/rateLimits/read"),
|
||||
safeRequest(request, "config/read", { includeLayers: false }),
|
||||
safeRequest(request, "configRequirements/read"),
|
||||
safeRequest(request, "externalAgentConfig/detect", { includeHome: true, cwds: [cwd] }),
|
||||
]);
|
||||
|
||||
const models = asArray(modelResult?.data)
|
||||
@@ -1441,6 +1513,11 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
|
||||
collaborationModes: normalizeDiscoveryCollaborationModes(collaborationModesResult).slice(0, limit),
|
||||
permissionProfiles: normalizeDiscoveryPermissionProfiles(permissionProfilesResult).slice(0, limit),
|
||||
mcpServers: normalizeDiscoveryMcpServers(mcpServersResult).slice(0, limit),
|
||||
accountSummary: normalizeDiscoveryAccountSummary(accountResult),
|
||||
rateLimitSummary: normalizeDiscoveryRateLimitSummary(rateLimitsResult),
|
||||
appConfigSummary: normalizeDiscoveryAppConfigSummary(configResult),
|
||||
configRequirements: normalizeDiscoveryConfigRequirements(configRequirementsResult),
|
||||
externalAgentMigration: normalizeDiscoveryExternalAgentMigration(externalAgentConfigResult),
|
||||
errors: [
|
||||
modelResult?.__bossError ? `model/list:${safeRuntimeDiagnosticText(modelResult.__bossError)}` : undefined,
|
||||
providerCapabilities?.__bossError
|
||||
@@ -1461,6 +1538,17 @@ export async function discoverCodexAppServerCapabilities(runnerConfig) {
|
||||
mcpServersResult?.__bossError
|
||||
? `mcpServerStatus/list:${safeRuntimeDiagnosticText(mcpServersResult.__bossError)}`
|
||||
: undefined,
|
||||
accountResult?.__bossError ? `account/read:${safeRuntimeDiagnosticText(accountResult.__bossError)}` : undefined,
|
||||
rateLimitsResult?.__bossError
|
||||
? `account/rateLimits/read:${safeRuntimeDiagnosticText(rateLimitsResult.__bossError)}`
|
||||
: undefined,
|
||||
configResult?.__bossError ? `config/read:${safeRuntimeDiagnosticText(configResult.__bossError)}` : undefined,
|
||||
configRequirementsResult?.__bossError
|
||||
? `configRequirements/read:${safeRuntimeDiagnosticText(configRequirementsResult.__bossError)}`
|
||||
: undefined,
|
||||
externalAgentConfigResult?.__bossError
|
||||
? `externalAgentConfig/detect:${safeRuntimeDiagnosticText(externalAgentConfigResult.__bossError)}`
|
||||
: undefined,
|
||||
].filter(Boolean),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -75,6 +75,15 @@ function textFromMetadata(value: unknown) {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : "未发现";
|
||||
}
|
||||
|
||||
function objectFromMetadata(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
function numberFromMetadata(value: unknown) {
|
||||
const numeric = Number(value);
|
||||
return Number.isFinite(numeric) ? Math.max(0, Math.round(numeric)) : 0;
|
||||
}
|
||||
|
||||
export function buildDeviceWorkspaceDetailCards(workspace: DeviceWorkspaceView) {
|
||||
const selectedDevice = workspace.selectedDevice;
|
||||
const primaryPolicy = workspace.projectExecutionPolicies?.[0];
|
||||
@@ -87,6 +96,11 @@ export function buildDeviceWorkspaceDetailCards(workspace: DeviceWorkspaceView)
|
||||
const codexCollaborationModeCount = arrayLength(codexAppServerMetadata.collaborationModes);
|
||||
const codexMcpServerCount = arrayLength(codexAppServerMetadata.mcpServers);
|
||||
const codexPermissionProfileCount = arrayLength(codexAppServerMetadata.permissionProfiles);
|
||||
const codexAccountSummary = objectFromMetadata(codexAppServerMetadata.accountSummary);
|
||||
const codexRateLimitSummary = objectFromMetadata(codexAppServerMetadata.rateLimitSummary);
|
||||
const codexAppConfigSummary = objectFromMetadata(codexAppServerMetadata.appConfigSummary);
|
||||
const codexConfigRequirements = objectFromMetadata(codexAppServerMetadata.configRequirements);
|
||||
const codexExternalAgentMigration = objectFromMetadata(codexAppServerMetadata.externalAgentMigration);
|
||||
|
||||
return {
|
||||
capabilities: {
|
||||
@@ -113,6 +127,14 @@ export function buildDeviceWorkspaceDetailCards(workspace: DeviceWorkspaceView)
|
||||
: "模型:未发现",
|
||||
codexExtensions: `扩展:Skill ${codexSkillCount} 个 · Plugin ${codexPluginCount} 个 · App ${codexAppCount} 个`,
|
||||
codexGovernance: `治理:实验特性 ${codexExperimentalFeatureCount} 个 · 协作模式 ${codexCollaborationModeCount} 个 · MCP ${codexMcpServerCount} 个 · 权限 ${codexPermissionProfileCount} 个`,
|
||||
codexAccount: `账号:${textFromMetadata(codexAccountSummary.authMode)} · 套餐 ${textFromMetadata(
|
||||
codexAccountSummary.planType,
|
||||
)} · 额度 ${numberFromMetadata(codexRateLimitSummary.maxUsedPercent)}%`,
|
||||
codexConfig: `配置:App ${numberFromMetadata(codexAppConfigSummary.appCount)} 个 · 已启用 ${numberFromMetadata(
|
||||
codexAppConfigSummary.enabledAppCount,
|
||||
)} 个 · 托管要求 ${numberFromMetadata(codexConfigRequirements.requirementCount)} 个 · 外部迁移 ${numberFromMetadata(
|
||||
codexExternalAgentMigration.itemCount,
|
||||
)} 项`,
|
||||
preferredExecutionMode: `默认执行模式:${
|
||||
selectedDevice?.preferredExecutionMode === "gui"
|
||||
? "GUI"
|
||||
@@ -747,6 +769,12 @@ export function DeviceEditorCard({
|
||||
<div className="rounded-2xl bg-[#F7F8FA] px-3 py-2">
|
||||
{detailCards.capabilities.items.codexGovernance}
|
||||
</div>
|
||||
<div className="rounded-2xl bg-[#F7F8FA] px-3 py-2">
|
||||
{detailCards.capabilities.items.codexAccount}
|
||||
</div>
|
||||
<div className="rounded-2xl bg-[#F7F8FA] px-3 py-2">
|
||||
{detailCards.capabilities.items.codexConfig}
|
||||
</div>
|
||||
<div className="rounded-2xl bg-[#F7F8FA] px-3 py-2">
|
||||
{detailCards.capabilities.items.preferredExecutionMode}
|
||||
</div>
|
||||
|
||||
@@ -83,6 +83,22 @@ test("device detail exposes Codex App Server discovered model and extension summ
|
||||
{ name: "github", toolCount: 2, authStatus: "oAuth" },
|
||||
{ name: "figma", toolCount: 0, authStatus: "notLoggedIn" },
|
||||
],
|
||||
accountSummary: { signedIn: true, authMode: "chatgpt", planType: "pro", requiresOpenaiAuth: true },
|
||||
rateLimitSummary: { bucketCount: 2, maxUsedPercent: 42, reached: false },
|
||||
appConfigSummary: {
|
||||
appCount: 2,
|
||||
enabledAppCount: 1,
|
||||
defaultEnabled: true,
|
||||
destructiveEnabled: false,
|
||||
openWorldEnabled: false,
|
||||
},
|
||||
configRequirements: { managed: true, requirementCount: 2, warningCount: 1 },
|
||||
externalAgentMigration: {
|
||||
itemCount: 3,
|
||||
homeItemCount: 1,
|
||||
projectItemCount: 2,
|
||||
itemTypes: ["AGENTS_MD", "MCP_SERVER_CONFIG", "SKILLS"],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -95,6 +111,8 @@ test("device detail exposes Codex App Server discovered model and extension summ
|
||||
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 个");
|
||||
assert.equal(cards.capabilities.items.codexAccount, "账号:chatgpt · 套餐 pro · 额度 42%");
|
||||
assert.equal(cards.capabilities.items.codexConfig, "配置:App 2 个 · 已启用 1 个 · 托管要求 2 个 · 外部迁移 3 项");
|
||||
});
|
||||
|
||||
test("device detail exposes folder and project conflict skeleton from workspace policy", async () => {
|
||||
|
||||
@@ -63,6 +63,11 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
|
||||
collaborationModes: [{ id: "plan" }],
|
||||
permissionProfiles: [{ id: ":workspace" }],
|
||||
mcpServers: [{ name: "github", toolCount: 2, authStatus: "oAuth" }],
|
||||
accountSummary: { signedIn: true, authMode: "chatgpt", planType: "pro", requiresOpenaiAuth: true },
|
||||
rateLimitSummary: { bucketCount: 2, maxUsedPercent: 42, reached: false },
|
||||
appConfigSummary: { appCount: 2, enabledAppCount: 1 },
|
||||
configRequirements: { managed: true, requirementCount: 2, warningCount: 1 },
|
||||
externalAgentMigration: { itemCount: 3, homeItemCount: 1, projectItemCount: 2 },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -85,4 +90,20 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
|
||||
?.toolCount,
|
||||
2,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
updatedDevice?.capabilities?.codexAppServer.metadata?.accountSummary as
|
||||
| { authMode?: string; planType?: string }
|
||||
| undefined
|
||||
)?.authMode,
|
||||
"chatgpt",
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
updatedDevice?.capabilities?.codexAppServer.metadata?.externalAgentMigration as
|
||||
| { itemCount?: number }
|
||||
| undefined
|
||||
)?.itemCount,
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
115
tests/fixtures/codex-app-server-runtime.mjs
vendored
115
tests/fixtures/codex-app-server-runtime.mjs
vendored
@@ -266,6 +266,121 @@ rl.on("line", (line) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "account/read") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
account: {
|
||||
type: "chatgpt",
|
||||
email: "private-user@example.com",
|
||||
planType: "pro",
|
||||
accessToken: "sk-secret-should-not-leak",
|
||||
},
|
||||
requiresOpenaiAuth: true,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "account/rateLimits/read") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
rateLimits: {
|
||||
limitId: "codex",
|
||||
limitName: "Primary Codex",
|
||||
primary: { usedPercent: 25, windowDurationMins: 15, resetsAt: 1730947200 },
|
||||
secondary: null,
|
||||
rateLimitReachedType: null,
|
||||
},
|
||||
rateLimitsByLimitId: {
|
||||
codex: {
|
||||
limitId: "codex",
|
||||
primary: { usedPercent: 25, windowDurationMins: 15, resetsAt: 1730947200 },
|
||||
rateLimitReachedType: null,
|
||||
},
|
||||
codex_other: {
|
||||
limitId: "codex_other",
|
||||
limitName: "codex_other",
|
||||
primary: { usedPercent: 42, windowDurationMins: 60, resetsAt: 1730950800 },
|
||||
rateLimitReachedType: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "config/read") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
config: {
|
||||
api_key: "sk-secret-should-not-leak",
|
||||
model: "gpt-5.4",
|
||||
apps: {
|
||||
_default: {
|
||||
enabled: true,
|
||||
destructive_enabled: false,
|
||||
open_world_enabled: false,
|
||||
},
|
||||
google_drive: {
|
||||
enabled: true,
|
||||
destructive_enabled: false,
|
||||
default_tools_approval_mode: "prompt",
|
||||
},
|
||||
secret_app: {
|
||||
enabled: false,
|
||||
token: "sk-secret-should-not-leak",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "configRequirements/read") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
managed: true,
|
||||
requirements: [
|
||||
{ keyPath: "apps._default.destructive_enabled", status: "blocked", sourcePath: "/Users/kris/.codex/config.toml" },
|
||||
{ keyPath: "sandbox_mode", status: "ok" },
|
||||
],
|
||||
warnings: [{ message: "policy reads /Users/kris/.ssh/id_ed25519" }],
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "externalAgentConfig/detect") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
items: [
|
||||
{
|
||||
itemType: "AGENTS_MD",
|
||||
description: "Import /Users/kris/code/boss/CLAUDE.md to /Users/kris/code/boss/AGENTS.md.",
|
||||
cwd: "/Users/kris/code/boss",
|
||||
},
|
||||
{
|
||||
itemType: "SKILLS",
|
||||
description: "Copy skill folders from /Users/kris/.claude/skills to /Users/kris/.agents/skills.",
|
||||
cwd: null,
|
||||
},
|
||||
{
|
||||
itemType: "MCP_SERVER_CONFIG",
|
||||
description: "Migrate server config with token sk-secret-should-not-leak",
|
||||
cwd: "/Users/kris/code/boss",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "thread/resume") {
|
||||
send({
|
||||
id: message.id,
|
||||
|
||||
@@ -36,6 +36,33 @@ test("codex app-server discovery includes governance and MCP summaries without l
|
||||
assert.equal(metadata.mcpServers[0].name, "github");
|
||||
assert.equal(metadata.mcpServers[0].toolCount, 2);
|
||||
assert.equal(metadata.mcpServers[0].authStatus, "oAuth");
|
||||
assert.deepEqual(metadata.accountSummary, {
|
||||
signedIn: true,
|
||||
authMode: "chatgpt",
|
||||
planType: "pro",
|
||||
requiresOpenaiAuth: true,
|
||||
});
|
||||
assert.equal(metadata.rateLimitSummary.bucketCount, 2);
|
||||
assert.equal(metadata.rateLimitSummary.maxUsedPercent, 42);
|
||||
assert.equal(metadata.rateLimitSummary.reached, false);
|
||||
assert.deepEqual(metadata.appConfigSummary, {
|
||||
appCount: 2,
|
||||
enabledAppCount: 1,
|
||||
defaultEnabled: true,
|
||||
destructiveEnabled: false,
|
||||
openWorldEnabled: false,
|
||||
});
|
||||
assert.deepEqual(metadata.configRequirements, {
|
||||
managed: true,
|
||||
requirementCount: 2,
|
||||
warningCount: 1,
|
||||
});
|
||||
assert.deepEqual(metadata.externalAgentMigration, {
|
||||
itemCount: 3,
|
||||
homeItemCount: 1,
|
||||
projectItemCount: 2,
|
||||
itemTypes: ["AGENTS_MD", "MCP_SERVER_CONFIG", "SKILLS"],
|
||||
});
|
||||
|
||||
const serialized = JSON.stringify(metadata);
|
||||
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
|
||||
@@ -43,6 +70,9 @@ test("codex app-server discovery includes governance and MCP summaries without l
|
||||
assert.equal(serialized.includes("id_ed25519"), false);
|
||||
assert.equal(serialized.includes("filesystem"), false);
|
||||
assert.equal(serialized.includes("resources"), false);
|
||||
assert.equal(serialized.includes("private-user@example.com"), false);
|
||||
assert.equal(serialized.includes("CLAUDE.md"), false);
|
||||
assert.equal(serialized.includes("AGENTS.md"), false);
|
||||
});
|
||||
|
||||
function encodeWsTextFrame(value) {
|
||||
|
||||
Reference in New Issue
Block a user