codex-overleaf-link 2.2.1 → 2.3.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 +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +8 -6
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +17 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -2686,13 +2686,23 @@
|
|
|
2686
2686
|
}
|
|
2687
2687
|
|
|
2688
2688
|
#codex-overleaf-panel .run-math--fallback {
|
|
2689
|
-
border-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2689
|
+
border-bottom: 1px dotted var(--tl-fg-4);
|
|
2690
|
+
border-radius: 0;
|
|
2691
|
+
background: transparent;
|
|
2692
|
+
color: inherit;
|
|
2693
|
+
padding: 0 2px;
|
|
2693
2694
|
white-space: pre-wrap;
|
|
2694
2695
|
overflow-wrap: anywhere;
|
|
2695
|
-
font:
|
|
2696
|
+
font: inherit;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
#codex-overleaf-panel .run-math--display.run-math--fallback {
|
|
2700
|
+
border: 1px solid var(--tl-border);
|
|
2701
|
+
border-left: 3px solid var(--tl-fg-4);
|
|
2702
|
+
border-radius: 6px;
|
|
2703
|
+
background: var(--tl-surface-2);
|
|
2704
|
+
padding: 8px 10px;
|
|
2705
|
+
text-align: left;
|
|
2696
2706
|
}
|
|
2697
2707
|
|
|
2698
2708
|
#codex-overleaf-panel .run-stream[data-stream-role="reasoning"] .run-stream-text {
|
|
@@ -3173,12 +3183,12 @@
|
|
|
3173
3183
|
white-space: nowrap;
|
|
3174
3184
|
}
|
|
3175
3185
|
|
|
3176
|
-
/* Mode switch (Ask/
|
|
3177
|
-
holding
|
|
3186
|
+
/* Mode switch (Ask/Auto) — a real segmented control: a bordered track
|
|
3187
|
+
holding two choices. Inactive choices are muted; hover is a light fill
|
|
3178
3188
|
(distinct from the active accent-wash fill) so hover never reads as active. */
|
|
3179
3189
|
#codex-overleaf-panel .codex-mode-switch {
|
|
3180
3190
|
display: grid;
|
|
3181
|
-
grid-template-columns: repeat(
|
|
3191
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
3182
3192
|
gap: 2px;
|
|
3183
3193
|
min-width: 0;
|
|
3184
3194
|
padding: 2px;
|
|
@@ -64,7 +64,7 @@ function buildCodexTurnPrompt(options = {}) {
|
|
|
64
64
|
'Mode for this turn:',
|
|
65
65
|
`- ${context.mode}`,
|
|
66
66
|
'- ask: inspect and explain only; do not edit files.',
|
|
67
|
-
'-
|
|
67
|
+
'- auto: edit the local workspace directly when the request calls for changes. The browser bridge handles authorization, deletion approval, and syncing back to Overleaf.',
|
|
68
68
|
'',
|
|
69
69
|
'Write expectation for this turn:',
|
|
70
70
|
formatWriteExpectation({
|
|
@@ -7,7 +7,7 @@ const { spawnSync } = require('node:child_process');
|
|
|
7
7
|
|
|
8
8
|
const RUNTIME_ENV_KEY = 'CODEX_OVERLEAF_CODEX_RUNTIME_JSON';
|
|
9
9
|
const MAX_CODEX_CANDIDATES = 8;
|
|
10
|
-
const VERSION_TIMEOUT_MS =
|
|
10
|
+
const VERSION_TIMEOUT_MS = 3000;
|
|
11
11
|
|
|
12
12
|
function buildCodexRuntimeIdentity(options = {}) {
|
|
13
13
|
const env = options.env || process.env;
|
|
@@ -10,7 +10,10 @@ const { computeTextPatches } = require('./textPatch');
|
|
|
10
10
|
const { buildCodexHomeEnv } = require('./codexHome');
|
|
11
11
|
const { buildCodexSpeedArgs } = require('./codexArgs');
|
|
12
12
|
const { truncateText } = require('./debugLog');
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
estimateBase64DecodedBytes,
|
|
15
|
+
reduceTaskResult
|
|
16
|
+
} = require('./nativeTransportEnvelope');
|
|
14
17
|
const { buildCodexTurnPrompt: buildCodexPromptParts } = require('./codexPromptAssembly');
|
|
15
18
|
const { evaluateSkillCommand } = require('./commandApproval');
|
|
16
19
|
const {
|
|
@@ -186,7 +189,7 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
|
|
|
186
189
|
throwIfAborted(signal);
|
|
187
190
|
|
|
188
191
|
if (skillInstallTurn) {
|
|
189
|
-
return
|
|
192
|
+
return reduceTaskResult({
|
|
190
193
|
status: 'completed',
|
|
191
194
|
projectId: mirror.projectKey,
|
|
192
195
|
workspacePath: mirror.workspacePath,
|
|
@@ -245,7 +248,7 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
|
|
|
245
248
|
ignoredChangedCount: rawSyncChanges.length,
|
|
246
249
|
ignoredUnsupportedCount: unsupportedChanges.length
|
|
247
250
|
}, rawSyncChanges.length || unsupportedChanges.length ? 'warning' : 'completed');
|
|
248
|
-
return
|
|
251
|
+
return reduceTaskResult({
|
|
249
252
|
status: 'completed',
|
|
250
253
|
projectId: mirror.projectKey,
|
|
251
254
|
workspacePath: mirror.workspacePath,
|
|
@@ -266,7 +269,7 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
|
|
|
266
269
|
}
|
|
267
270
|
return change;
|
|
268
271
|
});
|
|
269
|
-
const response =
|
|
272
|
+
const response = reduceTaskResult({
|
|
270
273
|
status: 'completed',
|
|
271
274
|
projectId: mirror.projectKey,
|
|
272
275
|
workspacePath: mirror.workspacePath,
|
|
@@ -370,15 +373,6 @@ function normalizeTurnAttachments(value) {
|
|
|
370
373
|
return result;
|
|
371
374
|
}
|
|
372
375
|
|
|
373
|
-
function estimateBase64DecodedBytes(value) {
|
|
374
|
-
const clean = String(value || '').replace(/\s+/g, '');
|
|
375
|
-
if (!clean) {
|
|
376
|
-
return 0;
|
|
377
|
-
}
|
|
378
|
-
const padding = clean.endsWith('==') ? 2 : clean.endsWith('=') ? 1 : 0;
|
|
379
|
-
return Math.max(0, Math.floor(clean.length * 3 / 4) - padding);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
376
|
function sanitizeAttachmentFileName(value) {
|
|
383
377
|
const basename = String(value || '')
|
|
384
378
|
.replace(/\0/g, '')
|
package/native-host/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const { decodeInputFrames, encodeOutputFrame } = require('./nativeTransportEnvelope');
|
|
5
5
|
const { logDebug } = require('./debugLog');
|
|
6
6
|
const { handleRequest } = require('./taskRunner');
|
|
7
7
|
const { abortAllActiveOperations, getActiveNativeWorkState } = require('./taskRunnerRuntime');
|
|
@@ -21,7 +21,7 @@ process.stdin.on('data', chunk => {
|
|
|
21
21
|
|
|
22
22
|
let decoded;
|
|
23
23
|
try {
|
|
24
|
-
decoded =
|
|
24
|
+
decoded = decodeInputFrames(buffered);
|
|
25
25
|
} catch (error) {
|
|
26
26
|
writeResponse({
|
|
27
27
|
ok: false,
|
|
@@ -111,24 +111,7 @@ function writeResponse(response) {
|
|
|
111
111
|
logDebug('stdout.write_skipped', { ok: response?.ok, code: response?.error?.code });
|
|
112
112
|
return false;
|
|
113
113
|
}
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
frame = encodeMessage(response);
|
|
117
|
-
} catch (error) {
|
|
118
|
-
const fallback = buildOversizeResponseFallback(response, error);
|
|
119
|
-
try {
|
|
120
|
-
frame = encodeMessage(fallback);
|
|
121
|
-
} catch (fallbackError) {
|
|
122
|
-
frame = encodeMessage({
|
|
123
|
-
id: response?.id,
|
|
124
|
-
ok: false,
|
|
125
|
-
error: {
|
|
126
|
-
code: 'native_response_too_large',
|
|
127
|
-
message: truncateForNativeFrame(fallbackError.message || error.message || 'Native response exceeded the browser frame limit.', 800)
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
114
|
+
const frame = encodeOutputFrame(response);
|
|
132
115
|
logDebug('stdout.write', { bytes: frame.length, ok: response?.ok, code: response?.error?.code });
|
|
133
116
|
try {
|
|
134
117
|
process.stdout.write(frame, error => {
|
|
@@ -168,55 +151,6 @@ function handleNativeDisconnect(source, error) {
|
|
|
168
151
|
exitTimer.unref?.();
|
|
169
152
|
}
|
|
170
153
|
|
|
171
|
-
function buildOversizeResponseFallback(response, error) {
|
|
172
|
-
if (response?.event) {
|
|
173
|
-
const event = response.event || {};
|
|
174
|
-
return {
|
|
175
|
-
id: response.id,
|
|
176
|
-
ok: true,
|
|
177
|
-
event: {
|
|
178
|
-
type: event.type || 'native.event.truncated',
|
|
179
|
-
title: truncateForNativeFrame(event.title || 'Native event was truncated', 500),
|
|
180
|
-
status: event.status || 'warning',
|
|
181
|
-
detail: {
|
|
182
|
-
code: 'native_event_truncated',
|
|
183
|
-
reason: truncateForNativeFrame(error?.message || 'Native event exceeded the browser frame limit.', 800),
|
|
184
|
-
originalType: truncateForNativeFrame(event.type || '', 160),
|
|
185
|
-
originalTitle: truncateForNativeFrame(event.title || '', 500),
|
|
186
|
-
originalDetailBytes: stringByteLength(safeJsonStringify(event.detail))
|
|
187
|
-
},
|
|
188
|
-
timestamp: event.timestamp || new Date().toISOString()
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
id: response?.id,
|
|
195
|
-
ok: false,
|
|
196
|
-
error: {
|
|
197
|
-
code: 'native_response_too_large',
|
|
198
|
-
message: truncateForNativeFrame(error?.message || 'Native response exceeded the browser frame limit.', 800),
|
|
199
|
-
originalOk: response?.ok === true
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function truncateForNativeFrame(value, limit) {
|
|
205
|
-
const text = String(value || '');
|
|
206
|
-
if (text.length <= limit) {
|
|
207
|
-
return text;
|
|
208
|
-
}
|
|
209
|
-
return `${text.slice(0, Math.max(0, limit - 24))}... [truncated]`;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function safeJsonStringify(value) {
|
|
213
|
-
try {
|
|
214
|
-
return JSON.stringify(value);
|
|
215
|
-
} catch (_error) {
|
|
216
|
-
return '';
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
154
|
function summarizeRequest(message) {
|
|
221
155
|
const params = message?.params || {};
|
|
222
156
|
return {
|
|
@@ -54,14 +54,19 @@ function installManagedDistribution(options = {}) {
|
|
|
54
54
|
const unique = process.pid + '-' + Date.now() + '-' + Math.random().toString(16).slice(2);
|
|
55
55
|
const extensionStage = path.join(parent, '.extension.staging-' + unique);
|
|
56
56
|
const extensionRollback = path.join(parent, '.extension.rollback-' + unique);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
try {
|
|
58
|
+
buildManagedExtensionTree({
|
|
59
|
+
packageRoot,
|
|
60
|
+
targetRoot: extensionStage,
|
|
61
|
+
version,
|
|
62
|
+
releaseRef,
|
|
63
|
+
releaseChannel,
|
|
64
|
+
allowedFiles: options.allowedFiles
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
safeRemove(extensionStage);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
65
70
|
|
|
66
71
|
const previousExtensionMarker = readManagedMarker(extensionRoot, EXTENSION_MARKER);
|
|
67
72
|
if (fs.existsSync(extensionRoot) && !isManagedMarker(previousExtensionMarker, 'extension')) {
|
|
@@ -145,6 +150,7 @@ function buildManagedExtensionTree(options = {}) {
|
|
|
145
150
|
copyPackageTree(packageRoot, 'extension/vendor', targetRoot, 'runtime/vendor', { allowedFiles });
|
|
146
151
|
copyPackageFile(packageRoot, 'extension/runtime-manifest.json', targetRoot, 'runtime/runtime-manifest.json', allowedFiles);
|
|
147
152
|
rewriteManagedRuntimeAssetPaths(targetRoot);
|
|
153
|
+
assertManagedRuntimeManifestFiles(targetRoot);
|
|
148
154
|
fs.writeFileSync(path.join(targetRoot, EXTENSION_MARKER), JSON.stringify({
|
|
149
155
|
managedBy: MANAGED_BY,
|
|
150
156
|
kind: 'extension',
|
|
@@ -413,6 +419,41 @@ function resolveForPlatform(value, platformPath) {
|
|
|
413
419
|
return platformPath.isAbsolute(value) ? value : platformPath.resolve(value);
|
|
414
420
|
}
|
|
415
421
|
|
|
422
|
+
function assertManagedRuntimeManifestFiles(targetRoot) {
|
|
423
|
+
const runtimeRoot = path.join(targetRoot, 'runtime');
|
|
424
|
+
const runtimeManifest = readJson(path.join(runtimeRoot, 'runtime-manifest.json'));
|
|
425
|
+
const runtimeFiles = [
|
|
426
|
+
...(Array.isArray(runtimeManifest.js) ? runtimeManifest.js : []),
|
|
427
|
+
...(Array.isArray(runtimeManifest.css) ? runtimeManifest.css : [])
|
|
428
|
+
];
|
|
429
|
+
const missing = [];
|
|
430
|
+
|
|
431
|
+
for (const relativePath of runtimeFiles) {
|
|
432
|
+
if (
|
|
433
|
+
typeof relativePath !== 'string'
|
|
434
|
+
|| relativePath.length === 0
|
|
435
|
+
|| path.isAbsolute(relativePath)
|
|
436
|
+
|| relativePath.split(/[\\/]+/).includes('..')
|
|
437
|
+
) {
|
|
438
|
+
throw new Error('Managed runtime manifest contains an unsafe path: ' + String(relativePath));
|
|
439
|
+
}
|
|
440
|
+
const filePath = path.resolve(runtimeRoot, relativePath);
|
|
441
|
+
const relativeToRuntime = path.relative(runtimeRoot, filePath);
|
|
442
|
+
if (
|
|
443
|
+
relativeToRuntime.startsWith('..')
|
|
444
|
+
|| path.isAbsolute(relativeToRuntime)
|
|
445
|
+
|| !fs.existsSync(filePath)
|
|
446
|
+
|| !fs.statSync(filePath).isFile()
|
|
447
|
+
) {
|
|
448
|
+
missing.push(relativePath);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (missing.length > 0) {
|
|
453
|
+
throw new Error('Managed runtime is incomplete; missing file(s): ' + missing.join(', '));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
416
457
|
function safeRemove(target) {
|
|
417
458
|
fs.rmSync(target, { recursive: true, force: true });
|
|
418
459
|
}
|
|
@@ -425,6 +466,7 @@ module.exports = {
|
|
|
425
466
|
EXTENSION_MARKER,
|
|
426
467
|
MANAGED_BY,
|
|
427
468
|
NATIVE_MARKER,
|
|
469
|
+
assertManagedRuntimeManifestFiles,
|
|
428
470
|
assertSafeManagedRoot,
|
|
429
471
|
buildManagedExtensionTree,
|
|
430
472
|
installManagedDistribution,
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
MAX_NATIVE_OUTPUT_MESSAGE_BYTES,
|
|
5
|
+
decodeFrames,
|
|
6
|
+
encodeMessage
|
|
7
|
+
} = require('./nativeMessaging');
|
|
8
|
+
const nativeQuotas = require('./nativeQuotas');
|
|
9
|
+
const {
|
|
10
|
+
enforceNativeOkResponseBudget,
|
|
11
|
+
truncateUtf8Text
|
|
12
|
+
} = require('./nativeResponseBudget');
|
|
13
|
+
|
|
14
|
+
function decodeInputFrames(buffer) {
|
|
15
|
+
return decodeFrames(buffer);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function encodeOutputFrame(response) {
|
|
19
|
+
try {
|
|
20
|
+
return encodeMessage(response);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
const fallback = buildOversizeResponseFallback(response, error);
|
|
23
|
+
try {
|
|
24
|
+
return encodeMessage(fallback);
|
|
25
|
+
} catch (fallbackError) {
|
|
26
|
+
return encodeMessage(buildFinalOversizeError(response, fallbackError, error));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getRequestQuotaViolation(request) {
|
|
32
|
+
if (typeof request?.id === "string" && request.id.length > 160) {
|
|
33
|
+
return {
|
|
34
|
+
field: "id",
|
|
35
|
+
limit: 160,
|
|
36
|
+
actual: request.id.length,
|
|
37
|
+
reason: "request id is too large"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return nativeQuotas.validateNativeRequestQuotas(request);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function reduceTaskResult(result) {
|
|
44
|
+
return enforceNativeOkResponseBudget(result);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildOversizeResponseFallback(response, error) {
|
|
48
|
+
if (response?.event) {
|
|
49
|
+
const event = response.event || {};
|
|
50
|
+
return {
|
|
51
|
+
id: boundedResponseId(response.id),
|
|
52
|
+
ok: true,
|
|
53
|
+
event: {
|
|
54
|
+
type: event.type || 'native.event.truncated',
|
|
55
|
+
title: truncateForNativeFrame(event.title || 'Native event was truncated', 500),
|
|
56
|
+
status: event.status || 'warning',
|
|
57
|
+
detail: {
|
|
58
|
+
code: 'native_event_truncated',
|
|
59
|
+
reason: truncateForNativeFrame(
|
|
60
|
+
error?.message || 'Native event exceeded the browser frame limit.',
|
|
61
|
+
800
|
|
62
|
+
),
|
|
63
|
+
originalType: truncateForNativeFrame(event.type || '', 160),
|
|
64
|
+
originalTitle: truncateForNativeFrame(event.title || '', 500),
|
|
65
|
+
originalDetailBytes: measureJsonBytes(event.detail)
|
|
66
|
+
},
|
|
67
|
+
timestamp: event.timestamp || new Date().toISOString()
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
id: boundedResponseId(response?.id),
|
|
74
|
+
ok: false,
|
|
75
|
+
error: {
|
|
76
|
+
code: 'native_response_too_large',
|
|
77
|
+
message: truncateForNativeFrame(
|
|
78
|
+
error?.message || 'Native response exceeded the browser frame limit.',
|
|
79
|
+
800
|
|
80
|
+
),
|
|
81
|
+
originalOk: response?.ok === true
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildFinalOversizeError(response, fallbackError, originalError) {
|
|
87
|
+
return {
|
|
88
|
+
id: boundedResponseId(response?.id),
|
|
89
|
+
ok: false,
|
|
90
|
+
error: {
|
|
91
|
+
code: 'native_response_too_large',
|
|
92
|
+
message: truncateForNativeFrame(
|
|
93
|
+
fallbackError?.message
|
|
94
|
+
|| originalError?.message
|
|
95
|
+
|| 'Native response exceeded the browser frame limit.',
|
|
96
|
+
800
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function boundedResponseId(value) {
|
|
103
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
return truncateForNativeFrame(value, 160);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function measureJsonBytes(value) {
|
|
110
|
+
try {
|
|
111
|
+
return Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
112
|
+
} catch (_error) {
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function truncateForNativeFrame(value, maxChars) {
|
|
118
|
+
const text = String(value || '');
|
|
119
|
+
if (text.length <= maxChars) {
|
|
120
|
+
return text;
|
|
121
|
+
}
|
|
122
|
+
return `${text.slice(0, Math.max(0, maxChars - 24))}... [truncated]`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
MAX_NATIVE_OUTPUT_MESSAGE_BYTES,
|
|
127
|
+
NATIVE_REQUEST_QUOTAS: nativeQuotas.NATIVE_REQUEST_QUOTAS,
|
|
128
|
+
buildOversizeResponseFallback,
|
|
129
|
+
decodeInputFrames,
|
|
130
|
+
encodeOutputFrame,
|
|
131
|
+
estimateBase64DecodedBytes: nativeQuotas.estimateBase64DecodedBytes,
|
|
132
|
+
firstQuotaViolation: nativeQuotas.firstQuotaViolation,
|
|
133
|
+
getRequestQuotaViolation,
|
|
134
|
+
measureJsonBytes,
|
|
135
|
+
measureOperationPayloads: nativeQuotas.measureOperationPayloads,
|
|
136
|
+
quotaViolation: nativeQuotas.quotaViolation,
|
|
137
|
+
reduceTaskResult,
|
|
138
|
+
truncateForNativeFrame,
|
|
139
|
+
truncateUtf8Text,
|
|
140
|
+
validateAttachmentQuota: nativeQuotas.validateAttachmentQuota,
|
|
141
|
+
validateCompileLogQuota: nativeQuotas.validateCompileLogQuota,
|
|
142
|
+
validateFilePayloadQuota: nativeQuotas.validateFilePayloadQuota,
|
|
143
|
+
validateOperationListQuota: nativeQuotas.validateOperationListQuota,
|
|
144
|
+
validateOperationPayloadQuota: nativeQuotas.validateOperationPayloadQuota,
|
|
145
|
+
validatePatchFileTextQuota: nativeQuotas.validatePatchFileTextQuota,
|
|
146
|
+
validateProjectSnapshotQuota: nativeQuotas.validateProjectSnapshotQuota,
|
|
147
|
+
validateSkillContentQuota: nativeQuotas.validateSkillContentQuota
|
|
148
|
+
};
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { buildCodexRuntimeEvent } = require('./codexRuntimeIdentity');
|
|
4
4
|
|
|
5
5
|
const { spawn } = require('node:child_process');
|
|
6
|
-
const crypto = require('node:crypto');
|
|
7
6
|
const { buildOperationSummary, splitDeletePlan } = require('../../extension/src/shared/summary');
|
|
8
7
|
const {
|
|
9
8
|
MIN_COMPATIBLE_EXTENSION_VERSION,
|
|
@@ -26,10 +25,10 @@ const { getNativeRuntimePlatform, summarizeNativeEnvironment } = require('./nati
|
|
|
26
25
|
const {
|
|
27
26
|
NATIVE_REQUEST_QUOTAS,
|
|
28
27
|
firstQuotaViolation,
|
|
29
|
-
|
|
28
|
+
getRequestQuotaViolation,
|
|
30
29
|
validateOperationListQuota,
|
|
31
30
|
validateOperationPayloadQuota
|
|
32
|
-
} = require('./
|
|
31
|
+
} = require('./nativeTransportEnvelope');
|
|
33
32
|
const { version: PACKAGE_VERSION } = require('../../package.json');
|
|
34
33
|
|
|
35
34
|
const activeProjectLocks = new Map();
|
|
@@ -40,8 +39,6 @@ const activeRunEntries = new Map();
|
|
|
40
39
|
// Overleaf tab was reloaded — the requestId lived in content-side JS state
|
|
41
40
|
// and is gone, but the native-host-side controller is still running).
|
|
42
41
|
const activeRunByProject = new Map();
|
|
43
|
-
const pendingPlans = new Map();
|
|
44
|
-
const PENDING_PLAN_TTL_MS = 30 * 60 * 1000;
|
|
45
42
|
const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed', 'codex_no_usable_result']);
|
|
46
43
|
|
|
47
44
|
async function handleRequest(request, env = process.env, emit = () => {}) {
|
|
@@ -49,7 +46,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
|
|
|
49
46
|
return errorResponse(undefined, 'invalid_request', 'Request must be an object');
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
const quotaError =
|
|
49
|
+
const quotaError = getRequestQuotaViolation(request);
|
|
53
50
|
if (quotaError) {
|
|
54
51
|
return quotaErrorResponse(request.id, quotaError);
|
|
55
52
|
}
|
|
@@ -136,7 +133,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
|
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
if (request.method === 'task.confirm') {
|
|
139
|
-
return
|
|
136
|
+
return errorResponse(request.id, 'suggest_mode_removed', 'Suggest mode has been removed. Reload the extension and choose Ask or Auto.');
|
|
140
137
|
}
|
|
141
138
|
|
|
142
139
|
return errorResponse(request.id, 'method_not_found', `Unknown method: ${request.method}`);
|
|
@@ -716,8 +713,11 @@ async function handleTaskRun(request, env, emit) {
|
|
|
716
713
|
const params = request.params || {};
|
|
717
714
|
const mode = params.mode;
|
|
718
715
|
|
|
719
|
-
if (
|
|
720
|
-
return errorResponse(request.id, '
|
|
716
|
+
if (mode === 'confirm') {
|
|
717
|
+
return errorResponse(request.id, 'suggest_mode_removed', 'Suggest mode has been removed. Choose Ask or Auto.');
|
|
718
|
+
}
|
|
719
|
+
if (!['ask', 'auto'].includes(mode)) {
|
|
720
|
+
return errorResponse(request.id, 'invalid_mode', 'Mode must be "ask" or "auto"');
|
|
721
721
|
}
|
|
722
722
|
|
|
723
723
|
if (mode === 'auto' && !params.checkpoint?.ok && !isVerifiedReviewing(params.reviewing)) {
|
|
@@ -805,24 +805,6 @@ function isVerifiedReviewing(reviewing) {
|
|
|
805
805
|
return reviewing?.ok === true && reviewing.status !== 'manual-override';
|
|
806
806
|
}
|
|
807
807
|
|
|
808
|
-
async function handleTaskConfirm(request) {
|
|
809
|
-
const planId = request.params?.planId;
|
|
810
|
-
purgeExpiredPendingPlans();
|
|
811
|
-
if (!planId || !pendingPlans.has(planId)) {
|
|
812
|
-
return errorResponse(request.id, 'plan_not_found', 'No pending task plan matched the supplied planId');
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
const plan = pendingPlans.get(planId);
|
|
816
|
-
pendingPlans.delete(planId);
|
|
817
|
-
|
|
818
|
-
return okResponse(request.id, {
|
|
819
|
-
status: 'confirmed',
|
|
820
|
-
notes: plan.notes || '',
|
|
821
|
-
userReport: plan.userReport,
|
|
822
|
-
operations: plan.operations
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
|
|
826
808
|
function buildDefaultTaskResult(mode, operations, params = {}) {
|
|
827
809
|
if (mode === 'ask') {
|
|
828
810
|
return {
|
|
@@ -836,16 +818,6 @@ function buildDefaultTaskResult(mode, operations, params = {}) {
|
|
|
836
818
|
|
|
837
819
|
const summary = buildOperationSummary(operations);
|
|
838
820
|
|
|
839
|
-
if (mode === 'confirm') {
|
|
840
|
-
return {
|
|
841
|
-
status: 'requires_task_confirmation',
|
|
842
|
-
summary,
|
|
843
|
-
notes: '',
|
|
844
|
-
userReport: buildDefaultUserReport(mode, operations, params),
|
|
845
|
-
operations
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
|
|
849
821
|
const split = splitDeletePlan(operations);
|
|
850
822
|
if (split.needsConfirmation.length > 0) {
|
|
851
823
|
return {
|
|
@@ -889,7 +861,7 @@ function normalizeAgentResult(mode, result, params = {}) {
|
|
|
889
861
|
};
|
|
890
862
|
|
|
891
863
|
if (!normalized.status) {
|
|
892
|
-
normalized.status =
|
|
864
|
+
normalized.status = 'completed';
|
|
893
865
|
}
|
|
894
866
|
|
|
895
867
|
return normalized;
|
|
@@ -911,40 +883,7 @@ function prepareResultForResponse(mode, result) {
|
|
|
911
883
|
}
|
|
912
884
|
}
|
|
913
885
|
|
|
914
|
-
|
|
915
|
-
if (confirmOperations.length > 0) {
|
|
916
|
-
result = {
|
|
917
|
-
...result,
|
|
918
|
-
status: 'requires_task_confirmation',
|
|
919
|
-
summary: buildOperationSummary(confirmOperations),
|
|
920
|
-
operations: confirmOperations
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
if (mode !== 'confirm' || result.status !== 'requires_task_confirmation') {
|
|
925
|
-
return result;
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
purgeExpiredPendingPlans();
|
|
929
|
-
const planId = `plan_${crypto.randomUUID()}`;
|
|
930
|
-
pendingPlans.set(planId, {
|
|
931
|
-
createdAt: Date.now(),
|
|
932
|
-
expiresAt: Date.now() + PENDING_PLAN_TTL_MS,
|
|
933
|
-
notes: result.notes || '',
|
|
934
|
-
userReport: result.userReport,
|
|
935
|
-
operations: Array.isArray(result.operations) ? result.operations : []
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
const {
|
|
939
|
-
operations: _operations,
|
|
940
|
-
pendingOperations: _pendingOperations,
|
|
941
|
-
deletePlan: _deletePlan,
|
|
942
|
-
...redacted
|
|
943
|
-
} = result;
|
|
944
|
-
return {
|
|
945
|
-
...redacted,
|
|
946
|
-
planId
|
|
947
|
-
};
|
|
886
|
+
return result;
|
|
948
887
|
}
|
|
949
888
|
|
|
950
889
|
function validateTaskResultOperationQuotas(result = {}) {
|
|
@@ -965,7 +904,7 @@ function buildDefaultUserReport(mode, operations = [], params = {}) {
|
|
|
965
904
|
|
|
966
905
|
return {
|
|
967
906
|
conclusion: hasOperations
|
|
968
|
-
? reportText(locale, 'Codex prepared
|
|
907
|
+
? reportText(locale, 'Codex prepared changes for writeback.', 'Codex 已准备好写回修改。')
|
|
969
908
|
: reportText(locale, 'This task completed without writing Overleaf files.', '这轮任务已完成,没有写入 Overleaf 文件。'),
|
|
970
909
|
checked,
|
|
971
910
|
findings: [],
|
|
@@ -975,7 +914,7 @@ function buildDefaultUserReport(mode, operations = [], params = {}) {
|
|
|
975
914
|
? ''
|
|
976
915
|
: (mode === 'ask' ? reportText(locale, 'This run was Ask mode.', '这轮是只问不改。') : ''),
|
|
977
916
|
nextStep: hasOperations
|
|
978
|
-
? reportText(locale, 'Review
|
|
917
|
+
? reportText(locale, 'Review the written changes in Overleaf.', '请在 Overleaf 中检查写入的修改。')
|
|
979
918
|
: reportText(locale, 'Continue the conversation, or add more @context and run another check.', '可以继续追问,或加入更多 @context 后再检查。')
|
|
980
919
|
};
|
|
981
920
|
}
|
|
@@ -1193,14 +1132,6 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
|
1193
1132
|
});
|
|
1194
1133
|
}
|
|
1195
1134
|
|
|
1196
|
-
function purgeExpiredPendingPlans(now = Date.now()) {
|
|
1197
|
-
for (const [planId, plan] of pendingPlans.entries()) {
|
|
1198
|
-
if (Number.isFinite(plan?.expiresAt) && plan.expiresAt <= now) {
|
|
1199
|
-
pendingPlans.delete(planId);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
1135
|
function parsePositiveInteger(value, fallback) {
|
|
1205
1136
|
const parsed = Number.parseInt(value, 10);
|
|
1206
1137
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -1295,6 +1226,5 @@ module.exports = {
|
|
|
1295
1226
|
buildDefaultTaskResult,
|
|
1296
1227
|
getActiveNativeWorkState,
|
|
1297
1228
|
handleRequest,
|
|
1298
|
-
parseAgentEventLines
|
|
1299
|
-
purgeExpiredPendingPlans
|
|
1229
|
+
parseAgentEventLines
|
|
1300
1230
|
};
|