codex-overleaf-link 2.5.0 → 2.6.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 (189) hide show
  1. package/README.md +155 -392
  2. package/README.zh-CN.md +154 -393
  3. package/extension/bootstrap/background.js +52 -9
  4. package/extension/bootstrap/manifest.template.json +1 -1
  5. package/extension/bootstrap/popup.html +1 -0
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/updateStatus.js +1 -0
  8. package/extension/src/background.js +283 -55
  9. package/extension/src/backgroundBootstrapMigration.js +181 -0
  10. package/extension/src/backgroundSharedSessions.js +129 -16
  11. package/extension/src/backgroundUpdateCoordinator.js +242 -29
  12. package/extension/src/backgroundUpdateSafety.js +173 -0
  13. package/extension/src/content/activeTurnControl.js +171 -8
  14. package/extension/src/content/applyResultFormatters.js +46 -73
  15. package/extension/src/content/assetTransferBroker.js +72 -14
  16. package/extension/src/content/composerAttachments.js +104 -47
  17. package/extension/src/content/composerPanel.js +110 -12
  18. package/extension/src/content/contentRuntime.js +1555 -926
  19. package/extension/src/content/contextTray.js +8 -2
  20. package/extension/src/content/contextUsageView.js +267 -0
  21. package/extension/src/content/diagnosticsController.js +122 -96
  22. package/extension/src/content/diagnosticsExport.js +19 -5
  23. package/extension/src/content/diagnosticsPanel.js +48 -0
  24. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  25. package/extension/src/content/generated/content.bundle.js +11077 -3412
  26. package/extension/src/content/generated/content.bundle.meta.json +14 -3
  27. package/extension/src/content/globalPreferencesController.js +1 -1
  28. package/extension/src/content/legacyGlobalRegistry.js +10 -0
  29. package/extension/src/content/localSkillsPanel.js +104 -14
  30. package/extension/src/content/markdownDomRenderer.js +5 -3
  31. package/extension/src/content/markdownText.js +47 -11
  32. package/extension/src/content/modelPicker.js +8 -4
  33. package/extension/src/content/nativeCompatibilityController.js +74 -30
  34. package/extension/src/content/otWarmMirror.js +5 -6
  35. package/extension/src/content/otWarmMirrorController.js +17 -20
  36. package/extension/src/content/pageBridgeClient.js +17 -4
  37. package/extension/src/content/panelMaintenance.js +46 -14
  38. package/extension/src/content/panelRenderer.js +57 -27
  39. package/extension/src/content/pendingInputView.js +1 -1
  40. package/extension/src/content/projectProviderSelection.js +13 -4
  41. package/extension/src/content/projectSessionCleanup.js +109 -62
  42. package/extension/src/content/projectSettingsCoordinator.js +67 -6
  43. package/extension/src/content/providerSettingsCoordinator.js +46 -8
  44. package/extension/src/content/providerSettingsDialog.js +73 -14
  45. package/extension/src/content/recentProjects.js +15 -8
  46. package/extension/src/content/runActivitySummary.js +160 -18
  47. package/extension/src/content/runCompletionMeta.js +532 -0
  48. package/extension/src/content/runController.js +39 -15
  49. package/extension/src/content/runFailureNotice.js +29 -13
  50. package/extension/src/content/runGuidanceController.js +72 -27
  51. package/extension/src/content/runGuidanceView.js +59 -10
  52. package/extension/src/content/runPresence.js +18 -8
  53. package/extension/src/content/runQueueScheduler.js +157 -47
  54. package/extension/src/content/runRecoveryActions.js +147 -0
  55. package/extension/src/content/runResultActions.js +28 -143
  56. package/extension/src/content/runReviewController.js +633 -0
  57. package/extension/src/content/runScrollLayout.js +21 -2
  58. package/extension/src/content/runTimelineView.js +367 -175
  59. package/extension/src/content/scopedPersistencePanelState.js +134 -20
  60. package/extension/src/content/selectionContextView.js +1 -1
  61. package/extension/src/content/sensitivePreflight.js +153 -0
  62. package/extension/src/content/sessionForkController.js +80 -17
  63. package/extension/src/content/sessionManager.js +50 -4
  64. package/extension/src/content/sessionMenuView.js +54 -18
  65. package/extension/src/content/sessionPersistence.js +25 -5
  66. package/extension/src/content/settingsPanel.js +22 -5
  67. package/extension/src/content/settingsWorkbench.js +132 -89
  68. package/extension/src/content/sharedSessionViewSync.js +49 -11
  69. package/extension/src/content/subagentActivityView.js +23 -7
  70. package/extension/src/content/updateIdle.js +4 -1
  71. package/extension/src/content/updateNotice.js +118 -12
  72. package/extension/src/content/updateSafetyReceiver.js +58 -0
  73. package/extension/src/content/writebackController.js +53 -4
  74. package/extension/src/content/writebackOrchestrator.js +116 -94
  75. package/extension/src/content/writingStyleSettings.js +35 -11
  76. package/extension/src/page/binaryAssetUploader.js +136 -14
  77. package/extension/src/page/compileBridge.js +193 -180
  78. package/extension/src/page/fileMover.js +256 -0
  79. package/extension/src/page/overleafCapabilities.js +4 -1
  80. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  81. package/extension/src/page/referenceProjects.js +1 -0
  82. package/extension/src/page/reviewContext.js +113 -0
  83. package/extension/src/page/saveState.js +6 -4
  84. package/extension/src/page/snapshotRouter.js +84 -39
  85. package/extension/src/page/textCoordinates.js +17 -1
  86. package/extension/src/page/textFileCreator.js +114 -26
  87. package/extension/src/page/trackedChangeCapture.js +12 -2
  88. package/extension/src/page/trackedChangeOwnership.js +19 -1
  89. package/extension/src/page/trackedChangeReplay.js +41 -21
  90. package/extension/src/page/trackedChangesLifecycle.js +217 -199
  91. package/extension/src/page/treeOperations.js +7 -10
  92. package/extension/src/page/updateSafetyProbe.js +41 -0
  93. package/extension/src/page/writebackReceiptJournal.js +2 -0
  94. package/extension/src/page/writebackRouter.js +103 -155
  95. package/extension/src/pageBridge.js +126 -69
  96. package/extension/src/popup.js +62 -10
  97. package/extension/src/shared/agentTranscript.js +373 -17
  98. package/extension/src/shared/auditRecords.js +4 -0
  99. package/extension/src/shared/compatibility.js +22 -6
  100. package/extension/src/shared/compileAdapter.js +129 -66
  101. package/extension/src/shared/composerAttachmentDraft.js +156 -0
  102. package/extension/src/shared/contextUsage.js +125 -0
  103. package/extension/src/shared/directoryCleanupProof.js +43 -0
  104. package/extension/src/shared/failureReasons.js +349 -4
  105. package/extension/src/shared/globalPreferences.js +30 -13
  106. package/extension/src/shared/governanceRules.js +4 -37
  107. package/extension/src/shared/i18n.js +590 -24
  108. package/extension/src/shared/lineReferences.js +38 -31
  109. package/extension/src/shared/managedUpdateProjection.js +2 -2
  110. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  111. package/extension/src/shared/pageRpcContract.js +5 -4
  112. package/extension/src/shared/panelIcons.js +39 -0
  113. package/extension/src/shared/projectFiles.js +21 -44
  114. package/extension/src/shared/providerProfiles.js +5 -3
  115. package/extension/src/shared/reviewing.js +130 -1
  116. package/extension/src/shared/runActivityModel.js +70 -4
  117. package/extension/src/shared/runExecutionSnapshot.js +13 -0
  118. package/extension/src/shared/runInputQueue.js +48 -5
  119. package/extension/src/shared/selectionContext.js +12 -3
  120. package/extension/src/shared/sessionState.js +132 -28
  121. package/extension/src/shared/sessionTitle.js +14 -1
  122. package/extension/src/shared/settlementFacts.js +10 -2
  123. package/extension/src/shared/sharedSessionBridge.js +118 -27
  124. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  125. package/extension/src/shared/staleGuard.js +166 -52
  126. package/extension/src/shared/storageDb.js +90 -26
  127. package/extension/src/shared/storageMigration.js +78 -112
  128. package/extension/src/shared/storageRunActions.js +267 -30
  129. package/extension/src/shared/subagentActivityModel.js +1 -1
  130. package/extension/src/shared/undoOperations.js +253 -32
  131. package/extension/src/shared/updateConsent.js +6 -4
  132. package/extension/src/shared/updateRuntimeIdentity.js +17 -3
  133. package/extension/src/shared/writebackIntent.js +16 -0
  134. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  135. package/extension/src/shared/writebackSettlement.js +58 -40
  136. package/extension/styles/panel.css +1123 -493
  137. package/native-host/src/anthropicBridgeRequest.js +4 -2
  138. package/native-host/src/anthropicBridgeResponse.js +6 -2
  139. package/native-host/src/anthropicMessagesBridge.js +24 -16
  140. package/native-host/src/bootstrapMigration.js +296 -0
  141. package/native-host/src/bootstrapMigrationContract.js +101 -0
  142. package/native-host/src/bootstrapUpdate.js +269 -0
  143. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  144. package/native-host/src/chatBridgeRequest.js +60 -6
  145. package/native-host/src/chatBridgeResponse.js +20 -1
  146. package/native-host/src/chatCompletionsBridge.js +48 -15
  147. package/native-host/src/codexConfigPaths.js +106 -0
  148. package/native-host/src/codexHome.js +66 -73
  149. package/native-host/src/codexHomeScope.js +124 -0
  150. package/native-host/src/codexInheritedAuth.js +90 -0
  151. package/native-host/src/codexLocalSkills.js +301 -0
  152. package/native-host/src/codexPromptAssembly.js +114 -127
  153. package/native-host/src/codexProviderTest.js +24 -26
  154. package/native-host/src/codexSessionRunner.js +234 -198
  155. package/native-host/src/codexSessionTiming.js +18 -8
  156. package/native-host/src/codexSkillIsolation.js +170 -0
  157. package/native-host/src/codexThreadFork.js +19 -3
  158. package/native-host/src/index.js +19 -7
  159. package/native-host/src/managedInstall.js +110 -35
  160. package/native-host/src/managedInstallRecovery.js +140 -0
  161. package/native-host/src/managedRuntimeEvidence.js +57 -0
  162. package/native-host/src/mirrorWorkspace.js +92 -22
  163. package/native-host/src/nativeDoctor.js +15 -0
  164. package/native-host/src/nativeHelperLifecycle.js +21 -0
  165. package/native-host/src/nativeHostRegistration.js +33 -0
  166. package/native-host/src/nativeRunLifecycle.js +71 -0
  167. package/native-host/src/processStopBarrier.js +42 -7
  168. package/native-host/src/projectPathPlatform.js +30 -0
  169. package/native-host/src/providerContextError.js +20 -0
  170. package/native-host/src/providerHttp.js +80 -0
  171. package/native-host/src/providerProfile.js +4 -2
  172. package/native-host/src/providerReasoning.js +2 -7
  173. package/native-host/src/providerRuntime.js +1 -0
  174. package/native-host/src/providerStore.js +72 -14
  175. package/native-host/src/responsesPassthroughBridge.js +266 -52
  176. package/native-host/src/runtimeInstaller.js +21 -1
  177. package/native-host/src/selectionScope.js +36 -8
  178. package/native-host/src/subagentAdmission.js +26 -0
  179. package/native-host/src/subagentBroker.js +79 -66
  180. package/native-host/src/subagentTelemetry.js +19 -10
  181. package/native-host/src/subagentWorkspacePath.js +5 -2
  182. package/native-host/src/taskRunnerRuntime.js +92 -57
  183. package/native-host/src/textPatch.js +473 -377
  184. package/native-host/src/tomlStatements.js +140 -0
  185. package/native-host/src/updateManager.js +409 -46
  186. package/native-host/src/writingStyleRuntime.js +57 -29
  187. package/native-host/src/writingStyleStore.js +73 -0
  188. package/package.json +5 -8
  189. package/scripts/install-managed.mjs +1 -0
