codex-overleaf-link 1.9.13 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +24 -23
  2. package/extension/bootstrap/manifest.template.json +2 -1
  3. package/extension/runtime-manifest.json +13 -6
  4. package/extension/src/background.js +7 -0
  5. package/extension/src/content/composerPanel.js +13 -3
  6. package/extension/src/content/contentRuntime.js +130 -65
  7. package/extension/src/content/contextTray.js +173 -9
  8. package/extension/src/content/markdownText.js +16 -1
  9. package/extension/src/content/mathText.js +233 -0
  10. package/extension/src/content/modelPicker.js +117 -106
  11. package/extension/src/content/modelPickerSupport.js +115 -0
  12. package/extension/src/content/nativeChannel.js +20 -1
  13. package/extension/src/content/panelMaintenance.js +2 -0
  14. package/extension/src/content/projectSettingsCoordinator.js +2 -0
  15. package/extension/src/content/providerSettingsCoordinator.js +413 -0
  16. package/extension/src/content/providerSettingsDialog.js +981 -0
  17. package/extension/src/content/runTimelineView.js +24 -5
  18. package/extension/src/content/settingsPanel.js +33 -2
  19. package/extension/src/content/writebackOrchestrator.js +3 -1
  20. package/extension/src/page/overleafProjectSnapshot.js +29 -33
  21. package/extension/src/page/snapshotRouter.js +75 -8
  22. package/extension/src/page/trackedChangesLifecycle.js +154 -26
  23. package/extension/src/page/writebackRouter.js +31 -60
  24. package/extension/src/pageBridge.js +1 -1
  25. package/extension/src/shared/agentTranscript.js +130 -0
  26. package/extension/src/shared/compatibility.js +10 -7
  27. package/extension/src/shared/failureReasons.js +75 -0
  28. package/extension/src/shared/i18n.js +22 -0
  29. package/extension/src/shared/models.js +1 -0
  30. package/extension/src/shared/providerProfiles.js +283 -0
  31. package/extension/src/shared/sessionState.js +12 -2
  32. package/extension/src/shared/storageDb.js +27 -19
  33. package/extension/src/shared/storageRunActions.js +99 -0
  34. package/extension/styles/panel.css +587 -1
  35. package/extension/vendor/katex/LICENSE +21 -0
  36. package/extension/vendor/katex/VERSION +2 -0
  37. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
  38. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  39. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  40. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  41. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  42. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  43. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  44. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  45. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  46. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  47. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  48. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  49. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  50. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  51. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  52. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
  53. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  54. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  55. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  56. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  57. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  58. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
  59. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  60. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  61. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
  62. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  63. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  64. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  65. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  66. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  67. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
  68. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  69. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  70. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  71. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  72. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  73. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  74. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  75. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  76. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  77. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  78. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  79. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
  80. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff +0 -0
  81. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  82. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
  83. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  84. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  85. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
  86. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  87. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  88. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
  89. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  90. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  91. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
  92. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  93. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  94. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  95. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  96. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  97. package/extension/vendor/katex/katex.min.css +1 -0
  98. package/extension/vendor/katex/katex.min.js +1 -0
  99. package/native-host/src/anthropicBridgeRequest.js +435 -0
  100. package/native-host/src/anthropicBridgeResponse.js +431 -0
  101. package/native-host/src/anthropicMessagesBridge.js +281 -0
  102. package/native-host/src/chatBridgeRequest.js +274 -0
  103. package/native-host/src/chatBridgeResponse.js +594 -0
  104. package/native-host/src/chatCompletionsBridge.js +485 -0
  105. package/native-host/src/codexCommand.js +25 -0
  106. package/native-host/src/codexPromptAssembly.js +9 -0
  107. package/native-host/src/codexProviderLaunch.js +213 -0
  108. package/native-host/src/codexProviderTest.js +196 -0
  109. package/native-host/src/codexSessionRunner.js +29 -25
  110. package/native-host/src/managedInstall.js +1 -0
  111. package/native-host/src/providerBridgeRoutes.js +11 -0
  112. package/native-host/src/providerModelCatalog.js +107 -0
  113. package/native-host/src/providerProfile.js +454 -0
  114. package/native-host/src/providerReasoning.js +235 -0
  115. package/native-host/src/providerRedaction.js +18 -0
  116. package/native-host/src/providerRuntime.js +365 -0
  117. package/native-host/src/providerStore.js +691 -0
  118. package/native-host/src/readProgressGuard.js +360 -0
  119. package/native-host/src/taskRunnerRuntime.js +40 -4
  120. package/native-host/src/updateArchive.js +1 -0
  121. package/native-host/src/updateTrust.js +2 -1
  122. package/package.json +2 -1
