Add sandbox and approval run profile controls#100
Conversation
There was a problem hiding this comment.
5 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/codex-claw/src/server/codex-cli.ts">
<violation number="1" location="apps/codex-claw/src/server/codex-cli.ts:407">
P1: `runProfile` fallback is derived from the default workspace instead of the workspace sandbox, which can silently change execution permissions for existing or partially updated workspaces.</violation>
</file>
<file name="apps/codex-claw/src/screens/chat/components/chat-composer.tsx">
<violation number="1" location="apps/codex-claw/src/screens/chat/components/chat-composer.tsx:155">
P2: Run profile changes can be saved to a stale workspace id. `activeWorkspaceId` is captured at mount and reused later, so after switching workspaces this PATCH may update the wrong workspace.</violation>
<violation number="2" location="apps/codex-claw/src/screens/chat/components/chat-composer.tsx:156">
P2: `updateWorkspace` result discarded with `void` — local state updates before it, so a failure silently leaves UI and server out of sync. Add `.catch()` that reverts the optimistic state or surfaces an error.</violation>
</file>
<file name="apps/codex-claw/src/screens/chat/components/settings-dialog.tsx">
<violation number="1" location="apps/codex-claw/src/screens/chat/components/settings-dialog.tsx:102">
P2: Approval dropdown is missing the supported `on-failure` policy, causing UI/backend mismatch for workspace approval settings.</violation>
<violation number="2" location="apps/codex-claw/src/screens/chat/components/settings-dialog.tsx:432">
P2: Changing sandbox or approval individually leaves `runProfile` stale — the saved profile won't match the actual sandbox/approval values if they were overridden after profile selection.
Either reset `runProfile` to a sentinel (e.g. `'custom'` or `undefined`) when sandbox/approval are changed independently, or disable the individual sandbox/approval selects when a named profile is selected so the two don't drift.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| codexApproval: normalizeApproval( | ||
| value.codexApproval || fallback.codexApproval, | ||
| ), | ||
| runProfile: normalizeRunProfile(value.runProfile || fallback.runProfile), |
There was a problem hiding this comment.
P1: runProfile fallback is derived from the default workspace instead of the workspace sandbox, which can silently change execution permissions for existing or partially updated workspaces.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/server/codex-cli.ts, line 407:
<comment>`runProfile` fallback is derived from the default workspace instead of the workspace sandbox, which can silently change execution permissions for existing or partially updated workspaces.</comment>
<file context>
@@ -317,6 +401,10 @@ function normalizeWorkspaceRecord(
+ codexApproval: normalizeApproval(
+ value.codexApproval || fallback.codexApproval,
+ ),
+ runProfile: normalizeRunProfile(value.runProfile || fallback.runProfile),
codexWorkdir: normalizePath(value.codexWorkdir, fallback.codexWorkdir),
stateDir: normalizePath(value.stateDir, fallback.stateDir),
</file context>
| runProfile: normalizeRunProfile(value.runProfile || fallback.runProfile), | |
| runProfile: normalizeRunProfile( | |
| value.runProfile || profileFromSandbox(value.codexSandbox || fallback.codexSandbox), | |
| ), |
| setRunProfile(nextProfile.id) | ||
| setConfirmedRisk(false) | ||
| if (activeWorkspaceId) { | ||
| void updateWorkspace({ |
There was a problem hiding this comment.
P2: updateWorkspace result discarded with void — local state updates before it, so a failure silently leaves UI and server out of sync. Add .catch() that reverts the optimistic state or surfaces an error.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/chat-composer.tsx, line 156:
<comment>`updateWorkspace` result discarded with `void` — local state updates before it, so a failure silently leaves UI and server out of sync. Add `.catch()` that reverts the optimistic state or surfaces an error.</comment>
<file context>
@@ -89,6 +141,28 @@ function ChatComposerComponent({
+ setRunProfile(nextProfile.id)
+ setConfirmedRisk(false)
+ if (activeWorkspaceId) {
+ void updateWorkspace({
+ id: activeWorkspaceId,
+ runProfile: nextProfile.id,
</file context>
| <WorkspaceField label="Approval"> | ||
| <select | ||
| value={draft.codexApproval ?? 'untrusted'} | ||
| onChange={(event) => |
There was a problem hiding this comment.
P2: Changing sandbox or approval individually leaves runProfile stale — the saved profile won't match the actual sandbox/approval values if they were overridden after profile selection.
Either reset runProfile to a sentinel (e.g. 'custom' or undefined) when sandbox/approval are changed independently, or disable the individual sandbox/approval selects when a named profile is selected so the two don't drift.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/settings-dialog.tsx, line 432:
<comment>Changing sandbox or approval individually leaves `runProfile` stale — the saved profile won't match the actual sandbox/approval values if they were overridden after profile selection.
Either reset `runProfile` to a sentinel (e.g. `'custom'` or `undefined`) when sandbox/approval are changed independently, or disable the individual sandbox/approval selects when a named profile is selected so the two don't drift.</comment>
<file context>
@@ -376,6 +413,34 @@ export function SettingsDialog({
+ <WorkspaceField label="Approval">
+ <select
+ value={draft.codexApproval ?? 'untrusted'}
+ onChange={(event) =>
+ updateDraft('codexApproval', event.target.value)
+ }
</file context>
|
|
||
| const newWorkspaceId = '__new__' | ||
| const sandboxOptions = ['read-only', 'workspace-write', 'danger-full-access'] | ||
| const approvalOptions = ['untrusted', 'on-request', 'never'] |
There was a problem hiding this comment.
P2: Approval dropdown is missing the supported on-failure policy, causing UI/backend mismatch for workspace approval settings.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/settings-dialog.tsx, line 102:
<comment>Approval dropdown is missing the supported `on-failure` policy, causing UI/backend mismatch for workspace approval settings.</comment>
<file context>
@@ -97,6 +99,27 @@ function WorkspaceField({ label, children }: WorkspaceFieldProps) {
const newWorkspaceId = '__new__'
const sandboxOptions = ['read-only', 'workspace-write', 'danger-full-access']
+const approvalOptions = ['untrusted', 'on-request', 'never']
+const runProfileOptions = [
+ {
</file context>
| const approvalOptions = ['untrusted', 'on-request', 'never'] | |
| const approvalOptions = ['untrusted', 'on-request', 'on-failure', 'never'] |
| if (activeWorkspaceId) { | ||
| void updateWorkspace({ | ||
| id: activeWorkspaceId, | ||
| runProfile: nextProfile.id, | ||
| codexSandbox: nextProfile.sandbox, | ||
| codexApproval: nextProfile.approval, | ||
| }) | ||
| } |
There was a problem hiding this comment.
P2: Run profile changes can be saved to a stale workspace id. activeWorkspaceId is captured at mount and reused later, so after switching workspaces this PATCH may update the wrong workspace.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/chat-composer.tsx, line 155:
<comment>Run profile changes can be saved to a stale workspace id. `activeWorkspaceId` is captured at mount and reused later, so after switching workspaces this PATCH may update the wrong workspace.</comment>
<file context>
@@ -89,6 +141,28 @@ function ChatComposerComponent({
+ runProfiles.find((profile) => profile.id === value) ?? runProfiles[0]
+ setRunProfile(nextProfile.id)
+ setConfirmedRisk(false)
+ if (activeWorkspaceId) {
+ void updateWorkspace({
+ id: activeWorkspaceId,
</file context>
| if (activeWorkspaceId) { | |
| void updateWorkspace({ | |
| id: activeWorkspaceId, | |
| runProfile: nextProfile.id, | |
| codexSandbox: nextProfile.sandbox, | |
| codexApproval: nextProfile.approval, | |
| }) | |
| } | |
| void fetchWorkspaces() | |
| .then((data) => { | |
| if (!data.activeWorkspaceId) return | |
| return updateWorkspace({ | |
| id: data.activeWorkspaceId, | |
| runProfile: nextProfile.id, | |
| codexSandbox: nextProfile.sandbox, | |
| codexApproval: nextProfile.approval, | |
| }) | |
| }) | |
| .catch(() => { | |
| // ignore | |
| }) |
Summary\n- add named Codex run profiles for read-only inspect, workspace write, and elevated manual review\n- show effective sandbox and approval flags before send and require confirmation for write/elevated profiles\n- persist the selected default profile per workspace and pass the selected profile through the send flow\n- record the profile, sandbox, and approval policy on session history messages\n\n## Testing\n- pnpm -C apps/codex-claw lint\n- pnpm -C apps/codex-claw build\n- pnpm -C apps/codex-claw test\n\nCloses #87
Summary by cubic
Adds named Codex run profiles with sandbox and approval controls across chat and settings. Profiles sync with the workspace sandbox by default; write and elevated runs require confirmation and are recorded in history. Implements Linear #87.
/api/sendacceptsrunProfileandconfirmedRisk; workspaces acceptcodexApprovalandrunProfile.codex-clipasses-sand-a, includes the profile in the prompt, stores profile/sandbox/approval on messages, and rejects risky sends without confirmation. Tests cover the confirmation guard and workspace defaults.Written for commit 442c29f. Summary will update on new commits.