Skip to content

fix(github-copilot): preserve reasoning IDs for Copilot Codex models#71684

Merged
steipete merged 5 commits into
openclaw:mainfrom
InvalidPandaa:fix/github-copilot-preserve-reasoning-ids
Apr 25, 2026
Merged

fix(github-copilot): preserve reasoning IDs for Copilot Codex models#71684
steipete merged 5 commits into
openclaw:mainfrom
InvalidPandaa:fix/github-copilot-preserve-reasoning-ids

Conversation

@InvalidPandaa

@InvalidPandaa InvalidPandaa commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: gpt-5.3-codex via GitHub Copilot fails with LLM request failed: provider rejected the request schema or tool payload on every request after the first reasoning turn. The raw provider error is 400 The encrypted content for item rs_2a72bff773173087 could not be verified. Reason: Encrypted content item_id did not match the target item id.
  • Why it matters: Any user running gpt-5.3-codex (or future Codex models) through GitHub Copilot cannot have multi-turn conversations — every second message hard-fails.
  • What changed: (1) connection-bound-ids.ts — skip all type: "reasoning" items during ID rewriting, not just those with a non-null encrypted_content string. (2) models.ts — add gpt-5.3-codex as a forward-compat clone target and update the catch-all regex to mark Codex-named model IDs as reasoning: true. (3) index.ts — add gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS.
  • What did NOT change: No change to message or function_call ID rewriting, no change to the Anthropic stream path, no change to non-Copilot providers.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: gpt-5.3-codex is not in the Copilot model registry and falls through to resolveCopilotForwardCompatModel's catch-all with reasoning: false. With reasoning: false, pi-ai never includes include: ["reasoning.encrypted_content"] in the request, so Copilot returns reasoning items with encrypted_content: null. The guard added in fix(github-copilot): preserve reasoning ids with encrypted_content #71448typeof item.encrypted_content === "string" — evaluates to false for null, so the connection-bound item ID is still rewritten to rs_<hash>. On the next turn, Copilot looks up its server-side reasoning state by item ID, finds the original connection-bound ID but receives rs_<hash>, and returns 400.
  • Missing detection / guardrail: The encrypted_content string check in fix(github-copilot): preserve reasoning ids with encrypted_content #71448 was too narrow. Copilot validates reasoning item IDs server-side regardless of whether the client holds the encrypted_content blob.
  • Contributing context: The second error in the logs (09:38 UTC) occurred after fix(github-copilot): preserve reasoning ids with encrypted_content #71448 landed (07:57 UTC), confirming that fix was insufficient for this model.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/github-copilot/connection-bound-ids.test.ts, extensions/github-copilot/models.test.ts
  • Scenario the test should lock in: reasoning items with encrypted_content: null and reasoning items with no encrypted_content field must not have their IDs rewritten; Codex-named model IDs must resolve with reasoning: true from the catch-all.
  • Why this is the smallest reliable guardrail: the rewriting logic and model resolution are pure functions — no network or runtime needed.
  • Existing test that already covers this (if any): fix(github-copilot): preserve reasoning ids with encrypted_content #71448 added "preserves reasoning IDs when encrypted_content is present" — updated here to a single test covering all three cases (string, null, absent).
  • If no new test is added, why not: N/A — tests are added/updated.

User-visible / Behavior Changes

Multi-turn conversations with gpt-5.3-codex (and any future *-codex model) via GitHub Copilot no longer fail after the first reasoning turn. No config or defaults change.

Diagram (if applicable)

Before (broken):
reasoning item {id: "<base64>", encrypted_content: null}
  → encrypted_content check: null is not string → guard skips
  → looksLikeConnectionBoundId → true
  → id rewritten to rs_<hash>
  → Copilot 400: item_id mismatch

