codex-overleaf-link 1.9.13 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +24 -23
  2. package/extension/bootstrap/manifest.template.json +2 -1
  3. package/extension/runtime-manifest.json +13 -6
  4. package/extension/src/background.js +7 -0
  5. package/extension/src/content/composerPanel.js +13 -3
  6. package/extension/src/content/contentRuntime.js +130 -65
  7. package/extension/src/content/contextTray.js +173 -9
  8. package/extension/src/content/markdownText.js +16 -1
  9. package/extension/src/content/mathText.js +233 -0
  10. package/extension/src/content/modelPicker.js +117 -106
  11. package/extension/src/content/modelPickerSupport.js +115 -0
  12. package/extension/src/content/nativeChannel.js +20 -1
  13. package/extension/src/content/panelMaintenance.js +2 -0
  14. package/extension/src/content/projectSettingsCoordinator.js +2 -0
  15. package/extension/src/content/providerSettingsCoordinator.js +413 -0
  16. package/extension/src/content/providerSettingsDialog.js +981 -0
  17. package/extension/src/content/runTimelineView.js +24 -5
  18. package/extension/src/content/settingsPanel.js +33 -2
  19. package/extension/src/content/writebackOrchestrator.js +3 -1
  20. package/extension/src/page/overleafProjectSnapshot.js +29 -33
  21. package/extension/src/page/snapshotRouter.js +75 -8
  22. package/extension/src/page/trackedChangesLifecycle.js +154 -26
  23. package/extension/src/page/writebackRouter.js +31 -60
  24. package/extension/src/pageBridge.js +1 -1
  25. package/extension/src/shared/agentTranscript.js +130 -0
  26. package/extension/src/shared/compatibility.js +10 -7
  27. package/extension/src/shared/failureReasons.js +75 -0
  28. package/extension/src/shared/i18n.js +22 -0
  29. package/extension/src/shared/models.js +1 -0
  30. package/extension/src/shared/providerProfiles.js +283 -0
  31. package/extension/src/shared/sessionState.js +12 -2
  32. package/extension/src/shared/storageDb.js +27 -19
  33. package/extension/src/shared/storageRunActions.js +99 -0
  34. package/extension/styles/panel.css +587 -1
  35. package/extension/vendor/katex/LICENSE +21 -0
  36. package/extension/vendor/katex/VERSION +2 -0
  37. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
  38. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  39. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  40. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  41. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  42. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  43. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  44. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  45. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  46. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  47. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  48. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  49. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  50. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  51. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  52. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
  53. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  54. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  55. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  56. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  57. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  58. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
  59. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  60. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  61. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
  62. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  63. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  64. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  65. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  66. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  67. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
  68. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  69. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  70. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  71. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  72. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  73. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  74. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  75. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  76. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  77. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  78. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  79. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
  80. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff +0 -0
  81. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  82. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
  83. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  84. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  85. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
  86. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  87. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  88. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
  89. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  90. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  91. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
  92. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  93. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  94. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  95. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  96. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  97. package/extension/vendor/katex/katex.min.css +1 -0
  98. package/extension/vendor/katex/katex.min.js +1 -0
  99. package/native-host/src/anthropicBridgeRequest.js +435 -0
  100. package/native-host/src/anthropicBridgeResponse.js +431 -0
  101. package/native-host/src/anthropicMessagesBridge.js +281 -0
  102. package/native-host/src/chatBridgeRequest.js +274 -0
  103. package/native-host/src/chatBridgeResponse.js +594 -0
  104. package/native-host/src/chatCompletionsBridge.js +485 -0
  105. package/native-host/src/codexCommand.js +25 -0
  106. package/native-host/src/codexPromptAssembly.js +9 -0
  107. package/native-host/src/codexProviderLaunch.js +213 -0
  108. package/native-host/src/codexProviderTest.js +196 -0
  109. package/native-host/src/codexSessionRunner.js +29 -25
  110. package/native-host/src/managedInstall.js +1 -0
  111. package/native-host/src/providerBridgeRoutes.js +11 -0
  112. package/native-host/src/providerModelCatalog.js +107 -0
  113. package/native-host/src/providerProfile.js +454 -0
  114. package/native-host/src/providerReasoning.js +235 -0
  115. package/native-host/src/providerRedaction.js +18 -0
  116. package/native-host/src/providerRuntime.js +365 -0
  117. package/native-host/src/providerStore.js +691 -0
  118. package/native-host/src/readProgressGuard.js +360 -0
  119. package/native-host/src/taskRunnerRuntime.js +40 -4
  120. package/native-host/src/updateArchive.js +1 -0
  121. package/native-host/src/updateTrust.js +2 -1
  122. package/package.json +2 -1
