feat(agent): standardize approval policy and sandbox naming conventions in Codex settings

This commit is contained in:
HouYunFei
2026-07-29 16:50:51 +08:00
parent b926feeb14
commit fe1ba781d1
5 changed files with 149 additions and 126 deletions
+1
View File
@@ -2,6 +2,7 @@
## Unreleased
+ [新增] 画布 Agent 支持请求批准、自动审查和完全访问三档 Codex 权限,并可在对话中审批文件、命令与网络访问。
+ [优化] Agent Markdown 代码块与外链确认弹窗改为紧凑中文样式,改善右侧面板阅读体验。
+ [优化] Agent 思考摘要支持 Markdown 渲染,改善强调、列表和代码内容的阅读效果。
+ [修复] Agent 对话启用 Codex 可读思考摘要,并修复一轮结束或刷新恢复后思考摘要、命令和工具过程记录缺失的问题。
+2 -2
View File
@@ -346,12 +346,12 @@ function codexConfig(permissionMode: AgentPermissionMode) {
}
function threadSettings(permissionMode: AgentPermissionMode) {
return { approvalPolicy: permissionMode === "full" ? "never" as const : "onRequest" as const, sandbox: permissionMode === "full" ? "dangerFullAccess" as const : "workspaceWrite" as const, config: codexConfig(permissionMode) };
return { approvalPolicy: permissionMode === "full" ? "never" as const : "on-request" as const, sandbox: permissionMode === "full" ? "danger-full-access" as const : "workspace-write" as const, config: codexConfig(permissionMode) };
}
function turnSettings(permissionMode: AgentPermissionMode) {
return {
approvalPolicy: permissionMode === "full" ? "never" as const : "onRequest" as const,
approvalPolicy: permissionMode === "full" ? "never" as const : "on-request" as const,
sandboxPolicy: permissionMode === "full" ? { type: "dangerFullAccess" as const } : { type: "workspaceWrite" as const, networkAccess: false },
};
}
+3 -3
View File
@@ -12,8 +12,8 @@ export type CodexTurnInput =
| { type: "localImage"; path: string };
type ThreadOptions = {
approvalPolicy: "never" | "onRequest";
sandbox: "workspaceWrite" | "dangerFullAccess";
approvalPolicy: "never" | "on-request";
sandbox: "workspace-write" | "danger-full-access";
config: JsonRecord;
cwd?: string;
};
@@ -54,7 +54,7 @@ type CodexRequestSpec = {
result: Record<string, never>;
};
"turn/start": {
params: { threadId: string; input: CodexTurnInput[]; approvalPolicy: "never" | "onRequest"; sandboxPolicy: { type: "workspaceWrite"; networkAccess: boolean } | { type: "dangerFullAccess" } };
params: { threadId: string; input: CodexTurnInput[]; approvalPolicy: "never" | "on-request"; sandboxPolicy: { type: "workspaceWrite"; networkAccess: boolean } | { type: "dangerFullAccess" } };
result: { turn: CodexTurn };
};
"turn/interrupt": {
+2 -1
View File
@@ -5,7 +5,7 @@ description: 当前版本已实现但仍需人工验证的变更项
# 待测试
- Agent Markdown 样式:右侧 Agent 回复中的代码块应使用紧凑细边框样式,不再出现大面积空白,只保留复制操作;行内代码、链接在浅色和深色主题下应清晰;点击外部链接后应显示中文紧凑确认弹窗,长路径能够正常换行且复制、继续打开和关闭操作可用。
- Agent Markdown 样式:右侧 Agent 回复中的代码块应为横向占满消息区域、无语言标题和无双层边框的紧凑代码条,单行内容不应保留纵向大面积空白,复制操作仅在悬停时弱化显示;行内代码、链接在浅色和深色主题下应清晰;点击外部链接后应显示中文紧凑确认弹窗,长路径能够正常换行且复制、继续打开和关闭操作可用。
- Agent 工具确认模式:右侧面板标题栏不应再显示「工具确认」开关;对话输入框左下方应显示确认模式选择,默认选中「自动确认」,画布写入工具应直接执行;切换为「手动确认」后,画布写入工具应在对话中展示等待确认卡片,并支持批准或拒绝。
- Canvas Agent Codex 升级:启动 Canvas Agent 后应能正常连接 Codex、创建或恢复会话、发送消息并调用画布工具,实际运行的 Codex CLI 版本应为 0.145.0;运行中停止任务应只中断当前 turn,随后无需重启 Agent 即可继续发送新任务。
- Canvas Agent Debug:使用 `npx -y @basketikun/canvas-agent --debug` 启动后,终端应按“时间 级别 消息 详情”的纯文本单行格式输出日志并显示日志文件路径,`~/.infinite-canvas/logs/` 下应按启动日期生成相同格式的 `canvas-agent-YYYY-MM-DD.log`,同一天多次启动应追加到同一文件;普通启动保持原有简洁输出,日志中不应出现连接 token 或图片 Data URL 原文。
@@ -14,6 +14,7 @@ description: 当前版本已实现但仍需人工验证的变更项
- Agent 回复实时显示:在右侧 Agent 发送消息后,用户消息下方应立即出现“正在思考...”,任务运行期间不应闪退;工具完成后应显示 Codex 正在继续处理及已等待时长,等待超过 30 秒时提示可继续等待或停止本轮;Codex 的回复应在当前对话中持续显示,实时事件缺失时也应在任务完成后自动同步完整内容,无需切换到历史或日志再返回对话。模型繁忙等任务失败时应立即结束等待状态,在对话中显示中文错误原因和重试建议,诊断日志不应再把失败轮次记为“处理完成”,刷新历史后错误仍应保留。新建尚未发送首条消息的空会话不应反复出现历史读取失败。
- Agent 流式交互性能:发送长回复时文字应连续平滑出现,输入框、滚动和画布操作不应随回复变长而明显卡顿;长历史会话中只有当前流式消息持续更新,屏幕外消息不应造成明显布局压力;任务完成后应通过 SSE 自动同步完整历史,不再持续请求 `/health` 轮询状态。
- Agent 过程时间线:新建 Agent 对话后 Codex 应生成可读思考摘要,并在运行时依次显示中文的思考摘要、执行计划、命令执行、网页搜索、文件修改和画布工具活动;思考摘要的图标、标题和箭头应稳定保持在同一行,默认收起且无边框,点击箭头后才展开 Codex 实际返回的具体摘要,其中 Markdown 强调、列表和代码应正确渲染而非显示原始符号,完成事件不应再用「已完成分析」覆盖已经收到的摘要;命令执行也应使用无边框紧凑行,显示「已执行 1 条命令」和单行命令预览,点击后再展开工作目录、耗时、退出状态和运行输出;其他工具调用继续使用紧凑卡片排版,状态图标、标题和状态文字保持在同一行;结构化任务进度不应混在对话时间线中,而应独立放在对话区下方、Token 统计上方,支持展开和折叠,并逐项实时更新「待处理」「进行中」「已完成」状态;新任务生成时默认展开,任务结束后保留最新结果,同一计划更新不应生成重复内容;文件详情展示文件路径与新增/修改/删除动作,工具详情不应出现请求 ID、英文工具名或原始 JSON;一轮对话结束自动同步历史以及刷新页面后从历史中 resume 对话时,思考摘要、命令和工具过程记录仍应完整保留并保持相同展示。
- Agent 权限控制:输入框可选择「请求批准」「自动审查」「完全访问」;请求批准模式下,Codex 编辑工作区外文件、执行受限命令或访问网络时应在对话中显示审批卡片,支持拒绝、允许一次和本会话允许,并能依次处理多个并发请求;自动审查模式应仅将需要用户决定的风险操作送入审批;完全访问必须先显示风险确认,启用后可访问网络和本机文件且不再请求审批;选择应在刷新后保留。
- Agent 历史记录:点击记录卡片应直接进入对应对话,不再显示「进入」按钮;可勾选单条或全选多条记录并批量删除,删除当前对话后聊天内容应清空。
- Agent 默认新对话:每次进入任一画布并连接 Agent 后,对话区应保持空白且不自动恢复上一次会话;第一次发送消息时才创建新线程,不应产生未发送消息的空历史记录;需要继续旧对话时可在「历史」中主动选择恢复。
- Agent 当前画布优先:在已打开某个画布时要求 Agent 创建、修改、整理或生成内容,Agent 应直接读取并操作当前画布,不应先调用 `canvas_list_projects` 或使用 `site_navigate` 重复进入画布;只有明确要求查看或切换其他画布时才允许查询画布列表并导航。
+141 -120
View File
@@ -472,124 +472,145 @@
mix-blend-mode: normal;
}
@layer components {
.agent-streamdown {
overflow-wrap: anywhere;
}
.agent-streamdown > :first-child {
margin-top: 0;
}
.agent-streamdown > :last-child {
margin-bottom: 0;
}
.agent-streamdown [data-streamdown="code-block"] {
margin-block: 0.75rem;
overflow: hidden;
border-color: color-mix(in srgb, currentColor 13%, transparent);
border-radius: 0.75rem;
background: color-mix(in srgb, currentColor 3%, transparent);
}
.agent-streamdown [data-streamdown="code-block-header"] {
min-height: 0;
padding: 0.45rem 0.75rem;
border-color: color-mix(in srgb, currentColor 9%, transparent);
background: transparent;
font-size: 0.625rem;
line-height: 1rem;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.52;
}
.agent-streamdown [data-streamdown="code-block-body"] {
padding: 0.8rem 0.9rem;
background: transparent !important;
font-size: 0.75rem;
line-height: 1.25rem;
}
.agent-streamdown [data-streamdown="code-block-actions"] {
gap: 0;
margin: 0.35rem;
padding: 0;
border: 0;
border-radius: 0.375rem;
background: color-mix(in srgb, var(--background) 88%, transparent);
box-shadow: none;
}
.agent-streamdown [data-streamdown="code-block-actions"] button {
padding: 0.35rem;
border-radius: 0.375rem;
}
.agent-streamdown [data-streamdown="inline-code"] {
padding: 0.12rem 0.35rem;
border: 0;
border-radius: 0.3rem;
background: color-mix(in srgb, currentColor 7%, transparent);
font-size: 0.88em;
}
.agent-streamdown [data-streamdown="link"] {
color: inherit;
text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
text-underline-offset: 0.2em;
}
[data-streamdown="link-safety-modal"] {
padding: 1rem;
background: rgb(28 25 23 / 24%);
backdrop-filter: blur(8px);
}
[data-streamdown="link-safety-modal"] > div {
max-width: 24rem;
gap: 1rem;
padding: 1.25rem;
border-color: color-mix(in srgb, currentColor 12%, transparent);
border-radius: 1rem;
box-shadow: 0 20px 60px rgb(28 25 23 / 18%);
}
[data-streamdown="link-safety-modal"] > div > button {
top: 0.9rem;
right: 0.9rem;
}
[data-streamdown="link-safety-modal"] > div > div:first-of-type {
gap: 0.25rem;
padding-right: 2rem;
}
[data-streamdown="link-safety-modal"] > div > div:first-of-type > div {
gap: 0.5rem;
font-size: 1rem;
}
[data-streamdown="link-safety-modal"] > div > div:nth-of-type(2) {
max-height: 7rem;
padding: 0.75rem;
border: 1px solid color-mix(in srgb, currentColor 9%, transparent);
background: color-mix(in srgb, currentColor 4%, transparent);
font-size: 0.7rem;
line-height: 1.15rem;
overflow-wrap: anywhere;
}
[data-streamdown="link-safety-modal"] > div > div:last-child {
justify-content: flex-end;
}
[data-streamdown="link-safety-modal"] > div > div:last-child button {
flex: none;
min-width: 7rem;
padding: 0.5rem 0.8rem;
border-radius: 0.6rem;
font-size: 0.75rem;
}
.agent-streamdown {
overflow-wrap: anywhere;
}
.agent-streamdown > :first-child {
margin-top: 0;
}
.agent-streamdown > :last-child {
margin-bottom: 0;
}
.agent-streamdown [data-streamdown="code-block"] {
position: relative;
display: block;
contain: none;
contain-intrinsic-size: none !important;
width: 100%;
margin-block: 0.35rem;
padding: 0;
gap: 0;
overflow: hidden;
border: 0;
border-radius: 0.4rem;
background: color-mix(in srgb, currentColor 5%, transparent);
}
.agent-streamdown [data-streamdown="code-block-header"] {
display: none;
}
.agent-streamdown [data-streamdown="code-block"] > div:has(> [data-streamdown="code-block-actions"]) {
position: absolute;
top: 0.2rem;
right: 0.2rem;
z-index: 1;
height: auto;
margin: 0;
}
.agent-streamdown [data-streamdown="code-block-body"] {
min-height: 0;
padding: 0.4rem 2rem 0.4rem 0.6rem;
overflow-x: auto;
border: 0;
border-radius: 0;
background: transparent !important;
font-size: 0.75rem;
line-height: 1.15rem;
}
.agent-streamdown [data-streamdown="code-block-actions"] {
gap: 0;
margin: 0;
padding: 0;
border: 0;
border-radius: 0.3rem;
background: transparent;
box-shadow: none;
opacity: 0;
transition: opacity 150ms ease;
}
.agent-streamdown [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"],
.agent-streamdown [data-streamdown="code-block-actions"]:focus-within {
opacity: 0.58;
}
.agent-streamdown [data-streamdown="code-block-actions"] button {
padding: 0.25rem;
border-radius: 0.3rem;
}
.agent-streamdown [data-streamdown="code-block-actions"] button:hover {
background: color-mix(in srgb, currentColor 8%, transparent);
}
.agent-streamdown [data-streamdown="inline-code"] {
padding: 0.12rem 0.35rem;
border: 0;
border-radius: 0.3rem;
background: color-mix(in srgb, currentColor 7%, transparent);
font-size: 0.88em;
}
.agent-streamdown [data-streamdown="link"] {
color: inherit;
text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
text-underline-offset: 0.2em;
}
[data-streamdown="link-safety-modal"] {
padding: 1rem;
background: rgb(28 25 23 / 24%);
backdrop-filter: blur(8px);
}
[data-streamdown="link-safety-modal"] > div {
max-width: 24rem;
gap: 1rem;
padding: 1.25rem;
border-color: color-mix(in srgb, currentColor 12%, transparent);
border-radius: 1rem;
box-shadow: 0 20px 60px rgb(28 25 23 / 18%);
}
[data-streamdown="link-safety-modal"] > div > button {
top: 0.9rem;
right: 0.9rem;
}
[data-streamdown="link-safety-modal"] > div > div:first-of-type {
gap: 0.25rem;
padding-right: 2rem;
}
[data-streamdown="link-safety-modal"] > div > div:first-of-type > div {
gap: 0.5rem;
font-size: 1rem;
}
[data-streamdown="link-safety-modal"] > div > div:nth-of-type(2) {
max-height: 7rem;
padding: 0.75rem;
border: 1px solid color-mix(in srgb, currentColor 9%, transparent);
background: color-mix(in srgb, currentColor 4%, transparent);
font-size: 0.7rem;
line-height: 1.15rem;
overflow-wrap: anywhere;
}
[data-streamdown="link-safety-modal"] > div > div:last-child {
justify-content: flex-end;
}
[data-streamdown="link-safety-modal"] > div > div:last-child button {
flex: none;
min-width: 7rem;
padding: 0.5rem 0.8rem;
border-radius: 0.6rem;
font-size: 0.75rem;
}