codex-overleaf-link 2.5.0 → 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 (168) hide show
  1. package/README.md +38 -26
  2. package/README.zh-CN.md +39 -27
  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 +206 -53
  9. package/extension/src/backgroundBootstrapMigration.js +181 -0
  10. package/extension/src/backgroundSharedSessions.js +74 -14
  11. package/extension/src/backgroundUpdateCoordinator.js +118 -21
  12. package/extension/src/backgroundUpdateSafety.js +173 -0
  13. package/extension/src/content/activeTurnControl.js +8 -2
  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 +93 -45
  17. package/extension/src/content/composerPanel.js +18 -0
  18. package/extension/src/content/contentRuntime.js +661 -676
  19. package/extension/src/content/diagnosticsController.js +122 -96
  20. package/extension/src/content/diagnosticsExport.js +19 -5
  21. package/extension/src/content/diagnosticsPanel.js +30 -0
  22. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  23. package/extension/src/content/generated/content.bundle.js +6879 -2786
  24. package/extension/src/content/generated/content.bundle.meta.json +11 -3
  25. package/extension/src/content/globalPreferencesController.js +1 -1
  26. package/extension/src/content/legacyGlobalRegistry.js +6 -0
  27. package/extension/src/content/localSkillsPanel.js +102 -12
  28. package/extension/src/content/markdownText.js +5 -6
  29. package/extension/src/content/modelPicker.js +7 -4
  30. package/extension/src/content/nativeCompatibilityController.js +45 -20
  31. package/extension/src/content/otWarmMirror.js +4 -5
  32. package/extension/src/content/otWarmMirrorController.js +17 -20
  33. package/extension/src/content/pageBridgeClient.js +15 -2
  34. package/extension/src/content/panelMaintenance.js +40 -12
  35. package/extension/src/content/panelRenderer.js +6 -0
  36. package/extension/src/content/projectProviderSelection.js +13 -4
  37. package/extension/src/content/projectSessionCleanup.js +109 -62
  38. package/extension/src/content/projectSettingsCoordinator.js +39 -2
  39. package/extension/src/content/providerSettingsCoordinator.js +46 -8
  40. package/extension/src/content/providerSettingsDialog.js +71 -13
  41. package/extension/src/content/recentProjects.js +11 -5
  42. package/extension/src/content/runActivitySummary.js +40 -6
  43. package/extension/src/content/runCompletionMeta.js +344 -0
  44. package/extension/src/content/runController.js +37 -14
  45. package/extension/src/content/runFailureNotice.js +21 -12
  46. package/extension/src/content/runGuidanceController.js +72 -27
  47. package/extension/src/content/runGuidanceView.js +59 -10
  48. package/extension/src/content/runQueueScheduler.js +148 -42
  49. package/extension/src/content/runRecoveryActions.js +146 -0
  50. package/extension/src/content/runResultActions.js +8 -125
  51. package/extension/src/content/runReviewController.js +574 -0
  52. package/extension/src/content/runScrollLayout.js +21 -2
  53. package/extension/src/content/runTimelineView.js +146 -153
  54. package/extension/src/content/scopedPersistencePanelState.js +51 -6
  55. package/extension/src/content/selectionContextView.js +1 -1
  56. package/extension/src/content/sensitivePreflight.js +140 -0
  57. package/extension/src/content/sessionForkController.js +80 -17
  58. package/extension/src/content/sessionManager.js +1 -1
  59. package/extension/src/content/settingsPanel.js +7 -3
  60. package/extension/src/content/settingsWorkbench.js +5 -0
  61. package/extension/src/content/sharedSessionViewSync.js +45 -8
  62. package/extension/src/content/updateIdle.js +4 -1
  63. package/extension/src/content/updateNotice.js +60 -8
  64. package/extension/src/content/updateSafetyReceiver.js +58 -0
  65. package/extension/src/content/writebackController.js +53 -4
  66. package/extension/src/content/writebackOrchestrator.js +82 -73
  67. package/extension/src/content/writingStyleSettings.js +35 -11
  68. package/extension/src/page/binaryAssetUploader.js +136 -14
  69. package/extension/src/page/fileMover.js +256 -0
  70. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  71. package/extension/src/page/referenceProjects.js +1 -0
  72. package/extension/src/page/reviewContext.js +113 -0
  73. package/extension/src/page/saveState.js +6 -4
  74. package/extension/src/page/snapshotRouter.js +84 -39
  75. package/extension/src/page/textFileCreator.js +105 -25
  76. package/extension/src/page/trackedChangeCapture.js +12 -2
  77. package/extension/src/page/trackedChangeOwnership.js +19 -1
  78. package/extension/src/page/trackedChangeReplay.js +41 -21
  79. package/extension/src/page/trackedChangesLifecycle.js +217 -199
  80. package/extension/src/page/treeOperations.js +7 -10
  81. package/extension/src/page/updateSafetyProbe.js +41 -0
  82. package/extension/src/page/writebackReceiptJournal.js +2 -0
  83. package/extension/src/page/writebackRouter.js +80 -79
  84. package/extension/src/pageBridge.js +19 -3
  85. package/extension/src/popup.js +62 -10
  86. package/extension/src/shared/agentTranscript.js +23 -0
  87. package/extension/src/shared/auditRecords.js +2 -0
  88. package/extension/src/shared/compatibility.js +22 -6
  89. package/extension/src/shared/compileAdapter.js +79 -57
  90. package/extension/src/shared/composerAttachmentDraft.js +151 -0
  91. package/extension/src/shared/directoryCleanupProof.js +43 -0
  92. package/extension/src/shared/failureReasons.js +309 -3
  93. package/extension/src/shared/globalPreferences.js +30 -13
  94. package/extension/src/shared/governanceRules.js +4 -37
  95. package/extension/src/shared/i18n.js +298 -2
  96. package/extension/src/shared/managedUpdateProjection.js +2 -2
  97. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  98. package/extension/src/shared/pageRpcContract.js +5 -4
  99. package/extension/src/shared/projectFiles.js +21 -44
  100. package/extension/src/shared/providerProfiles.js +5 -3
  101. package/extension/src/shared/runInputQueue.js +48 -5
  102. package/extension/src/shared/selectionContext.js +12 -3
  103. package/extension/src/shared/sessionState.js +72 -27
  104. package/extension/src/shared/settlementFacts.js +10 -2
  105. package/extension/src/shared/sharedSessionBridge.js +37 -14
  106. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  107. package/extension/src/shared/staleGuard.js +118 -13
  108. package/extension/src/shared/storageDb.js +58 -26
  109. package/extension/src/shared/storageMigration.js +71 -111
  110. package/extension/src/shared/storageRunActions.js +209 -26
  111. package/extension/src/shared/subagentActivityModel.js +1 -1
  112. package/extension/src/shared/undoOperations.js +136 -13
  113. package/extension/src/shared/updateConsent.js +6 -4
  114. package/extension/src/shared/updateRuntimeIdentity.js +1 -1
  115. package/extension/src/shared/writebackIntent.js +16 -0
  116. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  117. package/extension/src/shared/writebackSettlement.js +38 -38
  118. package/extension/styles/panel.css +19 -5
  119. package/native-host/src/anthropicBridgeRequest.js +4 -2
  120. package/native-host/src/anthropicBridgeResponse.js +6 -2
  121. package/native-host/src/anthropicMessagesBridge.js +24 -16
  122. package/native-host/src/bootstrapMigration.js +296 -0
  123. package/native-host/src/bootstrapMigrationContract.js +101 -0
  124. package/native-host/src/bootstrapUpdate.js +233 -0
  125. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  126. package/native-host/src/chatBridgeResponse.js +10 -0
  127. package/native-host/src/chatCompletionsBridge.js +21 -15
  128. package/native-host/src/codexConfigPaths.js +106 -0
  129. package/native-host/src/codexHome.js +66 -73
  130. package/native-host/src/codexHomeScope.js +124 -0
  131. package/native-host/src/codexInheritedAuth.js +90 -0
  132. package/native-host/src/codexLocalSkills.js +301 -0
  133. package/native-host/src/codexPromptAssembly.js +31 -12
  134. package/native-host/src/codexProviderTest.js +24 -26
  135. package/native-host/src/codexSessionRunner.js +110 -183
  136. package/native-host/src/codexSessionTiming.js +18 -8
  137. package/native-host/src/codexSkillIsolation.js +170 -0
  138. package/native-host/src/codexThreadFork.js +19 -3
  139. package/native-host/src/index.js +19 -7
  140. package/native-host/src/managedInstall.js +110 -35
  141. package/native-host/src/managedInstallRecovery.js +140 -0
  142. package/native-host/src/managedRuntimeEvidence.js +57 -0
  143. package/native-host/src/mirrorWorkspace.js +92 -22
  144. package/native-host/src/nativeDoctor.js +15 -0
  145. package/native-host/src/nativeHelperLifecycle.js +21 -0
  146. package/native-host/src/nativeHostRegistration.js +33 -0
  147. package/native-host/src/nativeRunLifecycle.js +71 -0
  148. package/native-host/src/processStopBarrier.js +42 -7
  149. package/native-host/src/projectPathPlatform.js +30 -0
  150. package/native-host/src/providerHttp.js +80 -0
  151. package/native-host/src/providerProfile.js +4 -2
  152. package/native-host/src/providerReasoning.js +2 -7
  153. package/native-host/src/providerRuntime.js +1 -0
  154. package/native-host/src/providerStore.js +72 -14
  155. package/native-host/src/responsesPassthroughBridge.js +266 -52
  156. package/native-host/src/runtimeInstaller.js +21 -1
  157. package/native-host/src/selectionScope.js +36 -8
  158. package/native-host/src/subagentAdmission.js +26 -0
  159. package/native-host/src/subagentBroker.js +79 -66
  160. package/native-host/src/subagentTelemetry.js +19 -10
  161. package/native-host/src/subagentWorkspacePath.js +5 -2
  162. package/native-host/src/taskRunnerRuntime.js +77 -54
  163. package/native-host/src/tomlStatements.js +140 -0
  164. package/native-host/src/updateManager.js +297 -39
  165. package/native-host/src/writingStyleRuntime.js +57 -29
  166. package/native-host/src/writingStyleStore.js +73 -0
  167. package/package.json +1 -1
  168. package/scripts/install-managed.mjs +1 -0
