{"record":{"id":"d0f2368467f70af9","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-d0f236","errorCode":null,"errorMessage":"当前 Codex 版本的对话读取接口不兼容","messagePattern":"当前 Codex 版本的对话读取接口不兼容","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tools/conversation-canvas/public/canvas.user.js","lineNumber":465,"sourceCode":"        onProgress(`正在读取历史 ${index+1}/${turns.length} · 已读 ${parsed.length} 条消息…`);\n        if(turn.status==='completed'&&itemCursor!==null)await cache.set(`${key}:partial`,{messages:parsed,cursor:itemCursor});\n      }while(itemCursor!==null);\n      if(turn.status==='completed'){await cache.set(key,parsed);await cache.delete(`${key}:partial`);}\n    }\n    // Older completed-turn caches contain text and IDs but no turn metadata.\n    // The current directory and cache key provide the authoritative owner.\n    for(const message of parsed)if(!seen.has(message.id)){seen.add(message.id);messages.push({...message,turnId:turn.id,turnStatus:turn.status});}\n  }\n  // Bound cross-task retention, without truncating the returned conversation.\n  while(cache.size>500)cache.delete(cache.keys().next().value);\n  return messages;\n}\n\n// Runtime bindings are versioned separately. History reading does not require\n// the private side-conversation submission API to be available.\nasync function nativeContext(threadId){\n  const native=await loadNativeRuntime();\n  if(typeof native.ESt!=='function'||typeof native.kr!=='function')throw Error('当前 Codex 版本的对话读取接口不兼容');\n  native.kr();\n  const scopes=[],callbacks=[],seeds=[],navigation=[];\n  const visited=new Set(),domVisited=new Set();\n  // ProseMirror owns the inner editable DOM. React's expando is on a wrapper,\n  // not necessarily on the contenteditable itself. Start from native surfaces\n  // too, so history reading remains available while the composer is hidden.\n  for(const element of document.querySelectorAll('[contenteditable],textarea,main,[data-testid=\"app-shell-header-context-menu-surface\"]')){\n    for(let dom=element;dom&&!domVisited.has(dom);dom=dom.parentElement){\n      domVisited.add(dom);\n      for(const key of Object.keys(dom)){\n        if(key.startsWith('__reactFiber$'))seeds.push(dom[key]);\n        if(key.startsWith('__reactContainer$')){\n          const root=dom[key];seeds.push(root?.stateNode?.current??root);\n        }\n      }\n    }\n  }\n  const inspect=fiber=>{","sourceCodeStart":447,"sourceCodeEnd":483,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/tools/conversation-canvas/public/canvas.user.js#L447-L483","documentation":"nativeContext() loads Codex's private runtime module and checks that the exported functions it binds (ESt, kr) are still functions before reading thread context from React fibers. If those minified exports are absent or not functions, the userscript cannot read history on this Codex build, so it throws.","triggerScenarios":"Calling nativeContext(threadId) on a Codex version where loadNativeRuntime() returns a module without ESt/kr function exports — i.e. after a Codex release that renamed/removed those internals.","commonSituations":"Codex auto-updated and internal minified symbol names changed; running an old canvas.user.js against a new Codex build (or vice versa); runtime module failed to load partially.","solutions":["Update canvas.user.js to the release matching your Codex version.","Check loadNativeRuntime() output in devtools to find the new export names and update the bindings.","Pin/rollback the Codex app version to one compatible with the current script.","Add a version probe that maps multiple candidate export names so the script survives minor renames."],"exampleFix":"// before\nif(typeof native.ESt!=='function'||typeof native.kr!=='function')throw Error('当前 Codex 版本的对话读取接口不兼容');\n// after\nconst kr=native.kr??native.newerKrName;\nif(typeof native.ESt!=='function'||typeof kr!=='function')throw Error('当前 Codex 版本的对话读取接口不兼容');","handlingStrategy":"try-catch","validationCode":"const native=await loadNativeRuntime();\nif(typeof native?.ESt!=='function'||typeof native?.kr!=='function')return null;","typeGuard":"const hasNativeReadApi=n=>n!=null&&typeof n.ESt==='function'&&typeof n.kr==='function';","tryCatchPattern":"try{return await nativeContext(threadId);}catch(e){if(e.message==='当前 Codex 版本的对话读取接口不兼容'){showUpdateNeededBanner();return null;}throw e;}","preventionTips":["Check the script/Codex compatibility matrix before updating Codex","Probe candidate export names instead of one hard-coded symbol","Disable history import when the runtime probe fails","Subscribe to Codex release notes for internal API changes"],"tags":["version-compatibility","internal-api","codex"],"backgroundTag":"incompatible-source-type","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}