diff --git a/README.md b/README.md index a9bf03d..5dcf21b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ A tiny Go module containing the shared wire protocol types used by both the [Age | `control.go` | `ControlMessage`, `ControlType` (12 constants), 11 payload structs | Relay control protocol | | `policy.go` | `PolicyJSON`, `PolicyMatchJSON` | Policy rule wire format | | `capabilities.go` | `AgentCapability` | Per-agent feature flags the daemon emits to the PWA (MCP reconnect, session-scoped approvals, free-text replies, etc.) | +| `codex_sandbox.go` | `CodexSandboxMode` | Shared Codex per-session sandbox literals for daemon and PWA runtime controls | | `replay.go` | `ReplayRequest`, `ReplayCompletePayload` | Durable session journal replay recovery using inner per-session `AgentMessage.seq` values | | `trace.go` | `NewTraceID()`, `ValidTraceID()` | W3C-compatible trace ID generation | diff --git a/codex_sandbox.go b/codex_sandbox.go new file mode 100644 index 0000000..3b4bf4e --- /dev/null +++ b/codex_sandbox.go @@ -0,0 +1,32 @@ +package protocol + +// CodexSandboxMode is the cross-repo wire enum for Codex per-session sandbox +// startup controls. Values intentionally match the Codex SDK thread/start +// sandbox literals. +type CodexSandboxMode string + +const ( + CodexSandboxReadOnly CodexSandboxMode = "read-only" + CodexSandboxWorkspaceWrite CodexSandboxMode = "workspace-write" + CodexSandboxDangerFullAccess CodexSandboxMode = "danger-full-access" +) + +// KnownCodexSandboxModes returns the daemon/PWA-supported sandbox modes in UI +// order from safest to most permissive. +func KnownCodexSandboxModes() []CodexSandboxMode { + return []CodexSandboxMode{ + CodexSandboxReadOnly, + CodexSandboxWorkspaceWrite, + CodexSandboxDangerFullAccess, + } +} + +// IsKnownCodexSandboxMode reports whether mode is a valid wire literal. +func IsKnownCodexSandboxMode(mode CodexSandboxMode) bool { + switch mode { + case CodexSandboxReadOnly, CodexSandboxWorkspaceWrite, CodexSandboxDangerFullAccess: + return true + default: + return false + } +} diff --git a/codex_sandbox_test.go b/codex_sandbox_test.go new file mode 100644 index 0000000..d310cb2 --- /dev/null +++ b/codex_sandbox_test.go @@ -0,0 +1,67 @@ +package protocol + +import "testing" + +func TestCodexSandboxModeConstants(t *testing.T) { + t.Parallel() + + tests := map[string]CodexSandboxMode{ + "read-only": CodexSandboxReadOnly, + "workspace-write": CodexSandboxWorkspaceWrite, + "danger-full-access": CodexSandboxDangerFullAccess, + } + for want, got := range tests { + if string(got) != want { + t.Errorf("CodexSandboxMode constant = %q, want %q", got, want) + } + } +} + +func TestKnownCodexSandboxModes(t *testing.T) { + t.Parallel() + + got := KnownCodexSandboxModes() + want := []CodexSandboxMode{ + CodexSandboxReadOnly, + CodexSandboxWorkspaceWrite, + CodexSandboxDangerFullAccess, + } + if len(got) != len(want) { + t.Fatalf("KnownCodexSandboxModes len = %d, want %d", len(got), len(want)) + } + for i := range want { + if got[i] != want[i] { + t.Errorf("KnownCodexSandboxModes()[%d] = %q, want %q", i, got[i], want[i]) + } + } + + got[0] = CodexSandboxDangerFullAccess + if KnownCodexSandboxModes()[0] != CodexSandboxReadOnly { + t.Error("KnownCodexSandboxModes must return a fresh slice") + } +} + +func TestIsKnownCodexSandboxMode(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + mode CodexSandboxMode + want bool + }{ + {name: "read-only", mode: CodexSandboxReadOnly, want: true}, + {name: "workspace-write", mode: CodexSandboxWorkspaceWrite, want: true}, + {name: "danger-full-access", mode: CodexSandboxDangerFullAccess, want: true}, + {name: "empty", mode: "", want: false}, + {name: "unknown", mode: "sandbox-but-not-really", want: false}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := IsKnownCodexSandboxMode(tt.mode); got != tt.want { + t.Errorf("IsKnownCodexSandboxMode(%q) = %v, want %v", tt.mode, got, tt.want) + } + }) + } +}