@@ -2,9 +2,9 @@
2
2
  "schemaVersion": 1,
3
3
  "entry": "extension/entries/content-entry.mjs",
4
4
  "output": "extension/src/content/generated/content.bundle.js",
5
- "sourceDigest": "a96cd1d3e417ecc9e124f48d9ad6dd728256c084f09a804d809299de8e4a7da3",
6
- "outputDigest": "09a6e2a4d4c6be0f8f8ad284c601e6cdf1843be09161f70a225abd56277b9c19",
7
- "byteLength": 2838686,
5
+ "sourceDigest": "548acfc19dd7be907e0c5494f2da2e11fa68dfc501e0552846ad63d4e5a35dba",
6
+ "outputDigest": "404e0523fc534b56e87ea96e62cd112514c06ce034209ec7a05feef83d7a64a9",
7
+ "byteLength": 3162346,
8
8
  "inputs": [
9
9
  "extension/entries/content-entry.mjs",
10
10
  "extension/src/content/activeTurnControl.js",
@@ -19,6 +19,7 @@
19
19
  "extension/src/content/diagnosticsExport.js",
20
20
  "extension/src/content/diagnosticsPanel.js",
21
21
  "extension/src/content/diffReviewPanel.js",
22
+ "extension/src/content/directoryCleanupRecovery.js",
22
23
  "extension/src/content/globalPreferencesController.js",
23
24
  "extension/src/content/legacyGlobalRegistry.js",
24
25
  "extension/src/content/localSkillsPanel.js",
@@ -46,13 +47,16 @@
46
47
  "extension/src/content/recentProjects.js",
47
48
  "extension/src/content/reviewHunks.js",
48
49
  "extension/src/content/runActivitySummary.js",
50
+ "extension/src/content/runCompletionMeta.js",
49
51
  "extension/src/content/runController.js",
50
52
  "extension/src/content/runFailureNotice.js",
51
53
  "extension/src/content/runGuidanceController.js",
52
54
  "extension/src/content/runGuidanceView.js",
53
55
  "extension/src/content/runPresence.js",
54
56
  "extension/src/content/runQueueScheduler.js",
57
+ "extension/src/content/runRecoveryActions.js",
55
58
  "extension/src/content/runResultActions.js",
59
+ "extension/src/content/runReviewController.js",
56
60
  "extension/src/content/runScrollLayout.js",
57
61
  "extension/src/content/runSettlementPersistence.js",
58
62
  "extension/src/content/runTimelineView.js",
@@ -60,6 +64,7 @@
60
64
  "extension/src/content/scopedPersistenceDetachedCommit.js",
61
65
  "extension/src/content/scopedPersistencePanelState.js",
62
66
  "extension/src/content/selectionContextView.js",
67
+ "extension/src/content/sensitivePreflight.js",
63
68
  "extension/src/content/sessionForkController.js",
64
69
  "extension/src/content/sessionManager.js",
65
70
  "extension/src/content/sessionMenuView.js",
@@ -83,6 +88,8 @@
83
88
  "extension/src/shared/changeHistoryModel.js",
84
89
  "extension/src/shared/compatibility.js",
85
90
  "extension/src/shared/compileAdapter.js",
91
+ "extension/src/shared/composerAttachmentDraft.js",
92
+ "extension/src/shared/directoryCleanupProof.js",
86
93
  "extension/src/shared/failureReasons.js",
87
94
  "extension/src/shared/globalPreferences.js",
88
95
  "extension/src/shared/governanceRules.js",
@@ -110,6 +117,7 @@
110
117
  "extension/src/shared/sessionTitle.js",
111
118
  "extension/src/shared/settlementFacts.js",
112
119
  "extension/src/shared/sharedSessionBridge.js",
120
+ "extension/src/shared/sharedSessionTransfer.js",
113
121
  "extension/src/shared/staleGuard.js",
114
122
  "extension/src/shared/storageDb.js",
115
123
  "extension/src/shared/storageKeys.js",
@@ -40,7 +40,7 @@
40
40
  if (force || changed.includes('theme')) options.applyTheme?.(values.theme);
41
41
  if (changed.includes('locale')) options.applyLocale?.();
42
42
  if (force || changed.includes('preloadProjectContext')) options.onPreloadChange?.(values.preloadProjectContext);
43
- if (changed.some(key => key === 'codexOverleafSkillEnabled' || key.startsWith('loadCodex'))) options.onSkillsChange?.();
43
+ if (changed.some(key => key === 'codexOverleafSkillEnabled' || key === 'codexLocalSkillEnabled' || key.startsWith('loadCodex'))) options.onSkillsChange?.();
44
44
  }
45
45
 
46
46
  function accept(value) {
@@ -20,10 +20,12 @@
20
20
  'SelectionContext',
21
21
  'SelectionContextView',
22
22
  'WritebackIntent',
23
+ 'DirectoryCleanupProof',
23
24
  'ChangeHistoryModel',
24
25
  'ChangeHistoryView',
25
26
  'Compatibility',
26
27
  'ComposerAttachments',
28
+ 'ComposerAttachmentDraft',
27
29
  'ComposerPanel',
28
30
  'ContentRuntime',
29
31
  'ContextTray',
@@ -61,6 +63,7 @@
61
63
  'RunPresence',
62
64
  'RunActivityModel',
63
65
  'RunController',
66
+ 'RunReviewController',
64
67
  'RunExecutionSnapshot',
65
68
  'RunGuidanceController',
66
69
  'RunInputQueue',
@@ -73,6 +76,7 @@
73
76
  'ScopedPersistenceCoordinator',
74
77
  'ScopedPersistenceTransaction',
75
78
  'SensitiveScan',
79
+ 'SensitivePreflight',
76
80
  'SessionManager',
77
81
  'SessionForkController',
78
82
  'SessionPanel',
@@ -93,6 +97,7 @@
93
97
  'UpdateIdle',
94
98
  'UpdateNotice',
95
99
  'WritebackController',
100
+ 'DirectoryCleanupRecovery',
96
101
  'WritebackIntent',
97
102
  'WritebackOrchestrator',
98
103
  'WritebackPlan',
@@ -111,6 +116,7 @@
111
116
  'ProviderProfiles',
112
117
  'ProviderSettingsDialog',
113
118
  'RunGuidanceView',
119
+ 'RunRecoveryActions',
114
120
  'SessionMenuView'
115
121
  ]);