@@ -2,16 +2,19 @@
2
2
  if (typeof module === 'object' && module.exports) {
3
3
  module.exports = factory({
4
4
  getFailureReasons: function getFailureReasonsCjs() { return require('./failureReasons.js'); },
5
- getI18n: function getI18nCjs() { return require('./i18n.js'); }
5
+ getI18n: function getI18nCjs() { return require('./i18n.js'); },
6
+ RunActivityModel: require('./runActivityModel.js')
6
7
  });
7
8
  } else {
8
9
  root.CodexOverleafModuleRegistry.define('AgentTranscript', [
9
10
  'FailureReasons',
10
- 'I18n'
11
- ], function createAgentTranscript(FailureReasons, I18n) {
11
+ 'I18n',
12
+ 'RunActivityModel'
13
+ ], function createAgentTranscript(FailureReasons, I18n, RunActivityModel) {
12
14
  return factory({
13
15
  getFailureReasons: function getFailureReasonsWindow() { return FailureReasons; },
14
- getI18n: function getI18nWindow() { return I18n; }
16
+ getI18n: function getI18nWindow() { return I18n; },
17
+ RunActivityModel
15
18
  });
16
19
  });
17
20
  }
@@ -25,6 +28,221 @@
25
28
  ? deps.getI18n
26
29
  : function noI18n() { return null; };
27
30
 
31
+ // Only explicit product producers may attach these semantic identities.
32
+ const PRODUCT_CONCLUSIONS = {
33
+ "context_compacted": ["Conversation context compacted.", "会话上下文已压缩。"],
34
+ "compact_busy": ["Wait for the current task to finish before compacting context.", "请等待当前任务结束后再压缩上下文。"],
35
+ "compact_unsupported": ["This local Codex connection does not support manual compaction. Update Codex and reload the extension.", "当前本地 Codex 连接不支持手动压缩,请更新 Codex 并重新加载扩展。"],
36
+ "compact_failed": ["Context compaction did not complete. Check Technical Details before retrying.", "上下文压缩未完成,请查看技术详情后再重试。"],
37
+ "full_project_unread": [
38
+ "This run did not continue: the full Overleaf project was not read.",
39
+ "这轮没有继续:没有读到完整的 Overleaf 项目内容。"
40
+ ],
41
+ "empty_result": [
42
+ "Codex completed, but it produced no assistant response and no local file changes.",
43
+ "Codex 已完成,但没有产生助手回复,也没有产生本地文件改动。"
44
+ ],
45
+ "skill_installer_completed": [
46
+ "Skill installer completed.",
47
+ "Skill installer 已完成。"
48
+ ],
49
+ "launch_not_started": [
50
+ "This task did not start: Codex did not run and no files were written.",
51
+ "这轮任务没有开始:Codex 没有运行,也没有写入文件。"
52
+ ],
53
+ "cancelled": [
54
+ "This run was cancelled. It did not continue syncing or writing to Overleaf.",
55
+ "这轮已中断,没有继续同步或写入 Overleaf。"
56
+ ],
57
+ "reload_interrupted": [
58
+ "The plugin reloaded while this run was still marked in progress. It has been marked interrupted to avoid showing a stale status — run the task again to continue.",
59
+ "插件重新加载时发现这轮任务还标记为处理中。为了避免继续显示过期状态,已把它标记为中断;可以重新运行任务。"
60
+ ],
61
+ "ask_inspected": [
62
+ "This run only inspected and explained; no files were written.",
63
+ "这轮只做了检查和说明,没有写入文件。"
64
+ ],
65
+ "delete_handled": [
66
+ "Changes were written, and delete items were handled after your confirmation.",
67
+ "已写入修改,并按你的确认处理删除项。"
68
+ ],
69
+ "delete_rejected": [
70
+ "Non-delete changes were kept; delete items were not written.",
71
+ "已保留非删除修改;删除项没有写入。"
72
+ ],
73
+ "run_complete": [
74
+ "This run is complete.",
75
+ "这轮任务已完成。"
76
+ ],
77
+ "withheld_changes": [
78
+ "Some generated changes were blocked.",
79
+ "部分生成的改动已被阻止。"
80
+ ],
81
+ "ask_ignored_changes": [
82
+ "Codex finished in Ask mode. Any local file changes were ignored and were not synced to Overleaf.",
83
+ "Codex 已在 Ask 模式完成;本地文件改动已忽略,未同步到 Overleaf。"
84
+ ],
85
+ "no_sync_changes": [
86
+ "Codex finished locally. There are no changes to sync back to Overleaf.",
87
+ "Codex 已完成本地处理,没有需要同步回 Overleaf 的改动。"
88
+ ],
89
+ "no_approved_changes": [
90
+ "No approved changes were written back to Overleaf.",
91
+ "没有已确认的改动写回 Overleaf。"
92
+ ],
93
+ "reviewing_unavailable": [
94
+ "No files were written: Track is enabled, but Codex could not verify Overleaf Reviewing/Track Changes.",
95
+ "这轮没有写入:已开启“留痕”要求,但 Codex 没能确认 Overleaf 正在用 Reviewing/Track Changes。"
96
+ ],
97
+ "sensitive_blocked": [
98
+ "This run was blocked before Codex received project context because sensitive content was detected.",
99
+ "检测到敏感内容,本轮已在向 Codex 发送项目上下文前阻止。"
100
+ ],
101
+ "synced": [
102
+ "Local Codex changes were synced back to Overleaf.",
103
+ "本地 Codex 改动已同步回 Overleaf。"
104
+ ],
105
+ "partial_sync": [
106
+ "Local Codex changes were sent to Overleaf, but some items were skipped.",
107
+ "本地 Codex 改动已尝试同步回 Overleaf,但有部分项目被跳过。"
108
+ ],
109
+ "blocked_sync": [
110
+ "No changes were written to Overleaf; every change was blocked before writing.",
111
+ "没有向 Overleaf 写入任何改动;所有改动都在写入前被拦下。"
112
+ ],
113
+ "unconfirmed_sync": [
114
+ "Codex tried to write local changes to Overleaf, but the write result did not confirm any applied or skipped entries.",
115
+ "Codex 已尝试把本地改动写入 Overleaf,但写入结果没有确认任何已写入或已跳过条目。"
116
+ ],
117
+ "partial_sync_note": [
118
+ "Sync note: local Codex changes were sent to Overleaf, but some items were skipped.",
119
+ "同步提示:本地 Codex 改动已尝试写回 Overleaf,但有部分项目被跳过。"
120
+ ],
121
+ "blocked_sync_note": [
122
+ "Sync note: no changes were written to Overleaf; every change was blocked before writing.",
123
+ "同步提示:没有向 Overleaf 写入任何改动;所有改动都在写入前被拦下。"
124
+ ],
125
+ "unconfirmed_sync_note": [
126
+ "Writeback note: Codex could not confirm any Overleaf write result entries, so local mirror refresh and auto compile were skipped.",
127
+ "写入提示:Codex 没能确认任何 Overleaf 写入结果条目,因此已跳过本地 mirror 刷新和自动编译。"
128
+ ],
129
+ "retry_sync": [
130
+ "Retried the remaining file transfers for this task.",
131
+ "已重试本轮未完成的文件同步。"
132
+ ],
133
+ "default_no_write": [
134
+ "This task completed without writing Overleaf files.",
135
+ "这轮任务已完成,没有写入 Overleaf 文件。"
136
+ ],
137
+ "default_complete": [
138
+ "This task completed.",
139
+ "这轮任务已完成。"
140
+ ],
141
+ "warm_mirror_stale": [
142
+ "This run did not continue: Overleaf did not provide the full project, and the local workspace is not fresh enough.",
143
+ "这轮没有继续:Overleaf 没有提供完整项目内容,本地 workspace 也不够新。"
144
+ ]
145
+ };
146
+
147
+ const ERROR_CONCLUSIONS = {
148
+ "local_skill_unavailable": {
149
+ "code": "codex_local_skill_unavailable"
150
+ },
151
+ "local_skills_failed": {
152
+ "code": "codex_local_skills_failed"
153
+ },
154
+ "mode_removed": {
155
+ "message": "suggest_mode_removed"
156
+ },
157
+ "mode_write_required": {
158
+ "message": "Mode must be \"ask\" or \"auto\""
159
+ },
160
+ "invalid_json": {
161
+ "message": "Agent returned invalid JSON"
162
+ },
163
+ "invalid_output": {
164
+ "message": "Could not parse Codex output"
165
+ },
166
+ "timeout": {
167
+ "message": "timed out"
168
+ },
169
+ "output_limit": {
170
+ "message": "output limit exceeded"
171
+ },
172
+ "context_limit": {
173
+ "code": "codex_context_limit"
174
+ },
175
+ "codex_not_found": {
176
+ "message": "codex_not_found"
177
+ },
178
+ "project_locked": {
179
+ "message": "project_locked"
180
+ },
181
+ "reasoning_unsupported": {
182
+ "message": "unsupported_parameter reasoning.summary"
183
+ },
184
+ "storage_quota": {
185
+ "message": "quota"
186
+ },
187
+ "checkpoint": {
188
+ "message": "checkpoint"
189
+ },
190
+ "stale_source": {
191
+ "message": "changed while Codex was working"
192
+ },
193
+ "post_processing": {
194
+ "codexReturned": true
195
+ },
196
+ "ask_failed": {
197
+ "mode": "ask"
198
+ },
199
+ "auto_failed": {
200
+ "mode": "auto"
201
+ },
202
+ "provider_revision_conflict": {
203
+ "message": "provider_revision_conflict"
204
+ },
205
+ "provider_auth_rejected": {
206
+ "message": "provider_auth_rejected"
207
+ },
208
+ "provider_model_not_found": {
209
+ "message": "provider_model_not_found"
210
+ },
211
+ "provider_model_not_configured": {
212
+ "message": "provider_model_not_configured"
213
+ },
214
+ "provider_protocol_unverified": {
215
+ "message": "provider_protocol_unverified"
216
+ },
217
+ "provider_protocol_incompatible": {
218
+ "message": "provider_protocol_incompatible"
219
+ },
220
+ "provider_rate_limited": {
221
+ "message": "provider_rate_limited"
222
+ },
223
+ "provider_connection_timeout": {
224
+ "message": "provider_connection_timeout"
225
+ },
226
+ "provider_dns_failed": {
227
+ "message": "provider_dns_failed"
228
+ },
229
+ "provider_tls_failed": {
230
+ "message": "provider_tls_failed"
231
+ },
232
+ "provider_not_found": {
233
+ "message": "provider_not_found"
234
+ },
235
+ "provider_store_corrupt": {
236
+ "message": "provider_store_corrupt"
237
+ },
238
+ "provider_store_unavailable": {
239
+ "message": "provider_store_unavailable"
240
+ },
241
+ "provider_response_invalid": {
242
+ "message": "provider_response_invalid"
243
+ }
244
+ };
245
+
28
246
  const TECHNICAL_EVENT_PATTERNS = [
29
247
  /^agent\.command\./,
30
248
  /^native\.task\./,
@@ -63,6 +281,14 @@
63
281
  const locale = normalizeLocale(options);
64
282
  const type = String(event.type || '');
65
283
 
284
+ if (type === 'codex.local_skills.missing' || type === 'codex.local_skill_invocation.ignored') {
285
+ return { kind: 'activity', visible: true,
286
+ title: type === 'codex.local_skills.missing'
287
+ ? textFor(locale, '所选的本地技能未能加载。', 'A selected local skill could not be loaded.')
288
+ : textFor(locale, '所选的本地技能已关闭或不可用。', 'A selected local skill is disabled or unavailable.'),
289
+ status: event.status || 'failed', technicalDetail: normalizeRawEvent(event) };
290
+ }
291
+
66
292
  if (isContextCompactionEvent(event)) {
67
293
  return formatContextCompactionCheckpoint(event, locale);
68
294
  }
@@ -428,16 +654,11 @@
428
654
  }
