Skip to content

[Bug]: Recognize reordered and escaped freeform wrapper keys in direct SSE previews #5151

Description

@lidge-jun

Client or integration

Direct HTTP/API client

Area

Streaming

Summary

Direct Responses SSE can publish raw JSON for a freeform wrapper with a reordered or escaped input property, then complete with its unwrapped value. For example, {"metadata":1,"input":"cmd"} and {"\u0069nput":"cmd"} can preview JSON and complete as cmd.

This is present before #5129, which extracts the existing decoder and repairs routed preview behavior. The routed restoration path suppresses these unchanged object prefixes; this issue covers the remaining direct bridge path. Related: #5047, #5113, and the review discussion.

Reproduction

Static source trace at 4e7d7132d8f8a7d46e460b48c5104a6d5ced4567; no local runtime reproduction was executed.

  1. Declare a custom freeform exec tool on the direct Responses bridge.
  2. Feed a fake upstream function call whose complete arguments are either example above, across every split boundary and characterwise chunks.
  3. Capture the custom-tool input deltas and final item input.
  4. The literal-first-property matcher returns unrecognized object text unchanged, while completion parses the object and extracts input. Assert that preview never publishes wrapper syntax that completion removes.

Version

4e7d713; still present at PR #5129 head b1b604e.

Operating system

Source review on macOS; the decoder is platform-independent. Execution proof must come from hosted CI.

Provider and model

Not provider-specific. Use a fake upstream without credentials.

Logs or error output

No live traffic or runtime logs collected. This report is based on the source trace and existing contract tests.

Screenshots and supporting files

Redacted configuration

A custom freeform exec declaration, a fake upstream event fixture and an isolated translator budget. No live provider, user state directory or credentials are required.

Implementation path

  1. Replace literal-only detection at the shared progressive decoder with bounded JSON object scanning. Track strings, escapes, nested values and top-level property boundaries. Decode property names according to JSON rules and recognize canonical input after preceding properties.
  2. Hold uncertainty until classification is possible. Preserve completion's own-input precedence and exactly-one-string-fallback-candidate rule. If no wrapper applies, release the original bytes as deltas rather than permanently suppressing ambiguous raw objects.
  3. Preserve fence/patch holds, tool-name ownership, caller-owned accounting and terminal/disposal release. Update the transport owner documentation with the precise buffering policy.
  4. Add sibling focused regressions and register new files in both test-layout inventories. Do not grow a capped bridge test file or raise the ratchet.

Acceptance criteria and verification

  • Reordered and escaped canonical keys are covered across every split and characterwise chunks; preview and authoritative completion agree without exposing wrapper syntax.
  • {"code":"a","script":"b"} and {"code":1} retain original raw delta and final bytes once classified as non-wrappers.
  • Canonical ordinary input and plain raw bodies remain progressive where their classification is safe.
  • Cover nested preceding values, escaped fallback keys, non-string input, tool-name negatives, cancellation and buffer release.
  • Retain explicit duplicate-key and late-invalid-input limitations rather than claiming a universal invariant unsupported by the buffering policy.
  • Hosted regression execution and exact-head applicable CI are required; this issue claims no passing runtime result.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstreamingSSE, WebSocket, terminal stream frames

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions