From 3cec76d337bde62afe88fbc28ceef40da21f600b Mon Sep 17 00:00:00 2001 From: Hesham Karm <24391550+hishamkaram@users.noreply.github.com> Date: Tue, 19 May 2026 19:58:20 +0200 Subject: [PATCH] feat(protocol): advertise runtime full access capability --- CHANGELOG.md | 1 + capabilities.go | 8 ++++++++ capabilities_test.go | 29 ++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7065ef..90b24b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Versioning convention (see [README.md](./README.md) for full policy): - `PushNotifyResultPayload` for accepted/retryable/permanent Web Push result acknowledgements - `CostPayload` shared wire type with additive cached-token, pricing-availability, and Codex credit estimate fields - `RateLimitsUpdatedPayload` additive Codex plan and actual credit snapshot fields +- `AgentCapability.supports_runtime_full_access` additive capability flag for Codex runtime Full access opt-in ## [v0.1.0] — 2026-04-29 diff --git a/capabilities.go b/capabilities.go index 2981a44..5588918 100644 --- a/capabilities.go +++ b/capabilities.go @@ -52,6 +52,13 @@ package protocol // decide whether to include "bypass" in the approval-mode cycle. See // specs/195-bypass-mode-optin/contracts/agent-capability-bypass.md for // the canonical contract. +// +// Feature 229 (codex-runtime-full-access) adds one field: +// - SupportsRuntimeFullAccess — whether the live session supports the +// Codex-only runtime transition to approval_policy=never plus +// sandbox=danger-full-access. This is true only when the daemon was +// configured with `agents.codex.allow_runtime_full_access_mode: true`. +// Claude remains false. type AgentCapability struct { // Deprecated: As of feature 186 (codex-remaining-gaps), consumers // should read AnswerQuestionFreeText instead. This field is retained @@ -71,4 +78,5 @@ type AgentCapability struct { SessionScopedApproval bool `json:"session_scoped_approval"` AnswerQuestionFreeText bool `json:"answer_question_free_text"` SupportsBypassPermissions bool `json:"supports_bypass_permissions"` + SupportsRuntimeFullAccess bool `json:"supports_runtime_full_access"` } diff --git a/capabilities_test.go b/capabilities_test.go index 7231444..caa9adf 100644 --- a/capabilities_test.go +++ b/capabilities_test.go @@ -9,12 +9,13 @@ import ( protocol "github.com/hishamkaram/agentd-protocol" ) -// TestAgentCapabilityFields asserts the struct has exactly the eight required +// TestAgentCapabilityFields asserts the struct has exactly the required // bool fields per specs/185-codex-parity-gaps/contracts/agent-capability.md // (original 4 fields) and specs/186-codex-remaining-gaps/contracts/ // session-scoped-approval-capability.md + answer-question-free-text-capability.md // (2 new fields added by feature 186), plus feature 188's two MCP parity -// flags. +// flags, feature 195's bypass flag, and feature 229's Codex runtime full-access +// flag. func TestAgentCapabilityFields(t *testing.T) { t.Parallel() @@ -33,6 +34,7 @@ func TestAgentCapabilityFields(t *testing.T) { "SessionScopedApproval": "session_scoped_approval", "AnswerQuestionFreeText": "answer_question_free_text", "SupportsBypassPermissions": "supports_bypass_permissions", + "SupportsRuntimeFullAccess": "supports_runtime_full_access", } if got := typ.NumField(); got != len(want) { @@ -163,7 +165,7 @@ func TestAgentCapabilityJSONKeys(t *testing.T) { } // Assert no CamelCase / PascalCase leakage. - forbidden := []string{"AnswerQuestion", "SendToolResult", "RewindFiles", "MCPHotApply", "MCPReconnect", "MCPLiveStatusLimited", "SessionScopedApproval", "AnswerQuestionFreeText", "SupportsBypassPermissions"} + forbidden := []string{"AnswerQuestion", "SendToolResult", "RewindFiles", "MCPHotApply", "MCPReconnect", "MCPLiveStatusLimited", "SessionScopedApproval", "AnswerQuestionFreeText", "SupportsBypassPermissions", "SupportsRuntimeFullAccess"} for _, k := range forbidden { if strings.Contains(payload, k) { t.Errorf("unexpected Go field name %q in JSON payload: %s", k, payload) @@ -264,6 +266,27 @@ func TestAgentCapabilityMCPLiveStatusLimitedField(t *testing.T) { } } +func TestAgentCapabilitySupportsRuntimeFullAccessField(t *testing.T) { + t.Parallel() + + typ := reflect.TypeOf(protocol.AgentCapability{}) + field, ok := typ.FieldByName("SupportsRuntimeFullAccess") + if !ok { + t.Fatalf("AgentCapability missing field SupportsRuntimeFullAccess") + } + if field.Type.Kind() != reflect.Bool { + t.Fatalf("SupportsRuntimeFullAccess: want bool, got %s", field.Type.Kind()) + } + if got := field.Tag.Get("json"); got != "supports_runtime_full_access" { + t.Fatalf("SupportsRuntimeFullAccess: json tag want %q, got %q", "supports_runtime_full_access", got) + } + + var zero protocol.AgentCapability + if zero.SupportsRuntimeFullAccess != false { + t.Fatalf("zero-value SupportsRuntimeFullAccess: want false, got %v", zero.SupportsRuntimeFullAccess) + } +} + // TestAgentCapability186RoundTrip verifies the new fields marshal and // unmarshal with the other 4 correctly across the per-agent shapes called out // in data-model.md (Claude: AnswerQuestionFreeText=true, SSA=false; Codex: