codex-overleaf-link 2.0.0 → 2.1.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.
- package/README.md +24 -23
- package/extension/bootstrap/manifest.template.json +2 -1
- package/extension/runtime-manifest.json +12 -6
- package/extension/src/background.js +7 -0
- package/extension/src/content/composerPanel.js +13 -3
- package/extension/src/content/contentRuntime.js +129 -64
- package/extension/src/content/contextTray.js +173 -9
- package/extension/src/content/markdownText.js +16 -1
- package/extension/src/content/mathText.js +233 -0
- package/extension/src/content/modelPicker.js +117 -106
- package/extension/src/content/modelPickerSupport.js +115 -0
- package/extension/src/content/nativeChannel.js +20 -1
- package/extension/src/content/panelMaintenance.js +2 -0
- package/extension/src/content/projectSettingsCoordinator.js +2 -0
- package/extension/src/content/providerSettingsCoordinator.js +413 -0
- package/extension/src/content/providerSettingsDialog.js +1006 -0
- package/extension/src/content/runTimelineView.js +24 -5
- package/extension/src/content/settingsPanel.js +33 -2
- package/extension/src/content/writebackOrchestrator.js +3 -1
- package/extension/src/page/overleafProjectSnapshot.js +29 -33
- package/extension/src/page/snapshotRouter.js +75 -8
- package/extension/src/page/writebackRouter.js +20 -33
- package/extension/src/pageBridge.js +1 -1
- package/extension/src/shared/agentTranscript.js +130 -0
- package/extension/src/shared/compatibility.js +3 -2
- package/extension/src/shared/failureReasons.js +75 -0
- package/extension/src/shared/i18n.js +22 -0
- package/extension/src/shared/models.js +1 -0
- package/extension/src/shared/providerProfiles.js +283 -0
- package/extension/src/shared/sessionState.js +12 -2
- package/extension/src/shared/storageDb.js +1 -1
- package/extension/styles/panel.css +689 -1
- package/extension/vendor/katex/LICENSE +21 -0
- package/extension/vendor/katex/VERSION +2 -0
- package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/extension/vendor/katex/katex.min.css +1 -0
- package/extension/vendor/katex/katex.min.js +1 -0
- package/native-host/src/anthropicBridgeRequest.js +435 -0
- package/native-host/src/anthropicBridgeResponse.js +431 -0
- package/native-host/src/anthropicMessagesBridge.js +281 -0
- package/native-host/src/chatBridgeRequest.js +274 -0
- package/native-host/src/chatBridgeResponse.js +594 -0
- package/native-host/src/chatCompletionsBridge.js +485 -0
- package/native-host/src/codexCommand.js +25 -0
- package/native-host/src/codexPromptAssembly.js +9 -0
- package/native-host/src/codexProviderLaunch.js +213 -0
- package/native-host/src/codexProviderTest.js +196 -0
- package/native-host/src/codexSessionRunner.js +29 -25
- package/native-host/src/managedInstall.js +1 -0
- package/native-host/src/providerBridgeRoutes.js +11 -0
- package/native-host/src/providerModelCatalog.js +107 -0
- package/native-host/src/providerProfile.js +454 -0
- package/native-host/src/providerReasoning.js +235 -0
- package/native-host/src/providerRedaction.js +18 -0
- package/native-host/src/providerRuntime.js +365 -0
- package/native-host/src/providerStore.js +691 -0
- package/native-host/src/readProgressGuard.js +360 -0
- package/native-host/src/taskRunnerRuntime.js +39 -3
- package/native-host/src/updateArchive.js +1 -0
- 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') {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-overleaf-link",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
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",
|