@@ -0,0 +1,360 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+
5
+ const DEFAULT_MIN_READ_COMMANDS = 6;
6
+ const DEFAULT_OVERLAP_RATIO = 0.85;
7
+ const DEFAULT_REDUNDANT_STREAK = 2;
8
+ const DEFAULT_POST_STEER_REDUNDANT_STREAK = 2;
9
+ const DEFAULT_MIN_RANGE_LINES = 20;
10
+
11
+ function buildReadProgressRules() {
12
+ return [
13
+ 'Inspection progress rules:',
14
+ '- Map relevant files or sections once, then read targeted, non-overlapping ranges.',
15
+ '- Keep track of ranges already inspected. Re-read one only when a named unresolved question requires it.',
16
+ '- If a broad tool result is truncated, continue with bounded chunks; do not restart from the beginning.',
17
+ '- After the relevant evidence is covered, stop inspecting and complete the requested analysis or edit.',
18
+ '- Avoid narrating repeated plans such as “I will keep reading”; report substantive findings instead.'
19
+ ].join('\n');
20
+ }
21
+
22
+ function createReadProgressGuard(options = {}) {
23
+ const settings = {
24
+ minReadCommands: positiveInteger(options.minReadCommands, DEFAULT_MIN_READ_COMMANDS),
25
+ overlapRatio: boundedRatio(options.overlapRatio, DEFAULT_OVERLAP_RATIO),
26
+ redundantStreak: positiveInteger(options.redundantStreak, DEFAULT_REDUNDANT_STREAK),
27
+ postSteerRedundantStreak: positiveInteger(
28
+ options.postSteerRedundantStreak,
29
+ DEFAULT_POST_STEER_REDUNDANT_STREAK
30
+ ),
31
+ minRangeLines: positiveInteger(options.minRangeLines, DEFAULT_MIN_RANGE_LINES),
32
+ workspacePath: String(options.workspacePath || '')
33
+ };
34
+ const coverageByFile = new Map();
35
+ const commandCounts = new Map();
36
+ let readCount = 0;
37
+ let redundantStreak = 0;
38
+ let postSteerRedundantStreak = 0;
39
+ let steerIssued = false;
40
+ let abortIssued = false;
41
+
42
+ return {
43
+ observe(item) {
44
+ if (abortIssued) return noAction();
45
+ const inspection = extractReadInspection(item, settings.workspacePath);
46
+ if (!inspection) {
47
+ if (item?.type === 'commandExecution') {
48
+ redundantStreak = 0;
49
+ postSteerRedundantStreak = 0;
50
+ }
51
+ return noAction();
52
+ }
53
+
54
+ readCount += 1;
55
+ const signatureCount = (commandCounts.get(inspection.signature) || 0) + 1;
56
+ commandCounts.set(inspection.signature, signatureCount);
57
+ let overlapRatio = 0;
58
+ let redundant = signatureCount > 1;
59
+
60
+ if (inspection.range) {
61
+ const intervals = coverageByFile.get(inspection.fileKey) || [];
62
+ const overlapLines = countOverlapLines(intervals, inspection.range.startLine, inspection.range.endLine);
63
+ const rangeLines = inspection.range.endLine - inspection.range.startLine + 1;
64
+ overlapRatio = rangeLines > 0 ? overlapLines / rangeLines : 0;
65
+ if (rangeLines >= settings.minRangeLines && overlapRatio >= settings.overlapRatio) {
66
+ redundant = true;
67
+ }
68
+ coverageByFile.set(
69
+ inspection.fileKey,
70
+ mergeInterval(intervals, inspection.range.startLine, inspection.range.endLine)
71
+ );
72
+ }
73
+
74
+ if (redundant) {
75
+ redundantStreak += 1;
76
+ } else {
77
+ redundantStreak = 0;
78
+ }
79
+
80
+ const evidence = buildEvidence({
81
+ inspection,
82
+ overlapRatio,
83
+ readCount,
84
+ redundantStreak
85
+ });
86
+
87
+ if (steerIssued) {
88
+ postSteerRedundantStreak = redundant ? postSteerRedundantStreak + 1 : 0;
89
+ if (postSteerRedundantStreak >= settings.postSteerRedundantStreak) {
90
+ abortIssued = true;
91
+ return { action: 'abort', evidence };
92
+ }
93
+ return noAction(evidence);
94
+ }
95
+
96
+ if (readCount >= settings.minReadCommands && redundantStreak >= settings.redundantStreak) {
97
+ steerIssued = true;
98
+ postSteerRedundantStreak = 0;
99
+ return { action: 'steer', evidence };
100
+ }
101
+ return noAction(evidence);
102
+ },
103
+
104
+ snapshot() {
105
+ return {
106
+ readCount,
107
+ redundantStreak,
108
+ postSteerRedundantStreak,
109
+ steerIssued,
110
+ abortIssued
111
+ };
112
+ }
113
+ };
114
+ }
115
+
116
+ function createReadProgressController({ input = {}, request, fail, getTurn, emitEvent = () => {} } = {}) {
117
+ const guard = createReadProgressGuard({
118
+ workspacePath: input.workspacePath,
119
+ ...(input.readProgressGuardOptions || {})
120
+ });
121
+ let pendingSteer = null;
122
+ let steerInFlight = false;
123
+
124
+ return {
125
+ observe(item) {
126
+ const decision = guard.observe(item);
127
+ if (decision.action === 'steer') {
128
+ steer(decision);
129
+ } else if (decision.action === 'abort') {
130
+ emitEvent('codex.no_progress.aborted', 'Codex kept rereading inspected project content', {
131
+ ...decision.evidence,
132
+ guardAction: 'abort'
133
+ }, 'failed');
134
+ fail(createNoProgressError(decision.evidence));
135
+ return false;
136
+ }
137
+ return true;
138
+ },
139
+
140
+ flush() {
141
+ if (!pendingSteer) return;
142
+ const decision = pendingSteer;
143
+ pendingSteer = null;
144
+ steer(decision);
145
+ }
146
+ };
147
+
148
+ function steer(decision) {
149
+ if (steerInFlight) return;
150
+ const turn = getTurn?.() || {};
151
+ if (!turn.threadId || !turn.turnId) {
152
+ pendingSteer = decision;
153
+ return;
154
+ }
155
+ steerInFlight = true;
156
+ emitEvent('codex.no_progress.steered', 'Repeated file reads detected; asking Codex to synthesize', {
157
+ ...decision.evidence,
158
+ guardAction: 'steer'
159
+ }, 'warning');
160
+ request('turn/steer', {
161
+ threadId: turn.threadId,
162
+ expectedTurnId: turn.turnId,
163
+ input: [{
164
+ type: 'text',
165
+ text: buildReadProgressSteerText(decision.evidence, input.mode),
166
+ text_elements: []
167
+ }]
168
+ }).catch(error => {
169
+ const progressError = createNoProgressError(decision.evidence);
170
+ progressError.message += ` Automatic steering failed: ${String(error?.message || error).slice(0, 500)}`;
171
+ fail(progressError);
172
+ });
173
+ }
174
+ }
175
+
176
+ function extractReadInspection(item = {}, workspacePath = '') {
177
+ if (item.type !== 'commandExecution') return null;
178
+ const command = String(item.command || '').trim();
179
+ if (!command) return null;
180
+ const actions = Array.isArray(item.commandActions) ? item.commandActions : [];
181
+ const readAction = actions.find(action => action?.type === 'read');
182
+ if (!readAction && !looksLikeReadCommand(command)) return null;
183
+
184
+ const bounded = extractBoundedRange(command);
185
+ const rawPath = bounded?.path || readAction?.path || '';
186
+ const fileKey = normalizeFileKey(rawPath, workspacePath);
187
+ const displayPath = safeDisplayPath(fileKey, workspacePath) || path.basename(fileKey) || 'project file';
188
+ const range = bounded
189
+ ? { startLine: bounded.startLine, endLine: bounded.endLine }
190
+ : null;
191
+ return {
192
+ fileKey: fileKey || displayPath,
193
+ displayPath,
194
+ range,
195
+ signature: normalizeCommandSignature(command, workspacePath)
196
+ };
197
+ }
198
+
199
+ function extractBoundedRange(command) {
200
+ const source = String(command || '');
201
+ const sedMatch = source.match(/\bsed\s+(?:-[A-Za-z]+\s+)*["']?(\d+)\s*,\s*(\d+)p["']?(?:\s+((?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s;&|]+)))?/i);
202
+ if (sedMatch) {
203
+ return normalizeRangeMatch(sedMatch[1], sedMatch[2], sedMatch[3]);
204
+ }
205
+ const headMatch = source.match(/\bhead\s+(?:(?:-n|--lines)\s+|--lines=)(\d+)\s+((?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s;&|]+))/i);
206
+ if (headMatch) {
207
+ return normalizeRangeMatch(1, headMatch[1], headMatch[2]);
208
+ }
209
+ return null;
210
+ }
211
+
212
+ function normalizeRangeMatch(startValue, endValue, pathValue) {
213
+ const startLine = Number(startValue);
214
+ const endLine = Number(endValue);
215
+ if (!Number.isSafeInteger(startLine) || !Number.isSafeInteger(endLine) || startLine < 1 || endLine < startLine) {
216
+ return null;
217
+ }
218
+ return {
219
+ startLine,
220
+ endLine,
221
+ path: unquoteShellWord(pathValue)
222
+ };
223
+ }
224
+
225
+ function looksLikeReadCommand(command) {
226
+ return /^\s*(?:cat|head|tail|sed|nl)\b/i.test(command)
227
+ || /(?:^|[;&|]\s*)(?:cat|head|tail|sed|nl)\b/i.test(command);
228
+ }
229
+
230
+ function normalizeFileKey(value, workspacePath) {
231
+ const source = unquoteShellWord(value);
232
+ if (!source) return '';
233
+ if (path.isAbsolute(source)) return path.normalize(source);
234
+ return path.resolve(workspacePath || '.', source);
235
+ }
236
+
237
+ function safeDisplayPath(fileKey, workspacePath) {
238
+ if (!fileKey) return '';
239
+ if (workspacePath) {
240
+ const relative = path.relative(path.resolve(workspacePath), path.resolve(fileKey));
241
+ if (relative && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)) {
242
+ return relative.replace(/\\/g, '/');
243
+ }
244
+ if (!relative) return path.basename(fileKey);
245
+ }
246
+ return path.basename(fileKey);
247
+ }
248
+
249
+ function normalizeCommandSignature(command, workspacePath) {
250
+ let value = String(command || '').replace(/\s+/g, ' ').trim();
251
+ if (workspacePath) {
252
+ value = value.split(path.resolve(workspacePath)).join('<workspace>');
253
+ }
254
+ return value;
255
+ }
256
+
257
+ function countOverlapLines(intervals, startLine, endLine) {
258
+ let overlap = 0;
259
+ for (const interval of intervals) {
260
+ const start = Math.max(startLine, interval.startLine);
261
+ const end = Math.min(endLine, interval.endLine);
262
+ if (end >= start) overlap += end - start + 1;
263
+ }
264
+ return overlap;
265
+ }
266
+
267
+ function mergeInterval(intervals, startLine, endLine) {
268
+ const result = [];
269
+ let next = { startLine, endLine };
270
+ let inserted = false;
271
+ for (const interval of intervals) {
272
+ if (interval.endLine + 1 < next.startLine) {
273
+ result.push(interval);
274
+ continue;
275
+ }
276
+ if (next.endLine + 1 < interval.startLine) {
277
+ if (!inserted) {
278
+ result.push(next);
279
+ inserted = true;
280
+ }
281
+ result.push(interval);
282
+ continue;
283
+ }
284
+ next = {
285
+ startLine: Math.min(next.startLine, interval.startLine),
286
+ endLine: Math.max(next.endLine, interval.endLine)
287
+ };
288
+ }
289
+ if (!inserted) result.push(next);
290
+ return result;
291
+ }
292
+
293
+ function buildEvidence({ inspection, overlapRatio, readCount, redundantStreak }) {
294
+ return {
295
+ file: inspection.displayPath,
296
+ startLine: inspection.range?.startLine || null,
297
+ endLine: inspection.range?.endLine || null,
298
+ overlapRatio: Number(overlapRatio.toFixed(3)),
299
+ readCount,
300
+ redundantStreak
301
+ };
302
+ }
303
+
304
+ function buildReadProgressSteerText(evidence = {}, mode = 'ask') {
305
+ const location = evidence.startLine && evidence.endLine
306
+ ? `${evidence.file}:${evidence.startLine}-${evidence.endLine}`
307
+ : evidence.file;
308
+ const overlap = Math.round(Number(evidence.overlapRatio || 0) * 100);
309
+ const action = mode === 'ask'
310
+ ? 'Produce the final analysis now, with concrete findings and explicit uncertainty where needed.'
311
+ : 'Proceed with the requested edit using the evidence already gathered, then report the result.';
312
+ return [
313
+ `Runtime progress check: the latest read of ${location || 'a project file'} overlapped already inspected content by ${overlap}%.`,
314
+ 'Stop issuing inspection commands and do not reread covered ranges.',
315
+ action,
316
+ 'Do not mention this progress check in the user-facing answer.'
317
+ ].join(' ');
318
+ }
319
+
320
+ function createNoProgressError(evidence = {}) {
321
+ const file = evidence.file || 'project files';
322
+ const error = new Error(
323
+ `Codex continued rereading already inspected ranges in ${file} after an automatic progress correction. ` +
324
+ 'The run was stopped to prevent an unbounded tool loop. Retry with a narrower @file or section scope.'
325
+ );
326
+ error.code = 'codex_no_usable_result';
327
+ return error;
328
+ }
329
+
330
+ function unquoteShellWord(value) {
331
+ const text = String(value || '').trim();
332
+ if (text.length >= 2 && ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'")))) {
333
+ return text.slice(1, -1);
334
+ }
335
+ return text;
336
+ }
337
+
338
+ function positiveInteger(value, fallback) {
339
+ const parsed = Number(value);
340
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
341
+ }
342
+
343
+ function boundedRatio(value, fallback) {
344
+ const parsed = Number(value);
345
+ return Number.isFinite(parsed) && parsed > 0 && parsed <= 1 ? parsed : fallback;
346
+ }
347
+
348
+ function noAction(evidence = null) {
349
+ return { action: 'none', evidence };
350
+ }
351
+
352
+ module.exports = {
353
+ buildReadProgressRules,
354
+ buildReadProgressSteerText,
355
+ createNoProgressError,
356
+ createReadProgressController,
357
+ createReadProgressGuard,
358
+ extractBoundedRange,
359
+ extractReadInspection
360
+ };
@@ -10,6 +10,13 @@ const {
10
10
  } = require('../../extension/src/shared/compatibility');