After (fixed):
reasoning item {id: "<base64>", encrypted_content: null}
  → type === "reasoning" → guard fires unconditionally
  → id preserved as "<base64>"
  → Copilot validates OK

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Raspberry Pi OS (server), macOS 15 (test runner)
  • Runtime/container: Node 22
  • Model/provider: gpt-5.3-codex / github-copilot
  • Integration/channel: Telegram
  • Relevant config: agents.defaults.models: ["github-copilot/gpt-5.3-codex"]

Steps

  1. Configure OpenClaw with GitHub Copilot and model gpt-5.3-codex
  2. Send a message via Telegram
  3. Send a second message (triggers reasoning replay)

Expected

  • Second message receives a normal assistant reply

Actual

  • LLM request failed: provider rejected the request schema or tool payload.
  • Gateway log: 400 The encrypted content for item rs_2a72bff773173087 could not be verified.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets

Gateway logs showing the error (before fix):

07:42:22 warn agent/embedded "rawErrorPreview":"400 The encrypted content for item rs_2a72bff773173087 could not be verified. Reason: Encrypted content item_id did not match the target item id."
09:38:31 warn agent/embedded (same error, after #71448 was already merged)

Live test passing after fix:

[github-copilot-live] token ok via env (fetched)
[github-copilot-live] sending Responses request
[github-copilot-live] Responses request completed
Tests  1 passed (1)

Human Verification (required)

  • Verified scenarios: live test against real Copilot endpoint with gpt-5.3-codex; unit tests for all three encrypted_content states (string / null / absent) and for codex-pattern model IDs in the catch-all.
  • Edge cases checked: gpt-5.4 template cloning still works; gpt-5.3-codex prefers itself as template source over gpt-5.2-codex when both are in registry; non-reasoning models (gpt-5.4-mini, claude-sonnet-4.6) remain unaffected.
  • What you did not verify: end-to-end Telegram round-trip on the fixed server (requires deployment).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: skipping all reasoning ID rewriting (not just encrypted ones) might leave stale connection-bound IDs in requests for models that don't need server-side validation.
    • Mitigation: Copilot already rejects stale connection-bound IDs for message/function_call items — that's why rewriting was introduced. For reasoning items, the server lookup by original ID is the correct behavior even across connection changes; the rewrite was always wrong for this item type.

@InvalidPandaa InvalidPandaa changed the title fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex… fix(github-copilot): preserve reasoning IDs for all Copilot models, add gpt-5.3-codex support Apr 25, 2026
@greptile-apps

greptile-apps Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a multi-turn failure for gpt-5.3-codex via GitHub Copilot by broadening the reasoning-item ID preservation guard from "skip only when encrypted_content is a string" to "skip unconditionally for all type: \"reasoning\" items," and by adding gpt-5.3-codex to the forward-compat target list and the xhigh thinking-level allow-list, plus a catch-all codex-substring check to infer reasoning: true.

The core logic is sound and the tests cover all three encrypted_content states (string / null / absent) as well as the new catch-all regex path.

Confidence Score: 4/5

Safe to merge; the fix is narrow, well-tested, and backward compatible — only P2 style nits remain.

No logic errors or security concerns found. The two findings are both P2: one is dead code in deriveReplacementId (the "rs" prefix branch is now unreachable) and one is a documentation gap around the dual role of gpt-5.3-codex in the template list. Neither affects runtime behavior.

No files require special attention.

Comments Outside Diff (1)

  1. extensions/github-copilot/connection-bound-ids.ts, line 21 (link)

    P2 Dead "rs" prefix branch in deriveReplacementId

    Since all type === "reasoning" items are now unconditionally skipped before deriveReplacementId is ever called, the type === "reasoning" ? "rs" branch is unreachable. It won't cause a bug, but leaving it silently implies the function still handles reasoning items which could mislead a future reader.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/github-copilot/connection-bound-ids.ts
    Line: 21
    
    Comment:
    **Dead `"rs"` prefix branch in `deriveReplacementId`**
    
    Since all `type === "reasoning"` items are now unconditionally skipped before `deriveReplacementId` is ever called, the `type === "reasoning" ? "rs"` branch is unreachable. It won't cause a bug, but leaving it silently implies the function still handles reasoning items which could mislead a future reader.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/github-copilot/connection-bound-ids.ts
Line: 21

Comment:
**Dead `"rs"` prefix branch in `deriveReplacementId`**

Since all `type === "reasoning"` items are now unconditionally skipped before `deriveReplacementId` is ever called, the `type === "reasoning" ? "rs"` branch is unreachable. It won't cause a bug, but leaving it silently implies the function still handles reasoning items which could mislead a future reader.

```suggestion
  const prefix = type === "function_call" ? "fc" : "msg";
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/github-copilot/models.ts
Line: 9-10

Comment:
**`gpt-5.3-codex` as a template is a no-op when it is itself the requested model**

When `resolveCopilotForwardCompatModel` is called for `gpt-5.3-codex`, the early-return guard on lines 33-36 has already confirmed the model is *not* in the registry. So `ctx.modelRegistry.find(PROVIDER_ID, "gpt-5.3-codex")` on line 42 will always return `null` for that path, making `"gpt-5.3-codex"` the first entry in `CODEX_TEMPLATE_MODEL_IDS` a no-op for `gpt-5.3-codex` requests specifically.

The template *is* useful when `gpt-5.4` is the target and `gpt-5.3-codex` is in the registry, so this isn't wrong — but a short clarifying comment would prevent future confusion about why the list includes a model that is also a target.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(github-copilot): preserve all reason..." | Re-trigger Greptile

Comment thread extensions/github-copilot/models.ts Outdated
Comment on lines +9 to +10
const CODEX_FORWARD_COMPAT_TARGET_IDS: readonly string[] = ["gpt-5.4", "gpt-5.3-codex"];
const CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.3-codex", "gpt-5.2-codex"] as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 gpt-5.3-codex as a template is a no-op when it is itself the requested model

When resolveCopilotForwardCompatModel is called for gpt-5.3-codex, the early-return guard on lines 33-36 has already confirmed the model is not in the registry. So ctx.modelRegistry.find(PROVIDER_ID, "gpt-5.3-codex") on line 42 will always return null for that path, making "gpt-5.3-codex" the first entry in CODEX_TEMPLATE_MODEL_IDS a no-op for gpt-5.3-codex requests specifically.

The template is useful when gpt-5.4 is the target and gpt-5.3-codex is in the registry, so this isn't wrong — but a short clarifying comment would prevent future confusion about why the list includes a model that is also a target.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/github-copilot/models.ts
Line: 9-10

Comment:
**`gpt-5.3-codex` as a template is a no-op when it is itself the requested model**

When `resolveCopilotForwardCompatModel` is called for `gpt-5.3-codex`, the early-return guard on lines 33-36 has already confirmed the model is *not* in the registry. So `ctx.modelRegistry.find(PROVIDER_ID, "gpt-5.3-codex")` on line 42 will always return `null` for that path, making `"gpt-5.3-codex"` the first entry in `CODEX_TEMPLATE_MODEL_IDS` a no-op for `gpt-5.3-codex` requests specifically.

The template *is* useful when `gpt-5.4` is the target and `gpt-5.3-codex` is in the registry, so this isn't wrong — but a short clarifying comment would prevent future confusion about why the list includes a model that is also a target.

How can I resolve this? If you propose a fix, please make it concise.

@InvalidPandaa

Copy link
Copy Markdown
Contributor Author

(I'm on phone rn that's why I did this small change with Claude)

@InvalidPandaa

Copy link
Copy Markdown
Contributor Author

Greptile Summary

This PR fixes a multi-turn failure for gpt-5.3-codex via GitHub Copilot by broadening the reasoning-item ID preservation guard from "skip only when encrypted_content is a string" to "skip unconditionally for all type: \"reasoning\" items," and by adding gpt-5.3-codex to the forward-compat target list and the xhigh thinking-level allow-list, plus a catch-all codex-substring check to infer reasoning: true.

The core logic is sound and the tests cover all three encrypted_content states (string / null / absent) as well as the new catch-all regex path.

Confidence Score: 4/5

Safe to merge; the fix is narrow, well-tested, and backward compatible — only P2 style nits remain.

No logic errors or security concerns found. The two findings are both P2: one is dead code in deriveReplacementId (the "rs" prefix branch is now unreachable) and one is a documentation gap around the dual role of gpt-5.3-codex in the template list. Neither affects runtime behavior.

No files require special attention.

Comments Outside Diff (1)

  1. extensions/github-copilot/connection-bound-ids.ts, line 21 (link)

    P2 Dead "rs" prefix branch in deriveReplacementId

    Since all type === "reasoning" items are now unconditionally skipped before deriveReplacementId is ever called, the type === "reasoning" ? "rs" branch is unreachable. It won't cause a bug, but leaving it silently implies the function still handles reasoning items which could mislead a future reader.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/github-copilot/connection-bound-ids.ts
    Line: 21
    
    Comment:
    **Dead `"rs"` prefix branch in `deriveReplacementId`**
    
    Since all `type === "reasoning"` items are now unconditionally skipped before `deriveReplacementId` is ever called, the `type === "reasoning" ? "rs"` branch is unreachable. It won't cause a bug, but leaving it silently implies the function still handles reasoning items which could mislead a future reader.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/github-copilot/connection-bound-ids.ts
Line: 21

Comment:
**Dead `"rs"` prefix branch in `deriveReplacementId`**

Since all `type === "reasoning"` items are now unconditionally skipped before `deriveReplacementId` is ever called, the `type === "reasoning" ? "rs"` branch is unreachable. It won't cause a bug, but leaving it silently implies the function still handles reasoning items which could mislead a future reader.

```suggestion
  const prefix = type === "function_call" ? "fc" : "msg";
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/github-copilot/models.ts
Line: 9-10

Comment:
**`gpt-5.3-codex` as a template is a no-op when it is itself the requested model**

When `resolveCopilotForwardCompatModel` is called for `gpt-5.3-codex`, the early-return guard on lines 33-36 has already confirmed the model is *not* in the registry. So `ctx.modelRegistry.find(PROVIDER_ID, "gpt-5.3-codex")` on line 42 will always return `null` for that path, making `"gpt-5.3-codex"` the first entry in `CODEX_TEMPLATE_MODEL_IDS` a no-op for `gpt-5.3-codex` requests specifically.

The template *is* useful when `gpt-5.4` is the target and `gpt-5.3-codex` is in the registry, so this isn't wrong — but a short clarifying comment would prevent future confusion about why the list includes a model that is also a target.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(github-copilot): preserve all reason..." | Re-trigger Greptile

done

@steipete steipete force-pushed the fix/github-copilot-preserve-reasoning-ids branch from dd9fbfa to 6d5eb01 Compare April 25, 2026 19:12
@steipete steipete changed the title fix(github-copilot): preserve reasoning IDs for all Copilot models, add gpt-5.3-codex support fix(github-copilot): preserve reasoning IDs for Copilot Codex models Apr 25, 2026
@steipete

Copy link
Copy Markdown
Contributor

Codex maintainer update: I rebased this PR onto current main and pushed a small fixup commit.

What changed:

Verification:

  • pnpm test extensions/github-copilot -> 9 files / 56 tests passed
  • pnpm lint:extensions -> passed
  • pnpm tsgo:extensions -> passed
  • pnpm tsgo:extensions:test -> passed
  • live .profile/auth-profile Copilot probe against gpt-5.3-codex: first turn produced a real reasoning item; replay with encrypted_content: null preserved the original reasoning ID and Copilot accepted the second turn (LIVE_TWO_OK)

@steipete

Copy link
Copy Markdown
Contributor

Codex follow-up: CI exposed an unrelated stale mock in src/plugins/bundled-runtime-deps.test.ts after the rebase. I pushed a tiny test-only fix so the mocked node:child_process module still exports execFile/spawn for code imported through src/process/exec.ts.

Additional verification:

  • pnpm test src/plugins/bundled-runtime-deps.test.ts -> 43 tests passed
  • node scripts/run-oxlint.mjs --tsconfig tsconfig.oxlint.core.json src/plugins/bundled-runtime-deps.test.ts -> passed
  • pnpm tsgo:test:src -> passed

@steipete

Copy link
Copy Markdown
Contributor

Codex follow-up 2: CI also exposed a brittle plugin-sdk fast-path fixture. The test wrote a CommonJS body to a .js sidecar, which can fall through to Jiti under package-type context in CI. I changed the synthetic fixture to .cjs, keeping the nodeRequire fast-path assertion explicit and stable.

Additional verification:

  • pnpm test src/plugin-sdk/channel-entry-contract.test.ts -> 8 tests passed
  • pnpm test src/plugins/bundled-runtime-deps.test.ts -> 43 tests passed
  • targeted oxlint for both touched test files -> passed
  • pnpm tsgo:test:src -> passed

InvalidPandaa and others added 5 commits April 25, 2026 20:36
… support

The existing guard (8fd15ed) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.
@steipete steipete force-pushed the fix/github-copilot-preserve-reasoning-ids branch from 2793c8c to 035532d Compare April 25, 2026 19:42
@steipete steipete merged commit b64bfc5 into openclaw:main Apr 25, 2026
64 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed. Thanks @InvalidPandaa!

  • Source SHA: 035532d
  • Merge commit: b64bfc5
  • Gate: PR CI green on the rebased source SHA; pnpm check:changed completed conflict markers, type lanes, lint, import-cycle, and policy guards locally; direct reruns passed pnpm test extensions/github-copilot, pnpm test src/plugin-sdk/channel-entry-contract.test.ts, pnpm tsgo:extensions, pnpm tsgo:extensions:test, pnpm tsgo:test:src, pnpm lint:extensions, and targeted core oxlint. Live gpt-5.3-codex Copilot reasoning replay probe passed earlier in review.

ayesha-aziz123 pushed a commit to ayesha-aziz123/openclaw that referenced this pull request Apr 26, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (8fd15ed) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (2654ced) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (82fbd10) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (26a7738) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (0048671) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (8b1a21f) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
…penclaw#71684)

* fix(github-copilot): preserve all reasoning IDs and add gpt-5.3-codex support

The existing guard (32a83e9) only skipped rewriting reasoning item IDs
when encrypted_content was a non-null string. When gpt-5.3-codex is used
via GitHub Copilot, the model falls through to the forward-compat catch-all
with reasoning:false, so encrypted_content is never requested and arrives
as null — bypassing the guard and causing a rewrite. Copilot validates
reasoning item IDs server-side regardless of whether the client includes
encrypted_content, so the rewritten id triggers the 400 error.

Two changes:

1. connection-bound-ids.ts: skip ALL reasoning items unconditionally.
   Reasoning items always reference server-side state bound to their
   original ID; rewriting any of them breaks Copilot's lookup.

2. models.ts + index.ts: extend the forward-compat cloning logic to
   cover gpt-5.3-codex (adds it to the template-target set and to
   CODEX_TEMPLATE_MODEL_IDS so it can also serve as a template source
   for gpt-5.4). Adds gpt-5.3-codex to COPILOT_XHIGH_MODEL_IDS for
   the thinking profile.

Thanks @InvalidPandaa.

* docs(github-copilot): clarify gpt-5.3-codex is a no-op template for itself

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): remove dead reasoning prefix branch in deriveReplacementId

https://claude.ai/code/session_01EAFmq4WyKkiUkVAqRXp4Bm

* fix(github-copilot): align reasoning id replay tests

* test(plugin-sdk): use cjs sidecar for require fast path

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants