Skip to content

Add provider audit envelopes and user-input audit metadata#12

Merged
Savid merged 12 commits intomasterfrom
refactor/provider-audit-cutover
Apr 10, 2026
Merged

Add provider audit envelopes and user-input audit metadata#12
Savid merged 12 commits intomasterfrom
refactor/provider-audit-cutover

Conversation

@Savid
Copy link
Copy Markdown
Member

@Savid Savid commented Mar 29, 2026

Summary

Major feature additions, protocol fixes, and schema enforcement across the SDK.

Provider Audit Envelopes

  • Add AuditEnvelope type (event_type, subtype, payload) attached to all message types (UserMessage, AssistantMessage, SystemMessage, ResultMessage, StreamEvent, TaskStartedMessage, TaskCompleteMessage, ThreadRolledBackMessage)
  • Preserve raw JSON bytes in audit envelopes for wire-fidelity via AnnotateRawJSON / extractRawJSON helpers (internal/message/raw_json.go)
  • Add public NewAuditEnvelope(eventType, subtype, payload) constructor for cross-SDK consistency
  • Propagate audit metadata through requestUserInput parsing and response serialization
  • Propagate audit metadata through MCP elicitation request parsing

MCP Elicitation Callback

  • Add WithOnElicitation option and typed elicitation.Request / elicitation.Response types (internal/elicitation/elicitation.go)
  • Support form and url elicitation modes with optional fields (ElicitationID, URL, RequestedSchema, TurnID)
  • Register mcpServer_elicitation/request handler in session; auto-declines when no callback is set
  • Full test coverage: callback invocation, URL mode, nil response handling, audit attachment

Permissions Approval Wire Format Fix

  • Fix HandlePermissionsApproval response format from {"decision": "accept"} to {"permissions": {...}, "scope": "turn"} matching current Codex app-server protocol
  • Register item_permissions/requestApproval handler (previously unregistered)
  • Deny returns empty permissions map instead of "decline" string

Reasoning / Thinking Delta Support

  • Distinguish reasoning deltas as thinking_delta (not text_delta) in StreamEvent during partial message streaming
  • Accumulate reasoning text into ThinkingBlock content blocks on completed AssistantMessage
  • Rewrite reasoning summary tracking in appserver_adapter.go to map Codex reasoning items into SDK thinking types
  • Update extended_thinking and include_partial_messages examples to handle thinking_delta events
  • Add integration test TestPartialMessages_ThinkingDeltaDistinguished

ResultMessage Enrichment

  • Add stop_reason, duration_ms, num_turns, and total_cost_usd fields to ResultMessage
  • Parse these fields from both Claude-style result and Codex-style response.completed events

WithMaxTurns Option

  • Add WithMaxTurns(n) option to limit conversation turns
  • Wire through exec backend (--max-turns CLI flag) and app-server backend (maxTurns init payload)
  • Add capability mapping (supported on both backends)

MultiSelect Support for User Input

  • Parse multiSelect / multi_select field on user input questions
  • Add MultiSelect field to userinput.Question type

Schema Enforcement (Structured Output)

  • Add internal/schema/enforce.go with EnforceStrictMode and EnforceAdditionalProperties
  • Recursively set additionalProperties: false and ensure required lists all property keys on every object node (OpenAI strict structured-output compliance)
  • Handle nested properties, $defs, anyOf, and array items
  • Apply enforcement in extractOutputSchema during session initialization

Fixes & Maintenance

  • Fix .gitignore: scope extended_thinking to root-only match (/extended_thinking)
  • Increase integration test timeout from 240s to 600s
  • Fix data race in budget_stderr_test.go stderr callback with mutex
  • Fix test_examples.sh to handle examples with their own go.mod (nested modules)
  • Remove stale negative proof test TestSessionRegisterHandlers_DoesNotRegisterRequestTypesMissingFromCurrentCodexSchema
  • Add mcpServer/elicitation/request and item/permissions/requestApproval to positive handler coverage test

Files Changed (30 files, +1867 / -120)

Area Files
Public API options.go, types.go, sessions.go
Message parsing internal/message/message.go, internal/message/parse.go, internal/message/raw_json.go, internal/message/parse_test.go
Elicitation internal/elicitation/elicitation.go
Protocol internal/protocol/session.go, internal/protocol/session_test.go, internal/protocol/session_integration_test.go
Schema internal/schema/enforce.go, internal/schema/enforce_test.go
Config internal/config/options.go, internal/config/capability.go
CLI internal/cli/command.go, internal/cli/cli_test.go
User Input internal/userinput/userinput.go
Subprocess internal/subprocess/appserver.go, internal/subprocess/appserver_adapter.go, internal/subprocess/appserver_adapter_test.go, internal/subprocess/cli.go, internal/subprocess/jsonrpc.go
Examples examples/extended_thinking/main.go, examples/include_partial_messages/main.go
Integration integration/budget_stderr_test.go, integration/streaming_test.go
Build Makefile, .gitignore, scripts/test_examples.sh