@@ -37,6 +37,13 @@
37
37
  'mirror.status',
38
38
  'codex.models',
39
39
  'codex.run',
40
+ 'codex.providers.list',
41
+ 'codex.providers.test',
42
+ 'codex.providers.test.cancel',
43
+ 'codex.providers.upsert',
44
+ 'codex.providers.activate',
45
+ 'codex.providers.clear-secret',
46
+ 'codex.providers.delete',
40
47
  'task.run',
41
48
  'task.confirm',
42
49
  'mirror.sync',
@@ -49,6 +56,12 @@
49
56
  'skills.remove'
50
57
  ]);
51
58
  const CANCELLABLE_PAGE_BRIDGE_METHODS = new Set([
59
+ 'getProjectSnapshot',
60
+ 'getProjectFileList',
61
+ 'getCompileLog',
62
+ 'ensureReviewing',
63
+ 'ensureEditing',
64
+ 'waitForSaveState',
52
65
  'applyOperations',
53
66
  'acceptTrackedChanges',
54
67
  'rejectTrackedChanges'
@@ -343,6 +356,7 @@
343
356
  saveState: (...args) => saveState(...args),
344
357
  saveStateSoon: () => saveStateSoon(),
345
358
  autosizeTaskTextarea: () => autosizeTaskTextarea(),
359
+ syncComposerSendAvailability: () => syncComposerSendAvailability(),
346
360
  applyStateToPanel: () => applyStateToPanel(),
347
361
  normalizePanelState,
348
362
  openCustomInstructionsSettings: () => openCustomInstructionsSettings(),
@@ -575,6 +589,7 @@
575
589
  applyFallbackModelOptions,
576
590
  getModelCatalog,
577
591
  renderModelOptions,
592
+ renderReasoningOptions,
578
593
  renderSpeedOptions,
579
594
  renderModelConfigChoices,
580
595
  resolveSelectedModel,
@@ -582,6 +597,16 @@
582
597
  updateModelDisplay
583
598
  } = modelPicker;
584
599
 
600
+ const providerSettingsCoordinator = window.CodexOverleafProviderSettingsCoordinator.create({
601
+ tx,
602
+ sendBackgroundNative,
603
+ document,
604
+ window,
605
+ getSettingsPanelInstance: () => settingsPanelInstance,
606
+ getSelectedModel: () => state?.model || '',
607
+ clearSelectedModel: () => { if (state) state.model = ''; },
608
+ refreshModelOptions: loadModelOptions, persistInputs: persistPanelInputs
609
+ });
585
610
  // v1.8.1 D3: the dashboard was a one-shot render — another tab finishing a
586
611
  // run, clearing history or creating sessions never showed up. Refresh it
587
612
  // (debounced) when the tab regains visibility or a cross-tab run signal
@@ -761,6 +786,7 @@
761
786
  let slashCodexOverleafSkillsLoading = false;
762
787
  let renderedSlashCommands = new Map();
763
788
  let runCancellationRequested = false;
789
+ let runCancellationController = null;
764
790
  let activePluginConfirmResolve = null;
765
791
  const updateIdleClient = root.CodexOverleafUpdateIdle?.create({
766
792
  document,
@@ -813,6 +839,9 @@
813
839
  sendResponse?.(getPanelStateResponse());
814
840
  return;
815
841
  }
842
+ if (nativeChannel.handleBackgroundNativeEvent(message)) {
843
+ return;
844
+ }
816
845
  if (nativeChannel.shouldHandleNativeEvent(message)) {
817
846
  appendNativeEvent(message.event);
818
847
  }
@@ -1109,6 +1138,7 @@
1109
1138
  onInputChange: () => persistPanelInputs(),
1110
1139
  onSkillsOpen: () => openSkillsView(),
1111
1140
  onSkillsBack: () => closeSkillsView(),
1141
+ onProvidersOpen: () => providerSettingsCoordinator.open(),
1112
1142
  onClearAllHistory: () => clearAllHistoryWithConfirm(),
1113
1143
  onHistoryOpen: () => renderAuditHistoryPanel(),
1114
1144
  onHistoryFilter: () => applyAuditHistoryFilter(),
@@ -1117,6 +1147,7 @@
1117
1147
  onOtToggleClick: handleExperimentalOtToggleClick
1118
1148
  }
1119
1149
  });