116
122
 
@@ -17,11 +17,17 @@
17
17
  document: documentDep = null,
18
18
  getPanel = () => null,
19
19
  getState = () => null,
20
+ getScope = () => '',
20
21
  setState = () => {},
21
22
  saveState = null,
22
23
  sendBackgroundNative = () => Promise.resolve({ ok: false }),
23
24
  getSkillLoadingSettings = () => ({ loadCodexOverleafSkills: true }),
24
25
  isCodexOverleafSkillEnabled = () => true,
26
+ isCodexLocalSkillEnabled = (id, skill) => skill?.enabled !== false,
27
+ setCodexLocalSkillEnabled = () => {},
28
+ getCodexLocalSkills = () => [],
29
+ getCodexLocalSkillWarnings = () => [],
30
+ refreshCodexLocalSkills = async () => [],
25
31
  setCodexOverleafSkillEnabled = () => {},
26
32
  setProjectSettingsStatus = () => {},
27
33
  tr = (key) => key,
@@ -36,27 +42,51 @@
36
42
  || root.document
37
43
  || (typeof globalThis !== 'undefined' ? globalThis.document : null);
38
44
 
45
+ let localSkillsListError = false;
46
+ let refreshGeneration = 0;
39
47
  async function refreshLocalSkills() {
48
+ const generation = ++refreshGeneration;
49
+ const scope = getScope();
50
+ const isCurrent = () => generation === refreshGeneration && scope === getScope();
51
+ localSkillsListError = false;
40
52
  showSkillListLoading();
53
+ const localList = getPanel()?.querySelector('[data-codex-local-skill-list]');
54
+ if (localList) {
55
+ localList.textContent = '';
56
+ appendLocalSkillEmpty(localList, 'codexOverleafSkillsLoading');
57
+ }
58
+ const localRefresh = localList ? refreshCodexLocalSkills({ force: true }) : Promise.resolve([]);
59
+ const localOutcome = localRefresh.then(() => {
60
+ if (isCurrent()) renderCodexLocalSkillList();
61
+ return null;
62
+ }, error => {
63
+ if (isCurrent()) { localSkillsListError = true; renderCodexLocalSkillList(); }
64
+ return error;
65
+ });
41
66
  try {
42
67
  const codexOverleafResponse = await sendBackgroundNative({
43
68
  method: 'skills.list',
44
69
  params: { scope: 'codex-overleaf' }
45
70
  });
46
71
  if (!codexOverleafResponse?.ok) {
47
- throw new Error(codexOverleafResponse?.error?.message || 'native host did not return Codex Overleaf skills');
72
+ throw new Error(tx('Could not load Codex Overleaf skills. Try again.', '无法加载 Codex Overleaf 技能,请重试。'));
48
73
  }
49
74
  const codexOverleafSkills = Array.isArray(codexOverleafResponse.result?.skills)
50
75
  ? codexOverleafResponse.result.skills
51
76
  : [];
77
+ if (!isCurrent()) return;
52
78
  const state = getState() || {};
53
79
  setState({
54
80
  ...state,
55
81
  codexOverleafSkills
56
82
  });
57
83
  setSlashCodexOverleafSkills(codexOverleafSkills);
84
+ const localError = await localOutcome;
85
+ if (!isCurrent()) return;
58
86
  renderLocalSkillList();
87
+ if (localError) throw localError;
59
88
  } catch (err) {
89
+ if (!isCurrent()) return;
60
90
  renderLocalSkillList();
61
91
  throw err;
62
92
  }
@@ -71,6 +101,7 @@
71
101
  const placeholder = document.createElement('div');
72
102
  placeholder.className = 'codex-project-settings-empty';
73
103
  placeholder.textContent = tr('codexOverleafSkillsLoading');
104
+ placeholder.dataset.i18n = 'codexOverleafSkillsLoading';
74
105
  list.append(placeholder);
75
106
  }
76
107
 
@@ -84,6 +115,7 @@
84
115
  }
85
116
 