Test plan

  • go test ./... passes
  • go test -race ./... passes
  • Audit envelope tests verify attachment on all message types
  • Raw wire data preservation test confirms unknown fields survive in payload
  • Public NewAuditEnvelope constructor round-trip and marshal error tests
  • ResultMessage direct fields parsed from both result and response.completed events
  • WithMaxTurns CLI flag and init payload tests
  • MultiSelect question parsing test
  • MCP elicitation callback, URL mode, nil response, and audit tests
  • Permissions approval wire format tests (no-callback, with-callback, allowed-write-policy)
  • Schema EnforceStrictMode / EnforceAdditionalProperties unit tests (nested, $defs, anyOf, items)
  • Thinking delta appserver adapter unit tests
  • Integration test for thinking_delta stream events
  • Stderr callback race fix with mutex

🤖 Generated with Claude Code

Savid and others added 12 commits March 29, 2026 13:06
Expose provider-native audit envelopes on surfaced message types and propagate audit metadata through requestUserInput parsing and response serialization.

This keeps provider event capture SDK-owned while preserving the existing normalized public surface for downstream consumers.
Expose NewAuditEnvelope(eventType, subtype, payload) as a public API
matching the OpenRouter SDK signature, enabling consumers to construct
audit envelopes from typed payloads. The existing private parse-time
constructor remains for raw wire data attachment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add WithMaxTurns option with exec CLI flag and app-server protocol
support across both backends. Add MultiSelect field to user input
Question struct with camelCase/snake_case parsing.
The audit envelope payload was previously produced by re-marshaling the
decoded map[string]any, which loses byte-level details like number
formatting (1e+06 vs 1000000), key ordering, and whitespace from the
original wire data. This is problematic for audit/provenance use cases
where exact reproduction of the provider response matters.

Changes:
- Add internal/message/raw_json.go with AnnotateRawJSON, extractRawJSON,
  and stripRawJSON helpers that thread original bytes through decoded maps
  via a hidden sentinel key.
- Widen Parse() signature from map[string]any to any, accepting []byte
  and json.RawMessage directly so callers can pass raw wire data.
- Thread raw line bytes through the JSON-RPC notification path
  (RPCNotification.Raw field, toNotification parameter) and annotate
  events in both AppServerAdapter and CLITransport before dispatch.
- Update sessions.go GetSessionMessages to pass raw line bytes to Parse.
- newAuditEnvelope now prefers the annotated original bytes, falling back
  to re-marshal only when raw bytes are unavailable.
- Add test verifying audit payloads preserve original JSON bytes verbatim.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Message

Extend ResultMessage with session-level metadata fields returned by the
Codex turn-completed event: stop_reason (optional), duration_ms,
num_turns, and total_cost_usd (optional). Parse these fields from the
raw event map in parseCodexTurnCompleted and add tests covering full
field presence, the Parse entry point, and missing optional field
defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…udit-cutover

# Conflicts:
#	internal/subprocess/appserver_adapter.go
…easoning text

Reasoning stream deltas (item/reasoning/textDelta, item/reasoning/summaryTextDelta)
now emit as thinking_delta with a "thinking" key instead of text_delta with "text",
allowing consumers to distinguish reasoning content from regular text output in the
streaming path.

Added reasoningTextByItem accumulator that collects streaming reasoning deltas per
item ID. When item.completed arrives for a reasoning item with an empty summary
array, the accumulated reasoning text is used as fallback, ensuring reasoning
content is not lost.

Added ensureDefaultModel helper to mark gpt-5.3-codex as the default model when
the API model list does not flag any model with isDefault.

Updated examples to handle thinking_delta events. Added comprehensive unit tests
for delta accumulation, summary precedence, thinking delta emission, reasoning item
parsing, and default model selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bare `extended_thinking` pattern was matching examples/extended_thinking/,
preventing the example source from being tracked. Prefix with `/` to only
match the root-level binary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… wire format

Add full MCP elicitation support with typed request/response structures,
callback dispatch, and auto-decline behavior when no callback is set.
The previous stub unconditionally auto-accepted; now the flow properly
parses elicitation fields (mode, URL, schema, elicitation ID) and routes
through the OnElicitation callback.

Fix HandlePermissionsApproval to return {permissions, scope: "turn"}
instead of {decision: "accept"/"decline"}, matching the actual app-server
protocol. Denied requests now return empty permissions with turn scope
rather than a "decline" decision string.

Register mcpServer_elicitation/request and item_permissions/requestApproval
as live handlers — these were previously tracked as stale/future request
types but are now present in the current codex schema.

- Add internal/elicitation package with Mode, Action, Request, Response types
- Add WithOnElicitation option and capability entry (exec: unsupported, app-server: supported)
- Re-export elicitation types and constants from root package
- Add comprehensive tests for elicitation (form, URL, no-callback, nil-response)
- Update permissions approval tests to assert new wire format
- Remove stale handler negative test that is no longer applicable
- Bump integration test timeout from 240s to 600s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Savid Savid merged commit ae3a403 into master Apr 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants