codex-overleaf-link 2.4.1 → 2.5.1

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 (397) hide show
  1. package/README.md +275 -305
  2. package/README.zh-CN.md +290 -320
  3. package/extension/bootstrap/background.js +99 -19
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +11 -16
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/update.js +25 -2
  8. package/extension/bootstrap/updateStatus.js +1 -0
  9. package/extension/runtime-manifest.json +3 -1
  10. package/extension/src/background.js +234 -56
  11. package/extension/src/backgroundBootstrapMigration.js +181 -0
  12. package/extension/src/backgroundSharedSessions.js +440 -0
  13. package/extension/src/backgroundUpdateCoordinator.js +541 -381
  14. package/extension/src/backgroundUpdateSafety.js +173 -0
  15. package/extension/src/content/activeTurnControl.js +16 -2
  16. package/extension/src/content/applyResultFormatters.js +46 -73
  17. package/extension/src/content/assetTransferBroker.js +244 -12
  18. package/extension/src/content/changeHistoryView.js +175 -0
  19. package/extension/src/content/composerAttachments.js +117 -67
  20. package/extension/src/content/composerPanel.js +41 -8
  21. package/extension/src/content/contentRuntime.js +1084 -1099
  22. package/extension/src/content/diagnosticsController.js +122 -97
  23. package/extension/src/content/diagnosticsExport.js +236 -0
  24. package/extension/src/content/diagnosticsPanel.js +78 -19
  25. package/extension/src/content/diffReviewPanel.js +8 -5
  26. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  27. package/extension/src/content/generated/content.bundle.js +14680 -3606
  28. package/extension/src/content/generated/content.bundle.meta.json +30 -3
  29. package/extension/src/content/globalPreferencesController.js +1 -1
  30. package/extension/src/content/legacyGlobalRegistry.js +25 -1
  31. package/extension/src/content/localSkillsPanel.js +141 -16
  32. package/extension/src/content/markdownDomRenderer.js +9 -4
  33. package/extension/src/content/markdownText.js +5 -6
  34. package/extension/src/content/modelPicker.js +8 -5
  35. package/extension/src/content/nativeCompatibilityController.js +45 -20
  36. package/extension/src/content/otWarmMirror.js +4 -5
  37. package/extension/src/content/otWarmMirrorController.js +17 -20
  38. package/extension/src/content/pageBridgeClient.js +33 -5
  39. package/extension/src/content/panelMaintenance.js +87 -132
  40. package/extension/src/content/panelRenderer.js +10 -0
  41. package/extension/src/content/pendingInputView.js +109 -76
  42. package/extension/src/content/projectProviderSelection.js +13 -4
  43. package/extension/src/content/projectSessionCleanup.js +109 -62
  44. package/extension/src/content/projectSettingsCoordinator.js +39 -2
  45. package/extension/src/content/providerSettingsCoordinator.js +63 -14
  46. package/extension/src/content/providerSettingsDialog.js +724 -113
  47. package/extension/src/content/recentProjects.js +21 -6
  48. package/extension/src/content/runActivitySummary.js +665 -0
  49. package/extension/src/content/runCompletionMeta.js +344 -0
  50. package/extension/src/content/runController.js +40 -15
  51. package/extension/src/content/runFailureNotice.js +34 -12
  52. package/extension/src/content/runGuidanceController.js +76 -30
  53. package/extension/src/content/runGuidanceView.js +59 -10
  54. package/extension/src/content/runPresence.js +306 -0
  55. package/extension/src/content/runQueueScheduler.js +150 -43
  56. package/extension/src/content/runRecoveryActions.js +146 -0
  57. package/extension/src/content/runResultActions.js +11 -8
  58. package/extension/src/content/runReviewController.js +574 -0
  59. package/extension/src/content/runScrollLayout.js +21 -2
  60. package/extension/src/content/runTimelineView.js +315 -290
  61. package/extension/src/content/scopedPersistencePanelState.js +51 -6
  62. package/extension/src/content/selectionContextView.js +300 -0
  63. package/extension/src/content/sensitivePreflight.js +140 -0
  64. package/extension/src/content/sessionForkController.js +80 -17
  65. package/extension/src/content/sessionManager.js +1 -1
  66. package/extension/src/content/sessionPersistence.js +6 -1
  67. package/extension/src/content/settingsPanel.js +84 -55
  68. package/extension/src/content/settingsWorkbench.js +379 -0
  69. package/extension/src/content/sharedSessionViewSync.js +109 -0
  70. package/extension/src/content/subagentActivityView.js +195 -0
  71. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  72. package/extension/src/content/updateIdle.js +4 -1
  73. package/extension/src/content/updateNotice.js +179 -56
  74. package/extension/src/content/updateSafetyReceiver.js +58 -0
  75. package/extension/src/content/writebackController.js +308 -2
  76. package/extension/src/content/writebackOrchestrator.js +213 -199
  77. package/extension/src/content/writebackPlan.js +231 -0
  78. package/extension/src/content/writebackRecovery.js +122 -0
  79. package/extension/src/content/writingStyleSettings.js +600 -0
  80. package/extension/src/content/writtenChangesView.js +152 -0
  81. package/extension/src/page/binaryAssetUploader.js +223 -23
  82. package/extension/src/page/fileMover.js +256 -0
  83. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  84. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  85. package/extension/src/page/referenceProjects.js +149 -0
  86. package/extension/src/page/reviewContext.js +113 -0
  87. package/extension/src/page/saveState.js +20 -6
  88. package/extension/src/page/snapshotRouter.js +124 -48
  89. package/extension/src/page/textCoordinates.js +102 -0
  90. package/extension/src/page/textFileCreator.js +442 -71
  91. package/extension/src/page/trackedChangeCapture.js +53 -57
  92. package/extension/src/page/trackedChangeOwnership.js +216 -0
  93. package/extension/src/page/trackedChangeReplay.js +184 -0
  94. package/extension/src/page/trackedChangesLifecycle.js +343 -339
  95. package/extension/src/page/treeOperations.js +33 -12
  96. package/extension/src/page/updateSafetyProbe.js +41 -0
  97. package/extension/src/page/writebackReceiptJournal.js +114 -0
  98. package/extension/src/page/writebackRouter.js +242 -201
  99. package/extension/src/pageBridge.js +73 -102
  100. package/extension/src/popup.js +261 -65
  101. package/extension/src/shared/agentTranscript.js +41 -4
  102. package/extension/src/shared/auditRecords.js +73 -0
  103. package/extension/src/shared/changeHistoryModel.js +94 -0
  104. package/extension/src/shared/compatibility.js +22 -6
  105. package/extension/src/shared/compileAdapter.js +101 -37
  106. package/extension/src/shared/composerAttachmentDraft.js +151 -0
  107. package/extension/src/shared/directoryCleanupProof.js +43 -0
  108. package/extension/src/shared/failureReasons.js +329 -3
  109. package/extension/src/shared/globalPreferences.js +31 -14
  110. package/extension/src/shared/governanceRules.js +4 -37
  111. package/extension/src/shared/i18n.js +389 -25
  112. package/extension/src/shared/managedUpdateProjection.js +8 -5
  113. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  114. package/extension/src/shared/pageRpcContract.js +9 -2
  115. package/extension/src/shared/projectFiles.js +21 -44
  116. package/extension/src/shared/providerProfiles.js +114 -3
  117. package/extension/src/shared/runActivityModel.js +361 -0
  118. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  119. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  120. package/extension/src/shared/runInputQueue.js +48 -5
  121. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  122. package/extension/src/shared/selectionContext.js +37 -0
  123. package/extension/src/shared/sessionState.js +166 -30
  124. package/extension/src/shared/settlementFacts.js +10 -2
  125. package/extension/src/shared/sharedSessionBridge.js +311 -0
  126. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  127. package/extension/src/shared/staleGuard.js +118 -13
  128. package/extension/src/shared/storageDb.js +77 -78
  129. package/extension/src/shared/storageMigration.js +74 -111
  130. package/extension/src/shared/storageRunActions.js +211 -22
  131. package/extension/src/shared/storageValuePruning.js +40 -0
  132. package/extension/src/shared/subagentActivityModel.js +174 -0
  133. package/extension/src/shared/undoOperations.js +216 -6
  134. package/extension/src/shared/updateConsent.js +42 -11
  135. package/extension/src/shared/updateRuntimeIdentity.js +1 -1
  136. package/extension/src/shared/writebackIntent.js +78 -0
  137. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  138. package/extension/src/shared/writebackSettlement.js +67 -48
  139. package/extension/src/shared/writingStyle.js +33 -0
  140. package/extension/styles/panel.css +1987 -385
  141. package/extension/styles/popup.css +110 -0
  142. package/native-host/src/anthropicBridgeRequest.js +6 -3
  143. package/native-host/src/anthropicBridgeResponse.js +6 -2
  144. package/native-host/src/anthropicMessagesBridge.js +24 -16
  145. package/native-host/src/bootstrapMigration.js +296 -0
  146. package/native-host/src/bootstrapMigrationContract.js +101 -0
  147. package/native-host/src/bootstrapUpdate.js +233 -0
  148. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  149. package/native-host/src/chatBridgeResponse.js +10 -0
  150. package/native-host/src/chatCompletionsBridge.js +21 -15
  151. package/native-host/src/codexConfigPaths.js +106 -0
  152. package/native-host/src/codexHome.js +66 -73
  153. package/native-host/src/codexHomeScope.js +124 -0
  154. package/native-host/src/codexInheritedAuth.js +90 -0
  155. package/native-host/src/codexLocalSkills.js +301 -0
  156. package/native-host/src/codexModelRecovery.js +107 -0
  157. package/native-host/src/codexPromptAssembly.js +33 -12
  158. package/native-host/src/codexProviderLaunch.js +3 -3
  159. package/native-host/src/codexProviderTest.js +24 -26
  160. package/native-host/src/codexRuntimeIdentity.js +1 -0
  161. package/native-host/src/codexSessionRunner.js +211 -318
  162. package/native-host/src/codexSessionTiming.js +21 -8
  163. package/native-host/src/codexSkillIsolation.js +170 -0
  164. package/native-host/src/codexThreadFork.js +19 -3
  165. package/native-host/src/index.js +19 -7
  166. package/native-host/src/managedInstall.js +115 -39
  167. package/native-host/src/managedInstallRecovery.js +140 -0
  168. package/native-host/src/managedRuntimeEvidence.js +57 -0
  169. package/native-host/src/mirrorWorkspace.js +92 -22
  170. package/native-host/src/nativeDoctor.js +15 -0
  171. package/native-host/src/nativeEnvironment.js +70 -0
  172. package/native-host/src/nativeHelperLifecycle.js +21 -0
  173. package/native-host/src/nativeHostRegistration.js +33 -0
  174. package/native-host/src/nativeQuotas.js +15 -8
  175. package/native-host/src/nativeRunLifecycle.js +71 -0
  176. package/native-host/src/overleafWorkspaceRules.js +16 -0
  177. package/native-host/src/processStopBarrier.js +115 -0
  178. package/native-host/src/projectPathPlatform.js +30 -0
  179. package/native-host/src/providerHttp.js +80 -0
  180. package/native-host/src/providerModelCatalog.js +4 -3
  181. package/native-host/src/providerProfile.js +17 -3
  182. package/native-host/src/providerReasoning.js +10 -11
  183. package/native-host/src/providerRuntime.js +1 -0
  184. package/native-host/src/providerStore.js +76 -14
  185. package/native-host/src/releaseTransport.js +14 -5
  186. package/native-host/src/responsesPassthroughBridge.js +266 -52
  187. package/native-host/src/runtimeInstaller.js +21 -1
  188. package/native-host/src/selectionScope.js +86 -0
  189. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  190. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  191. package/native-host/src/subagentAdmission.js +26 -0
  192. package/native-host/src/subagentBroker.js +176 -159
  193. package/native-host/src/subagentTelemetry.js +325 -0
  194. package/native-host/src/subagentWorkspace.js +103 -0
  195. package/native-host/src/subagentWorkspacePath.js +34 -0
  196. package/native-host/src/taskRunnerRuntime.js +130 -57
  197. package/native-host/src/tomlStatements.js +140 -0
  198. package/native-host/src/turnAttachments.js +113 -0
  199. package/native-host/src/updateManager.js +406 -38
  200. package/native-host/src/updateNetworkControl.js +75 -0
  201. package/native-host/src/updateTrust.js +21 -2
  202. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  203. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  303. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  304. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  305. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  306. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  307. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  308. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  309. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  310. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  311. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  312. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  313. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  314. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  315. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  316. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  317. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  318. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  319. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  320. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  321. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  322. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  323. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  324. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  325. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  326. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  327. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  328. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  329. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  330. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  331. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  332. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  333. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  334. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  335. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  336. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  337. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  338. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  339. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  340. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  341. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  342. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  343. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  344. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  345. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  346. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  347. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  348. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  349. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  350. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  351. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  352. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  353. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  354. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  355. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  356. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  357. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  358. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  359. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  360. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  361. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  362. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  363. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  364. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  365. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  366. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  367. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  368. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  369. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  370. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  371. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  372. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  373. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  374. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  375. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  376. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  377. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  378. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  379. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  380. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  381. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  382. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  383. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  384. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  385. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  386. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  387. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  388. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  389. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  390. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  391. package/native-host/src/writingStyleEvidence.js +102 -0
  392. package/native-host/src/writingStylePdfWorker.js +35 -0
  393. package/native-host/src/writingStyleProfile.js +165 -0
  394. package/native-host/src/writingStyleRuntime.js +475 -0
  395. package/native-host/src/writingStyleStore.js +73 -0
  396. package/package.json +1 -1
  397. package/scripts/install-managed.mjs +1 -0
@@ -1,21 +1,27 @@
1
- (function initCodexOverleafRunTimelineView() {
1
+ (function (root, factory) {
2
+ if (typeof module === 'object' && module.exports) module.exports = factory(require('./runRecoveryActions'));
3
+ else root.CodexOverleafModuleRegistry.define('RunTimelineView', ['RunRecoveryActions'], factory);
4
+ })(typeof window !== 'undefined' ? window : globalThis, function (RunRecoveryActions) {
2
5
  'use strict';
3
6
 
4
- // Run-timeline view — the timeline render pipeline carved out of
5
- // contentRuntime.js (v1.4.6 structural-debt phase 2): the scroll engine +
6
- // jump-to-latest button, the live-elapsed tick and collapsed-header summary,
7
- // run-card / stream-event / activity rendering, the completion report, and
8
- // the run-card Undo/Accept controls. Code moved verbatim (original
9
- // indentation kept); runtime collaborators are factory-injected and mutable
10
- // runtime state (panel, panel state, the live run view) is read through lazy
11
- // getters. The view-local scroll/timer state below moved here with the code
12
- // that owns it.
7
+
8
+ // Owns scroll, cards, reports and mature run actions. The optional activity
9
+ // presenter projects stored events without taking ownership of settlement.
13
10
  function create(deps = {}) {
14
11
  const {
12
+ RunActivitySummary,
13
+ RunPresence,
14
+ RunActivityModel,
15
+ SubagentActivityView,
15
16
  RunGuidanceView,
17
+ getPendingGuidance = () => [],
16
18
  RunResultActions,
17
19
  RunScrollLayout,
18
20
  RunFailureNotice,
21
+ I18n,
22
+ FailureReasons,
23
+ localizeVisibleReason = value => value,
24
+ localizeCompileReason = value => value,
19
25
  tr,
20
26
  tx,
21
27
  getLocale,
@@ -25,6 +31,7 @@
25
31
  formatEventDetail,
26
32
  formatEventTime,
27
33
  renderAttachmentPreviewList,
34
+ renderSentSelection,
28
35
  formatModeLabel,
29
36
  undoRun,
30
37
  acceptRun,
@@ -41,11 +48,16 @@
41
48
  getState,
42
49
  getCurrentRunView,
43
50
  refillComposerForRetry,
51
+ canEditRun,
52
+ retryWriteback,
44
53
  showNativeSetupGuidance,
45
54
  openProjectFileForFailure,
46
55
  openStorageSettings,
47
56
  trackedChangeInFlight,
48
- projectRunSettlement
57
+ projectRunSettlement,
58
+ getCurrentProjectId,
59
+ getAccountScopeId,
60
+ getProjectName
49
61
  } = deps;
50
62
 
51
63
  let logAutoFollow = true;
@@ -57,11 +69,10 @@
57
69
  // drives the floating "jump to latest" button's counter.
58
70
  let scrollLogRafId = 0;
59
71
  let scrollLogPendingForce = false;
72
+ let pendingGuidanceReveal = null;
60
73
  let jumpToLatestButton = null;
61
74
  let unreadSinceDetach = 0;
62
- // Live-elapsed tick for the sticky run-process header. Without it the header
63
- // reads a static "Processing…" and the user can't tell a working run from a
64
- // hung one — the single highest-value streaming signal per competitor UX.
75
+ // Elapsed time belongs to the run; tool rows retain their own lifecycle.
65
76
  let runElapsedTimer = null;
66
77
  const scrollLayout = RunScrollLayout?.create({
67
78
  getScroller: getLogScrollContainer,
@@ -69,12 +80,32 @@
69
80
  onLayoutChange: scroller => logAutoFollow ? scrollLogToBottom() : updateJumpToLatestButton(scroller)
70
81
  });
71
82
  const failureNotice = RunFailureNotice?.create({ tr, projectRunSettlement, sanitizeText: sanitizeAssistantVisibleText });
83
+ const { buildRecoveryButton, appendRecoveryActionForFailure } = RunRecoveryActions.create({
84
+ document: typeof document === 'undefined' ? undefined : document,
85
+ window: typeof window === 'undefined' ? undefined : window,
86
+ tx, failureNotice, refillComposerForRetry, openProjectFileForFailure, showNativeSetupGuidance,
87
+ openStorageSettings, forceCancelStuckTaskForCurrentProject
88
+ });
89
+ const presence = RunPresence?.create({ tx, getLocale, formatElapsed });
90
+ const displayRun = run => RunFailureNotice?.projectResolvedUndo?.(run) || run;
91
+ const displayLocalizedRun = run => {
92
+ const displayed = displayRun(run);
93
+ return displayed ? { ...displayed, events: (displayed.events || []).map(sanitizeRunEventForRender) } : displayed;
94
+ };
95
+ const activitySummary = RunActivitySummary?.create({ RunActivityModel, RunGuidanceView, getPendingGuidance, SubagentActivityView, tx, findRunRecord: (...args) => displayLocalizedRun(findRunRecord(...args)), getPanel, cssEscape,
96
+ projectReviewResultEvent: sanitizeRunEventForRender,
97
+ formatReviewResultFailure,
98
+ formatEventTime, formatElapsed, renderMarkdownBlockText, renderRunEvent, formatEventDetail,
99
+ presence, classifyStatus: classifyRunPresence, getProjectId: () => getCurrentProjectId?.() || '',
100
+ summarizeCommand: RunPresence?.commandSummary,
101
+ isGuidance: event => Boolean(RunGuidanceView.getGuidanceText(event)), sanitizeAssistantVisibleText });
72
102
 
73
103
  // Re-arm auto-follow (called by the runtime when a new run starts, so the
74
104
  // log snaps back to following the live stream).
75
105
  function resetAutoFollow() {
76
106
  logAutoFollow = true;
77
107
  userScrollIntentUntil = 0;
108
+ pendingGuidanceReveal = null;
78
109
  }
79
110
 
80
111
  function bindLogAutoFollow() {
@@ -176,6 +207,7 @@
176
207
  if ((event?.type === 'wheel' && event.deltaY < 0) || ['ArrowUp', 'PageUp', 'Home'].includes(event?.key)) {
177
208
  logAutoFollow = false;
178
209
  scrollLogPendingForce = false;
210
+ pendingGuidanceReveal = null;
179
211
  }
180
212
  }
181
213
 
@@ -187,6 +219,7 @@
187
219
  }
188
220
 
189
221
  function scrollLogToBottom(options = {}) {
222
+ if (activitySummary?.isSubagentViewOpen?.()) return;
190
223
  const scroller = getLogScrollContainer();
191
224
  if (!scroller) {
192
225
  return;
@@ -199,6 +232,7 @@
199
232
  userScrollIntentUntil = 0;
200
233
  unreadSinceDetach = 0;
201
234
  scrollLogPendingForce = true;
235
+ pendingGuidanceReveal = null;
202
236
  } else if (!logAutoFollow) {
203
237
  // The user has scrolled up: do not yank them. Just keep the jump button
204
238
  // state current.
@@ -233,10 +267,25 @@
233
267
  writeNow();
234
268
  });
235
269
  }
270
+ function revealGuidance(row) {
271
+ if (!logAutoFollow || activitySummary?.isSubagentViewOpen?.() || !getLogScrollContainer()?.contains?.(row)) return false;
272
+ pendingGuidanceReveal = row;
273
+ scrollLogToBottom();
274
+ return true;
275
+ }
236
276
  function setLogScrollPosition(scroller) {
277
+ const reveal = pendingGuidanceReveal;
278
+ pendingGuidanceReveal = null;
279
+ const result = reveal && scrollLayout?.revealGuidance(reveal);
280
+ if (result) {
281
+ logAutoFollow = result.following;
282
+ if (!logAutoFollow) scrollLayout.captureAnchor(true);
283
+ return;
284
+ }
237
285
  scroller.scrollTop = scroller.scrollHeight;
238
286
  }
239
287
  function collapseRunProcess(view, statusText) {
288
+ if (activitySummary) { activitySummary.settle(view, statusText); return; }
240
289
  const runProcess = view?.runProcess || view?.root?.querySelector('[data-run-process]');
241
290
  if (runProcess) {
242
291
  runProcess.open = false;
@@ -245,23 +294,15 @@
245
294
  if (statusEl) {
246
295
  // Append the step count to the collapsed header ("Processed 18s · 6 steps")
247
296
  // so the user sees how much work the run did without expanding it.
248
- const stepCount = countRunActivitySteps(view);
297
+ const record = view?.recordId ? findRunRecord(view.recordId, view.sessionId) : null;
298
+ const stepCount = (record?.events || []).filter(event => (event.kind || 'activity') === 'activity').length;
249
299
  statusEl.textContent = stepCount > 0
250
300
  ? `${statusText} · ${tx(`${stepCount} steps`, `${stepCount} 步`)}`
251
301
  : statusText;
252
302
  }
253
303
  }
254
304
 
255
- function countRunActivitySteps(view) {
256
- const record = view?.recordId ? findRunRecord(view.recordId, view.sessionId) : null;
257
- if (!Array.isArray(record?.events)) {
258
- return 0;
259
- }
260
- return record.events.filter(event => (event.kind || 'activity') === 'activity').length;
261
- }
262
-
263
- // The sticky run-process header shows a live "Processing… {elapsed}" while a
264
- // run is in flight so the user can distinguish a working run from a hung one.
305
+ // Keep elapsed labels live without creating new activity records.
265
306
  function startRunElapsedTick() {
266
307
  stopRunElapsedTick();
267
308
  if (typeof window.setInterval !== 'function') {
@@ -272,6 +313,7 @@
272
313
  stopRunElapsedTick();
273
314
  return;
274
315
  }
316
+ if (activitySummary?.update(getCurrentRunView()) && presence) return;
275
317
  const statusEl = getCurrentRunView().status
276
318
  || getCurrentRunView().root?.querySelector('[data-run-status]');
277
319
  if (statusEl) {
@@ -289,6 +331,13 @@
289
331
  runElapsedTimer = null;
290
332
  }
291
333
 
334
+ function classifyRunPresence(run = {}) {
335
+ if (run.retryingWriteback) return 'plain';
336
+ if (run.status === 'completed') return 'done';
337
+ if (run.status === 'cancelled' || run.status === 'rejected') return 'stopped';
338
+ return run.status === 'failed' || run.status === 'abandoned_after_navigation' ? 'failed' : 'plain';
339
+ }
340
+
292
341
  function formatProcessedSummary(status, elapsedMs) {
293
342
  const elapsed = formatElapsed(elapsedMs);
294
343
  if (status === 'failed') {
@@ -303,6 +352,7 @@
303
352
  return tr('processed', { elapsed });
304
353
  }
305
354
  function renderRunHistory(options = {}) {
355
+ activitySummary?.closeSubagentView?.();
306
356
  const log = getPanel()?.querySelector('[data-log]');
307
357
  if (!log) {
308
358
  return;
@@ -323,7 +373,12 @@
323
373
  const hint = document.createElement('div');
324
374
  hint.className = 'empty-runs-hint';
325
375
  hint.textContent = tr('emptyRunsHint');
376
+ label.className = 'empty-runs-title';
326
377
  empty.append(icon, label, hint);
378
+ if (presence) {
379
+ icon.remove();
380
+ presence.decorateEmpty(empty, { projectName: getProjectName?.() || '' });
381
+ }
327
382
  log.append(empty);
328
383
  return;
329
384
  }
@@ -342,6 +397,7 @@
342
397
  }
343
398
 
344
399
  function renderRunCard(run) {
400
+ run = displayLocalizedRun(run);
345
401
  const root = document.createElement('section');
346
402
  root.className = 'transcript-turn run-card';
347
403
  root.dataset.status = run.status || 'completed';
@@ -356,11 +412,14 @@
356
412
  root.innerHTML = `
357
413
  <div class="transcript-turn-main">
358
414
  <div class="run-attachments codex-attachment-preview-list" data-run-attachments hidden></div>
415
+ <div class="run-selection" data-run-selection hidden></div>
359
416
  <div class="run-prompt" data-run-task></div>
360
417
  <div class="run-guidance-list" data-run-guidance></div>
361
418
  <div class="run-turn-meta">
362
419
  <button type="button" data-run-accept hidden title="Accept this run's tracked changes in Overleaf">Accept changes</button>
363
420
  <button type="button" data-run-undo hidden title="Undo this run's writes to Overleaf">Undo</button>
421
+ <button type="button" class="run-copy-action run-edit-action" data-run-edit hidden></button>
422
+ <button type="button" class="run-copy-action run-sync-action" data-run-sync-retry hidden></button>
364
423
  </div>
365
424
  <details class="run-process" data-run-process>
366
425
  <summary data-run-process-summary>
@@ -379,6 +438,7 @@
379
438
  `;
380
439
 
381
440
  renderAttachmentPreviewList(run.attachments, root.querySelector('[data-run-attachments]'), { readonly: true });
441
+ renderSentSelection?.(root.querySelector('[data-run-selection]'), run.executionSnapshot?.selectionContext, { document, tx });
382
442
  const runTask = root.querySelector('[data-run-task]');
383
443
  runTask.classList.add('run-user-message');
384
444
  runTask.textContent = run.task || '';
@@ -398,42 +458,41 @@
398
458
  truncated.textContent = tr('eventsTruncatedNote');
399
459
  events.append(truncated);
400
460
  }
401
- const guidanceTarget = run.status === 'running' ? events : guidance;
402
- const renderedGuidanceIds = new Set();
403
- const completedLegacyGuidance = new Set((run.events || [])
404
- .filter(event => event.kind === 'guidance' && !event.guidanceId && event.status === 'completed')
405
- .map(event => event.title));
461
+ if (!activitySummary) RunGuidanceView.reconcileGuidance?.(guidance, (run.events || []).concat(getPendingGuidance(run)), tx, { active: run.status === 'running' });
406
462
  for (const event of run.events || []) {
407
463
  if (event.kind === 'report') {
408
464
  report.hidden = false;
409
465
  report.replaceChildren(renderCompletionReport(event, run));
410
- } else if (event.kind === 'technical') {
466
+ } else if (activitySummary || event.kind === 'technical') {
411
467
  continue;
412
468
  } else if (RunGuidanceView.getGuidanceText(event)) {
413
- if (event.guidanceId && renderedGuidanceIds.has(event.guidanceId)) {
414
- continue;
415
- }
416
- if (!event.guidanceId && event.status === 'queued' && completedLegacyGuidance.has(event.title)) {
417
- continue;
418
- }
419
- if (event.guidanceId) {
420
- renderedGuidanceIds.add(event.guidanceId);
421
- }
422
- guidanceTarget.append(renderRunEvent(event));
469
+ continue;
423
470
  } else if (event.kind === 'stream') {
424
471
  upsertStreamEvent({ events }, event);
425
472
  } else {
426
- events.append(renderRunEvent(event));
473
+ appendActivityDetail({ events }, renderRunEvent(event), event);
427
474
  }
428
475
  }
429
476
 
430
477
  RunResultActions.configureResultActions(root, run);
431
478
  configureAcceptButton(root, run);
432
479
  configureUndoButton(root, run);
480
+ configureEditButton(root, run);
481
+ configureWritebackButton(root, run);
482
+ activitySummary?.mount(root, run);
433
483
  return root;
434
484
  }
435
485
 
436
486
  function getRunStatusText(run = {}) {
487
+ if (run.retryingWriteback) return tx('Checking file sync', '正在检查文件同步');
488
+ if (['Stopped tracking after a page refresh', '页面刷新后已停止跟踪'].includes(run.statusText)) return tr('restoredRunStoppedStatus');
489
+ const knownStatus = /^(?:Done|Failed|Cancelled|Running|已处理|处理失败|已取消|处理中)(?:\s+[\d.]+(?:ms|s|m|h|秒|分钟)(?:\s+[\d.]+(?:ms|s|m|h|秒|分钟))*)?$/.test(run.statusText || '');
490
+ if (knownStatus) {
491
+ const elapsed = String(run.statusText).replace(/^(?:Done|Failed|Cancelled|Running|已处理|处理失败|已取消|处理中)\s*/, '');
492
+ const key = run.status === 'running' ? 'processing' : run.status === 'failed' ? 'processedFailed'
493
+ : ['rejected', 'cancelled'].includes(run.status) ? 'processedCancelled' : 'processed';
494
+ return tr(key, { elapsed }).trim();
495
+ }
437
496
  if (run.statusText) {
438
497
  return run.statusText;
439
498
  }
@@ -461,6 +520,7 @@
461
520
  if (!view?.events) {
462
521
  return;
463
522
  }
523
+ if (activitySummary?.update(view, event)) return;
464
524
  const streamKey = event.streamKey || event.streamRole || 'codex-stream';
465
525
  const selector = `[data-stream-key="${cssEscape(streamKey)}"]`;
466
526
  const existing = view.events.querySelector(selector);
@@ -477,7 +537,12 @@
477
537
  }
478
538
  return;
479
539
  }
480
- view.events.append(renderStreamEvent({ ...event, streamKey }));
540
+ appendActivityDetail(view, renderStreamEvent({ ...event, streamKey }), event);
541
+ }
542
+
543
+ function appendActivityDetail(view, row, event) {
544
+ if (activitySummary) activitySummary.appendDetail(view, row, event);
545
+ else view.events.append(row);
481
546
  }
482
547
 
483
548
  function renderStreamEvent(input) {
@@ -504,18 +569,61 @@
504
569
  function sanitizeRunEventForRender(event = {}) {
505
570
  const streamRole = sanitizeAssistantVisibleText(event.streamRole);
506
571
  const title = sanitizeAssistantVisibleText(event.title);
572
+ const activity = !streamRole && (!event.kind || event.kind === 'activity') && !event.guidanceId;
573
+ const localized = activity ? localizeProductActivity(title, sanitizeAssistantVisibleValue(event.detail)) : null;
507
574
  return {
508
575
  ...event,
576
+ ...(activity && event.activity?.kind === 'compile' ? { activity: { ...event.activity, output: localizeCompileReason(event.activity.output) } } : {}),
509
577
  title: streamRole === 'reasoning'
510
578
  ? stripEmptyHtmlCommentPlaceholders(title)
511
- : title,
579
+ : localized?.title || title,
512
580
  status: sanitizeAssistantVisibleText(event.status),
513
- detail: sanitizeAssistantVisibleValue(event.detail),
581
+ detail: localized?.detail ?? sanitizeAssistantVisibleValue(event.detail),
514
582
  technicalDetail: sanitizeAssistantVisibleValue(event.technicalDetail),
515
583
  streamKey: sanitizeAssistantVisibleText(event.streamKey),
516
584
  streamRole
517
585
  };
518
586
  }
587
+
588
+ function localizeProductActivity(title, detail) {
589
+ let known = false, nextTitle = title;
590
+ const keys = ['undoUnsafeFullFileTitle', 'undoNoTraceTitle', 'undoFileSelectTitle', 'undoNoTraceStarted', 'undoTrackedTitle', 'undoNativeTitle', 'undoTrackedStarted', 'undoNativeStarted', 'undoSwitchedEditing', 'undoReviewingRestoreUnverified', 'runAcceptTrackedStarted', 'runAcceptTrackedFailed', 'undoResult', 'undoTrackedResult', 'undoNativeResult', 'runAcceptTrackedResult'];
591
+ if (I18n?.t) outer: for (const key of keys) for (const locale of ['en', 'zh']) {
592
+ const template = I18n.t(locale, key, { applied: '__APPLIED__', skipped: '__SKIPPED__' });
593
+ const pattern = template.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace('__APPLIED__', '(\\d+)').replace('__SKIPPED__', '(\\d+)');
594
+ const match = new RegExp('^' + pattern + '$').exec(title);
595
+ if (match) { known = true; nextTitle = tr(key, { applied: match[1], skipped: match[2] }); break outer; }
596
+ }
597
+ let countedTitle;
598
+ const writeResult = /^(?:Write result: wrote (\d+) item\(s\), skipped (\d+)|写入结果:已写入 (\d+) 项,跳过 (\d+) 项)$/.exec(title);
599
+ const governance = /^(?:Project governance blocked (\d+) write\(s\) before review\.|项目治理规则在审核前阻止了 (\d+) 项写入。)$/.exec(title);
600
+ if (writeResult) {
601
+ const applied = writeResult[1] ?? writeResult[3], skipped = writeResult[2] ?? writeResult[4];
602
+ countedTitle = tx(`Write result: wrote ${applied} item(s), skipped ${skipped}`, `写入结果:已写入 ${applied} 项,跳过 ${skipped} 项`);
603
+ } else if (governance) {
604
+ const count = governance[1] ?? governance[2];
605
+ countedTitle = tx(`Project governance blocked ${count} write(s) before review.`, `项目治理规则在审核前阻止了 ${count} 项写入。`);
606
+ }
607
+ const labels = ['detailAccepted', 'detailRejected', 'detailUndone', 'detailSkipped', 'detailFile', 'detailRecord', 'detailReason'];
608
+ const project = (value, reason = false) => {
609
+ if (typeof value === 'string') return reason ? localizeVisibleReason(value) : value;
610
+ if (Array.isArray(value)) return value.map(item => project(item, reason));
611
+ if (!value || typeof value !== 'object') return value;
612
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => {
613
+ const label = labels.find(id => ['en', 'zh'].some(locale => I18n?.t?.(locale, id) === key));
614
+ return [label ? tr(label) : key, project(child, label === 'detailReason')];
615
+ }));
616
+ };
617
+ return { title: countedTitle || (known ? nextTitle : localizeCompileReason(localizeVisibleReason(title))),
618
+ detail: typeof detail === 'string' ? localizeVisibleReason(detail) : known ? project(detail) : detail };
619
+ }
620
+ function formatReviewResultFailure(failure) {
621
+ if (failure?.code === 'unknown_legacy_failure' && (!failure.evidence?.originalCode || failure.evidence.originalCode === 'unknown_legacy_failure')
622
+ && ['Unknown reason', '未知原因'].includes(failure.userMessage)) {
623
+ failure = { ...failure, userMessage: tr('unknownReason') };
624
+ }
625
+ return FailureReasons?.formatFailureReasonForDisplay(failure, getLocale?.(), key => { const value = tr(key, failure?.file ? undefined : { file: tr('unknownFile'), activeFile: tr('unknownFile') }); return value && value !== key ? value : undefined; });
626
+ }
519
627
  function renderActivityLine(input) {
520
628
  const event = sanitizeRunEventForRender(input);
521
629
  const row = document.createElement('div');
@@ -543,54 +651,8 @@
543
651
  return row;
544
652
  }
545
653
 
546
- function hasNonEmptyDetail(value) {
547
- if (value === undefined || value === null || value === '') {
548
- return false;
549
- }
550
- if (Array.isArray(value)) {
551
- return value.length > 0;
552
- }
553
- if (typeof value === 'object') {
554
- return Object.keys(value).length > 0;
555
- }
556
- return true;
557
- }
558
-
559
- function renderTechnicalEvent(event) {
560
- const block = document.createElement('section');
561
- block.className = 'run-technical-event';
562
- block.dataset.status = event.status || 'info';
563
654
 
564
- const title = document.createElement('div');
565
- title.className = 'run-technical-event-title';
566
- title.textContent = event.title || tr('technicalDetails');
567
-
568
- const body = document.createElement('pre');
569
- body.textContent = formatEventDetail(buildTechnicalEventDetail(event));
570
- block.append(title, body);
571
- return block;
572
- }
573
655
 
574
- function buildTechnicalEventDetail(event) {
575
- if (event?.kind === 'technical') {
576
- return event.detail || {};
577
- }
578
-
579
- const detail = {
580
- [tx('Step', '步骤')]: event?.title || '',
581
- [tx('Status', '状态')]: event?.status || ''
582
- };
583
- if (event?.timestamp) {
584
- detail[tx('Time', '时间')] = formatEventTime(event.timestamp);
585
- }
586
- if (hasNonEmptyDetail(event?.detail)) {
587
- detail[tx('Content', '内容')] = event.detail;
588
- }
589
- if (hasNonEmptyDetail(event?.technicalDetail)) {
590
- detail[tx('Raw event', '原始事件')] = event.technicalDetail;
591
- }
592
- return detail;
593
- }
594
656
 
595
657
  function buildActivityTooltip(event) {
596
658
  return [
@@ -599,71 +661,73 @@
599
661
  ].filter(Boolean).join('\n');
600
662
  }
601
663
 
602
- // The trailing status sections emitted by formatHumanReport (agentTranscript):
603
- // run metadata, not part of Codex's answer. Each entry matches the bilingual
604
- // "Label: value" line so the flat-text fallback can split them out of the
605
- // body and demote them into the same muted meta block the structured render
606
- // uses. Order mirrors the structured meta order.
607
- const FLAT_REPORT_STATUS_SECTIONS = [
608
- { key: 'unchangedReason', prefixes: ['Why nothing changed:', '未修改原因:'], en: 'Why nothing changed', zh: '未修改原因' },
609
- { key: 'writeResult', prefixes: ['Write result:', '写入结果:'], en: 'Write result', zh: '写入结果' },
610
- { key: 'undo', prefixes: ['Undo:', '可撤销:'], en: 'Undo', zh: '可撤销' },
611
- { key: 'nextStep', prefixes: ['Next:', '下一步:'], en: 'Next', zh: '下一步' }
612
- ];
613
-
614
- // Splits a flat completion-report string (formatHumanReport output) into the
615
- // answer body and the demoted meta rows. Only single-line sections whose head
616
- // matches a known status label are demoted, so a multi-paragraph conclusion
617
- // that happens to contain "Next: …" prose stays in the body.
618
- function splitFlatCompletionReport(text) {
619
- const raw = typeof text === 'string' ? text : '';
620
- if (!raw.trim()) {
621
- return { body: raw, meta: [] };
622
- }
623
- const bodySections = [];
624
- const meta = [];
625
- for (const section of raw.split(/\n{2,}/)) {
626
- const trimmed = section.trim();
627
- if (!trimmed) continue;
628
- const entry = trimmed.includes('\n')
629
- ? null
630
- : FLAT_REPORT_STATUS_SECTIONS.find(item => item.prefixes.some(prefix => trimmed.startsWith(prefix)));
631
- if (entry) {
632
- const prefix = entry.prefixes.find(p => trimmed.startsWith(p));
633
- const value = trimmed.slice(prefix.length).trim();
634
- if (value) {
635
- meta.push({ key: entry.key, label: tx(entry.en, entry.zh), value });
636
- continue;
637
- }
638
- }
639
- bodySections.push(trimmed);
640
- }
641
- return { body: bodySections.join('\n\n'), meta };
642
- }
664
+ // The result presenter owns parsing and current lifecycle metadata.
665
+ const splitFlatCompletionReport = text => RunResultActions.splitFlatCompletionReport(text, tx);
666
+ const projectCompletionMeta = (meta, run) => RunResultActions.projectCompletionMeta(meta, run, {
667
+ tx, getLocale, tr, trackedChangeInFlight, isTrackedChangeLifecycleRun, projectRunSettlement,
668
+ projectId: getCurrentProjectId?.(), accountScopeId: getAccountScopeId?.()
669
+ });
643
670
 
644
- // Renders the demoted run-metadata block (Why nothing changed / Write result /
645
- // Undo / Next) beneath the answer. Shared by the structured and flat-fallback
646
- // render paths so both demote identically.
647
- function appendCompletionMetaBlock(report, meta) {
671
+ // The user's own open/closed choice per run survives card re-renders.
672
+ const metaOpenChoices = new Map();
673
+
674
+ // Renders the run-metadata rows (Why nothing changed / Write result / Undo /
675
+ // Next) behind a one-line summary beneath the answer. Shared by the
676
+ // structured and flat-fallback render paths so both demote identically.
677
+ function appendCompletionMetaBlock(report, meta, run) {
648
678
  if (!Array.isArray(meta) || !meta.length) {
649
679
  return;
650
680
  }
681
+ const rows = projectCompletionMeta(meta, run).filter(row => row?.label && row.value)
682
+ .map(row => ({ ...row, shown: failureNotice?.formatMetaValue(report, row) ?? row.value }));
683
+ if (!rows.length) return;
684
+ const summary = RunResultActions.summarizeCompletionMeta(rows, run, {
685
+ tx, failed: report.dataset.status === 'failed', unconfirmed: rows.some(row => row.shown !== row.value)
686
+ });
651
687
  const metaBlock = document.createElement('dl');
652
688
  metaBlock.className = 'run-final-answer__meta';
653
- for (const row of meta) {
654
- if (!row || !row.label || !row.value) continue;
689
+ metaBlock.id = `codex-run-meta-${String(run?.id || 'report').replace(/[^a-zA-Z0-9_-]/g, '-')}`;
690
+ for (const row of rows) {
655
691
  const dt = document.createElement('dt');
656
692
  dt.className = 'run-final-answer__meta-label';
657
693
  dt.textContent = row.label;
658
694
  if (row.key) dt.dataset.metaKey = row.key;
659
695
  const dd = document.createElement('dd');
660
696
  dd.className = 'run-final-answer__meta-value';
661
- dd.textContent = failureNotice?.formatMetaValue(report, row) ?? row.value;
697
+ dd.textContent = row.shown;
698
+ if (summary.warnKeys.has(row.key)) dt.dataset.tone = dd.dataset.tone = 'warn';
662
699
  metaBlock.append(dt, dd);
663
700
  }
664
- if (metaBlock.children.length) {
665
- report.append(metaBlock);
666
- }
701
+ const facts = document.createElement('div');
702
+ facts.className = 'run-final-answer__facts';
703
+ const toggle = document.createElement('button');
704
+ toggle.type = 'button';
705
+ toggle.className = 'run-final-answer__facts-sum';
706
+ toggle.setAttribute('aria-controls', metaBlock.id);
707
+ summary.facts.forEach((fact, index) => {
708
+ if (index) toggle.append(Object.assign(document.createElement('span'), { className: 'run-final-answer__facts-sep', textContent: '·' }));
709
+ const item = Object.assign(document.createElement('span'), { className: 'run-final-answer__fact', textContent: fact.text });
710
+ if (fact.tone) item.dataset.tone = fact.tone;
711
+ item.dataset.fact = fact.key;
712
+ toggle.append(item);
713
+ });
714
+ const more = Object.assign(document.createElement('span'), { className: 'run-final-answer__facts-more' });
715
+ toggle.append(Object.assign(document.createElement('span'), { className: 'run-final-answer__facts-sep', textContent: '·' }), more);
716
+ const setOpen = open => {
717
+ facts.dataset.open = String(open);
718
+ metaBlock.hidden = !open;
719
+ toggle.setAttribute('aria-expanded', String(open));
720
+ more.innerHTML = `${open ? tx('Hide', '收起') : tx('Details', '详情')}<svg class="codex-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="m6 4 4 4-4 4"/></svg>`;
721
+ };
722
+ setOpen(metaOpenChoices.get(run?.id) ?? summary.open);
723
+ toggle.addEventListener('click', event => {
724
+ event.stopPropagation();
725
+ const open = facts.dataset.open !== 'true';
726
+ if (run?.id) metaOpenChoices.set(run.id, open);
727
+ setOpen(open);
728
+ });
729
+ facts.append(toggle, metaBlock);
730
+ report.append(facts);
667
731
  }
668
732
 
669
733
  function renderCompletionReport(input, run) {
@@ -724,10 +788,10 @@
724
788
  main.className = 'run-final-answer';
725
789
  const mainSections = [];
726
790
  if (structured.conclusion) {
727
- mainSections.push(formatConclusionMarkdown(structured.conclusion));
791
+ mainSections.push(String(structured.conclusion));
728
792
  }
729
793
  if (structured.body) {
730
- mainSections.push(structured.body);
794
+ mainSections.push(RunResultActions.projectCompletionBody?.(structured.body, tx) ?? structured.body);
731
795
  }
732
796
  if (mainSections.length) {
733
797
  renderMarkdownBlockText(main, mainSections.join('\n\n'));
@@ -736,7 +800,7 @@
736
800
  }
737
801
 
738
802
  failureNotice?.append(report, event, run);
739
- appendCompletionMetaBlock(report, structured.meta);
803
+ appendCompletionMetaBlock(report, structured.meta, run);
740
804
  appendCompileFix(report);
741
805
  appendRejectedRedo(report);
742
806
  appendRecoveryActionForFailure(report, event, run);
@@ -758,24 +822,11 @@
758
822
  appendCompileFix(report);
759
823
  appendRejectedRedo(report);
760
824
  failureNotice?.append(report, event, run);
761
- appendCompletionMetaBlock(report, split.meta);
825
+ appendCompletionMetaBlock(report, split.meta, run);
762
826
  appendRecoveryActionForFailure(report, event, run);
763
827
  return report;
764
828
  }
765
829
 
766
- function formatConclusionMarkdown(value) {
767
- const conclusion = String(value || '');
768
- const label = getLocale() === 'zh' ? '结论:' : 'Conclusion:';
769
- return hasLeadingMarkdownBlock(conclusion)
770
- ? `${label}\n${conclusion}`
771
- : `${label} ${conclusion}`;
772
- }
773
-
774
- function hasLeadingMarkdownBlock(value) {
775
- const firstLine = String(value || '').trimStart().split(/\r?\n/, 1)[0];
776
- return /^(?:#{1,6}\s+\S|```|~~~|(?:[-+*]|\d+[.)])\s+\S|\*\*[^*]+\*\*:?\s*$)/.test(firstLine);
777
- }
778
-
779
830
  // For failure codes that have an actionable recovery path the user can
780
831
  // invoke directly from the run card, append a button inside the completion
781
832
  // report. Today's only consumer is codex_project_locked → "Force-release
@@ -783,128 +834,6 @@
783
834
  // the normal cancel button is hidden because currentRunView is null).
784
835
  // Recovery-action registry: failure codes whose "next step" is executable
785
836
  // from the panel get a button, not prose. One primary action per failure.
786
- const RETRYABLE_FAILURE_CODES = new Set([
787
- 'native_request_failed', 'codex_timeout', 'codex_no_usable_result',
788
- 'codex_output_limit', 'stale_source_changed', 'patch_anchor_not_found',
789
- 'target_editor_not_ready', 'write_timeout', 'partial_write_needs_review',
790
- 'write_operation_failed'
791
- ]);
792
- const NATIVE_SETUP_FAILURE_CODES = new Set([
793
- 'native_bridge_unavailable', 'native_protocol_incompatible'
794
- ]);
795
- const OPEN_FILE_FAILURE_CODES = new Set([
796
- 'target_file_not_active', 'target_file_open_failed', 'target_file_not_found'
797
- ]);
798
- const STORAGE_FAILURE_CODES = new Set([
799
- 'storage_quota_exceeded', 'run_state_persist_failed'
800
- ]);
801
- // A missing `codex` binary is fixed in the terminal, not the panel — the
802
- // action opens the README troubleshooting section (PATH / login checks).
803
- const CODEX_INSTALL_FAILURE_CODES = new Set(['codex_not_found']);
804
- const CODEX_CLI_TROUBLESHOOTING_URL = 'https://github.com/Ghqqqq/codex-overleaf-link#faq-and-troubleshooting';
805
-
806
- function buildRecoveryButton(failureCode, label, title) {
807
- const button = document.createElement('button');
808
- button.type = 'button';
809
- button.className = 'run-final-answer__recovery-action';
810
- button.dataset.recoveryFor = failureCode;
811
- button.textContent = label;
812
- if (title) {
813
- button.title = title;
814
- }
815
- return button;
816
- }
817
-
818
- function appendRecoveryActionForFailure(report, event, run) {
819
- const recovery = failureNotice?.prepareRecovery(report, { openFile: openProjectFileForFailure });
820
- if (recovery?.handled) return;
821
- report = recovery?.target || report;
822
- const failureCode = event?.failure?.code || '';
823
- if (RETRYABLE_FAILURE_CODES.has(failureCode) && typeof refillComposerForRetry === 'function') {
824
- const retry = buildRecoveryButton(failureCode,
825
- tx('Edit & resend', '编辑后重发'),
826
- tx('Put this run\u2019s task back into the composer so you can adjust and resend it.', '把本轮任务填回输入框,修改后可直接重发。'));
827
- retry.addEventListener('click', clickEvent => {
828
- clickEvent.stopPropagation();
829
- refillComposerForRetry(run || null);
830
- });
831
- report.append(retry);
832
- return;
833
- }
834
- if (NATIVE_SETUP_FAILURE_CODES.has(failureCode) && typeof showNativeSetupGuidance === 'function') {
835
- const fix = buildRecoveryButton(failureCode,
836
- tx('Fix the native host', '修复 native host'),
837
- tx('Opens the install/update guidance with the copyable command.', '打开安装/更新指引(含可复制命令)。'));
838
- fix.addEventListener('click', clickEvent => {
839
- clickEvent.stopPropagation();
840
- showNativeSetupGuidance();
841
- });
842
- report.append(fix);
843
- return;
844
- }
845
- if (OPEN_FILE_FAILURE_CODES.has(failureCode) && event?.failure?.file && typeof openProjectFileForFailure === 'function') {
846
- const open = buildRecoveryButton(failureCode,
847
- tx(`Open ${event.failure.file} in Overleaf`, `在 Overleaf 打开 ${event.failure.file}`), '');
848
- open.addEventListener('click', clickEvent => {
849
- clickEvent.stopPropagation();
850
- openProjectFileForFailure(event.failure.file);
851
- });
852
- report.append(open);
853
- return;
854
- }
855
- if (STORAGE_FAILURE_CODES.has(failureCode) && typeof openStorageSettings === 'function') {
856
- const clean = buildRecoveryButton(failureCode,
857
- tx('Open history & storage cleanup', '打开历史与存储清理'), '');
858
- clean.addEventListener('click', clickEvent => {
859
- clickEvent.stopPropagation();
860
- openStorageSettings();
861
- });
862
- report.append(clean);
863
- return;
864
- }
865
- if (CODEX_INSTALL_FAILURE_CODES.has(failureCode)) {
866
- const guide = buildRecoveryButton(failureCode,
867
- tx('Codex CLI troubleshooting guide', '查看 Codex CLI 排查指引'),
868
- tx('Opens the README section on fixing a missing `codex` command (PATH / login).', '打开 README 中「找不到 codex 命令」的排查说明(PATH/登录)。'));
869
- guide.addEventListener('click', clickEvent => {
870
- clickEvent.stopPropagation();
871
- window.open(CODEX_CLI_TROUBLESHOOTING_URL, '_blank', 'noopener');
872
- });
873
- report.append(guide);
874
- return;
875
- }
876
- if (failureCode !== 'codex_project_locked') return;
877
- const button = document.createElement('button');
878
- button.type = 'button';
879
- button.className = 'run-final-answer__recovery-action';
880
- button.dataset.recoveryFor = failureCode;
881
- button.textContent = tx('Force-release the stuck task', '强制释放卡住的任务');
882
- button.title = tx(
883
- 'Sends a force-cancel to the native host that drops the project lock so a new run can start. Use this when refreshing the tab did not free the project.',
884
- '向 native host 发送强制取消请求,释放当前 Overleaf 项目的占用,让新的任务可以启动。刷新页面没用时使用。'
885
- );
886
- button.addEventListener('click', event => {
887
- event.stopPropagation();
888
- button.disabled = true;
889
- button.textContent = tx('Releasing…', '正在释放…');
890
- forceCancelStuckTaskForCurrentProject().then(result => {
891
- if (result?.ok) {
892
- button.textContent = tx('Force-released — you can retry the run.', '已释放,可以重试本轮任务。');
893
- return;
894
- }
895
- // The helper resolves (never throws) with a non-ok result when the
896
- // request was not delivered. Report the real outcome instead of a
897
- // blanket success, and re-enable the button so the user can retry.
898
- button.disabled = false;
899
- button.textContent = tx('Release failed — refresh the tab, then try again.', '释放失败,请刷新页面后重试。');
900
- }).catch(() => {
901
- button.disabled = false;
902
- button.textContent = tx('Release failed — refresh the tab, then try again.', '释放失败,请刷新页面后重试。');
903
- });
904
- });
905
- report.append(button);
906
- }
907
-
908
837
  // A run belongs to the tracked-change lifecycle iff trackedChangeStatus is set
909
838
  // or it still has tracked-change refs. Every other run is a legacy-undo run.
910
839
  function isTrackedChangeLifecycleRun(run) {
@@ -917,6 +846,47 @@
917
846
  return Array.isArray(run.undoTrackedChanges) && run.undoTrackedChanges.length > 0;
918
847
  }
919
848
 
849
+ function configureEditButton(root, run, options = {}) {
850
+ const existing = root.querySelector('[data-run-edit]');
851
+ if (!existing) return;
852
+ const button = existing.cloneNode(true);
853
+ existing.replaceWith(button);
854
+ const available = ['cancelled', 'rejected'].includes(run?.status)
855
+ && Boolean(String(run?.task || '').trim())
856
+ && typeof canEditRun === 'function' && canEditRun(run)
857
+ && typeof refillComposerForRetry === 'function';
858
+ button.hidden = !available;
859
+ if (!available) return;
860
+ button.disabled = typeof options.running === 'boolean'
861
+ ? options.running : Boolean(getCurrentRunView?.());
862
+ button.title = tx('Edit & resend', '编辑后重发');
863
+ button.setAttribute('aria-label', button.title);
864
+ button.innerHTML = '<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m12.8 3.2 4 4M3.5 16.5l4.2-.9 9-9a1.7 1.7 0 0 0 0-2.4l-.9-.9a1.7 1.7 0 0 0-2.4 0l-9 9z"/></svg>';
865
+ button.addEventListener('click', event => {
866
+ event.stopPropagation();
867
+ if (getCurrentRunView?.()) return;
868
+ refillComposerForRetry(run);
869
+ });
870
+ }
871
+
872
+ function configureWritebackButton(root, run, options = {}) {
873
+ const existing = root.querySelector('[data-run-sync-retry]');
874
+ if (!existing) return;
875
+ const button = existing.cloneNode(true);
876
+ existing.replaceWith(button);
877
+ const count = run?.retryWriteback?.operations?.length || 0;
878
+ const available = !run?.forkSnapshot && (count || run?.saveCheck?.files?.length)
879
+ && run.undoStatus !== 'applied' && run.trackedChangeStatus !== 'rejected' && typeof retryWriteback === 'function';
880
+ button.hidden = !available;
881
+ if (!available) return;
882
+ button.disabled = typeof options.running === 'boolean' ? options.running : Boolean(getCurrentRunView?.());
883
+ button.title = count ? tx('Retry sync (' + count + ' files)', '重试同步(' + count + ' 个文件)')
884
+ : tx('Check save status', '检查保存状态');
885
+ button.setAttribute('aria-label', button.title);
886
+ button.innerHTML = '<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M16.5 7A7 7 0 0 0 4 5L2 7m0-4v4h4M3.5 13A7 7 0 0 0 16 15l2-2m0 4v-4h-4"/></svg>';
887
+ button.addEventListener('click', event => { event.stopPropagation(); if (!getCurrentRunView?.()) void retryWriteback(run.id); });
888
+ }
889
+
920
890
  function configureUndoButton(root, run) {
921
891
  const existing = root.querySelector('[data-run-undo]');
922
892
  const button = existing.cloneNode(true);
@@ -939,13 +909,16 @@
939
909
  }
940
910
 
941
911
  button.hidden = false;
942
- button.disabled = run.undoStatus === 'running' || run.undoStatus === 'applied';
943
- button.textContent = run.undoStatus === 'applied'
912
+ const inFlight = trackedChangeInFlight?.get(run.id) === 'reject';
913
+ const applied = run.undoStatus === 'applied' && !inFlight;
914
+ button.disabled = inFlight || (run.undoStatus === 'running' && !run.undoRecoveryCheckRequired) || run.undoStatus === 'applied';
915
+ button.textContent = applied
944
916
  ? tr('undoApplied')
945
917
  : (run.partialWriteback ? tr('undoPartialRun') : tr('undoRun'));
946
- button.title = run.undoStatus === 'applied'
918
+ button.title = applied
947
919
  ? tr('undoAppliedTitle')
948
920
  : (run.partialWriteback ? tr('undoPartialRunTitle') : tr('undoRunTitle'));
921
+ if (run.undoRecoveryUnavailable) button.title = tx('Undo recovery checks are unavailable. Reload this page after the connection recovers.', '撤销恢复检查暂不可用,请在连接恢复后重新加载此页面。');
949
922
  button.addEventListener('click', event => {
950
923
  event.stopPropagation();
951
924
  undoRun(run.id);
@@ -1028,7 +1001,11 @@
1028
1001
  }
1029
1002
 
1030
1003
  if (!isTrackedChangeLifecycleRun(run)) {
1031
- button.hidden = true;
1004
+ const captures = run.undoStatus !== 'applied' && Array.isArray(run.trackedChangeCaptures) ? run.trackedChangeCaptures.filter(c => c && c.state !== 'observed') : [];
1005
+ button.hidden = !captures.length; button.disabled = true;
1006
+ button.textContent = tr('runAcceptTracked');
1007
+ button.title = captures.length ? tx('Tracked-change ownership is unconfirmed. Check the reasons below.', '本轮留痕归属尚未确认,请先核对下方原因。')
1008
+ + '\n' + captures.map(c => c.path + ': ' + (c.reason || c.state) + (c.diagnostics?.sourceReason ? ' / ' + c.diagnostics.sourceReason : '')).join('\n') : tr('runAcceptTrackedTitle');
1032
1009
  return;
1033
1010
  }
1034
1011
 
@@ -1125,17 +1102,63 @@
1125
1102
  const log = getPanel()?.querySelector('[data-log]');
1126
1103
  const existing = log?.querySelector(`[data-run-id="${cssEscape(runId)}"]`);
1127
1104
  const run = findRunRecord(runId);
1128
- if (!log || !existing || !run) {
1129
- return;
1105
+ if (!log || !existing || !run || displayRun(run) === run) {
1106
+ return false;
1130
1107
  }
1131
- existing.replaceWith(renderRunCard(run));
1108
+ const reading = scrollLayout?.snapshot(), open = existing.querySelector('[data-run-process]')?.open;
1109
+ const next = renderRunCard(run);
1110
+ const process = next.querySelector('[data-run-process]');
1111
+ if (process && typeof open === 'boolean') process.open = open;
1112
+ existing.replaceWith(next);
1113
+ if (reading) scrollLayout?.restore(reading);
1114
+ return true;
1115
+ }
1116
+
1117
+ function refreshLocale() {
1118
+ const log = getPanel()?.querySelector('[data-log]');
1119
+ if (!log) return;
1120
+ if (!getState().runs?.length) { renderRunHistory({ preserveScroll: true }); return; }
1121
+ const reading = scrollLayout?.snapshot() || { scrollTop: log.scrollTop };
1122
+ for (const run of getState().runs) {
1123
+ const existing = log.querySelector(`[data-run-id="${cssEscape(run.id)}"]`);
1124
+ if (!existing) continue;
1125
+ const current = getCurrentRunView();
1126
+ if (current?.root === existing) {
1127
+ // Streaming owners retain their DOM references and buffered model text.
1128
+ const status = existing.querySelector('[data-run-status]');
1129
+ if (status) status.textContent = getRunStatusText(run);
1130
+ configureAcceptButton(existing, run); configureUndoButton(existing, run);
1131
+ configureEditButton(existing, run); configureWritebackButton(existing, run);
1132
+ RunResultActions.configureResultActions(existing, run);
1133
+ const reportEvent = (run.events || []).filter(event => event.kind === 'report').at(-1);
1134
+ const report = current.report || existing.querySelector('[data-run-report]');
1135
+ if (reportEvent && report) report.replaceChildren(renderCompletionReport(reportEvent, displayLocalizedRun(run)));
1136
+ activitySummary?.update(current);
1137
+ if (!activitySummary) RunGuidanceView.reconcileGuidance?.(existing.querySelector('[data-run-guidance]'),
1138
+ (run.events || []).concat(getPendingGuidance(run)), tx, { active: run.status === 'running' });
1139
+ continue;
1140
+ }
1141
+ const open = existing.querySelector('[data-run-process]')?.open;
1142
+ const next = renderRunCard(run);
1143
+ const process = next.querySelector('[data-run-process]');
1144
+ if (process && typeof open === 'boolean') process.open = open;
1145
+ existing.replaceWith(next);
1146
+ }
1147
+ log.scrollTop = reading.scrollTop;
1148
+ scrollLayout?.restore(reading);
1149
+ updateJumpToLatestButton(log);
1132
1150
  }
1133
1151
 
1134
1152
  return {
1153
+ refreshLocale,
1154
+ refreshResolvedUndo: refreshRunCard,
1155
+ refreshActivitySummary: activitySummary?.update,
1156
+ appendActivityDetail,
1135
1157
  resetAutoFollow,
1136
1158
  bindLogAutoFollow,
1137
1159
  bumpUnreadIfDetached,
1138
1160
  scrollLogToBottom,
1161
+ revealGuidance,
1139
1162
  collapseRunProcess,
1140
1163
  startRunElapsedTick,
1141
1164
  stopRunElapsedTick,
@@ -1147,10 +1170,12 @@
1147
1170
  upsertStreamEvent,
1148
1171
  renderCompletionReport,
1149
1172
  isTrackedChangeLifecycleRun,
1173
+ configureEditButton,
1174
+ configureWritebackButton,
1150
1175
  configureUndoButton,
1151
1176
  configureAcceptButton
1152
1177
  };
1153
1178
  }
1154
1179
 
1155
- window.CodexOverleafRunTimelineView = { create };
1156
- })();
1180
+ return { create };
1181
+ });