429
655
 
430
656
  function isContextCompactionEvent(event = {}) {
431
- const type = String(event.type || '');
432
- const method = String(event.detail?.method || event.title || '');
433
- const label = `${type} ${method}`;
434
- return /(compact|compaction|compacted)/i.test(label)
435
- && /(context|thread|turn|conversation|codex)/i.test(label);
657
+ return Boolean(deps.RunActivityModel.contextCompactionState(event));
436
658
  }
437
659
 
438
660
  function formatContextCompactionCheckpoint(event = {}, locale = 'en') {
439
- const method = String(event.detail?.method || event.title || '');
440
- const running = event.status === 'running' || /(started|starting|begin|prepar)/i.test(method);
661
+ const running = deps.RunActivityModel.contextCompactionState(event) === 'running';
441
662
  return {
442
663
  kind: 'checkpoint',
443
664
  visible: true,
@@ -720,7 +941,7 @@
720
941
  };
721
942
  }
722
943
 
723
- function translateRawError(message, context = {}) {
944
+ function translateRawErrorCopy(message, context = {}) {
724
945
  const locale = normalizeLocale(context);
725
946
  const text = String(message || '');
726
947
  // Each branch returns the user-visible {conclusion, nextStep} pair and,
@@ -730,6 +951,28 @@
730
951
  // failure data alongside the human-readable text. The string text is kept
731
952
  // verbatim because it has mode-aware nuance (e.g. "No files were written"
732
953
  // vs "This run did not start") the catalog's fallback strings don't capture.
954
+ if (context.code === 'codex_local_skill_unavailable' || /(?:The )?selected local Codex skill is unavailable/i.test(text)) {
955
+ return {
956
+ conclusion: textFor(locale, '所选 Codex 本地技能不可用或已关闭,本轮没有启动。', 'The selected local Codex skill is unavailable or disabled. This run did not start.'),
957
+ nextStep: textFor(locale, '请打开技能页,选择可用且已启用的技能后重新提交。', 'Open Skills, select an available enabled skill, and submit again.'),
958
+ failureCode: 'codex_local_skill_unavailable'
959
+ };
960
+ }
961
+ if (/^codex_local_skills_/.test(String(context.code || ''))
962
+ || /local Codex skill discovery|Codex could not (?:list local skills|start local skill discovery)|Codex returned an invalid local skill catalog/i.test(text)) {
963
+ return {
964
+ conclusion: textFor(locale, 'Codex 本地技能未能加载,本轮没有启动。', 'Local Codex skills could not be loaded. This run did not start.'),
965
+ nextStep: textFor(locale, '请打开技能页重新加载本地目录,然后重新提交。', 'Open Skills to reload the local catalog, then submit again.'),
966
+ failureCode: 'codex_local_skills_failed'
967
+ };
968
+ }
969
+ if (getFailureReasonsModule()?.isContextLimitError(text, context.code)) {
970
+ return {
971
+ conclusion: textFor(locale, '本轮已停止:上下文超出模型的容量限制。', 'This run stopped because the model context limit was exceeded.'),
972
+ nextStep: textFor(locale, '请重试任务;如果再次超限,可使用 /compact,或新建会话并减少上下文。', 'Retry the task. If the context limit is reached again, use /compact or start a new conversation with less context.'),
973
+ failureCode: 'codex_context_limit'
974
+ };
975
+ }
733
976
  const providerError = translateProviderError(text, locale);
734
977
  if (providerError) {
735
978
  return providerError;
@@ -848,12 +1091,107 @@
848
1091
  };
849
1092
  }
850
1093
 
1094
+ function productConclusionText(key, locale = 'en') {
1095
+ if (Object.hasOwn(PRODUCT_CONCLUSIONS, key)) return PRODUCT_CONCLUSIONS[key][locale === 'zh' ? 1 : 0];
1096
+ const error = key.startsWith('error:') && Object.hasOwn(ERROR_CONCLUSIONS, key.slice(6)) && ERROR_CONCLUSIONS[key.slice(6)];
1097
+ return error ? translateRawErrorCopy(error.message || '', { ...error, locale }).conclusion : null;
1098
+ }
1099
+
1100
+ function translateRawError(message, context = {}) {
1101
+ const translated = translateRawErrorCopy(message, context), locale = normalizeLocale(context);
1102
+ const key = Object.keys(ERROR_CONCLUSIONS).find(id => productConclusionText(`error:${id}`, locale) === translated.conclusion);
1103
+ return { ...translated, ...(key ? { conclusionKey: `error:${key}` } : {}) };
1104
+ }
1105
+
1106
+ // Old Native failures retained the real transport error even when their
1107
+ // product summary used the generic empty-result category. Re-project only
1108
+ // those identified product events; model prose and stored records stay intact.
1109
+ function projectContextLimitEvent(event, locale = 'en') {
1110
+ const failure = event?.failure;
1111
+ if (event?.status !== 'failed' || event.streamRole || event.guidanceId || event.subagent) return event;
1112
+ if (failure?.code === 'codex_context_limit' && failure.stage === 'codex'
1113
+ && (!event.kind || event.kind === 'activity')
1114
+ && ['en', 'zh'].some(language => productConclusionText('error:context_limit', language) === event.title)) {
1115
+ return { ...event, title: productConclusionText('error:context_limit', locale) };
1116
+ }
1117
+ if (failure?.code !== 'codex_no_usable_result' || failure.stage !== 'codex'
1118
+ || !getFailureReasonsModule()?.isContextLimitError(failure.technicalMessage)) return event;
1119
+ const generic = ['ask_failed', 'auto_failed'].flatMap(key => ['en', 'zh'].map(language =>
1120
+ productConclusionText(`error:${key}`, language)));
1121
+ const translated = translateRawError(failure.technicalMessage, { locale });
1122
+ const projectedFailure = { ...failure, code: translated.failureCode,
1123
+ userMessage: translated.conclusion, nextAction: translated.nextStep };
1124
+ if ((!event.kind || event.kind === 'activity') && generic.includes(event.title)) {
1125
+ return { ...event, title: translated.conclusion, failure: projectedFailure };
1126
+ }
1127
+ const structured = event.detailStructured, source = structured?.conclusionSource;
1128
+ if (event.kind !== 'report' || source?.origin !== 'product'
1129
+ || !['error:ask_failed', 'error:auto_failed'].includes(source.key)
1130
+ || !generic.includes(structured.conclusion)) return event;
1131
+ const oldNext = ['en', 'zh'].map(language => translateRawError('', { locale: language }).nextStep);
1132
+ return { ...event, failure: projectedFailure, detailStructured: { ...structured,
1133
+ conclusion: translated.conclusion, conclusionSource: { origin: 'product', key: translated.conclusionKey },
1134
+ meta: (structured.meta || []).map(row => row.key === 'nextStep' && oldNext.includes(row.value)
1135
+ ? { ...row, value: translated.nextStep } : row) } };
1136
+ }
1137
+
1138
+ function normalizeConclusionSource(source) {
1139
+ if (source?.origin !== 'product' || typeof source.key !== 'string') return null;
1140
+ if (productConclusionText(source.key) !== null) return { origin: 'product', key: source.key };
1141
+ if (source.key !== 'writeback' || !['synced', 'partial_sync', 'blocked_sync', 'unconfirmed_sync'].includes(source.outcome)) return null;
1142
+ return { origin: 'product', key: 'writeback', outcome: source.outcome,
1143
+ prefixLength: Number.isSafeInteger(source.prefixLength) && source.prefixLength >= 0 ? source.prefixLength : -1,
1144
+ prefixKey: source.prefixKey === 'retry_sync' ? 'retry_sync' : '',
1145
+ hasCompile: source.hasCompile === true,
1146
+ compileStatus: typeof source.compileStatus === 'string' ? source.compileStatus : '',
1147
+ compileReason: typeof source.compileReason === 'string' ? source.compileReason : '',
1148
+ errorCount: Number.isSafeInteger(source.errorCount) && source.errorCount >= 0 ? source.errorCount : 0,
1149
+ errors: Array.isArray(source.errors) ? source.errors.slice(0, 3).filter(value => typeof value === 'string') : [],
1150
+ warningCount: Number.isSafeInteger(source.warningCount) && source.warningCount >= 0 ? source.warningCount : 0 };
1151
+ }
1152
+
1153
+ function projectProductConclusion(source, locale = 'en', localizeCompileReason = value => value, raw = '') {
1154
+ const identity = normalizeConclusionSource(source);
1155
+ if (!identity) return null;
1156
+ if (identity.key !== 'writeback') return productConclusionText(identity.key, locale);
1157
+ if (identity.prefixLength < 0 || identity.prefixLength > raw.length) return null;
1158
+ const originalPrefix = raw.slice(0, identity.prefixLength);
1159
+ if (identity.prefixKey && !['en', 'zh'].some(language => productConclusionText(identity.prefixKey, language) === originalPrefix)) return null;
1160
+ const compose = (language, projectReason) => {
1161
+ const tx = (en, zh) => language === 'zh' ? zh : en;
1162
+ const prefix = projectReason && identity.prefixKey ? productConclusionText(identity.prefixKey, language) : originalPrefix;
1163
+ const note = productConclusionText(identity.outcome + (prefix && identity.outcome !== 'synced' ? '_note' : ''), language);
1164
+ const conclusion = prefix ? [prefix, identity.outcome === 'synced' ? '' : note].filter(Boolean).join('\n\n') : note;
1165
+ const { compileStatus: status, errors, errorCount, warningCount: warnings } = identity;
1166
+ if (!identity.hasCompile) return conclusion;
1167
+ const reason = projectReason ? localizeCompileReason(identity.compileReason) : identity.compileReason;
1168
+ const summary = errorCount
1169
+ ? tx(`Post-write compile check: ${errorCount} remaining error(s): ${errors.slice(0, 3).join('; ')}`,
1170
+ `写后编译检查:仍有 ${errorCount} 个错误:${errors.slice(0, 3).join(';')}`)
1171
+ : status === 'success' ? (warnings
1172
+ ? tx(`Post-write compile check succeeded with ${warnings} warning(s).`, `写后编译检查已通过,但仍有 ${warnings} 个警告。`)
1173
+ : tx('Post-write compile check succeeded with no reported errors.', '写后编译检查已通过,未发现错误。'))
1174
+ : status === 'triggered' ? (warnings
1175
+ ? tx(`Post-write compile was triggered; latest log shows ${warnings} warning(s).`, `已触发写后编译;最新日志显示 ${warnings} 个警告。`)
1176
+ : tx('Post-write compile was triggered; Overleaf may still be updating the result.', '已触发写后编译;Overleaf 可能仍在更新结果。'))
1177
+ : status === 'unconfirmed' ? getI18nModule().t(language, 'runCompileUnconfirmed')
1178
+ : status === 'failed' ? tx(`Post-write compile check failed: ${reason || 'unknown reason'}`,
1179
+ `写后编译检查失败:${reason || '未知原因'}`)
1180
+ : tx(`Post-write compile finished with status: ${status || 'unknown'}.`, `写后编译完成,状态:${status || '未知'}。`);
1181
+ return [conclusion, summary].filter(Boolean).join('\n');
1182
+ };
1183
+ // A compacted or changed raw answer must never be reconstructed from metadata.
1184
+ if (!['en', 'zh'].some(language => compose(language, false) === raw)) return null;
1185
+ return compose(locale, true);
1186
+ }
1187
+
851
1188
  function buildHumanCompletionReport(input = {}) {
852
1189
  const locale = normalizeLocale(input);
853
1190
  const applyResults = normalizeApplyResults(input.applyResults);
854
1191
  const appliedCount = countApplyResultEntries(applyResults, 'applied');
855
1192
  const skippedCount = countApplyResultEntries(applyResults, 'skipped');
856
- const translatedError = input.errorMessage ? translateRawError(input.errorMessage, { mode: input.mode, locale }) : null;
1193
+ const translatedError = input.errorMessage ? translateRawError(input.errorMessage, { mode: input.mode, locale, code: input.failure?.code,
1194
+ codexReturned: input.conclusionKey === 'error:post_processing' }) : null;
857
1195
  const userReport = normalizeUserReport(input.userReport);
858
1196
  const report = userReport || buildFallbackReport(input, {
859
1197
  appliedCount,
@@ -905,6 +1243,16 @@
905
1243
  return report.text;
906
1244
  }
907
1245
 
1246
+ function completionConclusionText(report) {
1247
+ const source = report.conclusionSource;
1248
+ // The new blocked composite preserves its exact model prefix. Keep the
1249
+ // historical formatter for every other report shape.
1250
+ if (source?.key === 'writeback' && source.outcome === 'blocked_sync'
1251
+ && typeof report.conclusion === 'string' && ['en', 'zh'].some(locale =>
1252
+ projectProductConclusion(source, locale, undefined, report.conclusion) === report.conclusion)) return report.conclusion;
1253
+ return cleanVisibleMarkdownText(report.conclusion || '');
1254
+ }
1255
+
908
1256
  /**
909
1257
  * Splits the human report into a conclusion (the human-language outcome),
910
1258
  * a body of list-style content sections (checked / findings / planned /
@@ -915,7 +1263,7 @@
915
1263
  * shape for transcripts, storage fallback, and existing assertions.
916
1264
  */
917
1265
  function buildStructuredHumanReport(report = {}, locale = 'en') {
918
- const conclusion = cleanVisibleMarkdownText(report.conclusion || '');
1266
+ const conclusion = completionConclusionText(report);
919
1267
 
920
1268
  const bodySections = [];
921
1269
  addListSection(bodySections, textFor(locale, '检查范围', 'Checked'), report.checked, locale);
@@ -959,12 +1307,13 @@
959
1307
  }
960
1308
 
961
1309
  if (report.saveState) meta.push({ key: 'saveState', label: textFor(locale, '保存', 'Save'), value: report.saveState });
962
- return { conclusion, body: bodySections.join('\n\n'), meta };
1310
+ return { conclusion, body: bodySections.join('\n\n'), meta,
1311
+ ...(normalizeConclusionSource(report.conclusionSource) ? { conclusionSource: normalizeConclusionSource(report.conclusionSource) } : {}) };
963
1312
  }
964
1313
 
965
1314
  function formatHumanReport(report = {}, locale = 'en') {
966
1315
  const sections = [];
967
- const conclusion = cleanVisibleMarkdownText(report.conclusion || '');
1316
+ const conclusion = completionConclusionText(report);
968
1317
  if (conclusion) {
969
1318
  sections.push(textFor(locale, `结论:${conclusion}`, `Conclusion: ${conclusion}`));
970
1319
  }
@@ -1005,8 +1354,13 @@
1005
1354
  ? textFor(locale, '这轮任务已完成,没有写入 Overleaf 文件。', 'This task completed without writing Overleaf files.')
1006
1355
  : textFor(locale, '这轮任务已完成。', 'This task completed.'));
1007
1356
 
1357
+ const conclusionSource = counts.translatedError?.conclusionKey
1358
+ ? { origin: 'product', key: counts.translatedError.conclusionKey }
1359
+ : input.conclusionKey ? { origin: 'product', key: input.conclusionKey }
1360
+ : input.conclusionSource || (!input.conclusion && !input.notes
1361
+ ? { origin: 'product', key: noWrites ? 'default_no_write' : 'default_complete' } : null);
1008
1362
  return {
1009
- conclusion,
1363
+ conclusion, conclusionSource: normalizeConclusionSource(conclusionSource),
1010
1364
  checked: normalizeStringList(input.checked || input.userReport?.checked),
1011
1365
  findings: normalizeStringList(input.findings),
1012
1366
  plannedChanges: counts.appliedCount ? [] : operations.map(operation => formatOperationLine(operation, locale)),
@@ -1733,6 +2087,8 @@
1733
2087
 
1734
2088
  return {
1735
2089
  buildHumanCompletionReport,
2090
+ projectProductConclusion,
2091
+ projectContextLimitEvent,
1736
2092
  buildStructuredHumanReport,
1737
2093
  formatFallbackFinalReport,
1738
2094
  formatFailureBlock,
@@ -25,12 +25,15 @@
25
25
  const now = new Date().toISOString();
26
26
  return {
27
27
  id: input.id || generateId('aud'),
28
+ accountScopeId: redactSecretLikeText(stringField(input.accountScopeId)),
28
29
  projectId: redactSecretLikeText(stringField(input.projectId)),
29
30
  sessionId: redactSecretLikeText(stringField(input.sessionId)),
30
31
  turnId: redactSecretLikeText(stringField(input.turnId)),
31
32
  createdAt: redactSecretLikeText(stringField(input.createdAt)) || now,
32
33
  completedAt: '',
33
34
  mode: redactSecretLikeText(stringField(input.mode)),
35
+ requireReviewing: typeof input.requireReviewing === 'boolean' ? input.requireReviewing : undefined,
36
+ trackingDecision: ['continue', 'disable'].includes(input.trackingDecision) ? input.trackingDecision : '',
34
37
  providerId: redactSecretLikeText(stringField(input.providerId)) || 'builtin',
35
38
  providerName: redactSecretLikeText(stringField(input.providerName)),
36
39
  providerRevision: finiteNumber(input.providerRevision) || 0,
@@ -84,6 +87,7 @@
84
87
  function normalizeAuditLogRecord(input = {}) {
85
88
  return {
86
89
  id: redactSecretLikeText(stringField(input.id)) || generateId('aud'),
90
+ accountScopeId: redactSecretLikeText(stringField(input.accountScopeId)),
87
91
  projectId: redactSecretLikeText(stringField(input.projectId)),
88
92
  sessionId: redactSecretLikeText(stringField(input.sessionId)),
89
93
  turnId: redactSecretLikeText(stringField(input.turnId)),
@@ -12,7 +12,7 @@
12
12
  const MIN_NATIVE_VERSION = '1.0.0';
13
13
  const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
14
14
  const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
15
- const BUILD_TARGET_VERSION = '2.5.0';
15
+ const BUILD_TARGET_VERSION = '2.6.0';
16
16
  const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
17
17
  const REQUIRED_CAPABILITIES = Object.freeze([
18
18
  'bridgePing',
@@ -25,6 +25,7 @@
25
25
  'codexModels',
26
26
  'historyClearPlugin',
27
27
  'localSkills',
28
+ 'localCodexSkills',
28
29
  'mirrorSensitiveScan',
29
30
  'providerProfiles',
30
31
  'assetTransfer',
@@ -64,6 +65,8 @@
64
65
  }
65
66
 
66
67
  function evaluateNativeCompatibility(response, metadata = {}) {
68
+ if (response?.extensionInstallIdentity) metadata = { ...metadata,
69
+ installIdentity: { ...(metadata.installIdentity || {}), ...response.extensionInstallIdentity } };
67
70
  const native = unwrapNativeResponse(response);
68
71
  if (!native) {
69
72
  return compatibilityResult('native_missing', response, metadata, 'incompatible');
@@ -86,13 +89,25 @@
86
89
  return analyzeNativeCompatibility(native, metadata).classification;
87
90
  }
88
91
 
89
- function buildInstallCommand(version = BUILD_TARGET_VERSION, platform, extensionId) {
92
+ function buildInstallCommand(version = BUILD_TARGET_VERSION, platform, extensionId, options = {}) {
90
93
  const normalized = normalizeReleaseVersion(version) || BUILD_TARGET_VERSION;
91
94
  const allowedExtensionId = normalizeInstallExtensionId(extensionId);
92
95
  const extensionIdArg = allowedExtensionId && allowedExtensionId !== DEFAULT_CHROME_EXTENSION_ID
93
- ? ` --extension-id ${allowedExtensionId}`
94
- : '';
95
-
96
+ ? ` --extension-id ${allowedExtensionId}` : '';
97
+ if (options.managed === true) {
98
+ if (!allowedExtensionId || options.extensionId !== allowedExtensionId) return '';
99
+ const managedRoot = options.managedRoot;
100
+ const browser = options.browser === 'auto' ? 'chrome' : options.browser;
101
+ if (!['chrome', 'chromium'].includes(browser) || typeof managedRoot !== 'string'
102
+ || !managedRoot || managedRoot.length > 4096 || managedRoot.trim() !== managedRoot
103
+ || /[\u0000-\u001f\u007f]/.test(managedRoot)
104
+ || !(/^(?:\/|[a-zA-Z]:[\\/]|\\\\)/.test(managedRoot))) return '';
105
+ // Windows setup commands are shown for PowerShell; POSIX uses sh quoting.
106
+ const quotedRoot = platform === 'win32' || options.platform === 'win32'
107
+ ? "'" + managedRoot.replace(/'/g, "''") + "'"
108
+ : "'" + managedRoot.replace(/'/g, "'\\''") + "'";
109
+ return `npm exec --yes codex-overleaf-link@${normalized} -- install-managed --browser ${browser} --managed-root ${quotedRoot}${extensionIdArg}`;
110
+ }
96
111
  return `npm exec --yes codex-overleaf-link@${normalized} -- install-native${extensionIdArg}`;
97
112
  }
98
113
 
@@ -126,7 +141,7 @@
126
141
  const extensionVersion = resolveMetadataVersion(metadata);
127
142
  const nativeVersion = native && typeof native === 'object' ? native.version : undefined;
128
143
  const platform = native && typeof native === 'object' ? native.platform : undefined;
129
- const updateCommand = buildInstallCommand(BUILD_TARGET_VERSION, platform, metadata.extensionId);
144
+ const updateCommand = buildInstallCommand(BUILD_TARGET_VERSION, platform, metadata.extensionId, metadata.installIdentity);
130
145
  const updateAvailable = isOlderReleaseVersion(nativeVersion, BUILD_TARGET_VERSION);
131
146
  return {
132
147
  status,
@@ -139,6 +154,7 @@
139
154
  requiredVersion: MIN_COMPATIBLE_NATIVE_VERSION,
140
155
  recommendedVersion: BUILD_TARGET_VERSION,
141
156
  updateAvailable,
157
+ ...(metadata.installIdentity ? { installIdentity: metadata.installIdentity } : {}),
142
158
  installCommand: updateCommand,
143
159
  updateCommand,
144
160
  releaseUrl: buildReleaseUrl(BUILD_TARGET_VERSION),