codex-overleaf-link 2.5.1 → 2.6.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.
- package/README.md +149 -398
- package/README.zh-CN.md +153 -404
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +78 -3
- package/extension/src/backgroundSharedSessions.js +55 -2
- package/extension/src/backgroundUpdateCoordinator.js +128 -12
- package/extension/src/content/activeTurnControl.js +164 -7
- package/extension/src/content/composerAttachments.js +11 -2
- package/extension/src/content/composerPanel.js +92 -12
- package/extension/src/content/contentRuntime.js +920 -276
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsPanel.js +18 -0
- package/extension/src/content/generated/content.bundle.js +4496 -924
- package/extension/src/content/generated/content.bundle.meta.json +6 -3
- package/extension/src/content/legacyGlobalRegistry.js +4 -0
- package/extension/src/content/localSkillsPanel.js +2 -2
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +42 -5
- package/extension/src/content/modelPicker.js +1 -0
- package/extension/src/content/nativeCompatibilityController.js +29 -10
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/pageBridgeClient.js +2 -2
- package/extension/src/content/panelMaintenance.js +6 -2
- package/extension/src/content/panelRenderer.js +51 -27
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectSettingsCoordinator.js +30 -6
- package/extension/src/content/providerSettingsDialog.js +2 -1
- package/extension/src/content/recentProjects.js +4 -3
- package/extension/src/content/runActivitySummary.js +122 -14
- package/extension/src/content/runCompletionMeta.js +194 -6
- package/extension/src/content/runController.js +2 -1
- package/extension/src/content/runFailureNotice.js +8 -1
- package/extension/src/content/runPresence.js +18 -8
- package/extension/src/content/runQueueScheduler.js +9 -5
- package/extension/src/content/runRecoveryActions.js +2 -1
- package/extension/src/content/runResultActions.js +22 -20
- package/extension/src/content/runReviewController.js +94 -35
- package/extension/src/content/runTimelineView.js +224 -25
- package/extension/src/content/scopedPersistencePanelState.js +92 -23
- package/extension/src/content/sensitivePreflight.js +14 -1
- package/extension/src/content/sessionManager.js +49 -3
- package/extension/src/content/sessionMenuView.js +54 -18
- package/extension/src/content/sessionPersistence.js +25 -5
- package/extension/src/content/settingsPanel.js +15 -2
- package/extension/src/content/settingsWorkbench.js +127 -89
- package/extension/src/content/sharedSessionViewSync.js +4 -3
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateNotice.js +60 -6
- package/extension/src/content/writebackOrchestrator.js +35 -22
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/overleafCapabilities.js +4 -1
- package/extension/src/page/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +9 -1
- package/extension/src/page/writebackRouter.js +24 -77
- package/extension/src/pageBridge.js +107 -66
- package/extension/src/popup.js +1 -1
- package/extension/src/shared/agentTranscript.js +350 -17
- package/extension/src/shared/auditRecords.js +2 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +58 -17
- package/extension/src/shared/composerAttachmentDraft.js +6 -1
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/failureReasons.js +42 -3
- package/extension/src/shared/i18n.js +299 -29
- package/extension/src/shared/lineReferences.js +38 -31
- package/extension/src/shared/pageRpcContract.js +1 -1
- package/extension/src/shared/panelIcons.js +39 -0
- package/extension/src/shared/reviewing.js +130 -1
- package/extension/src/shared/runActivityModel.js +70 -4
- package/extension/src/shared/runExecutionSnapshot.js +13 -0
- package/extension/src/shared/sessionState.js +63 -4
- package/extension/src/shared/sessionTitle.js +14 -1
- package/extension/src/shared/sharedSessionBridge.js +81 -13
- package/extension/src/shared/staleGuard.js +72 -63
- package/extension/src/shared/storageDb.js +35 -3
- package/extension/src/shared/storageMigration.js +7 -1
- package/extension/src/shared/storageRunActions.js +62 -8
- package/extension/src/shared/undoOperations.js +126 -28
- package/extension/src/shared/updateRuntimeIdentity.js +16 -2
- package/extension/src/shared/writebackSettlement.js +21 -3
- package/extension/styles/panel.css +1106 -490
- package/native-host/src/bootstrapUpdate.js +52 -16
- package/native-host/src/chatBridgeRequest.js +60 -6
- package/native-host/src/chatBridgeResponse.js +10 -1
- package/native-host/src/chatCompletionsBridge.js +29 -2
- package/native-host/src/codexPromptAssembly.js +86 -118
- package/native-host/src/codexSessionRunner.js +126 -17
- package/native-host/src/providerContextError.js +20 -0
- package/native-host/src/taskRunnerRuntime.js +18 -6
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/updateManager.js +120 -15
- package/package.json +5 -8
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
(function initContextUsage(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
3
|
+
else root.CodexOverleafModuleRegistry.define('ContextUsage', [], factory);
|
|
4
|
+
})(typeof globalThis !== 'undefined' ? globalThis : window, function contextUsageFactory() {
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const count = value => Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
8
|
+
const identity = value => typeof value === 'string' && value.trim() && value.length <= 512
|
|
9
|
+
&& !/[\0\r\n]/.test(value) ? value.trim() : '';
|
|
10
|
+
const timestamp = value => typeof value === 'string' && Number.isFinite(Date.parse(value))
|
|
11
|
+
? new Date(value).toISOString() : '';
|
|
12
|
+
|
|
13
|
+
// Store a bounded fact from one model response, never the cumulative billed
|
|
14
|
+
// token total. The last reported response is an estimate of context occupancy;
|
|
15
|
+
// tool output added since then is not included until the next server update.
|
|
16
|
+
function normalize(value) {
|
|
17
|
+
if (!value || value.v !== 1) return null;
|
|
18
|
+
const threadId = identity(value.threadId), turnId = identity(value.turnId);
|
|
19
|
+
const runId = identity(value.runId), providerId = identity(value.providerId), model = identity(value.model);
|
|
20
|
+
const usedTokens = count(value.usedTokens), providerRevision = count(value.providerRevision);
|
|
21
|
+
const observedAt = timestamp(value.observedAt);
|
|
22
|
+
if (!threadId || !turnId || !runId || !providerId || !model || usedTokens === null
|
|
23
|
+
|| providerRevision === null || !observedAt) return null;
|
|
24
|
+
const capacity = count(value.contextWindow);
|
|
25
|
+
const eventSeq = count(value.eventSeq);
|
|
26
|
+
const inputTokens = count(value.inputTokens), outputTokens = count(value.outputTokens);
|
|
27
|
+
const cachedInput = count(value.cachedInputTokens);
|
|
28
|
+
// Cache reads are a subset of input, never an additional category. Older
|
|
29
|
+
// snapshots and providers without these counters retain unknown values.
|
|
30
|
+
const cachedInputTokens = inputTokens !== null && cachedInput !== null && cachedInput <= inputTokens
|
|
31
|
+
? cachedInput : null;
|
|
32
|
+
return { v: 1, threadId, turnId, runId, providerId, providerRevision, model,
|
|
33
|
+
usedTokens, contextWindow: capacity > 0 ? capacity : null,
|
|
34
|
+
inputTokens, outputTokens, cachedInputTokens,
|
|
35
|
+
eventSeq: eventSeq > 0 ? eventSeq : null, observedAt };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function matches(value, scope = {}) {
|
|
39
|
+
const snapshot = normalize(value);
|
|
40
|
+
return Boolean(snapshot && snapshot.threadId === identity(scope.threadId || scope.codexThreadId)
|
|
41
|
+
&& snapshot.providerId === identity(scope.providerId)
|
|
42
|
+
&& snapshot.providerRevision === count(scope.providerRevision)
|
|
43
|
+
&& snapshot.model === identity(scope.model));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The caller supplies the captured run owner, after its Native thread/turn
|
|
47
|
+
// binding is known. Reject worker, previous-turn and unbound notifications.
|
|
48
|
+
function fromEvent(event, owner = {}) {
|
|
49
|
+
if (!event || (event.type && event.type !== 'codex.session.event') || event.subagent === true) return null;
|
|
50
|
+
const notification = event.detail || event;
|
|
51
|
+
if (notification.method !== 'thread/tokenUsage/updated') return null;
|
|
52
|
+
const params = notification.params || {};
|
|
53
|
+
const threadId = identity(params.threadId), turnId = identity(params.turnId);
|
|
54
|
+
if (!threadId || !turnId || threadId !== identity(owner.threadId || owner.codexThreadId)
|
|
55
|
+
|| turnId !== identity(owner.turnId || owner.codexTurnId) || owner.forkSnapshot === true) return null;
|
|
56
|
+
const last = params.tokenUsage?.last;
|
|
57
|
+
const usedTokens = count(last?.totalTokens);
|
|
58
|
+
if (usedTokens === null) return null;
|
|
59
|
+
return normalize({ v: 1, threadId, turnId, runId: owner.runId || owner.id,
|
|
60
|
+
providerId: owner.providerId, providerRevision: owner.providerRevision, model: owner.model,
|
|
61
|
+
usedTokens, contextWindow: params.tokenUsage?.modelContextWindow,
|
|
62
|
+
inputTokens: last?.inputTokens, outputTokens: last?.outputTokens, cachedInputTokens: last?.cachedInputTokens,
|
|
63
|
+
eventSeq: event.nativeEventSeq ?? event.eventSeq ?? event.seq ?? owner.eventSeq,
|
|
64
|
+
observedAt: timestamp(event.timestamp || event.at) || timestamp(owner.observedAt) || new Date().toISOString() });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isNewer(next, previous) {
|
|
68
|
+
next = normalize(next); previous = normalize(previous);
|
|
69
|
+
if (!next) return false;
|
|
70
|
+
if (!previous) return true;
|
|
71
|
+
if (next.runId === previous.runId && next.threadId === previous.threadId && next.turnId === previous.turnId
|
|
72
|
+
&& next.eventSeq !== null && previous.eventSeq !== null)
|
|
73
|
+
return next.eventSeq > previous.eventSeq;
|
|
74
|
+
return Date.parse(next.observedAt) > Date.parse(previous.observedAt);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// `scope.compactedAt` (epoch ms) carries an automatic compaction completed
|
|
78
|
+
// inside a normal turn; manual compact runs are read from the session.
|
|
79
|
+
// `pending` means the old sample is invalid; `compacted` additionally requires
|
|
80
|
+
// successful completion of the most recent compaction that invalidated it.
|
|
81
|
+
function status(session, scope = {}) {
|
|
82
|
+
if (!session || session.forkSnapshot === true) return { usage: null, pending: false, compacted: false };
|
|
83
|
+
const inThread = value => value && value.threadId === identity(scope.threadId || scope.codexThreadId)
|
|
84
|
+
&& value.providerId === identity(scope.providerId)
|
|
85
|
+
&& value.providerRevision === count(scope.providerRevision);
|
|
86
|
+
// A dispatched compaction can change this thread even when its completion
|
|
87
|
+
// is lost on reload. Do not re-display a pre-compaction sample as current.
|
|
88
|
+
const runs = Array.isArray(session.runs) ? session.runs : [];
|
|
89
|
+
let invalidatedAfter = Number.isFinite(scope.compactedAt) && scope.compactedAt > 0 ? scope.compactedAt : 0;
|
|
90
|
+
let completedAfter = invalidatedAfter;
|
|
91
|
+
for (const run of runs) {
|
|
92
|
+
if (run?.operation !== 'compact' || run.forkSnapshot
|
|
93
|
+
|| run.codexThreadId !== identity(scope.threadId || scope.codexThreadId)) continue;
|
|
94
|
+
const startedAt = Date.parse(run.startedAt || '') || 0;
|
|
95
|
+
invalidatedAfter = Math.max(invalidatedAfter, startedAt);
|
|
96
|
+
if (run.status === 'completed') completedAfter = Math.max(completedAfter, startedAt);
|
|
97
|
+
}
|
|
98
|
+
let selected = normalize(session.contextUsage);
|
|
99
|
+
if (!inThread(selected)) selected = null;
|
|
100
|
+
for (const run of runs) {
|
|
101
|
+
if (run?.forkSnapshot === true) continue;
|
|
102
|
+
const candidate = normalize(run.contextUsage);
|
|
103
|
+
if (!inThread(candidate) || candidate.runId !== run.id) continue;
|
|
104
|
+
if (isNewer(candidate, selected)) selected = candidate;
|
|
105
|
+
}
|
|
106
|
+
const beforeCompaction = !selected || Date.parse(selected.observedAt) < invalidatedAfter;
|
|
107
|
+
// A later turn can grow this thread using another model. Selecting an older
|
|
108
|
+
// model again must not resurrect its obsolete, smaller context estimate.
|
|
109
|
+
const usage = selected?.model === identity(scope.model) && !beforeCompaction ? selected : null;
|
|
110
|
+
const pending = !usage && invalidatedAfter > 0 && beforeCompaction;
|
|
111
|
+
return { usage, pending, compacted: pending && completedAfter >= invalidatedAfter };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function select(session, scope = {}) {
|
|
115
|
+
return status(session, scope).usage;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Presentation level of a reported share of the window, in percent.
|
|
119
|
+
function level(percent) {
|
|
120
|
+
if (typeof percent !== 'number' || !Number.isFinite(percent)) return '';
|
|
121
|
+
return percent >= 90 ? 'near-limit' : percent >= 70 ? 'high' : 'normal';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { normalize, fromEvent, matches, isNewer, select, status, level };
|
|
125
|
+
});
|
|
@@ -106,6 +106,11 @@
|
|
|
106
106
|
},
|
|
107
107
|
|
|
108
108
|
// 9.2 Preflight
|
|
109
|
+
sensitive_content_blocked: {
|
|
110
|
+
stage: 'preflight', severity: 'blocked', defaultRetryable: false,
|
|
111
|
+
fallbackUserMessage: 'Sensitive content was found before sending, so Codex did not receive this request.',
|
|
112
|
+
fallbackNextAction: 'Remove the sensitive content, narrow @context, or enable explicit sensitive confirmation in Project Settings.'
|
|
113
|
+
},
|
|
109
114
|
governance_blocked: {
|
|
110
115
|
stage: 'preflight', severity: 'blocked', defaultRetryable: false,
|
|
111
116
|
fallbackUserMessage: 'Project file rules blocked this write. Codex did not write the affected path.',
|
|
@@ -232,6 +237,11 @@
|
|
|
232
237
|
},
|
|
233
238
|
|
|
234
239
|
// 9.4 Reviewing
|
|
240
|
+
reviewing_unavailable: {
|
|
241
|
+
stage: 'reviewing', severity: 'blocked', defaultRetryable: false,
|
|
242
|
+
fallbackUserMessage: "Track Changes is unavailable for this project. It depends on the project owner's Overleaf plan.",
|
|
243
|
+
fallbackNextAction: 'Turn off Track in the composer to write without tracked changes. Codex still records each change so you can undo it.'
|
|
244
|
+
},
|
|
235
245
|
reviewing_state_unknown: {
|
|
236
246
|
stage: 'reviewing', severity: 'blocked', defaultRetryable: true,
|
|
237
247
|
fallbackUserMessage: 'Codex could not determine whether Reviewing/Track Changes is enabled.',
|
|
@@ -363,6 +373,11 @@
|
|
|
363
373
|
fallbackUserMessage: 'Local Codex skills could not be loaded. This run did not start.',
|
|
364
374
|
fallbackNextAction: 'Open Skills to reload the local catalog, then submit again.'
|
|
365
375
|
},
|
|
376
|
+
codex_context_limit: {
|
|
377
|
+
stage: 'codex', severity: 'error', defaultRetryable: true,
|
|
378
|
+
fallbackUserMessage: 'This run stopped because the model context limit was exceeded.',
|
|
379
|
+
fallbackNextAction: 'Retry the task. If the context limit is reached again, use /compact or start a new conversation with less context.'
|
|
380
|
+
},
|
|
366
381
|
codex_no_usable_result: {
|
|
367
382
|
stage: 'codex', severity: 'error', defaultRetryable: true,
|
|
368
383
|
fallbackUserMessage: 'Local Codex returned no usable final report or operations.',
|
|
@@ -616,6 +631,7 @@
|
|
|
616
631
|
nextAction: catalog.fallbackNextAction,
|
|
617
632
|
evidence: {}
|
|
618
633
|
};
|
|
634
|
+
if (canonicalCode === 'reviewing_unavailable') failure.changedDocument = false;
|
|
619
635
|
if (legacyReason) {
|
|
620
636
|
failure.technicalMessage = legacyReason;
|
|
621
637
|
}
|
|
@@ -791,8 +807,20 @@
|
|
|
791
807
|
return !path.startsWith('/') && !/^[A-Za-z]:/.test(path) && !path.split('/').includes('..') ? path : '';
|
|
792
808
|
}
|
|
793
809
|
|
|
810
|
+
function isContextLimitError(message, code) {
|
|
811
|
+
const text = String(message || '');
|
|
812
|
+
return ['codex_context_limit', 'context_length_exceeded', 'context_window_exceeded'].includes(code)
|
|
813
|
+
|| /\b(?:codex_context_limit|context_length_exceeded|context_window_exceeded)\b/i.test(text)
|
|
814
|
+
|| /Codex ran out of room in the model['’]s context window/i.test(text)
|
|
815
|
+
|| /maximum context length is \d+ tokens[\s\S]*?(?:you requested|resulted in) \d+ tokens/i.test(text);
|
|
816
|
+
}
|
|
817
|
+
|
|
794
818
|
function recoverDisplayFailure(failure, allowFilePrefix = true) {
|
|
795
819
|
if (!failure || typeof failure !== 'object') return failure;
|
|
820
|
+
if (failure.code === 'codex_no_usable_result' && failure.stage === 'codex'
|
|
821
|
+
&& isContextLimitError(failure.technicalMessage)) return { ...failure, code: 'codex_context_limit',
|
|
822
|
+
userMessage: FAILURE_CODE_CATALOG.codex_context_limit.fallbackUserMessage,
|
|
823
|
+
nextAction: FAILURE_CODE_CATALOG.codex_context_limit.fallbackNextAction };
|
|
796
824
|
const originalCode = failure.evidence?.originalCode;
|
|
797
825
|
const displayCodeAllowed = code => REVIEW_DISPLAY_CODES.has(code)
|
|
798
826
|
&& (code !== 'native_untracked_undo_pending_changes'
|
|
@@ -829,7 +857,8 @@
|
|
|
829
857
|
function formatFailureReasonForDisplay(failure, locale, i18nLookup) {
|
|
830
858
|
const display = recoverDisplayFailure(failure);
|
|
831
859
|
const lookup = typeof i18nLookup === 'function' ? i18nLookup : () => undefined;
|
|
832
|
-
const
|
|
860
|
+
const messageCode = getFailureMessageCode(display);
|
|
861
|
+
const key = messageCode ? 'failureReason_' + messageCode + '_user' : '';
|
|
833
862
|
const template = key ? lookup(key) : '';
|
|
834
863
|
const file = safeDisplayProjectPath(display?.file);
|
|
835
864
|
const localized = localizeFailureReason(display ? { ...display, file } : display, locale, lookup);
|
|
@@ -838,6 +867,15 @@
|
|
|
838
867
|
return { ...localized, code: display?.code, message, file, localized: Boolean(template && template !== key) };
|
|
839
868
|
}
|
|
840
869
|
|
|
870
|
+
function getFailureMessageCode(failure) {
|
|
871
|
+
// Reload recovery retains its historical retry code. Only its presentation
|
|
872
|
+
// identifies the page interruption instead of a failed bridge request.
|
|
873
|
+
if (failure?.code === 'native_request_failed' && failure.source === 'panel_reload') {
|
|
874
|
+
return 'panel_reload_interrupted';
|
|
875
|
+
}
|
|
876
|
+
return typeof failure?.code === 'string' ? failure.code : '';
|
|
877
|
+
}
|
|
878
|
+
|
|
841
879
|
function localizeFailureMessage(message, locale, i18nLookup) {
|
|
842
880
|
return formatFailureReasonForDisplay({ code: 'unknown_legacy_failure', userMessage: message }, locale, i18nLookup).message;
|
|
843
881
|
}
|
|
@@ -949,7 +987,7 @@
|
|
|
949
987
|
const value = typeof i18nLookup === 'function' ? i18nLookup(key) : undefined;
|
|
950
988
|
return value && value !== key ? value : undefined;
|
|
951
989
|
};
|
|
952
|
-
const code = failure
|
|
990
|
+
const code = getFailureMessageCode(failure);
|
|
953
991
|
const userTemplate = code ? lookup('failureReason_' + code + '_user') : undefined;
|
|
954
992
|
const nextTemplate = (code ? lookup('failureReason_' + code + '_next') : undefined)
|
|
955
993
|
|| (REVIEW_DISPLAY_CODES.has(code) ? lookup('failureReason_review_detail_next') : undefined);
|
|
@@ -989,6 +1027,7 @@
|
|
|
989
1027
|
selectPrimaryFailure,
|
|
990
1028
|
localizeFailureReason,
|
|
991
1029
|
formatFailureReasonForDisplay,
|
|
992
|
-
localizeFailureMessage
|
|
1030
|
+
localizeFailureMessage,
|
|
1031
|
+
isContextLimitError
|
|
993
1032
|
};
|
|
994
1033
|
});
|