11
11
  const { runCodexSession } = require('./codexSessionRunner');
12
12
  const { resolveCodexModels } = require('./codexModels');
13
+ const {
14
+ handleProviderRequest,
15
+ isProviderMethod,
16
+ providerErrorResponse,
17
+ resolveProviderModels,
18
+ resolveRunProviderForRun
19
+ } = require('./providerRuntime');
13
20
  const { clearPluginCodexHistory } = require('./codexHome');
14
21
  const { logDebug, truncateText } = require('./debugLog');
15
22
  const { HOST_NAME } = require('./manifest');
@@ -32,7 +39,7 @@ const activeRunControllers = new Map();
32
39
  const activeRunByProject = new Map();
33
40
  const pendingPlans = new Map();
34
41
  const PENDING_PLAN_TTL_MS = 30 * 60 * 1000;
35
- const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed']);
42
+ const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed', 'codex_no_usable_result']);
36
43
 
37
44
  async function handleRequest(request, env = process.env, emit = () => {}) {
38
45
  if (!request || typeof request !== 'object') {
@@ -48,7 +55,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
48
55
  return okResponse(request.id, {
49
56
  host: HOST_NAME,
50
57
  platform: getNativeRuntimePlatform({ env }),
51
- protocolVersion: 1,
58
+ protocolVersion: 2,
52
59
  supportedProtocol: { ...SUPPORTED_NATIVE_PROTOCOL },
53
60
  capabilities: Object.fromEntries(REQUIRED_CAPABILITIES.map(capability => [capability, true])),
54
61
  minExtensionVersion: MIN_COMPATIBLE_EXTENSION_VERSION,
@@ -77,8 +84,20 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
77
84
  return handleMirrorScanSensitive(request, env);
78
85
  }
79
86
 
87
+ if (isProviderMethod(request.method)) {
88
+ return handleProviderRequest(request, env, emit);
89
+ }
90
+
80
91
  if (request.method === 'codex.models') {
81
- return okResponse(request.id, resolveCodexModels(request.params || {}, env));
92
+ try {
93
+ return okResponse(request.id, resolveProviderModels(
94
+ request.params || {},
95
+ env,
96
+ resolveCodexModels
97
+ ));
98
+ } catch (error) {
99
+ return providerErrorResponse(request.id, error);
100
+ }
82
101
  }
83
102
 
84
103
  if (request.method === 'codex.run') {
@@ -130,7 +149,7 @@ function quotaErrorResponse(id, violation) {
130
149
  }
131
150
 
132
151
  async function handleCodexRun(request, env, emit) {
133
- const params = request.params || {};
152
+ let params = request.params || {};
134
153
  if (isCodexMissing(env)) {
135
154
  return errorResponse(
136
155
  request.id,
@@ -149,7 +168,23 @@ async function handleCodexRun(request, env, emit) {
149
168
  activeRunControllers.set(request.id, abortController);
150
169
  activeRunByProject.set(projectKey, { id: request.id, controller: abortController });
151
170
  }
171
+ let providerResolution;
152
172
  try {
173
+ try {
174
+ providerResolution = await resolveRunProviderForRun(params, env, {
175
+ signal: abortController.signal,
176
+ emit
177
+ });
178
+ params = {
179
+ ...params,
180
+ model: providerResolution.modelId,
181
+ reasoningEffort: providerResolution.reasoningEffort,
182
+ providerSelection: providerResolution.providerSelection
183
+ };
184
+ } catch (error) {
185
+ return providerErrorResponse(request.id, error);
186
+ }
187
+
153
188
  if (params.useExistingMirror) {
154
189
  const { getMirrorStatus, applyFileOverlays } = require('./mirrorWorkspace');
155
190
  const rootDir = env.CODEX_OVERLEAF_MIRROR_ROOT;
@@ -190,6 +225,7 @@ async function handleCodexRun(request, env, emit) {
190
225
  env,
191
226
  emit,
192
227
  rootDir: env.CODEX_OVERLEAF_MIRROR_ROOT,
228
+ providerLaunch: providerResolution.providerLaunch,
193
229
  signal: abortController.signal
194
230
  });
195
231
  const syncChanges = Array.isArray(result.syncChanges) ? result.syncChanges : [];
@@ -176,6 +176,7 @@ function isAllowedUpdatePath(value) {
176
176
  return entryPath === 'extension-runtime/runtime-manifest.json'
177
177
  || /^extension-runtime\/src\/.+/.test(entryPath)
178
178
  || /^extension-runtime\/styles\/.+/.test(entryPath)
179
+ || /^extension-runtime\/vendor\/katex\/(?:LICENSE|VERSION|katex\.min\.(?:css|js)|fonts\/[A-Za-z0-9_.-]+\.(?:ttf|woff2?))$/.test(entryPath)
179
180
  || entryPath === 'native-runtime/package.json'
180
181
  || /^native-runtime\/native-host\/src\/.+/.test(entryPath)
181
182
  || /^native-runtime\/extension\/src\/shared\/.+/.test(entryPath)
@@ -58,7 +58,8 @@ function validateReleaseManifest(manifest, options = {}) {
58
58
  throw updateError('update_manifest_origin_mismatch', 'Update manifest repository or channel does not match this installation.');
59
59
  }
60
60
  const version = parseSemver(manifest.version);
61
- if (!version || manifest.tag !== 'v' + manifest.version) {
61
+ const expectedTag = options.tag || 'v' + manifest.version;
62
+ if (!version || manifest.tag !== expectedTag) {
62
63
  throw updateError('update_manifest_version_invalid', 'Update manifest version or tag is invalid.');
63
64
  }
64
65
  if (!Number.isInteger(manifest.bootstrapProtocol) || manifest.bootstrapProtocol !== bootstrapProtocol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "1.9.13",
3
+ "version": "2.1.0",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -45,6 +45,7 @@
45
45
  "extension/runtime-manifest.json",
46
46
  "extension/src/",
47
47
  "extension/styles/",
48
+ "extension/vendor/",
48
49
  "scripts/codex-json-agent.mjs",
49
50
  "scripts/install-native-host.mjs",
50
51
  "scripts/install-managed.mjs",