Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"`
}
29 changes: 26 additions & 3 deletions capabilities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down