1150
+ providerSettingsCoordinator.refreshSummary().catch(() => {});
1120
1151
 
1121
1152
  sessionPanelInstance = SessionPanel.create({
1122
1153
  container: panelRendererInstance.sessionSlot,
@@ -1643,6 +1674,7 @@
1643
1674
  }
1644
1675
 
1645
1676
  runCancellationRequested = false;
1677
+ runCancellationController = new AbortController();
1646
1678
  setRunning(true);
1647
1679
  currentRunView = startRunView({
1648
1680
  task,
@@ -1654,36 +1686,37 @@
1654
1686
  skillInvocation: submittedSkillInvocation
1655
1687
  });
1656
1688
  const runSessionId = currentRunView.sessionId;
1657
- let runAuditDraft = await createAuditDraftForRun({
1658
- task,
1659
- sessionId: runSessionId,
1660
- mode: submittedMode,
1661
- focusFiles: getActiveFocusFiles()
1662
- });
1663
- announceCrossTabRunStart();
1664
- // Keep attachments across turns (v1.7.5): screenshots and files usually
1665
- // anchor several follow-up questions. The tray keeps them visible and
1666
- // hand-removable; only the task text resets after submit.
1667
- clearTaskComposer({ keepAttachments: true });
1668
- appendRunEvent({
1669
- title: submittedSkillInvocation?.id === 'skill-installer'
1670
- ? tx('I will use the Codex skill installer for this request.', '我会用 Codex skill installer 处理这个请求。')
1671
- : tx('I will first understand your request, then inspect the relevant Overleaf files.', '我会先理解你的请求,再检查相关 Overleaf 文件。'),
1672
- status: 'running',
1673
- detail: {
1674
- [tr('mode')]: formatModeLabel(submittedMode),
1675
- [tx('Model', '模型')]: state.model,
1676
- [tx('Reasoning effort', '推理强度')]: state.reasoningEffort,
1677
- [tx('Speed', '速度')]: state.speedTier,
1678
- [tx('Track required', '要求留痕')]: submittedRequireReviewing ? tx('yes', '是') : tx('no', '否'),
1679
- [tx('Skill', '技能')]: submittedSkillInvocation?.title || tr('noneValue'),
1680
- [tx('Attachments', '附件')]: submittedAttachments.map(attachment => attachment.name).join(listSeparator()) || tr('noneValue'),
1681
- '@context': formatContextItems(getActiveFocusFiles())
1682
- }
1683
- });
1689
+ let runAuditDraft = null;
1684
1690
  try {
1691
+ runAuditDraft = await awaitRunStep(createAuditDraftForRun({
1692
+ task,
1693
+ sessionId: runSessionId,
1694
+ mode: submittedMode,
1695
+ focusFiles: getActiveFocusFiles()
1696
+ }));
1697
+ announceCrossTabRunStart();
1698
+ // Keep attachments across turns (v1.7.5): screenshots and files usually
1699
+ // anchor several follow-up questions. The tray keeps them visible and
1700
+ // hand-removable; only the task text resets after submit.
1701
+ clearTaskComposer({ keepAttachments: true });
1702
+ appendRunEvent({
1703
+ title: submittedSkillInvocation?.id === 'skill-installer'
1704
+ ? tx('I will use the Codex skill installer for this request.', '我会用 Codex skill installer 处理这个请求。')
1705
+ : tx('I will first understand your request, then inspect the relevant Overleaf files.', '我会先理解你的请求,再检查相关 Overleaf 文件。'),
1706
+ status: 'running',
1707
+ detail: {
1708
+ [tr('mode')]: formatModeLabel(submittedMode),
1709
+ [tx('Model', '模型')]: state.model,
1710
+ [tx('Reasoning effort', '推理强度')]: state.reasoningEffort,
1711
+ [tx('Speed', '速度')]: state.speedTier,
1712
+ [tx('Track required', '要求留痕')]: submittedRequireReviewing ? tx('yes', '是') : tx('no', '否'),
1713
+ [tx('Skill', '技能')]: submittedSkillInvocation?.title || tr('noneValue'),
1714
+ [tx('Attachments', '附件')]: submittedAttachments.map(attachment => attachment.name).join(listSeparator()) || tr('noneValue'),
1715
+ '@context': formatContextItems(getActiveFocusFiles())
1716
+ }
1717
+ });
1685
1718
  if (submittedSkillInvocation?.id === 'skill-installer') {
1686
- await runSkillInstallerTask({
1719
+ await awaitRunStep(runSkillInstallerTask({
1687
1720
  task,
1688
1721
  runSessionId,
1689
1722
  runAuditDraft,
@@ -1692,7 +1725,7 @@
1692
1725
  submittedSkillLoadingSettings,
1693
1726
  submittedAttachments,
1694
1727
  submittedSkillInvocation
1695
- });
1728
+ }));
1696
1729
  return;
1697
1730
  }
1698
1731
  appendRunEvent({
@@ -1707,11 +1740,11 @@
1707
1740
  status: 'completed'
1708
1741
  });
1709
1742
 
1710
- await pauseOtWarmMirror('run-start');
1711
- const writeSafety = await preflightWriteSafety({
1743
+ await awaitRunStep(pauseOtWarmMirror('run-start'));
1744
+ const writeSafety = await awaitRunStep(preflightWriteSafety({
1712
1745
  mode: submittedMode,
1713
1746
  requireReviewing: submittedRequireReviewing
1714
- });
1747
+ }));
1715
1748
  if (!writeSafety.ok) {
1716
1749
  await finalizeAuditRecord(runAuditDraft, {
1717
1750
  resultStatus: 'blocked',
@@ -1721,7 +1754,7 @@
1721
1754
  }
1722
1755
 
1723
1756
  let focusFiles = getActiveFocusFiles();
1724
- const warmStart = await resolveWarmRunStart({ focusFiles, mode: submittedMode });
1757
+ const warmStart = await awaitRunStep(resolveWarmRunStart({ focusFiles, mode: submittedMode }));
1725
1758
  let project = warmStart.project || null;
1726
1759
  let useExistingMirror = warmStart.useExistingMirror;
1727
1760
  let fileOverlays = warmStart.fileOverlays || null;
@@ -1731,7 +1764,7 @@
1731
1764
  title: tx('Syncing the Overleaf project to the local Codex workspace.', '正在同步 Overleaf 项目到本地 Codex workspace。'),
1732
1765
  status: 'running'
1733
1766
  });
1734
- project = await getRunProjectSnapshot();
1767
+ project = await awaitRunStep(getRunProjectSnapshot());
1735
1768
  }
1736
1769
  throwIfRunCancellationRequested();
1737
1770
  if (!useExistingMirror) {
@@ -1743,11 +1776,11 @@
1743
1776
  : getProjectSnapshotWarnings(project);
1744
1777
  const warmMirrorReuse = useExistingMirror
1745
1778
  ? warmStart
1746
- : await resolveWarmMirrorReuse(project, {
1779
+ : await awaitRunStep(resolveWarmMirrorReuse(project, {
1747
1780
  snapshotWarnings,
1748
1781
  focusFiles,
1749
1782
  mode: submittedMode
1750
- });
1783
+ }));
1751
1784
  const focusedPartialSnapshot = runController.canUseFocusedPartialSnapshot({
1752
1785
  project,
1753
1786
  snapshotWarnings,
@@ -1827,7 +1860,7 @@
1827
1860
  let compileLogContext = null;
1828
1861
  if (/(^|[^\w])@compile-log\b/i.test(task)) {
1829
1862
  appendRunEvent({ title: tx('Fetching compile log (@compile-log).', '正在获取编译日志 (@compile-log)。'), status: 'running' });
1830
- compileLogContext = await resolveCompileLogContext();
1863
+ compileLogContext = await awaitRunStep(resolveCompileLogContext());
1831
1864
  if (compileLogContext.available) {
1832
1865
  appendRunEvent({
1833
1866
  title: tx(
@@ -1856,12 +1889,12 @@
1856
1889
  }
1857
1890
  }
1858
1891
 
1859
- const sensitiveFindings = await runSensitivePreflight({
1892
+ const sensitiveFindings = await awaitRunStep(runSensitivePreflight({
1860
1893
  task,
1861
1894
  project,
1862
1895
  rules: getGovernanceRulesForCurrentProject(),
1863
1896
  useExistingMirror
1864
- });
1897
+ }));
1865
1898
  if (sensitiveFindings.blocked) {
1866
1899
  await finalizeAuditRecord(runAuditDraft, {
1867
1900
  resultStatus: 'blocked_sensitive',
@@ -1878,9 +1911,10 @@
1878
1911
  runAuditDraft = await updateAuditSensitiveFindings(runAuditDraft, sensitiveFindings.findings);
1879
1912
  }
1880
1913
 
1881
- await settleMirrorPrefetchBeforeRun();
1914
+ await awaitRunStep(settleMirrorPrefetchBeforeRun());
1915
+ await awaitRunStep(providerSettingsCoordinator.ensureLoaded());
1882
1916
  appendRunEvent({ title: tx('Local Codex session is starting.', '本地 Codex session 开始运行。'), status: 'running' });
1883
- let response = await sendNative({
1917
+ let response = await awaitRunStep(sendNative({
1884
1918
  method: 'codex.run',
1885
1919
  params: buildCodexRunParams({
1886
1920
  task,
@@ -1898,15 +1932,15 @@
1898
1932
  skillInvocation: submittedSkillInvocation,
1899
1933
  submittedMode
1900
1934
  })
1901
- });
1935
+ }));
1902
1936
 
1903
1937
  // Handle mirror_stale error by retrying with full sync
1904
1938
  if (!response.ok && response.error?.code === 'mirror_stale' && useExistingMirror) {
1905
1939
  appendRunEvent({ title: tr('warmMirrorStaleRetryTitle'), status: 'running' });
1906
- const staleRetry = await prepareMirrorStaleRetry({
1907
- project: await getRunProjectSnapshot(),
1940
+ const staleRetry = await awaitRunStep(prepareMirrorStaleRetry({
1941
+ project: await awaitRunStep(getRunProjectSnapshot()),
1908
1942
  focusFiles
1909
- });
1943
+ }));
1910
1944
  if (!staleRetry.ok) {
1911
1945
  return;
1912
1946
  }
@@ -1916,7 +1950,7 @@
1916
1950
  fileOverlays = null;
1917
1951
  otWarmStart = false;
1918
1952
  restrictToFocusFiles = staleRetry.restrictToFocusFiles;
1919
- response = await sendNative({
1953
+ response = await awaitRunStep(sendNative({
1920
1954
  method: 'codex.run',
1921
1955
  params: buildCodexRunParams({
1922
1956
  task,
@@ -1934,7 +1968,7 @@
1934
1968
  skillInvocation: submittedSkillInvocation,
1935
1969
  submittedMode
1936
1970
  })
1937
- });
1971
+ }));
1938
1972
  }
1939
1973
 
1940
1974
  if (!response.ok && response.error?.code === 'thread_resume_failed') {
@@ -1980,8 +2014,8 @@
1980
2014
  if (!response.ok) {
1981
2015
  if (runCancellationRequested || isRunCancellationError(response.error)) {
1982
2016
  appendRunCancelledReport();
1983
- await finalizeAuditRecord(runAuditDraft, { resultStatus: 'cancelled' });
1984
2017
  finishRunView(tx('Cancelled', '已中断'), 'rejected');
2018
+ void finalizeAuditRecord(runAuditDraft, { resultStatus: 'cancelled' });
1985
2019
  return;
1986
2020
  }
1987
2021
  const translated = translateRawError(response.error.message, { mode: submittedMode, locale: getLocale() });
@@ -2225,8 +2259,8 @@
2225
2259
  } catch (error) {
2226
2260
  if (runCancellationRequested || isRunCancellationError(error)) {
2227
2261
  appendRunCancelledReport();
2228
- await finalizeAuditRecord(runAuditDraft, { resultStatus: 'cancelled' });
2229
2262
  finishRunView(tx('Cancelled', '已中断'), 'rejected');
2263
+ void finalizeAuditRecord(runAuditDraft, { resultStatus: 'cancelled' });
2230
2264
  return;
2231
2265
  }
2232
2266
  // codexReturned: assistantMessage arrived on the stream before the
@@ -2282,6 +2316,7 @@
2282
2316
  stopRunElapsedTick();
2283
2317
  currentRunView = null;
2284
2318
  runCancellationRequested = false;
2319
+ runCancellationController = null;
2285
2320
  if (isExperimentalOtEnabled()) {
2286
2321
  await resumeOtWarmMirror('run-settled');
2287
2322
  }
@@ -2531,6 +2566,7 @@
2531
2566
  nativeChannel.clearActiveRequest();
2532
2567
  stopRunElapsedTick();
2533
2568
  currentRunView = null;
2569
+ runCancellationController = null;
2534
2570
  console.error('[codex-overleaf] failed to start task', error);
2535
2571
  appendPlainLog(tx(`Could not start Codex task: ${error.message}`, `无法启动 Codex 任务:${error.message}`));
2536
2572
  });
@@ -2541,6 +2577,7 @@
2541
2577
  return;
2542
2578
  }
2543
2579
  runCancellationRequested = true;
2580
+ runCancellationController?.abort();
2544
2581
  cancelActivePageBridgeRequests();
2545
2582
  panel.dataset.cancelling = 'true';
2546
2583
  setRunning(true);
@@ -2647,12 +2684,32 @@
2647
2684
  }
2648
2685
 
2649
2686
  function throwIfRunCancellationRequested() {
2650
- if (!runCancellationRequested) {
2651
- return;
2652
- }
2653
- const error = new Error('Codex run was cancelled by the user');
2654
- error.code = 'codex_cancelled';
2655
- throw error;
2687
+ if (runCancellationRequested) throw createRunCancellationError();
2688
+ }
2689
+
2690
+ function createRunCancellationError() {
2691
+ const error = Object.assign(new Error('Codex run was cancelled by the user'), {
2692
+ code: 'codex_cancelled', cancelled: true
2693
+ });
2694
+ return error;
2695
+ }
2696
+
2697
+ function awaitRunStep(value) {
2698
+ const signal = runCancellationController?.signal;
2699
+ if (!signal) return Promise.resolve(value);
2700
+ if (signal.aborted || runCancellationRequested) return Promise.reject(createRunCancellationError());
2701
+ return new Promise((resolve, reject) => {
2702
+ let settled = false;
2703
+ const settle = (callback, result) => {
2704
+ if (settled) return;
2705
+ settled = true;
2706
+ signal.removeEventListener('abort', onAbort);
2707
+ callback(result);
2708
+ };
2709
+ const onAbort = () => settle(reject, createRunCancellationError());
2710
+ signal.addEventListener('abort', onAbort, { once: true });
2711
+ Promise.resolve(value).then(result => settle(resolve, result), error => settle(reject, error));
2712
+ });
2656
2713
  }
2657
2714
 
2658
2715
  function isRunCancellationError(error = {}) {
@@ -2851,7 +2908,7 @@
2851
2908
  skillInvocation,
2852
2909
  submittedMode
2853
2910
  } = {}) {
2854
- return runController.buildCodexRunParams({
2911
+ const params = runController.buildCodexRunParams({
2855
2912
  currentProjectId: getCurrentProjectId(),
2856
2913
  state,
2857
2914
  task,
@@ -2876,6 +2933,10 @@
2876
2933
  compileLogContext,
2877
2934
  submittedMode
2878
2935
  });
2936
+ return {
2937
+ ...params,
2938
+ providerSelection: providerSettingsCoordinator.getRunSelection()
2939
+ };
2879
2940
  }
2880
2941
 
2881
2942
  async function createAuditDraftForRun(input = {}) {
@@ -6090,7 +6151,7 @@
6090
6151
  focusFiles: Array.isArray(session.focusFiles) ? session.focusFiles : [],
6091
6152
  createdAt: session.createdAt,
6092
6153
  updatedAt: session.updatedAt
6093
- })
6154
+ }, { preserveRunActionPayload: true })
6094
6155
  ));
6095
6156
  if (sessionRecords.length) {
6096
6157
  await StorageDb.putRecords('sessions', sessionRecords);
@@ -6253,8 +6314,9 @@
6253
6314
  saveStateSoon();
6254
6315
  return;
6255
6316
  }
6256
- readPanelInputs();
6317
+ renderReasoningOptions(getRenderedModelEntries());
6257
6318
  renderSpeedOptions(getRenderedModelEntries());
6319
+ readPanelInputs();
6258
6320
  renderModelConfigChoices();
6259
6321
  updateModelDisplay();
6260
6322
  // Save feedback is per-card now (settingsPanel flashSaved); the global
@@ -6269,6 +6331,9 @@
6269
6331
  return Array.from(panel?.querySelector('[data-model]')?.options || []).map(option => ({
6270
6332
  id: option.value,
6271
6333
  label: option.textContent,
6334
+ reasoningEfforts: (option.dataset.reasoningEfforts || '').split(',').filter(Boolean),
6335
+ defaultReasoningEffort: option.dataset.defaultReasoningEffort || '',
6336
+ reasoningPresentation: option.dataset.reasoningPresentation || '',
6272
6337
  speedTiers: (option.dataset.speedTiers || 'standard').split(',').filter(Boolean),
6273
6338
  defaultSpeedTier: option.dataset.defaultSpeedTier || 'standard'
6274
6339
  }));
@@ -6392,13 +6457,13 @@
6392
6457
  renderModelOptions(getModelCatalog().FALLBACK_MODELS, state.model);
6393
6458
  }
6394
6459
  panel.querySelector('[data-model]').value = state.model;
6395
- panel.querySelector('[data-reasoning]').value = state.reasoningEffort;
6396
- renderSpeedOptions(Array.from(panel.querySelector('[data-model]')?.options || []).map(option => ({
6397
- id: option.value,
6398
- label: option.textContent,
6399
- speedTiers: (option.dataset.speedTiers || 'standard').split(',').filter(Boolean),
6400
- defaultSpeedTier: option.dataset.defaultSpeedTier || 'standard'
6401
- })));
6460
+ const renderedModels = getRenderedModelEntries();
6461
+ renderReasoningOptions(renderedModels);
6462
+ const reasoningSelect = panel.querySelector('[data-reasoning]');
6463
+ if (Array.from(reasoningSelect?.options || []).some(option => option.value === state.reasoningEffort)) {
6464
+ reasoningSelect.value = state.reasoningEffort;
6465
+ }
6466
+ renderSpeedOptions(renderedModels);
6402
6467
  panel.querySelector('[data-speed]').value = state.speedTier;
6403
6468
  panel.querySelector('[data-mode]').value = state.mode;
6404
6469
  panel.querySelector('[data-task]').value = state.task;