86
117
  function renderLocalSkillList() {
118
+ renderCodexLocalSkillList();
87
119
  const list = getPanel()?.querySelector('[data-local-skill-list]');
88
120
  if (!list) {
89
121
  return;
@@ -93,10 +125,10 @@
93
125
  const codexOverleafEnabled = getSkillLoadingSettings().loadCodexOverleafSkills !== false;
94
126
  list.textContent = '';
95
127
  if (!codexOverleafEnabled) {
96
- appendLocalSkillEmpty(list, tr('codexOverleafSkillsDisabled'));
128
+ appendLocalSkillEmpty(list, 'codexOverleafSkillsDisabled');
97
129
  }
98
130
  if (!codexOverleafSkills.length) {
99
- appendLocalSkillEmpty(list, tr('codexOverleafSkillsEmpty'));
131
+ appendLocalSkillEmpty(list, 'codexOverleafSkillsEmpty');
100
132
  return;
101
133
  }
102
134
  for (const skill of codexOverleafSkills) {
@@ -104,6 +136,31 @@
104
136
  }
105
137
  }
106
138
 
139
+ function renderCodexLocalSkillList() {
140
+ const list = getPanel()?.querySelector('[data-codex-local-skill-list]');
141
+ if (!list) return;
142
+ list.textContent = '';
143
+ const enabled = getSkillLoadingSettings().loadCodexLocalSkills !== false;
144
+ if (!enabled) appendLocalSkillEmpty(list, 'codexLocalSkillsDisabled');
145
+ const skills = getCodexLocalSkills();
146
+ if (localSkillsListError) { appendLocalSkillEmpty(list, 'codexLocalSkillsLoadFailed'); return; }
147
+ if (!Array.isArray(skills)) { appendLocalSkillEmpty(list, 'codexOverleafSkillsLoading'); return; }
148
+ if (!skills.length) appendLocalSkillEmpty(list, 'codexLocalSkillsEmpty');
149
+ const skippedCount = getCodexLocalSkillWarnings().reduce((sum, warning) => sum + (warning?.code === 'codex_local_skills_skipped' ? warning.count : 0), 0);
150
+ if (skippedCount > 0) {
151
+ const warning = document.createElement('div');
152
+ warning.className = 'codex-project-settings-empty';
153
+ warning._refreshLocale = () => { warning.textContent = tr('codexLocalSkillsSkipped', { count: skippedCount }); };
154
+ warning._refreshLocale();
155
+ list.append(warning);
156
+ }
157
+ for (const skill of skills) {
158
+ const title = String(skill.title || skill.name || skill.id).trim();
159
+ const duplicateTitle = skills.filter(other => String(other.title || other.name || other.id).trim() === title).length > 1;
160
+ renderCodexOverleafSkillRow(list, { ...skill, title, duplicateTitle, scope: 'codex-local', removable: false }, enabled);
161
+ }
162
+ }
163
+
107
164
  function createSkillMark(id) {
108
165
  const mark = document.createElement('span');
109
166
  mark.className = 'codex-local-skill-icon';
@@ -123,10 +180,11 @@
123
180
  if (!id) {
124
181
  return;
125
182
  }
183
+ const local = skill.scope === 'codex-local';
126
184
  const compact = Boolean(list.closest?.('.codex-settings-workbench'));
127
185
  const row = document.createElement('div');
128
186
  row.className = 'codex-local-skill-row';
129
- row.dataset.scope = 'codex-overleaf';
187
+ row.dataset.scope = local ? 'codex-local' : 'codex-overleaf';
130
188
  if (!masterEnabled) {
131
189
  row.dataset.disabled = 'true';
132
190
  }
@@ -148,7 +206,7 @@
148
206
  'annotated-rewrite': tx('Guided editing for document revisions.', '用于文稿修订的编辑流程。'),
149
207
  'parallel-subagents': tx('Split independent subtasks across multiple agents.', '将相互独立的子任务分配给多个 agent。')
150
208
  };
151
- const description = descriptions[id]
209
+ const description = (!local && descriptions[id])
152
210
  || (typeof skill.description === 'string' ? skill.description.replace(/\s+/g, ' ').trim() : '');
153
211
  if (description) {
154
212
  const help = document.createElement('span');
@@ -157,7 +215,17 @@
157
215
  help.title = description;
158
216
  copy.append(help);
159
217
  }
160
- if (compact) row.append(createSkillMark(id));
218
+ const sourceText = () => (skill.sourceKind === 'system' ? tx('Codex built-in', 'Codex 内置')
219
+ : skill.sourceKind === 'plugin' && skill.sourceLabel ? String(skill.sourceLabel) : tx('Local Codex', 'Codex 本地'))
220
+ + (skill.duplicateTitle ? ' · ' + (skill.sourceHint || id.slice(-8)) : '');
221
+ let sourceLabel = null;
222
+ if (local) {
223
+ sourceLabel = document.createElement('span');
224
+ sourceLabel.className = 'codex-local-skill-description';
225
+ sourceLabel.textContent = sourceText();
226
+ copy.append(sourceLabel);
227
+ }
228
+ if (compact) row.append(createSkillMark(local ? '' : id));
161
229
  row.append(copy);
162
230
  const controls = compact ? document.createElement('div') : row;
163
231
  if (compact) controls.className = 'codex-local-skill-controls';
@@ -166,14 +234,25 @@
166
234
  const toggle = document.createElement('input');
167
235
  toggle.type = 'checkbox';
168
236
  toggle.className = 'codex-switch';
169
- toggle.checked = isCodexOverleafSkillEnabled(id);
170
- toggle.setAttribute('aria-label', (trimmedTitle || id) + ': ' + tr('codexOverleafSkillEnableToggle'));
237
+ toggle.checked = local ? isCodexLocalSkillEnabled(id, skill) : isCodexOverleafSkillEnabled(id);
238
+ const accessibleSkillName = (trimmedTitle || id)
239
+ + (local && skill.duplicateTitle ? ': ' + (skill.sourceHint || id.slice(-8)) : '');
240
+ toggle.setAttribute('aria-label', accessibleSkillName + ': ' + tr('codexOverleafSkillEnableToggle'));
171
241
  if (!masterEnabled) {
172
242
  toggle.disabled = true;
173
243
  }
174
244
  toggle.addEventListener('change', event => {
175
- setCodexOverleafSkillEnabled(id, event.target.checked);
245
+ if (local) setCodexLocalSkillEnabled(id, event.target.checked);
246
+ else setCodexOverleafSkillEnabled(id, event.target.checked);
176
247
  });
248
+ row._refreshLocale = () => {
249
+ if (sourceLabel) sourceLabel.textContent = sourceText();
250
+ toggle.setAttribute('aria-label', accessibleSkillName + ': ' + tr('codexOverleafSkillEnableToggle'));
251
+ const label = !local && id === 'annotated-rewrite' ? tx('Guided editing for document revisions.', '用于文稿修订的编辑流程。')
252
+ : !local && id === 'parallel-subagents' ? tx('Split independent subtasks across multiple agents.', '将相互独立的子任务分配给多个 agent。') : '';
253
+ const help = copy.querySelector('.codex-local-skill-description');
254
+ if (label && help) { help.textContent = label; help.title = label; }
255
+ };
177
256
 
178
257
  if (skill.removable !== false) {
179
258
  // The Remove / Confirm / Cancel controls share an actions container so
@@ -198,6 +277,11 @@
198
277
  cancelBtn.type = 'button';
199
278
  cancelBtn.className = 'codex-set-btn';
200
279
  cancelBtn.textContent = tr('localSkillRemoveCancel');
280
+ const refreshRow = row._refreshLocale;
281
+ row._refreshLocale = () => {
282
+ refreshRow(); removeBtn.textContent = tr('localSkillRemove'); cancelBtn.textContent = tr('localSkillRemoveCancel');
283
+ confirmBtn.textContent = tr(confirmBtn.disabled ? 'localSkillRemoving' : 'localSkillRemoveConfirm');
284
+ };
201
285
 
202
286
  function enterConfirming() {
203
287
  // Reset any previously confirming row back to its Remove button state.
@@ -274,10 +358,11 @@
274
358
  }
275
359
  }
276
360
 
277
- function appendLocalSkillEmpty(list, text) {
361
+ function appendLocalSkillEmpty(list, key) {
278
362
  const empty = document.createElement('div');
279
363
  empty.className = 'codex-project-settings-empty';
280
- empty.textContent = text;
364
+ empty.textContent = tr(key);
365
+ empty.dataset.i18n = key;
281
366
  list.append(empty);
282
367
  }
283
368
 
@@ -293,7 +378,7 @@
293
378
  throw new Error(response?.error?.message || 'native host did not remove Codex Overleaf skill');
294
379
  }
295
380
  const activeSkill = normalizeComposerSkillInvocation(getComposerSkillInvocation());
296
- if (activeSkill?.scope === 'codex-overleaf' && activeSkill.skillId === id) {
381
+ if (activeSkill?.scope === 'codex-overleaf' && activeSkill.id === id) {
297
382
  clearComposerSkillInvocation();
298
383
  }
299
384
  setProjectSettingsStatus(tr('codexOverleafSkillRemoveDone'), 'completed');
@@ -304,6 +389,11 @@
304
389
 
305
390
  return {
306
391
  refreshLocalSkills,
392
+ refreshLocale: () => {
393
+ for (const selector of ['[data-local-skill-list]', '[data-codex-local-skill-list]']) {
394
+ for (const row of getPanel()?.querySelector(selector)?.children || []) row._refreshLocale?.();
395
+ }
396
+ },
307
397
  renderLocalSkillList,
308
398
  removeCodexOverleafSkill
309
399
  };
@@ -1,11 +1,8 @@
1
1
  (function initCodexOverleafMarkdownText() {
2
2
  'use strict';
3
3
 
4
- // Markdown / assistant-text rendering — block + inline markdown, the
5
- // assistant-visible sanitizers, and line-reference resolution/buttons carved
6
- // out of contentRuntime.js (v1.4.5 structural-debt phase 1). The code below
7
- // moved verbatim (original indentation kept); the four runtime collaborators
8
- // are factory-injected so behavior is unchanged.
4
+ // Assistant-text rendering, sanitizers, and line-reference controls.
5
+ // Runtime collaborators are factory-injected.
9
6
  function create(deps = {}) {
10
7
  const {
11
8
  LineReferences,
@@ -588,7 +585,9 @@
588
585
  return;
589
586
  }
590
587
  renderingDiagnosticKeys.add(key);
591
- if (typeof options.recordRenderingDiagnostic === 'function') options.recordRenderingDiagnostic({ code, detail });
588
+ try {
589
+ if (typeof deps.recordRenderingDiagnostic === 'function') deps.recordRenderingDiagnostic({ code, detail });
590
+ } catch (_error) { /* A diagnostic sink must not interrupt rendering. */ }
592
591
  }
593
592
 
594
593
  function appendMarkdownParagraphFlow(target, value) {
@@ -117,11 +117,12 @@
117
117
  if (generation !== modelLoadGeneration) {
118
118
  return { stale: true };
119
119
  }
120
+ options.assertCurrent?.();
120
121
  const responseProviderId = response?.result?.providerId || '';
121
122
  if (selection?.providerId && responseProviderId && responseProviderId !== selection.providerId) {
122
123
  return { stale: true };
123
124
  }
124
- if (selection?.providerId && selection.providerId !== 'builtin'
125
+ if (response?.ok && selection?.providerId && selection.providerId !== 'builtin'
125
126
  && Number(response?.result?.providerRevision) !== Number(selection.providerRevision)) {
126
127
  return { stale: true };
127
128
  }
@@ -170,6 +171,7 @@
170
171
  if (generation !== modelLoadGeneration) {
171
172
  return { stale: true };
172
173
  }
174
+ options.assertCurrent?.();
173
175
  applyUnavailableModelOptions(error);
174
176
  return { stale: false, error };
175
177
  }
@@ -251,7 +253,7 @@
251
253
  }
252
254
  const option = document.createElement('option');
253
255
  option.value = id;
254
- option.textContent = model.label;
256
+ option.textContent = model.unverified && model.label === `${id} (custom)` ? tx(`${id} (custom)`, `${id}(自定义)`) : model.label;
255
257
  option.dataset.reasoningEfforts = normalizeReasoningEffortsForSelect(model.reasoningEfforts).join(',');
256
258
  option.dataset.defaultReasoningEffort = model.defaultReasoningEffort || '';
257
259
  option.dataset.reasoningPresentation = model.reasoningPresentation || '';
@@ -269,7 +271,7 @@
269
271
  if (selectedId && !renderedSelected && options.allowUnknownSelected !== false) {
270
272
  const option = document.createElement('option');
271
273
  option.value = selectedId;
272
- option.textContent = `${selectedId} (custom)`;
274
+ option.textContent = tx(`${selectedId} (custom)`, `${selectedId}(自定义)`);
273
275
  option.dataset.reasoningEfforts = '';
274
276
  option.dataset.defaultReasoningEffort = '';
275
277
  option.dataset.reasoningPresentation = 'none';
@@ -370,7 +372,7 @@
370
372
  for (const option of Array.from(reasoningSelect.options || [])) {
371
373
  list.append(createModelConfigChoice({
372
374
  value: option.value,
373
- label: option.textContent || formatReasoningEffortLabel(option.value, reasoningSelect.dataset.presentation),
375
+ label: formatReasoningEffortLabel(option.value, reasoningSelect.dataset.presentation),
374
376
  datasetName: 'reasoningChoice'
375
377
  }));
376
378
  }
@@ -384,6 +386,7 @@
384
386
  }
385
387
  list.textContent = '';
386
388
  for (const option of Array.from(modelSelect.options || [])) {
389
+ if (option.dataset.unverified === 'true' && [`${option.value} (custom)`, `${option.value}(自定义)`].includes(option.textContent)) option.textContent = tx(`${option.value} (custom)`, `${option.value}(自定义)`);
387
390
  const choice = createModelConfigChoice({
388
391
  value: option.value,
389
392
  label: option.textContent || option.value,
@@ -12,27 +12,46 @@
12
12
  const tx = options.tx;
13
13
  const tr = options.tr;
14
14
 
15
+ let installIdentity = options.getExtensionCompatibilityMetadata?.()?.installIdentity || {};
16
+ function resolveInstallCommand(compatibility = {}) {
17
+ const metadata = options.getExtensionCompatibilityMetadata?.() || {};
18
+ installIdentity = { ...installIdentity, ...(metadata.installIdentity || {}), ...(compatibility.installIdentity || {}) };
19
+ if (installIdentity.managed) return Compatibility?.buildInstallCommand?.(
20
+ compatibility.recommendedVersion || Compatibility?.BUILD_TARGET_VERSION,
21
+ compatibility.native?.platform || compatibility.platform || installIdentity.platform,
22
+ metadata.extensionId, installIdentity
23
+ ) || '';
24
+ return compatibility.updateCommand || compatibility.installCommand || options.installCommand || '';
25
+ }
26
+
15
27
  function sendNative(payload) {
16
28
  return dispatch(payload, 'sendNative');
17
29
  }
18
30
 
19
- function sendBackgroundNative(payload) {
20
- return dispatch(payload, 'sendBackgroundNative');
31
+ function sendBackgroundNative(payload, context = {}) {
32
+ return dispatch(payload, 'sendBackgroundNative', context);
21
33
  }
22
34
 
23
- async function dispatch(payload, channelMethod) {
24
- const compatibilityGate = await ensureForMethod(payload?.method);
25
- if (!compatibilityGate.ok) return compatibilityGate.response;
26
- // Post-Undo cache bookkeeping must survive the ended run's cancellation.
27
- if (gatedMethods.has(payload?.method) && payload.method !== 'mirror.invalidate') {
28
- options.throwIfCancellationRequested?.();
35
+ async function dispatch(payload, channelMethod, context = {}) {
36
+ let compatibilityGate;
37
+ try {
38
+ compatibilityGate = await ensureForMethod(payload?.method, context);
39
+ if (!compatibilityGate.ok) return compatibilityGate.response;
40
+ // Post-Undo cache bookkeeping must survive the ended run's cancellation.
41
+ if (gatedMethods.has(payload?.method) && payload.method !== 'mirror.invalidate') {
42
+ options.throwIfCancellationRequested?.();
43
+ }
44
+ context.assertCurrent?.();
45
+ } catch (error) {
46
+ if (context.assertCurrent && error && typeof error === 'object') error.requestDispatched = false;
47
+ throw error;
29
48
  }
30
49
  return nativeChannel[channelMethod](
31
50
  attachEvidence(payload, compatibilityGate.compatibility)
32
51
  );
33
52
  }
34
53
 
35
- async function ensureForMethod(method) {
54
+ async function ensureForMethod(method, { assertCurrent } = {}) {
36
55
  const metadata = options.getExtensionCompatibilityMetadata?.() || {};
37
56
  const params = Compatibility?.buildBridgePingParams
38
57
  ? Compatibility.buildBridgePingParams(metadata)
@@ -41,9 +60,12 @@
41
60
  method: 'bridge.ping',
42
61
  params
43
62
  });
63
+ // A retired run must not publish even a compatibility failure into a successor.
64
+ assertCurrent?.();
44
65
  const compatibility = Compatibility?.evaluateNativeCompatibility
45
66
  ? Compatibility.evaluateNativeCompatibility(response, metadata)
46
67
  : options.fallbackNativeCompatibility(response);
68
+ const installCommand = resolveInstallCommand(compatibility);
47
69
  const allowed = !gatedMethods.has(method) ||
48
70
  (Compatibility?.isNativeMethodAllowed
49
71
  ? Compatibility.isNativeMethodAllowed(method, compatibility)
@@ -51,14 +73,14 @@
51
73
  if (allowed) return { ok: true, compatibility };
52
74
 
53
75
  const message = formatBlockedMessage(method, compatibility);
54
- const installCommand = options.installCommand;
55
76
  const error = {
56
77
  code: 'native_update_required',
57
78
  message,
58
79
  status: compatibility.status || 'unknown_native',
59
80
  classification: options.getNativeCompatibilityClassification(compatibility),
60
- installCommand: compatibility.installCommand || installCommand,
61
- updateCommand: compatibility.updateCommand || compatibility.installCommand || installCommand,
81
+ installIdentity,
82
+ installCommand,
83
+ updateCommand: installCommand,
62
84
  currentNativeVersion: compatibility.currentNativeVersion
63
85
  || compatibility.nativeVersion
64
86
  || compatibility.native?.version
@@ -102,6 +124,7 @@
102
124
  : {};
103
125
  const response = await sendBackgroundNative({ method: 'bridge.ping', params });
104
126
  const compatibility = Compatibility.evaluateNativeCompatibility(response, metadata);
127
+ resolveInstallCommand(compatibility);
105
128
  const classification = options.getNativeCompatibilityClassification(compatibility);
106
129
  if (classification === 'compatible') {
107
130
  options.setBadge?.(renderer.headerEl, { type: 'none' });
@@ -158,9 +181,7 @@
158
181
  || compatibility.nativeVersion
159
182
  || native.version
160
183
  || tx('missing', '未安装');
161
- const command = compatibility.updateCommand
162
- || compatibility.installCommand
163
- || options.installCommand;
184
+ const command = resolveInstallCommand(compatibility);
164
185
  const releaseUrl = compatibility.releaseUrl || '';
165
186
  const overlay = options.document.createElement('div');
166
187
  overlay.className = 'codex-plugin-confirm';
@@ -193,10 +214,12 @@
193
214
  `Extension v${extensionVersion} / Native v${nativeVersion}`,
194
215
  `扩展 v${extensionVersion} / Native v${nativeVersion}`
195
216
  ),
196
- tx(
197
- 'Run the platform-specific command below, reload the extension, then refresh Overleaf.',
198
- '运行下面的平台命令,重新加载扩展,然后刷新 Overleaf。'
199
- )
217
+ !command && installIdentity.managed
218
+ ? tx('Repair Managed using the original managed root, browser and extension ID with install-managed.',
219
+ '请使用原管理根目录、浏览器及扩展 ID,通过 install-managed 修复托管安装。')
220
+ : tx('Run the command below, reload the extension, then refresh Overleaf.', '运行下面的命令,重新加载扩展,然后刷新 Overleaf。')
221
+ + (installIdentity.managed && (native.platform || installIdentity.platform) === 'win32'
222
+ ? tx(' Use PowerShell.', ' 请使用 PowerShell。') : '')
200
223
  ].join('\n\n');
201
224
  const commandCode = options.document.createElement('code');
202
225
  commandCode.textContent = command;
@@ -221,7 +244,9 @@
221
244
  copy.type = 'button';
222
245
  copy.className = 'codex-plugin-confirm-confirm';
223
246
  copy.textContent = tx('Copy command', '复制命令');
247
+ copy.disabled = !command;
224
248
  copy.addEventListener('click', async () => {
249
+ if (!command) return;
225
250
  await options.navigator.clipboard.writeText(command);
226
251
  copy.textContent = tx('Copied', '已复制');
227
252
  });
@@ -237,7 +262,7 @@
237
262
  if (event.target === overlay) overlay.remove();
238
263
  });
239
264
  panel.append(overlay);
240
- copy.focus();
265
+ (command ? copy : close).focus();
241
266
  }
242
267
 
243
268
  function scheduleFirstUseOnboardingTip() {
@@ -833,7 +833,7 @@
833
833
  if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: 'project_changed' };
834
834
  const otWarmStart = otWarmMirrorController.canUseOtWarmStart({
835
835
  enabled: isExperimentalOtEnabled() && !otWarmMirrorState.failureActive,
836
- focusFiles,
836
+ focusFiles, focusFilesAreLiteral: true,
837
837
  mirrorStatus
838
838
  });
839
839
  if (!mirrorStatus?.exists || (!otWarmStart.ok && !isMirrorReusable(mirrorStatus))) {
@@ -858,10 +858,9 @@
858
858
  return { useExistingMirror: false, reason: 'overlay_probe_failed', mirrorStatus };
859
859
  }
860
860
  if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: 'project_changed' };
861
- const fileOverlays = buildSnapshotFileOverlays(overlayProject, focusFiles);
862
- if (!fileOverlays.length) {
863
- return { useExistingMirror: false, reason: 'missing_current_overlay', mirrorStatus };
864
- }
861
+ const fileOverlays = buildSnapshotFileOverlays(overlayProject, focusFiles).filter(file =>
862
+ !otWarmStart.ok || focusFiles.some(path => normalizeSnapshotPath(path) === normalizeSnapshotPath(file.path)));
863
+ if (!fileOverlays.length) return { useExistingMirror: false, reason: 'missing_current_overlay', mirrorStatus };
865
864
  if (focusFiles.length && !focusFiles.every(path => {
866
865
  const normalized = normalizeSnapshotPath(path);
867
866
  return fileOverlays.some(file => normalizeSnapshotPath(file.path) === normalized);