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,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function sanitizeProviderMessage(value, secrets = [], maxLength = 1200) {
|
|
4
|
+
let text = String(value || 'Provider operation failed.');
|
|
5
|
+
const exactSecrets = Array.from(new Set((Array.isArray(secrets) ? secrets : [secrets])
|
|
6
|
+
.map(secret => String(secret || ''))
|
|
7
|
+
.filter(Boolean)))
|
|
8
|
+
.sort((left, right) => right.length - left.length);
|
|
9
|
+
for (const secret of exactSecrets) {
|
|
10
|
+
text = text.split(secret).join('[redacted]');
|
|
11
|
+
}
|
|
12
|
+
return text
|
|
13
|
+
.replace(/Bearer\s+[^\s,;]+/gi, 'Bearer [redacted]')
|
|
14
|
+
.replace(/(?:api[_ -]?key|token)\s*[:=]\s*[^\s,;]+/gi, '$1=[redacted]')
|
|
15
|
+
.slice(0, maxLength);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { sanitizeProviderMessage };
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const {
|
|
5
|
+
activateProvider,
|
|
6
|
+
clearProviderSecret,
|
|
7
|
+
deleteProvider,
|
|
8
|
+
listProviders,
|
|
9
|
+
loadProviderState,
|
|
10
|
+
recordProviderDiagnostic,
|
|
11
|
+
resolveDraftSecret,
|
|
12
|
+
upsertProvider
|
|
13
|
+
} = require('./providerStore');
|
|
14
|
+
const {
|
|
15
|
+
computeConnectionFingerprint,
|
|
16
|
+
computeDraftFingerprint,
|
|
17
|
+
getEndpointHost,
|
|
18
|
+
normalizeProviderDraft,
|
|
19
|
+
providerError
|
|
20
|
+
} = require('./providerProfile');
|
|
21
|
+
const { sanitizeProviderMessage } = require('./providerRedaction');
|
|
22
|
+
const { createProviderLaunch } = require('./codexProviderLaunch');
|
|
23
|
+
const { runProviderConnectionTest } = require('./codexProviderTest');
|
|
24
|
+
const { getReasoningControl } = require('./providerReasoning');
|
|
25
|
+
|
|
26
|
+
const PROVIDER_METHODS = new Set([
|
|
27
|
+
'codex.providers.list',
|
|
28
|
+
'codex.providers.test',
|
|
29
|
+
'codex.providers.test.cancel',
|
|
30
|
+
'codex.providers.upsert',
|
|
31
|
+
'codex.providers.activate',
|
|
32
|
+
'codex.providers.clear-secret',
|
|
33
|
+
'codex.providers.delete'
|
|
34
|
+
]);
|
|
35
|
+
const AUTO_PROTOCOL_FALLBACK_CODES = new Set([
|
|
36
|
+
'provider_protocol_incompatible'
|
|
37
|
+
]);
|
|
38
|
+
const AUTO_RESPONSE_MODE_FALLBACK_CODES = new Set([
|
|
39
|
+
'provider_agent_tools_incompatible',
|
|
40
|
+
'provider_stream_tool_parse_failed'
|
|
41
|
+
]);
|
|
42
|
+
const activeTests = new Map();
|
|
43
|
+
|
|
44
|
+
function isProviderMethod(method) {
|
|
45
|
+
return PROVIDER_METHODS.has(method);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function handleProviderRequest(request, env = process.env, emit = () => {}) {
|
|
49
|
+
try {
|
|
50
|
+
const params = request.params || {};
|
|
51
|
+
switch (request.method) {
|
|
52
|
+
case 'codex.providers.list':
|
|
53
|
+
return okResponse(request.id, listProviders(env));
|
|
54
|
+
case 'codex.providers.upsert':
|
|
55
|
+
return okResponse(request.id, upsertProvider(params, env));
|
|
56
|
+
case 'codex.providers.activate':
|
|
57
|
+
return okResponse(request.id, activateProvider(params, env));
|
|
58
|
+
case 'codex.providers.clear-secret':
|
|
59
|
+
return okResponse(request.id, clearProviderSecret(params, env));
|
|
60
|
+
case 'codex.providers.delete':
|
|
61
|
+
return okResponse(request.id, deleteProvider(params, env));
|
|
62
|
+
case 'codex.providers.test.cancel':
|
|
63
|
+
return okResponse(request.id, cancelProviderTest(params.operationId));
|
|
64
|
+
case 'codex.providers.test':
|
|
65
|
+
return okResponse(request.id, await testProvider(params, env, emit));
|
|
66
|
+
default:
|
|
67
|
+
throw providerError('method_not_found', `Unknown provider method: ${request.method}`);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return providerErrorResponse(request.id, error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function testProvider(params, env, emit = () => {}, externalSignal = null) {
|
|
75
|
+
const operationId = String(params.operationId || '');
|
|
76
|
+
if (!operationId) {
|
|
77
|
+
throw providerError('invalid_request', 'Provider test requires an operation id.');
|
|
78
|
+
}
|
|
79
|
+
const draft = normalizeProviderDraft(params.draft || {});
|
|
80
|
+
const modelId = String(params.modelId || draft.defaultModelId).trim();
|
|
81
|
+
if (!draft.models.some(model => model.id === modelId)) {
|
|
82
|
+
throw providerError('provider_model_not_configured', 'The selected model is not configured for this provider.');
|
|
83
|
+
}
|
|
84
|
+
const secret = resolveDraftSecret(params, env);
|
|
85
|
+
const fingerprint = computeConnectionFingerprint(draft, secret, modelId);
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const totalBudgetMs = Math.min(120000, Math.max(15000, Number(params.totalBudgetMs) || 120000));
|
|
88
|
+
let budgetExpired = false;
|
|
89
|
+
const budgetTimer = setTimeout(() => {
|
|
90
|
+
budgetExpired = true;
|
|
91
|
+
controller.abort();
|
|
92
|
+
}, totalBudgetMs);
|
|
93
|
+
const onExternalAbort = () => controller.abort();
|
|
94
|
+
externalSignal?.addEventListener?.('abort', onExternalAbort, { once: true });
|
|
95
|
+
activeTests.get(operationId)?.abort?.();
|
|
96
|
+
activeTests.set(operationId, controller);
|
|
97
|
+
const protocols = draft.wireApiPreference === 'auto'
|
|
98
|
+
? ['responses', 'chat', 'anthropic']
|
|
99
|
+
: [draft.wireApiPreference];
|
|
100
|
+
try {
|
|
101
|
+
let lastError;
|
|
102
|
+
const attempts = protocols.flatMap(wireApi => {
|
|
103
|
+
const model = draft.models.find(item => item.id === modelId) || {};
|
|
104
|
+
const requestedMode = model.upstreamResponseMode || 'auto';
|
|
105
|
+
return wireApi === 'chat' && requestedMode === 'auto'
|
|
106
|
+
? [{ wireApi, upstreamResponseMode: 'streaming' }, { wireApi, upstreamResponseMode: 'buffered' }]
|
|
107
|
+
: [{ wireApi, upstreamResponseMode: requestedMode === 'buffered' ? 'buffered' : 'streaming' }];
|
|
108
|
+
});
|
|
109
|
+
let attemptNumber = 0;
|
|
110
|
+
for (const wireApi of protocols) {
|
|
111
|
+
const model = draft.models.find(item => item.id === modelId) || {};
|
|
112
|
+
const requestedMode = model.upstreamResponseMode || 'auto';
|
|
113
|
+
const responseModes = wireApi === 'chat' && requestedMode === 'auto'
|
|
114
|
+
? ['streaming', 'buffered']
|
|
115
|
+
: [requestedMode === 'buffered' ? 'buffered' : 'streaming'];
|
|
116
|
+
let protocolError;
|
|
117
|
+
for (let index = 0; index < responseModes.length; index += 1) {
|
|
118
|
+
const upstreamResponseMode = responseModes[index];
|
|
119
|
+
attemptNumber += 1;
|
|
120
|
+
emitProviderTestProgress(emit, {
|
|
121
|
+
operationId,
|
|
122
|
+
modelId,
|
|
123
|
+
wireApi,
|
|
124
|
+
upstreamResponseMode,
|
|
125
|
+
attempt: attemptNumber,
|
|
126
|
+
totalAttempts: attempts.length
|
|
127
|
+
});
|
|
128
|
+
const launch = createProviderLaunch({
|
|
129
|
+
profile: { id: params.profileId || 'draft', revision: params.expectedRevision || 0, ...draft },
|
|
130
|
+
secret,
|
|
131
|
+
modelId,
|
|
132
|
+
wireApi,
|
|
133
|
+
upstreamResponseMode
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
const result = await runProviderConnectionTest({ launch, env, signal: controller.signal });
|
|
137
|
+
const verification = {
|
|
138
|
+
ok: true,
|
|
139
|
+
operationId,
|
|
140
|
+
draftFingerprint: fingerprint,
|
|
141
|
+
connectionFingerprint: fingerprint,
|
|
142
|
+
resolvedWireApi: wireApi,
|
|
143
|
+
resolvedUpstreamResponseMode: upstreamResponseMode,
|
|
144
|
+
testedModelId: modelId,
|
|
145
|
+
modelId,
|
|
146
|
+
durationMs: result.durationMs,
|
|
147
|
+
capabilities: {
|
|
148
|
+
...(result.capabilities || { text: true, agentTools: true }),
|
|
149
|
+
upstreamStreaming: upstreamResponseMode === 'streaming'
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
if (params.profileId) {
|
|
153
|
+
recordProviderDiagnostic({
|
|
154
|
+
profileId: params.profileId,
|
|
155
|
+
expectedRevision: params.expectedRevision,
|
|
156
|
+
modelId,
|
|
157
|
+
connectionFingerprint: fingerprint,
|
|
158
|
+
wireApi,
|
|
159
|
+
upstreamResponseMode,
|
|
160
|
+
durationMs: result.durationMs
|
|
161
|
+
}, env);
|
|
162
|
+
}
|
|
163
|
+
return verification;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
protocolError = error;
|
|
166
|
+
const canTryBuffered = responseModes[index + 1] === 'buffered'
|
|
167
|
+
&& AUTO_RESPONSE_MODE_FALLBACK_CODES.has(error.code);
|
|
168
|
+
if (!canTryBuffered) break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
lastError = protocolError;
|
|
172
|
+
const canTryNextProtocol = draft.wireApiPreference === 'auto'
|
|
173
|
+
&& wireApi !== protocols[protocols.length - 1]
|
|
174
|
+
&& AUTO_PROTOCOL_FALLBACK_CODES.has(protocolError?.code);
|
|
175
|
+
if (!canTryNextProtocol) throw protocolError;
|
|
176
|
+
}
|
|
177
|
+
throw lastError || providerError('provider_protocol_incompatible', 'No compatible provider protocol was found.');
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (budgetExpired) {
|
|
180
|
+
error = providerError('provider_connection_timeout', 'The provider compatibility test exceeded its total time budget.');
|
|
181
|
+
}
|
|
182
|
+
error.message = sanitizeProviderMessage(error?.message, [secret]);
|
|
183
|
+
throw error;
|
|
184
|
+
} finally {
|
|
185
|
+
clearTimeout(budgetTimer);
|
|
186
|
+
externalSignal?.removeEventListener?.('abort', onExternalAbort);
|
|
187
|
+
if (activeTests.get(operationId) === controller) {
|
|
188
|
+
activeTests.delete(operationId);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function emitProviderTestProgress(emit, detail) {
|
|
194
|
+
emit({
|
|
195
|
+
type: 'provider.test.progress',
|
|
196
|
+
title: `Testing ${detail.modelId}: ${detail.wireApi} · ${detail.upstreamResponseMode} (${detail.attempt}/${detail.totalAttempts})`,
|
|
197
|
+
status: 'running',
|
|
198
|
+
detail,
|
|
199
|
+
timestamp: new Date().toISOString()
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function cancelProviderTest(operationId) {
|
|
204
|
+
const id = String(operationId || '');
|
|
205
|
+
const controller = activeTests.get(id);
|
|
206
|
+
if (!controller) {
|
|
207
|
+
return { cancelled: false };
|
|
208
|
+
}
|
|
209
|
+
controller.abort();
|
|
210
|
+
activeTests.delete(id);
|
|
211
|
+
return { cancelled: true };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function resolveProviderModels(params = {}, env = process.env, resolveBuiltInModels) {
|
|
215
|
+
const state = loadProviderState(env);
|
|
216
|
+
if (state.public.activeProviderId === 'builtin') {
|
|
217
|
+
return resolveBuiltInModels(params, env);
|
|
218
|
+
}
|
|
219
|
+
const profile = state.public.profiles.find(item => item.id === state.public.activeProviderId);
|
|
220
|
+
if (!profile) {
|
|
221
|
+
throw providerError('provider_not_found', 'The active provider no longer exists.');
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
providerId: profile.id,
|
|
225
|
+
providerRevision: profile.revision,
|
|
226
|
+
providerName: profile.name,
|
|
227
|
+
models: profile.models.map(model => {
|
|
228
|
+
const reasoning = getReasoningControl(profile, model);
|
|
229
|
+
return {
|
|
230
|
+
id: model.id,
|
|
231
|
+
label: model.label || model.id,
|
|
232
|
+
reasoningEfforts: reasoning.efforts,
|
|
233
|
+
defaultReasoningEffort: reasoning.defaultEffort,
|
|
234
|
+
reasoningPresentation: reasoning.presentation,
|
|
235
|
+
speedTiers: ['standard'],
|
|
236
|
+
defaultSpeedTier: 'standard'
|
|
237
|
+
};
|
|
238
|
+
}),
|
|
239
|
+
source: 'custom-provider',
|
|
240
|
+
fetchedAt: new Date().toISOString()
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function resolveRunProvider(params = {}, env = process.env) {
|
|
245
|
+
const state = loadProviderState(env);
|
|
246
|
+
const selection = params.providerSelection;
|
|
247
|
+
const activeProviderId = state.public.activeProviderId || 'builtin';
|
|
248
|
+
if (selection?.providerId && selection.providerId !== activeProviderId) {
|
|
249
|
+
throw providerError('provider_revision_conflict', 'The active provider changed in another tab. Refresh and retry.');
|
|
250
|
+
}
|
|
251
|
+
if (activeProviderId === 'builtin') {
|
|
252
|
+
return {
|
|
253
|
+
modelId: params.model || '',
|
|
254
|
+
reasoningEffort: params.reasoningEffort || '',
|
|
255
|
+
providerLaunch: null,
|
|
256
|
+
providerSelection: { providerId: 'builtin', providerRevision: 0 }
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const profile = state.public.profiles.find(item => item.id === activeProviderId);
|
|
260
|
+
if (!profile) {
|
|
261
|
+
throw providerError('provider_not_found', 'The active provider no longer exists.');
|
|
262
|
+
}
|
|
263
|
+
if (!selection?.providerId) {
|
|
264
|
+
throw providerError('provider_selection_unavailable', 'The active provider has not been confirmed by this Overleaf tab. Wait for Provider settings to load and retry.');
|
|
265
|
+
}
|
|
266
|
+
if (Number(selection.providerRevision) !== profile.revision) {
|
|
267
|
+
throw providerError('provider_revision_conflict', 'The active provider changed in another tab. Refresh and retry.');
|
|
268
|
+
}
|
|
269
|
+
const endpointHost = getEndpointHost(profile.baseUrl);
|
|
270
|
+
if (profile.endpointDisclosureHost !== endpointHost || profile.endpointDisclosureBaseUrl !== profile.baseUrl) {
|
|
271
|
+
throw providerError('provider_disclosure_required', 'Confirm the current provider endpoint before sending project content.');
|
|
272
|
+
}
|
|
273
|
+
const modelId = String(params.model || profile.defaultModelId).trim();
|
|
274
|
+
const model = profile.models.find(item => item.id === modelId);
|
|
275
|
+
if (!model) {
|
|
276
|
+
throw providerError('provider_model_not_configured', 'The selected model is not configured for the active provider.');
|
|
277
|
+
}
|
|
278
|
+
const secret = state.secrets.secrets[profile.id] || '';
|
|
279
|
+
const connectionFingerprint = computeConnectionFingerprint(profile, secret, modelId);
|
|
280
|
+
const diagnostic = profile.modelDiagnostics?.[modelId];
|
|
281
|
+
const validDiagnostic = diagnostic?.connectionFingerprint === connectionFingerprint
|
|
282
|
+
&& ['responses', 'chat', 'anthropic'].includes(diagnostic.wireApi);
|
|
283
|
+
const wireApi = profile.wireApiPreference === 'auto'
|
|
284
|
+
? (validDiagnostic ? diagnostic.wireApi : '')
|
|
285
|
+
: profile.wireApiPreference;
|
|
286
|
+
if (!wireApi) {
|
|
287
|
+
throw providerError('provider_protocol_negotiation_required', 'The Auto provider needs a one-time compatibility negotiation for this model.');
|
|
288
|
+
}
|
|
289
|
+
const reasoning = getReasoningControl(profile, model);
|
|
290
|
+
const supportedReasoningEfforts = reasoning.efforts;
|
|
291
|
+
const reasoningEffort = supportedReasoningEfforts.includes(params.reasoningEffort)
|
|
292
|
+
? params.reasoningEffort
|
|
293
|
+
: reasoning.defaultEffort;
|
|
294
|
+
return {
|
|
295
|
+
modelId,
|
|
296
|
+
reasoningEffort,
|
|
297
|
+
providerLaunch: createProviderLaunch({
|
|
298
|
+
profile,
|
|
299
|
+
secret,
|
|
300
|
+
modelId,
|
|
301
|
+
wireApi,
|
|
302
|
+
reasoningEffort,
|
|
303
|
+
upstreamResponseMode: validDiagnostic ? diagnostic.upstreamResponseMode : ''
|
|
304
|
+
}),
|
|
305
|
+
providerSelection: { providerId: profile.id, providerRevision: profile.revision }
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function resolveRunProviderForRun(params = {}, env = process.env, options = {}) {
|
|
310
|
+
try {
|
|
311
|
+
return resolveRunProvider(params, env);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
if (error?.code !== 'provider_protocol_negotiation_required') {
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const state = loadProviderState(env);
|
|
318
|
+
const profile = state.public.profiles.find(item => item.id === state.public.activeProviderId);
|
|
319
|
+
if (!profile) {
|
|
320
|
+
throw providerError('provider_not_found', 'The active provider no longer exists.');
|
|
321
|
+
}
|
|
322
|
+
const modelId = String(params.model || profile.defaultModelId).trim();
|
|
323
|
+
options.emit?.({
|
|
324
|
+
type: 'provider.auto-negotiation.started',
|
|
325
|
+
title: `Detecting a compatible API route for ${modelId}.`,
|
|
326
|
+
status: 'running',
|
|
327
|
+
detail: { providerId: profile.id, modelId },
|
|
328
|
+
timestamp: new Date().toISOString()
|
|
329
|
+
});
|
|
330
|
+
await testProvider({
|
|
331
|
+
operationId: `run-${crypto.randomUUID()}`,
|
|
332
|
+
profileId: profile.id,
|
|
333
|
+
expectedRevision: profile.revision,
|
|
334
|
+
draft: profile,
|
|
335
|
+
secretMutation: { kind: 'unchanged' },
|
|
336
|
+
modelId,
|
|
337
|
+
totalBudgetMs: 120000
|
|
338
|
+
}, env, options.emit, options.signal);
|
|
339
|
+
return resolveRunProvider(params, env);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function providerErrorResponse(id, error) {
|
|
343
|
+
const code = error?.code || 'provider_operation_failed';
|
|
344
|
+
return {
|
|
345
|
+
id,
|
|
346
|
+
ok: false,
|
|
347
|
+
error: {
|
|
348
|
+
code,
|
|
349
|
+
message: `[${code}] ${sanitizeProviderMessage(error?.message || 'Provider operation failed.')}`
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function okResponse(id, result) {
|
|
355
|
+
return { id, ok: true, result };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
module.exports = {
|
|
359
|
+
handleProviderRequest,
|
|
360
|
+
isProviderMethod,
|
|
361
|
+
providerErrorResponse,
|
|
362
|
+
resolveProviderModels,
|
|
363
|
+
resolveRunProvider,
|
|
364
|
+
resolveRunProviderForRun
|
|
365
|
+
};
|